diff --git a/bun.lock b/bun.lock index 7a0c19c..84062a7 100644 --- a/bun.lock +++ b/bun.lock @@ -8,8 +8,8 @@ }, "devDependencies": { "@types/bun": "^1.4.2", - "vitepress": "^2.0.0-alpha.19", - "vue": "^3.6.0-rc.2", + "vitepress": "^2.0.0-alpha.20", + "vue": "^3.6.0-rc.8", }, }, }, @@ -18,9 +18,9 @@ "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - "@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + "@babel/parser": ["@babel/parser@7.29.8", "", { "dependencies": { "@babel/types": "^7.29.8" }, "bin": "./bin/babel-parser.js" }, "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA=="], - "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@babel/types": ["@babel/types@7.29.8", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg=="], "@docsearch/css": ["@docsearch/css@4.7.0", "", {}, "sha512-Sk5xkdRFeE7PeWjG9l4AfTwdvMfr9wHiwNNCpHXT4v4SNyNMKdHGvEILc31BgaVFGDDNbv5u/a73tofRiwbEZw=="], @@ -28,41 +28,43 @@ "@docsearch/sidepanel-js": ["@docsearch/sidepanel-js@4.7.0", "", {}, "sha512-A8r34jCU8kcIk2viECEn2msA28ojUF1BLi/3v5OWWc5G2N3jOuuumBXoeYjfr8dA0UxgFSy5R2bt12dnFJQSyA=="], - "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.93", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-/XhANjfGYOuqvSR3TmUnkQkINvQ4GVjVuukvymRbxtVFBvIq/yiXJqCDycKcQPT401OYT9H2vIY6ihAlz1QIAw=="], + "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.96", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-/FmCYo/H5rIywTLV/P6OYsr0rg/31vuc8jYq0QnkOZl6m7PWIs+h5oAnSTT2cqRhOsMaKqbXcdkka6tnZ1WiUA=="], "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="], - "@oxc-project/types": ["@oxc-project/types@0.143.0", "", {}, "sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA=="], + "@oxc-project/types": ["@oxc-project/types@0.150.0", "", {}, "sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw=="], - "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.3", "", { "os": "android", "cpu": "arm64" }, "sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw=="], + "@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.9", "", { "os": "android", "cpu": "arm" }, "sha512-tNISae1QEf/vkb3xkRcjV5SEdzPE97We5IVaa2Z8jSszQPZ8U60B/YCYpw4QI7VidYsBtKavczXf+DyDs9WGxw=="], - "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.9", "", { "os": "android", "cpu": "arm64" }, "sha512-YC8YsI30o606GTZi0VyzYlsDKFP8W61i/QzayHDkLbNEz/IShqAmTa+hsJRj13xTHA0H+6fk4b2UmGn+Q/cMlg=="], - "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA=="], + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-IwhlH3qK5urrY8hZiEgGkHKEFN901p/p2bjxCxJlr4GyNnF7wYpUvK+Y43uaRYuC4hpfjzbR3SJC3arX1jGvmw=="], - "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg=="], + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-XxpJfVzFh+jilRxIXUqcfYAYcunIc/XEzIizsOL1fcJee5Sf7H3mH8WlLmfHfluz5amqR88QQo9izKtmMlavAw=="], - "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.3", "", { "os": "linux", "cpu": "arm" }, "sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw=="], + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.9", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kSfvhmgeWyfkbT3p/1s5vSgboogoah2zkm9fX2zjg2hHxSV7T4KhMWRUUaRk4OXNqoD3QAUeRqLcs1aZOK4U1g=="], - "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw=="], + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.9", "", { "os": "linux", "cpu": "arm" }, "sha512-1RVzG17pxqbTfYLC352JlLt6kKLG+6Hr30n8DlIJqsnV5luUDd2Qdx9Ayw1Cabfyb1K9k0jXEZ7evxkRoT+uiw=="], - "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q=="], + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-BXqPvZ2drqVD+/Z8UpKwcs4Mp7grM+eGFku4CAEKrEtcbAsUpzREphK1sogCRZGreVPiMkiiBtw0n3TPteuqvw=="], - "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w=="], + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-11vWvo8YDwLzukt27J3aYDWU+gg2P7J+ZOmiJ0hkF5BXZDW7pVya7r40MXDy6ya0i9KamoENSVKIugvJNgFXIA=="], - "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg=="], + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.9", "", { "os": "linux", "cpu": "ppc64" }, "sha512-a1tijMkdwsIARtc0F39ApURROkf3NwqinI6TOiSSWCTR7dT96dffNvMUtDHnq64wKNTIZOIlzKrFvvFUznJiyw=="], - "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w=="], + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.9", "", { "os": "linux", "cpu": "s390x" }, "sha512-x6SQNdAvv4c3hWqTMaWuawzMX9myaCs/yEmlGsxJzkdClnHW7FbrjQuSiRDhuSYzEYoEMhsaJy9qHG/XNemJPQ=="], - "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A=="], + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.9", "", { "os": "linux", "cpu": "x64" }, "sha512-9s0AZ8BFK5/n7B/TBoa2yJE3gI3KURrbXcPBlsAsvjU4VeJKgE90y1YtNxyEUIcHPQkg6/yfF3qihUrcM/Kf0Q=="], - "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.3", "", { "os": "none", "cpu": "arm64" }, "sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug=="], + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.9", "", { "os": "linux", "cpu": "x64" }, "sha512-P7VWAmV+WdJluH7ovnRGoiv2i8To7GAZ+kGzfGup635cyL7SyYl3lSUaA3Gp5THf0n/Co5EyEqb2zbqq+nMOHQ=="], - "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw=="], + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.9", "", { "os": "none", "cpu": "arm64" }, "sha512-1qixtsE4BK8h+yS3BfmZ09UhA7O/N4IACva6YBr7EBvCJraByTuRcgOTaiA62Tm0vey3UcKXLOaoGHtYmNGEVg=="], - "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.3", "", { "os": "win32", "cpu": "x64" }, "sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg=="], + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-ok8IQjcEPs1AKZfuEUznVBrJw+gK4soq+bx8b1X2XoMqVClarc1q5JDmVtWXY1xfr6ZuHTAsPXHTgTrqKTZeww=="], + + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.9", "", { "os": "win32", "cpu": "x64" }, "sha512-Ip2mXoU0hM0boq3Rf+ekuT653OROSo6aSYcPT1VHE4q52KvyxgFkQgrgb/IEsxOuvQ2fZZbs8khJAyCEPM24/g=="], "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], @@ -90,31 +92,31 @@ "@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="], - "@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="], + "@types/markdown-it": ["@types/markdown-it@14.2.0", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-NoQ2yGlLWj4wpxMs+TYmRKk3thDrQ97agr7sFqfLsAlvoS8SNQuTrlObhFqG9iugdTtgOE9jpJ6FNM4ZGsa5xQ=="], "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], "@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="], - "@types/node": ["@types/node@22.13.4", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg=="], + "@types/node": ["@types/node@22.20.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-DZmzkmwHzXrLPAXPyKNDzlIwMMUZCVacoD25ywdy5YTKGbOx/2ld+Q38Im2zJ0vBuZP5Prd3VZutKZyXwkOS8A=="], "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], "@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="], - "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + "@ungap/structured-clone": ["@ungap/structured-clone@1.4.0", "", {}, "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ=="], - "@vitejs/plugin-vue": ["@vitejs/plugin-vue@6.0.8", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", "vue": "^3.2.25" } }, "sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew=="], + "@vitejs/plugin-vue": ["@vitejs/plugin-vue@6.0.9", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", "vue": "^3.2.25" } }, "sha512-rD/MORlhaZMlXWW0rEn4FB4wMinWC0z/D7Ye160S5+Rs1mC8ZDAdcDp4SjKfUhG5t8q1ktcPVw4xuTkPRlzrSA=="], - "@vue/compiler-core": ["@vue/compiler-core@3.6.0-rc.2", "", { "dependencies": { "@babel/parser": "^7.29.7", "@vue/shared": "3.6.0-rc.2", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-Xx79h4EpJktQBShelXNKaRuvn9N6YsYL1j+RXdvPKzI+8tohYziBJbxOFGAkdcOKBwKc2SIWV4DOlTAMhE96yQ=="], + "@vue/compiler-core": ["@vue/compiler-core@3.6.0-rc.8", "", { "dependencies": { "@babel/parser": "^7.29.8", "@vue/shared": "3.6.0-rc.8", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-Z5Q4i8woJK2RRjw6hwFhCtsLOhq5DTPM+NwT6X2Yzw9jrFGxanQLiSmhYSW8sRQn41SdtIqUK959+ZN3YEDIbg=="], - "@vue/compiler-dom": ["@vue/compiler-dom@3.6.0-rc.2", "", { "dependencies": { "@vue/compiler-core": "3.6.0-rc.2", "@vue/shared": "3.6.0-rc.2" } }, "sha512-dRTdTdi/KXzHYIkHtyjGxepTIxnsZ/hB3v1osnS8c4MeycAyzkSoZW6OZkXXN4oVVVU6NKXds1XtKAEKVVw7nA=="], + "@vue/compiler-dom": ["@vue/compiler-dom@3.6.0-rc.8", "", { "dependencies": { "@vue/compiler-core": "3.6.0-rc.8", "@vue/shared": "3.6.0-rc.8" } }, "sha512-Bkajwf3oWFMFihpiWXXHQYG7iDPOS0IxFCGezVvUU80ScDzFTWtz75232IwOvswObR40wSelO6j1FTNdNeo21Q=="], - "@vue/compiler-sfc": ["@vue/compiler-sfc@3.6.0-rc.2", "", { "dependencies": { "@babel/parser": "^7.29.7", "@vue/compiler-core": "3.6.0-rc.2", "@vue/compiler-dom": "3.6.0-rc.2", "@vue/compiler-ssr": "3.6.0-rc.2", "@vue/compiler-vapor": "3.6.0-rc.2", "@vue/shared": "3.6.0-rc.2", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.19", "source-map-js": "^1.2.1" } }, "sha512-EZWTNh0UwdxvrBJkFc6+dqfwuUSdTVHDIohQcGDPCqGaYTHOfUL20b+grEAzlyxB741KrVBUb7qqpes92hAh2w=="], + "@vue/compiler-sfc": ["@vue/compiler-sfc@3.6.0-rc.8", "", { "dependencies": { "@babel/parser": "^7.29.8", "@vue/compiler-core": "3.6.0-rc.8", "@vue/compiler-dom": "3.6.0-rc.8", "@vue/compiler-ssr": "3.6.0-rc.8", "@vue/compiler-vapor": "3.6.0-rc.8", "@vue/shared": "3.6.0-rc.8", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.19", "source-map-js": "^1.2.1" } }, "sha512-+db2jOxM5v1HqtVbEemHPgqoJZYtmK++OnHEeiu1ZHqvTXg6fCTf1ufQBAY2SXdOk1hh88GTqKFJyml4ye1I3Q=="], - "@vue/compiler-ssr": ["@vue/compiler-ssr@3.6.0-rc.2", "", { "dependencies": { "@vue/compiler-dom": "3.6.0-rc.2", "@vue/shared": "3.6.0-rc.2" } }, "sha512-lqHSdQTmlhKX3dNEvR1TcspUzDlTR82T7BYLDzfvTwAqGXxAI2WJfyGJPcNpPjPKYEMI9JRGF32DU8TlHLx1AA=="], + "@vue/compiler-ssr": ["@vue/compiler-ssr@3.6.0-rc.8", "", { "dependencies": { "@vue/compiler-dom": "3.6.0-rc.8", "@vue/shared": "3.6.0-rc.8" } }, "sha512-IEl+wR5nH05+Vwu4VLGGtgNNgNwt2bEIXMTG8qNLMs6yU6spBLxTqvvLTUwT1dXpXC4HXGoDPG4wfTgeEPjVRw=="], - "@vue/compiler-vapor": ["@vue/compiler-vapor@3.6.0-rc.2", "", { "dependencies": { "@babel/parser": "^7.29.7", "@vue/compiler-dom": "3.6.0-rc.2", "@vue/shared": "3.6.0-rc.2", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-QbrA01pbj9xemF2McgBopyaOwj3kQGN0W1GW5EdFXORIT4fQQJpmUOj40h99S3bk09gLs5bFTRS4YE+Oyw/F9w=="], + "@vue/compiler-vapor": ["@vue/compiler-vapor@3.6.0-rc.8", "", { "dependencies": { "@babel/parser": "^7.29.8", "@vue/compiler-dom": "3.6.0-rc.8", "@vue/shared": "3.6.0-rc.8", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-N2G2U/e4PAfm/B7r3+EiVMS0m43l+60kavf5NeHH7sW9wexqjJG/XNOjoQw3S+rkkoT6rCydlIlqzmupL/eqRw=="], "@vue/devtools-api": ["@vue/devtools-api@8.2.1", "", { "dependencies": { "@vue/devtools-kit": "^8.2.1" } }, "sha512-6u4vXBlIBAC1wMplIZgpyPn7uh/s4Bf6F5bMzvLv+EdJ0aHs/+4B7Ygv864EStQSjRbsRzTko/kUG1A1IejQ3A=="], @@ -122,17 +124,17 @@ "@vue/devtools-shared": ["@vue/devtools-shared@8.2.1", "", {}, "sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g=="], - "@vue/reactivity": ["@vue/reactivity@3.6.0-rc.2", "", { "dependencies": { "@vue/shared": "3.6.0-rc.2" } }, "sha512-B01EoSTcaVQiJwZDoSoeKYClVgj60BHN43oHEcxXhPVLdxsXI0SOiQuskUskQDZn2ma08llICKJohL60o+UamA=="], + "@vue/reactivity": ["@vue/reactivity@3.6.0-rc.8", "", { "dependencies": { "@vue/shared": "3.6.0-rc.8" } }, "sha512-h5Ooef0SYigEjdB6HZx7+BdP+peVNxjpnuDiLFpRgeVltfYwNLtjRmzSMhEwMHFHOnAxJkxOSFz4qQZbp9hiCA=="], - "@vue/runtime-core": ["@vue/runtime-core@3.6.0-rc.2", "", { "dependencies": { "@vue/reactivity": "3.6.0-rc.2", "@vue/shared": "3.6.0-rc.2" } }, "sha512-qFV61Hbg6LyEGxJiVRJYuiIfryxrfO4+cgW1esxtkR1/pvemULwG6KpFImsJDwcC+YxrtvndXGM5PRVwUCGuiA=="], + "@vue/runtime-core": ["@vue/runtime-core@3.6.0-rc.8", "", { "dependencies": { "@vue/reactivity": "3.6.0-rc.8", "@vue/shared": "3.6.0-rc.8" } }, "sha512-PRH45UwDCxeDgQ6XkDaVZ/91XGIAyO1cdugJTDF+ZpmdHZ6QNtY44EFzWmr7wCxc7uqp7BRrxXFSmyt7R20+bg=="], - "@vue/runtime-dom": ["@vue/runtime-dom@3.6.0-rc.2", "", { "dependencies": { "@vue/reactivity": "3.6.0-rc.2", "@vue/runtime-core": "3.6.0-rc.2", "@vue/shared": "3.6.0-rc.2", "csstype": "^3.2.3" } }, "sha512-clIE/xk0b61t30BPKoOXZep253nhWdjyw7iac53yy2KN0nxAupkb6x3ImOI9iljjXqOW5lYdNVHNxNlcx3iA1Q=="], + "@vue/runtime-dom": ["@vue/runtime-dom@3.6.0-rc.8", "", { "dependencies": { "@vue/reactivity": "3.6.0-rc.8", "@vue/runtime-core": "3.6.0-rc.8", "@vue/shared": "3.6.0-rc.8", "csstype": "^3.2.3" } }, "sha512-gf4sGrMq2nGvj+NPtBKu3fZt8HbnjaLcUbj7l6GOUKSWACr4CeU9iXlFCT3ncu3e/xecj8dUIpKPGSiwNLvLMw=="], - "@vue/runtime-vapor": ["@vue/runtime-vapor@3.6.0-rc.2", "", { "dependencies": { "@vue/reactivity": "3.6.0-rc.2", "@vue/shared": "3.6.0-rc.2" }, "peerDependencies": { "@vue/runtime-dom": "3.6.0-rc.2" } }, "sha512-8EpxbNyT629k/52el4D7PxXmZtiYhJRBHPXJR7CZw0OluiADtT9W8J+XB83EVlzHmFUysXcd+mGWmuHwIhuHZA=="], + "@vue/runtime-vapor": ["@vue/runtime-vapor@3.6.0-rc.8", "", { "dependencies": { "@vue/reactivity": "3.6.0-rc.8", "@vue/shared": "3.6.0-rc.8" }, "peerDependencies": { "@vue/runtime-dom": "3.6.0-rc.8" } }, "sha512-GUXwL3J9O8l639N/vI949CZTglBRJvJKdsCgs2snGMdrVTVgt//tzlgWtZwxSM4BRPPTlZHrFqSWd1thrfUb0w=="], - "@vue/server-renderer": ["@vue/server-renderer@3.6.0-rc.2", "", { "dependencies": { "@vue/compiler-ssr": "3.6.0-rc.2", "@vue/runtime-dom": "3.6.0-rc.2", "@vue/shared": "3.6.0-rc.2" } }, "sha512-TIsqoYljs6b0wfoCDhv4EeU3Du2a8gTadkaTGMjIYLTLlO4p8r4VdDqfNp/ZMAm87bW4ST1naBMRe+bXQBpAeQ=="], + "@vue/server-renderer": ["@vue/server-renderer@3.6.0-rc.8", "", { "dependencies": { "@vue/compiler-ssr": "3.6.0-rc.8", "@vue/runtime-dom": "3.6.0-rc.8", "@vue/shared": "3.6.0-rc.8" } }, "sha512-aEJABwHk5cpGgeaq4vt8oIfIWlAq0S9Ly/zWu9ii3sjE+zbjQ28cAeeK4GxVSQhUL0tidhzeQReOcNJ/BCmWpQ=="], - "@vue/shared": ["@vue/shared@3.5.41", "", {}, "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA=="], + "@vue/shared": ["@vue/shared@3.5.42", "", {}, "sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g=="], "@vueuse/core": ["@vueuse/core@14.4.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "14.4.0", "@vueuse/shared": "14.4.0" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-X4WHz1HlCzCBoYXesUkifzzWBAcZgXG8Fi5iNPQg/epdzOB3gu8Fawj3hvuwYR1nGcXGnvxwYYcUC/71++svtQ=="], @@ -208,7 +210,7 @@ "mark.js": ["mark.js@8.11.1", "", {}, "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ=="], - "mdast-util-to-hast": ["mdast-util-to-hast@13.2.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA=="], + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], @@ -218,11 +220,11 @@ "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], - "micromark-util-types": ["micromark-util-types@2.0.1", "", {}, "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ=="], + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], "minisearch": ["minisearch@7.2.0", "", {}, "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg=="], - "nanoid": ["nanoid@3.3.18", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="], + "nanoid": ["nanoid@3.3.19", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug=="], "oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="], @@ -230,15 +232,15 @@ "or": ["or@0.2.0", "", {}, "sha512-BHB8VZq2isxkyRaCBZ6CZCbQBzCT+gy8LPiqdbMH1+Fd6biFj3v8ebjeYzzL51PbsApsPYnGegGTO6KLQMxxDw=="], - "perfect-debounce": ["perfect-debounce@2.0.0", "", {}, "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow=="], + "perfect-debounce": ["perfect-debounce@2.1.0", "", {}, "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], + "picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], - "postcss": ["postcss@8.5.26", "", { "dependencies": { "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ=="], + "postcss": ["postcss@8.5.28", "", { "dependencies": { "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A=="], - "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + "property-information": ["property-information@7.2.0", "", {}, "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg=="], "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], @@ -246,7 +248,7 @@ "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], - "rolldown": ["rolldown@1.2.3", "", { "dependencies": { "@oxc-project/types": "=0.143.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.2.3", "@rolldown/binding-darwin-arm64": "1.2.3", "@rolldown/binding-darwin-x64": "1.2.3", "@rolldown/binding-freebsd-x64": "1.2.3", "@rolldown/binding-linux-arm-gnueabihf": "1.2.3", "@rolldown/binding-linux-arm64-gnu": "1.2.3", "@rolldown/binding-linux-arm64-musl": "1.2.3", "@rolldown/binding-linux-ppc64-gnu": "1.2.3", "@rolldown/binding-linux-s390x-gnu": "1.2.3", "@rolldown/binding-linux-x64-gnu": "1.2.3", "@rolldown/binding-linux-x64-musl": "1.2.3", "@rolldown/binding-openharmony-arm64": "1.2.3", "@rolldown/binding-win32-arm64-msvc": "1.2.3", "@rolldown/binding-win32-x64-msvc": "1.2.3" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A=="], + "rolldown": ["rolldown@1.2.9", "", { "dependencies": { "@oxc-project/types": "=0.150.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.9", "@rolldown/binding-android-arm64": "1.2.9", "@rolldown/binding-darwin-arm64": "1.2.9", "@rolldown/binding-darwin-x64": "1.2.9", "@rolldown/binding-freebsd-x64": "1.2.9", "@rolldown/binding-linux-arm-gnueabihf": "1.2.9", "@rolldown/binding-linux-arm64-gnu": "1.2.9", "@rolldown/binding-linux-arm64-musl": "1.2.9", "@rolldown/binding-linux-ppc64-gnu": "1.2.9", "@rolldown/binding-linux-s390x-gnu": "1.2.9", "@rolldown/binding-linux-x64-gnu": "1.2.9", "@rolldown/binding-linux-x64-musl": "1.2.9", "@rolldown/binding-openharmony-arm64": "1.2.9", "@rolldown/binding-win32-arm64-msvc": "1.2.9", "@rolldown/binding-win32-x64-msvc": "1.2.9" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg=="], "shiki": ["shiki@4.4.3", "", { "dependencies": { "@shikijs/core": "4.4.3", "@shikijs/engine-javascript": "4.4.3", "@shikijs/engine-oniguruma": "4.4.3", "@shikijs/langs": "4.4.3", "@shikijs/themes": "4.4.3", "@shikijs/types": "4.4.3", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g=="], @@ -262,94 +264,72 @@ "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], - "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="], + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], - "unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="], + "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], - "unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="], + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], - "vfile-message": ["vfile-message@4.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="], + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], - "vite": ["vite@8.2.1", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", "postcss": "^8.5.25", "rolldown": "~1.2.1", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.4.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw=="], + "vite": ["vite@8.3.0", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", "postcss": "^8.5.28", "rolldown": "~1.2.6", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ=="], - "vitepress": ["vitepress@2.0.0-alpha.19", "", { "dependencies": { "@docsearch/css": "^4.7.0", "@docsearch/js": "^4.7.0", "@docsearch/sidepanel-js": "^4.7.0", "@iconify-json/simple-icons": "^1.2.92", "@shikijs/core": "^4.4.1", "@shikijs/transformers": "^4.4.1", "@shikijs/types": "^4.4.1", "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^6.0.8", "@vue/devtools-api": "^8.2.1", "@vue/shared": "^3.5.40", "@vueuse/core": "^14.4.0", "@vueuse/integrations": "^14.4.0", "focus-trap": "^8.2.2", "mark.js": "8.11.1", "minisearch": "^7.2.0", "shiki": "^4.4.1", "vite": "^8.2.0", "vue": "^3.5.40" }, "peerDependencies": { "markdown-it-mathjax3": "^4", "postcss": "^8" }, "optionalPeers": ["markdown-it-mathjax3", "postcss"], "bin": { "vitepress": "bin/vitepress.js" } }, "sha512-WnBsb0Bwr43kXKyiis+lld/7ri3hnMbthS8N3hpFtjjwsdLO4IRmiAE08D7aud4q6oMDf9uwRowxzNqRFe/amw=="], + "vitepress": ["vitepress@2.0.0-alpha.20", "", { "dependencies": { "@docsearch/css": "^4.7.0", "@docsearch/js": "^4.7.0", "@docsearch/sidepanel-js": "^4.7.0", "@iconify-json/simple-icons": "^1.2.93", "@shikijs/transformers": "^4.4.3", "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^6.0.8", "@vue/devtools-api": "^8.2.1", "@vue/shared": "^3.5.41", "@vueuse/core": "^14.4.0", "@vueuse/integrations": "^14.4.0", "focus-trap": "^8.2.2", "mark.js": "8.11.1", "minisearch": "^7.2.0", "shiki": "^4.4.3", "vite": "^8.2.1", "vue": "^3.5.41" }, "peerDependencies": { "markdown-it-mathjax3": "^4", "postcss": "^8" }, "optionalPeers": ["markdown-it-mathjax3", "postcss"], "bin": { "vitepress": "bin/vitepress.js" } }, "sha512-uQWKmP9PaBkf9zxHn0iOml/ZPPDaGKqQXVMBHz3yjwyyd9Eg+BKLGxbqmOGlfOMCt6Fj8TLiAnE59qlKZ0A3xw=="], - "vue": ["vue@3.6.0-rc.2", "", { "dependencies": { "@vue/compiler-dom": "3.6.0-rc.2", "@vue/compiler-sfc": "3.6.0-rc.2", "@vue/runtime-dom": "3.6.0-rc.2", "@vue/runtime-vapor": "3.6.0-rc.2", "@vue/server-renderer": "3.6.0-rc.2", "@vue/shared": "3.6.0-rc.2" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-pBgoISqETcufJ1e92vVW2/D5MEbCm2XHlC/5dq3hWq8s17tx9v5BEdYv4hojJ0yEEDidYxUL7itS+vV32wfZrw=="], + "vue": ["vue@3.6.0-rc.8", "", { "dependencies": { "@vue/compiler-dom": "3.6.0-rc.8", "@vue/compiler-sfc": "3.6.0-rc.8", "@vue/runtime-dom": "3.6.0-rc.8", "@vue/runtime-vapor": "3.6.0-rc.8", "@vue/server-renderer": "3.6.0-rc.8", "@vue/shared": "3.6.0-rc.8" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-9TU0oX/NWj9L4BHWKHXCeyMKmHqQoDLw+olIuqk07tryw0a8sCpmZrEmLps2vBiuRNknskDwcOupooe9J5P3aQ=="], "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], - "@vue/compiler-core/@vue/shared": ["@vue/shared@3.6.0-rc.2", "", {}, "sha512-Tz44lKLjxhWRu3GJ38768dc2hW4XhDtTB5aUo5fCMis6YDyFzwQYkrD/B9SuQ1v/ailv6UVR3wSHiujNXSjMCQ=="], + "@vue/compiler-core/@vue/shared": ["@vue/shared@3.6.0-rc.8", "", {}, "sha512-Un8Dd+UDUO8otYoHQFZzY9ySuH4Nk6SqEFbo3lWkZrQULIAEiMTp8JK7NgK/K5/QUQS2PvKC56jQTmaQ6CQcTA=="], - "@vue/compiler-dom/@vue/shared": ["@vue/shared@3.6.0-rc.2", "", {}, "sha512-Tz44lKLjxhWRu3GJ38768dc2hW4XhDtTB5aUo5fCMis6YDyFzwQYkrD/B9SuQ1v/ailv6UVR3wSHiujNXSjMCQ=="], + "@vue/compiler-dom/@vue/shared": ["@vue/shared@3.6.0-rc.8", "", {}, "sha512-Un8Dd+UDUO8otYoHQFZzY9ySuH4Nk6SqEFbo3lWkZrQULIAEiMTp8JK7NgK/K5/QUQS2PvKC56jQTmaQ6CQcTA=="], - "@vue/compiler-sfc/@vue/shared": ["@vue/shared@3.6.0-rc.2", "", {}, "sha512-Tz44lKLjxhWRu3GJ38768dc2hW4XhDtTB5aUo5fCMis6YDyFzwQYkrD/B9SuQ1v/ailv6UVR3wSHiujNXSjMCQ=="], + "@vue/compiler-sfc/@vue/shared": ["@vue/shared@3.6.0-rc.8", "", {}, "sha512-Un8Dd+UDUO8otYoHQFZzY9ySuH4Nk6SqEFbo3lWkZrQULIAEiMTp8JK7NgK/K5/QUQS2PvKC56jQTmaQ6CQcTA=="], - "@vue/compiler-sfc/postcss": ["postcss@8.5.22", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ=="], + "@vue/compiler-ssr/@vue/shared": ["@vue/shared@3.6.0-rc.8", "", {}, "sha512-Un8Dd+UDUO8otYoHQFZzY9ySuH4Nk6SqEFbo3lWkZrQULIAEiMTp8JK7NgK/K5/QUQS2PvKC56jQTmaQ6CQcTA=="], - "@vue/compiler-ssr/@vue/shared": ["@vue/shared@3.6.0-rc.2", "", {}, "sha512-Tz44lKLjxhWRu3GJ38768dc2hW4XhDtTB5aUo5fCMis6YDyFzwQYkrD/B9SuQ1v/ailv6UVR3wSHiujNXSjMCQ=="], + "@vue/compiler-vapor/@vue/shared": ["@vue/shared@3.6.0-rc.8", "", {}, "sha512-Un8Dd+UDUO8otYoHQFZzY9ySuH4Nk6SqEFbo3lWkZrQULIAEiMTp8JK7NgK/K5/QUQS2PvKC56jQTmaQ6CQcTA=="], - "@vue/compiler-vapor/@vue/shared": ["@vue/shared@3.6.0-rc.2", "", {}, "sha512-Tz44lKLjxhWRu3GJ38768dc2hW4XhDtTB5aUo5fCMis6YDyFzwQYkrD/B9SuQ1v/ailv6UVR3wSHiujNXSjMCQ=="], + "@vue/reactivity/@vue/shared": ["@vue/shared@3.6.0-rc.8", "", {}, "sha512-Un8Dd+UDUO8otYoHQFZzY9ySuH4Nk6SqEFbo3lWkZrQULIAEiMTp8JK7NgK/K5/QUQS2PvKC56jQTmaQ6CQcTA=="], - "@vue/reactivity/@vue/shared": ["@vue/shared@3.6.0-rc.2", "", {}, "sha512-Tz44lKLjxhWRu3GJ38768dc2hW4XhDtTB5aUo5fCMis6YDyFzwQYkrD/B9SuQ1v/ailv6UVR3wSHiujNXSjMCQ=="], + "@vue/runtime-core/@vue/shared": ["@vue/shared@3.6.0-rc.8", "", {}, "sha512-Un8Dd+UDUO8otYoHQFZzY9ySuH4Nk6SqEFbo3lWkZrQULIAEiMTp8JK7NgK/K5/QUQS2PvKC56jQTmaQ6CQcTA=="], - "@vue/runtime-core/@vue/shared": ["@vue/shared@3.6.0-rc.2", "", {}, "sha512-Tz44lKLjxhWRu3GJ38768dc2hW4XhDtTB5aUo5fCMis6YDyFzwQYkrD/B9SuQ1v/ailv6UVR3wSHiujNXSjMCQ=="], + "@vue/runtime-dom/@vue/shared": ["@vue/shared@3.6.0-rc.8", "", {}, "sha512-Un8Dd+UDUO8otYoHQFZzY9ySuH4Nk6SqEFbo3lWkZrQULIAEiMTp8JK7NgK/K5/QUQS2PvKC56jQTmaQ6CQcTA=="], - "@vue/runtime-dom/@vue/shared": ["@vue/shared@3.6.0-rc.2", "", {}, "sha512-Tz44lKLjxhWRu3GJ38768dc2hW4XhDtTB5aUo5fCMis6YDyFzwQYkrD/B9SuQ1v/ailv6UVR3wSHiujNXSjMCQ=="], + "@vue/runtime-vapor/@vue/shared": ["@vue/shared@3.6.0-rc.8", "", {}, "sha512-Un8Dd+UDUO8otYoHQFZzY9ySuH4Nk6SqEFbo3lWkZrQULIAEiMTp8JK7NgK/K5/QUQS2PvKC56jQTmaQ6CQcTA=="], - "@vue/runtime-vapor/@vue/shared": ["@vue/shared@3.6.0-rc.2", "", {}, "sha512-Tz44lKLjxhWRu3GJ38768dc2hW4XhDtTB5aUo5fCMis6YDyFzwQYkrD/B9SuQ1v/ailv6UVR3wSHiujNXSjMCQ=="], + "@vue/server-renderer/@vue/shared": ["@vue/shared@3.6.0-rc.8", "", {}, "sha512-Un8Dd+UDUO8otYoHQFZzY9ySuH4Nk6SqEFbo3lWkZrQULIAEiMTp8JK7NgK/K5/QUQS2PvKC56jQTmaQ6CQcTA=="], - "@vue/server-renderer/@vue/shared": ["@vue/shared@3.6.0-rc.2", "", {}, "sha512-Tz44lKLjxhWRu3GJ38768dc2hW4XhDtTB5aUo5fCMis6YDyFzwQYkrD/B9SuQ1v/ailv6UVR3wSHiujNXSjMCQ=="], + "vitepress/vue": ["vue@3.5.42", "", { "dependencies": { "@vue/compiler-dom": "3.5.42", "@vue/compiler-sfc": "3.5.42", "@vue/runtime-dom": "3.5.42", "@vue/server-renderer": "3.5.42", "@vue/shared": "3.5.42" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA=="], - "hast-util-to-html/@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + "vue/@vue/shared": ["@vue/shared@3.6.0-rc.8", "", {}, "sha512-Un8Dd+UDUO8otYoHQFZzY9ySuH4Nk6SqEFbo3lWkZrQULIAEiMTp8JK7NgK/K5/QUQS2PvKC56jQTmaQ6CQcTA=="], - "hast-util-whitespace/@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + "vitepress/vue/@vue/compiler-dom": ["@vue/compiler-dom@3.5.42", "", { "dependencies": { "@vue/compiler-core": "3.5.42", "@vue/shared": "3.5.42" } }, "sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA=="], - "mdast-util-to-hast/@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + "vitepress/vue/@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.42", "", { "dependencies": { "@babel/parser": "^7.29.8", "@vue/compiler-core": "3.5.42", "@vue/compiler-dom": "3.5.42", "@vue/compiler-ssr": "3.5.42", "@vue/shared": "3.5.42", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.19", "source-map-js": "^1.2.1" } }, "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw=="], - "vitepress/vue": ["vue@3.5.41", "", { "dependencies": { "@vue/compiler-dom": "3.5.41", "@vue/compiler-sfc": "3.5.41", "@vue/runtime-dom": "3.5.41", "@vue/server-renderer": "3.5.41", "@vue/shared": "3.5.41" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-2laE0p+aK+/AOPG/XL/WepOs/GlK755LJ1XECi9kDUrz1FKNw8rb2Xzlw9JS1rqEV55nb0ttsKxVlTCcd+R5cg=="], + "vitepress/vue/@vue/runtime-dom": ["@vue/runtime-dom@3.5.42", "", { "dependencies": { "@vue/reactivity": "3.5.42", "@vue/runtime-core": "3.5.42", "@vue/shared": "3.5.42", "csstype": "^3.2.3" } }, "sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g=="], - "vue/@vue/shared": ["@vue/shared@3.6.0-rc.2", "", {}, "sha512-Tz44lKLjxhWRu3GJ38768dc2hW4XhDtTB5aUo5fCMis6YDyFzwQYkrD/B9SuQ1v/ailv6UVR3wSHiujNXSjMCQ=="], + "vitepress/vue/@vue/server-renderer": ["@vue/server-renderer@3.5.42", "", { "dependencies": { "@vue/compiler-ssr": "3.5.42", "@vue/runtime-dom": "3.5.42", "@vue/shared": "3.5.42" } }, "sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ=="], - "@vue/compiler-sfc/postcss/nanoid": ["nanoid@3.3.16", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="], + "vitepress/vue/@vue/compiler-dom/@vue/compiler-core": ["@vue/compiler-core@3.5.42", "", { "dependencies": { "@babel/parser": "^7.29.8", "@vue/shared": "3.5.42", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ=="], - "vitepress/vue/@vue/compiler-dom": ["@vue/compiler-dom@3.5.41", "", { "dependencies": { "@vue/compiler-core": "3.5.41", "@vue/shared": "3.5.41" } }, "sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw=="], + "vitepress/vue/@vue/compiler-sfc/@vue/compiler-core": ["@vue/compiler-core@3.5.42", "", { "dependencies": { "@babel/parser": "^7.29.8", "@vue/shared": "3.5.42", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ=="], - "vitepress/vue/@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.41", "", { "dependencies": { "@babel/parser": "^7.29.8", "@vue/compiler-core": "3.5.41", "@vue/compiler-dom": "3.5.41", "@vue/compiler-ssr": "3.5.41", "@vue/shared": "3.5.41", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.19", "source-map-js": "^1.2.1" } }, "sha512-XJhip7R2wy6vX3knCxdZN4KracFaZUef58s1KYewqluedHIJaPIVfXoYT7MF1F8nCvv6k8bWWxDC8opMkg1VTQ=="], + "vitepress/vue/@vue/compiler-sfc/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.42", "", { "dependencies": { "@vue/compiler-dom": "3.5.42", "@vue/shared": "3.5.42" } }, "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA=="], - "vitepress/vue/@vue/runtime-dom": ["@vue/runtime-dom@3.5.41", "", { "dependencies": { "@vue/reactivity": "3.5.41", "@vue/runtime-core": "3.5.41", "@vue/shared": "3.5.41", "csstype": "^3.2.3" } }, "sha512-3vVBahVBS9+U6cmXBLyb8nE6/yYo4J/CGI9eVFs3KiMc0YHuudwKyShTD65jtJy/L9PUUxNAFu4cj4LiJ0UFbw=="], + "vitepress/vue/@vue/runtime-dom/@vue/reactivity": ["@vue/reactivity@3.5.42", "", { "dependencies": { "@vue/shared": "3.5.42" } }, "sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ=="], - "vitepress/vue/@vue/server-renderer": ["@vue/server-renderer@3.5.41", "", { "dependencies": { "@vue/compiler-ssr": "3.5.41", "@vue/runtime-dom": "3.5.41", "@vue/shared": "3.5.41" } }, "sha512-n6hx/pNFfbD6SuyeuMVkvqox8bwf/ET9JlA/kAz/imw8sw++wkqKe2mHX5KutjPpbKE4Z56yTHszoOjGMI9igQ=="], + "vitepress/vue/@vue/runtime-dom/@vue/runtime-core": ["@vue/runtime-core@3.5.42", "", { "dependencies": { "@vue/reactivity": "3.5.42", "@vue/shared": "3.5.42" } }, "sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA=="], - "vitepress/vue/@vue/compiler-dom/@vue/compiler-core": ["@vue/compiler-core@3.5.41", "", { "dependencies": { "@babel/parser": "^7.29.8", "@vue/shared": "3.5.41", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg=="], - - "vitepress/vue/@vue/compiler-sfc/@babel/parser": ["@babel/parser@7.29.8", "", { "dependencies": { "@babel/types": "^7.29.8" }, "bin": "./bin/babel-parser.js" }, "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA=="], - - "vitepress/vue/@vue/compiler-sfc/@vue/compiler-core": ["@vue/compiler-core@3.5.41", "", { "dependencies": { "@babel/parser": "^7.29.8", "@vue/shared": "3.5.41", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg=="], - - "vitepress/vue/@vue/compiler-sfc/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.41", "", { "dependencies": { "@vue/compiler-dom": "3.5.41", "@vue/shared": "3.5.41" } }, "sha512-U3v5OejKEGqOI0Wy0+Sz7hGuIFZHA4LSXzrNM3IMIeDyJEBBfTpX26n3SDgToRpP2bLc9FfI2j/kSgcJ8Emq5A=="], - - "vitepress/vue/@vue/compiler-sfc/postcss": ["postcss@8.5.22", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ=="], - - "vitepress/vue/@vue/runtime-dom/@vue/reactivity": ["@vue/reactivity@3.5.41", "", { "dependencies": { "@vue/shared": "3.5.41" } }, "sha512-rznsqKM0np0x18EjzF8x88MpEhdNsffbvFbckLL5+oUKz1BxAImEmO7J1ArRYSyo6aQaVoBDp7jEkT91OOxydA=="], - - "vitepress/vue/@vue/runtime-dom/@vue/runtime-core": ["@vue/runtime-core@3.5.41", "", { "dependencies": { "@vue/reactivity": "3.5.41", "@vue/shared": "3.5.41" } }, "sha512-Vcry58hiAKwGen9Z1jUZE0feFsNArPCMOImYI8el48A9Idf6DuQYD0U05zZIF2Iad1hGhPSvcbBbAOhNr55fhg=="], - - "vitepress/vue/@vue/server-renderer/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.41", "", { "dependencies": { "@vue/compiler-dom": "3.5.41", "@vue/shared": "3.5.41" } }, "sha512-U3v5OejKEGqOI0Wy0+Sz7hGuIFZHA4LSXzrNM3IMIeDyJEBBfTpX26n3SDgToRpP2bLc9FfI2j/kSgcJ8Emq5A=="], - - "vitepress/vue/@vue/compiler-dom/@vue/compiler-core/@babel/parser": ["@babel/parser@7.29.8", "", { "dependencies": { "@babel/types": "^7.29.8" }, "bin": "./bin/babel-parser.js" }, "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA=="], - - "vitepress/vue/@vue/compiler-sfc/@babel/parser/@babel/types": ["@babel/types@7.29.8", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg=="], - - "vitepress/vue/@vue/compiler-sfc/postcss/nanoid": ["nanoid@3.3.16", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="], - - "vitepress/vue/@vue/compiler-dom/@vue/compiler-core/@babel/parser/@babel/types": ["@babel/types@7.29.8", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg=="], + "vitepress/vue/@vue/server-renderer/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.42", "", { "dependencies": { "@vue/compiler-dom": "3.5.42", "@vue/shared": "3.5.42" } }, "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA=="], } } diff --git a/docs/.vitepress/de.ts b/docs/.vitepress/de.ts index 087fda6..34bac52 100644 --- a/docs/.vitepress/de.ts +++ b/docs/.vitepress/de.ts @@ -5,13 +5,25 @@ export const de = defineConfig({ description: "Hochperformante Minecraft-Server-Software in Rust", themeConfig: { - // https://vitepress.dev/reference/default-theme-config + siteTitle: "Pumpkin Docs", search: { provider: "local", }, nav: [ { - text: "Startseite", + text: "Administration", + link: "/de/admin/introduction", + }, + { + text: "Mitwirkende", + link: "/de/developer/introduction", + }, + { + text: "Plugins", + link: "/de/plugin-dev/introduction", + }, + { + text: "Webseite", link: "https://pumpkinmc.org/", }, ], @@ -23,34 +35,124 @@ export const de = defineConfig({ ], }, { - text: "Konfiguration", + text: "Administration", + collapsed: false, items: [ - { text: "Einführung", link: "/de/config/introduction" }, - { text: "Grundkonfiguration", link: "/de/config/basic" }, - { text: "Proxy", link: "/de/config/proxy" }, - { text: "Authentifizierung", link: "/de/config/authentication" }, - { text: "Kompression", link: "/de/config/compression" }, - { text: "Ressourcenpaket", link: "/de/config/resource-pack" }, - { text: "Befehle", link: "/de/config/commands" }, - { text: "RCON", link: "/de/config/rcon" }, - { text: "PVP", link: "/de/config/pvp" }, - { text: "Logging", link: "/de/config/logging" }, - { text: "Query", link: "/de/config/query" }, - { text: "LAN-Broadcast", link: "/de/config/lan-broadcast" }, + { text: "Übersicht", link: "/de/admin/introduction" }, + { text: "Migration von Bukkit", link: "/de/admin/migrating-from-bukkit" }, + { + text: "Konfiguration", + collapsed: true, + items: [ + { text: "Einführung", link: "/de/config/introduction" }, + { text: "Grundkonfiguration", link: "/de/config/basic" }, + { text: "Welt", link: "/de/config/world" }, + { text: "Chat & Anti-Spam", link: "/de/config/chat" }, + { text: "Befehle", link: "/de/config/commands" }, + { text: "Bedrock & NetherNet", link: "/de/config/bedrock" }, + { text: "Proxy", link: "/de/config/proxy" }, + { text: "Authentifizierung", link: "/de/config/authentication" }, + { text: "Paket-Begrenzer", link: "/de/config/packet-limiter" }, + { text: "Kompression", link: "/de/config/compression" }, + { text: "Ressourcenpaket", link: "/de/config/resource-pack" }, + { text: "Plugins", link: "/de/config/plugins" }, + { text: "Server-Links", link: "/de/config/server-links" }, + { text: "Spielerdaten", link: "/de/config/player-data" }, + { text: "Logging", link: "/de/config/logging" }, + { text: "Query", link: "/de/config/query" }, + { text: "RCON", link: "/de/config/rcon" }, + { text: "PVP", link: "/de/config/pvp" }, + { text: "LAN-Broadcast", link: "/de/config/lan-broadcast" }, + ], + }, ], }, { - text: "Entwickler", + text: "Mitwirkende", items: [ - { text: "Beitragen", link: "/de/developer/contributing" }, { text: "Einführung", link: "/de/developer/introduction" }, + { text: "Beitragsleitfaden", link: "/de/developer/contributing" }, + { text: "Architektur", link: "/de/developer/architecture" }, + { text: "Daten-Extraktor", link: "/de/developer/extractor" }, + { text: "Code-Generierung", link: "/de/developer/codegen" }, + { text: "Versionen portieren", link: "/de/developer/porting-versions" }, { text: "Netzwerk", - link: "/de/developer/networking/networking", + collapsed: true, items: [ { - text: "Authentifizierung", - link: "/de/developer/networking/authentication", + text: "Einführung", + link: "/de/developer/networking/", + }, + { + text: "Metadaten & Synchronisierte Daten", + link: "/de/developer/networking/metadata", + }, + { + text: "Java Edition", + collapsed: true, + items: [ + { + text: "Übersicht", + link: "/de/developer/networking/java/overview", + }, + { + text: "Paket hinzufügen", + link: "/de/developer/networking/java/adding-packets", + }, + { + text: "Authentifizierung", + link: "/de/developer/networking/java/authentication", + }, + ], + }, + { + text: "Bedrock Edition", + collapsed: true, + items: [ + { + text: "Übersicht", + link: "/de/developer/networking/bedrock/overview", + }, + { + text: "Paket hinzufügen", + link: "/de/developer/networking/bedrock/adding-packets", + }, + { + text: "Authentifizierung", + link: "/de/developer/networking/bedrock/authentication", + }, + { + text: "NetherNet", + link: "/de/developer/networking/bedrock/nethernet", + }, + ], + }, + { + text: "Proxy-Weiterleitung", + collapsed: true, + items: [ + { + text: "Übersicht", + link: "/de/developer/networking/proxy/", + }, + { + text: "BungeeCord", + link: "/de/developer/networking/proxy/bungeecord", + }, + { + text: "Velocity", + link: "/de/developer/networking/proxy/velocity", + }, + { + text: "Vine", + link: "/de/developer/networking/proxy/vine", + }, + ], + }, + { + text: "Query (GameSpy4)", + link: "/de/developer/networking/query", }, { text: "RCON", @@ -58,7 +160,51 @@ export const de = defineConfig({ }, ], }, - { text: "Welt", link: "/de/developer/world" }, + { + text: "Blöcke", + collapsed: true, + items: [ + { text: "Übersicht", link: "/de/developer/blocks/" }, + { text: "Block hinzufügen", link: "/de/developer/blocks/adding-blocks" }, + ], + }, + { + text: "Gegenstände", + collapsed: true, + items: [ + { text: "Übersicht", link: "/de/developer/items/" }, + { text: "Gegenstand hinzufügen", link: "/de/developer/items/adding-items" }, + ], + }, + { + text: "Entities", + collapsed: true, + items: [ + { text: "Übersicht & Hierarchie", link: "/de/developer/entities/" }, + { text: "Spawning & Tracking", link: "/de/developer/entities/spawning-and-tracking" }, + { text: "Mob-KI & Ziele", link: "/de/developer/entities/ai-and-mobs" }, + ], + }, + { + text: "Befehle", + collapsed: true, + items: [ + { text: "Übersicht & Dispatcher", link: "/de/developer/commands/" }, + { text: "Befehl hinzufügen", link: "/de/developer/commands/adding-commands" }, + { text: "Argumente & Vorschläge", link: "/de/developer/commands/arguments-and-suggestions" }, + { text: "Ausführung & Kontext", link: "/de/developer/commands/execution-and-context" }, + ], + }, + { + text: "Plugin-Engine", + collapsed: true, + items: [ + { text: "Übersicht", link: "/de/developer/plugins/" }, + { text: "Plugin-Loader hinzufügen", link: "/de/developer/plugins/loaders" }, + { text: "WASM-Signierung & Verifizierung", link: "/de/developer/plugins/wasm-signing" }, + ], + }, + { text: "Welt-Engine", link: "/de/developer/world" }, { text: "Mobile Entwicklung", link: "/de/developer/mobile" }, ], }, @@ -69,9 +215,35 @@ export const de = defineConfig({ text: "Einführung", link: "/de/plugin-dev/introduction", }, + { + text: "Migration von Bukkit", + collapsed: true, + items: [ + { + text: "Übersicht", + link: "/de/plugin-dev/migrating-from-bukkit/", + }, + { + text: "Befehle", + link: "/de/plugin-dev/migrating-from-bukkit/commands", + }, + { + text: "Events", + link: "/de/plugin-dev/migrating-from-bukkit/events", + }, + { + text: "Inventare & GUIs", + link: "/de/plugin-dev/migrating-from-bukkit/inventories", + }, + { + text: "Konfiguration & Daten", + link: "/de/plugin-dev/migrating-from-bukkit/configuration", + }, + ], + }, { text: "Rust", - link: "/de/plugin-dev/rust/introduction", + collapsed: true, items: [ { text: "Projekt erstellen", @@ -82,36 +254,44 @@ export const de = defineConfig({ link: "/de/plugin-dev/rust/basic-logic", }, { - text: "Join-Event", - link: "/de/plugin-dev/rust/join-event", + text: "Events", + link: "/de/plugin-dev/rust/events", }, { text: "Befehle", items: [ + { + text: "Erster Befehl", + link: "/de/plugin-dev/rust/command/first-command", + }, { text: "Schere-Stein-Papier", link: "/de/plugin-dev/rust/command/rock-paper-scissors", }, - { - text: "Inventare", - link: "/de/plugin-dev/rust/command/inventories", - }, ], }, + { + text: "Lizenzierung & Updates", + link: "/de/plugin-dev/rust/plugin-utils", + }, ], }, { text: "Python", - collapsed: false, + collapsed: true, items: [ { - text: "Quick Start", + text: "Schnellstart", link: "/de/plugin-dev/python/quick-start", }, { - text: "Basic Logic", + text: "Grundlogik", link: "/de/plugin-dev/python/basic-logic", }, + { + text: "Erster Befehl", + link: "/de/plugin-dev/python/first-command", + }, { text: "Events", link: "/de/plugin-dev/python/events", @@ -120,22 +300,72 @@ export const de = defineConfig({ }, { text: "C#", - collapsed: false, + collapsed: true, items: [ { - text: "Quick Start", + text: "Schnellstart", link: "/de/plugin-dev/csharp/quick-start", }, + { + text: "Erster Befehl", + link: "/de/plugin-dev/csharp/first-command", + }, ], }, { text: "C", - collapsed: false, + collapsed: true, items: [ { text: "Schnellstart", link: "/de/plugin-dev/c/quick-start", }, + { + text: "Erster Befehl", + link: "/de/plugin-dev/c/first-command", + }, + ], + }, + { + text: "Go", + collapsed: true, + items: [ + { + text: "Schnellstart", + link: "/de/plugin-dev/go/quick-start", + }, + { + text: "Grundlogik", + link: "/de/plugin-dev/go/basic-logic", + }, + { + text: "Erster Befehl", + link: "/de/plugin-dev/go/first-command", + }, + ], + }, + { + text: "Kotlin", + collapsed: true, + items: [ + { + text: "Schnellstart", + link: "/de/plugin-dev/kotlin/quick-start", + }, + { + text: "Erster Befehl", + link: "/de/plugin-dev/kotlin/first-command", + }, + ], + }, + { + text: "D", + collapsed: true, + items: [ + { + text: "Schnellstart", + link: "/de/plugin-dev/d/quick-start", + }, ], }, ], @@ -145,7 +375,7 @@ export const de = defineConfig({ items: [ { text: "Häufige Probleme", - link: "/de/troubleshooting/common_issues.md", + link: "/de/troubleshooting/common_issues", }, ], }, diff --git a/docs/.vitepress/en.ts b/docs/.vitepress/en.ts index 98122db..77271b8 100644 --- a/docs/.vitepress/en.ts +++ b/docs/.vitepress/en.ts @@ -359,6 +359,16 @@ export const en = defineConfig({ }, ], }, + { + text: "D", + collapsed: true, + items: [ + { + text: "Quick Start", + link: "/plugin-dev/d/quick-start", + }, + ], + }, ], }, { diff --git a/docs/.vitepress/ja_jp.ts b/docs/.vitepress/ja_jp.ts index b16f379..a15bbe4 100644 --- a/docs/.vitepress/ja_jp.ts +++ b/docs/.vitepress/ja_jp.ts @@ -5,13 +5,25 @@ export const jaJP = defineConfig({ description: "Rust で書かれた高性能 Minecraft サーバーソフトウェア", themeConfig: { - // https://vitepress.dev/reference/default-theme-config + siteTitle: "Pumpkin Docs", search: { provider: "local", }, nav: [ { - text: "ホーム", + text: "管理", + link: "/ja_jp/admin/introduction", + }, + { + text: "開発者・貢献者", + link: "/ja_jp/developer/introduction", + }, + { + text: "プラグイン", + link: "/ja_jp/plugin-dev/introduction", + }, + { + text: "ウェブサイト", link: "https://pumpkinmc.org/", }, ], @@ -23,34 +35,124 @@ export const jaJP = defineConfig({ ], }, { - text: "設定", + text: "管理", + collapsed: false, items: [ - { text: "はじめに", link: "/ja_jp/config/introduction" }, - { text: "基本設定", link: "/ja_jp/config/basic" }, - { text: "プロキシ", link: "/ja_jp/config/proxy" }, - { text: "認証", link: "/ja_jp/config/authentication" }, - { text: "圧縮", link: "/ja_jp/config/compression" }, - { text: "リソースパック", link: "/ja_jp/config/resource-pack" }, - { text: "コマンド", link: "/ja_jp/config/commands" }, - { text: "RCON", link: "/ja_jp/config/rcon" }, - { text: "PVP", link: "/ja_jp/config/pvp" }, - { text: "ロギング", link: "/ja_jp/config/logging" }, - { text: "Query", link: "/ja_jp/config/query" }, - { text: "LAN ブロードキャスト", link: "/ja_jp/config/lan-broadcast" }, + { text: "概要", link: "/ja_jp/admin/introduction" }, + { text: "Bukkit からの移行", link: "/ja_jp/admin/migrating-from-bukkit" }, + { + text: "設定", + collapsed: true, + items: [ + { text: "はじめに", link: "/ja_jp/config/introduction" }, + { text: "基本設定", link: "/ja_jp/config/basic" }, + { text: "ワールド", link: "/ja_jp/config/world" }, + { text: "チャット・スパム防止", link: "/ja_jp/config/chat" }, + { text: "コマンド", link: "/ja_jp/config/commands" }, + { text: "Bedrock & NetherNet", link: "/ja_jp/config/bedrock" }, + { text: "プロキシ", link: "/ja_jp/config/proxy" }, + { text: "認証", link: "/ja_jp/config/authentication" }, + { text: "パケットリミッター", link: "/ja_jp/config/packet-limiter" }, + { text: "圧縮", link: "/ja_jp/config/compression" }, + { text: "リソースパック", link: "/ja_jp/config/resource-pack" }, + { text: "プラグイン", link: "/ja_jp/config/plugins" }, + { text: "サーバーリンク", link: "/ja_jp/config/server-links" }, + { text: "プレイヤーデータ", link: "/ja_jp/config/player-data" }, + { text: "ロギング", link: "/ja_jp/config/logging" }, + { text: "Query", link: "/ja_jp/config/query" }, + { text: "RCON", link: "/ja_jp/config/rcon" }, + { text: "PVP", link: "/ja_jp/config/pvp" }, + { text: "LAN ブロードキャスト", link: "/ja_jp/config/lan-broadcast" }, + ], + }, ], }, { text: "開発者", items: [ - { text: "貢献ガイド", link: "/ja_jp/developer/contributing" }, { text: "はじめに", link: "/ja_jp/developer/introduction" }, + { text: "貢献ガイド", link: "/ja_jp/developer/contributing" }, + { text: "アーキテクチャ", link: "/ja_jp/developer/architecture" }, + { text: "データ抽出ツール", link: "/ja_jp/developer/extractor" }, + { text: "コード生成", link: "/ja_jp/developer/codegen" }, + { text: "バージョン移行", link: "/ja_jp/developer/porting-versions" }, { text: "ネットワーキング", - link: "/ja_jp/developer/networking/networking", + collapsed: true, items: [ { - text: "認証", - link: "/ja_jp/developer/networking/authentication", + text: "はじめに", + link: "/ja_jp/developer/networking/", + }, + { + text: "メタデータと同期データ", + link: "/ja_jp/developer/networking/metadata", + }, + { + text: "Java Edition", + collapsed: true, + items: [ + { + text: "概要", + link: "/ja_jp/developer/networking/java/overview", + }, + { + text: "パケットの追加", + link: "/ja_jp/developer/networking/java/adding-packets", + }, + { + text: "認証", + link: "/ja_jp/developer/networking/java/authentication", + }, + ], + }, + { + text: "Bedrock Edition", + collapsed: true, + items: [ + { + text: "概要", + link: "/ja_jp/developer/networking/bedrock/overview", + }, + { + text: "パケットの追加", + link: "/ja_jp/developer/networking/bedrock/adding-packets", + }, + { + text: "認証", + link: "/ja_jp/developer/networking/bedrock/authentication", + }, + { + text: "NetherNet", + link: "/ja_jp/developer/networking/bedrock/nethernet", + }, + ], + }, + { + text: "プロキシ転送", + collapsed: true, + items: [ + { + text: "概要", + link: "/ja_jp/developer/networking/proxy/", + }, + { + text: "BungeeCord", + link: "/ja_jp/developer/networking/proxy/bungeecord", + }, + { + text: "Velocity", + link: "/ja_jp/developer/networking/proxy/velocity", + }, + { + text: "Vine", + link: "/ja_jp/developer/networking/proxy/vine", + }, + ], + }, + { + text: "Query (GameSpy4)", + link: "/ja_jp/developer/networking/query", }, { text: "RCON", @@ -58,7 +160,51 @@ export const jaJP = defineConfig({ }, ], }, - { text: "ワールド", link: "/ja_jp/developer/world" }, + { + text: "ブロック", + collapsed: true, + items: [ + { text: "概要", link: "/ja_jp/developer/blocks/" }, + { text: "ブロックの追加", link: "/ja_jp/developer/blocks/adding-blocks" }, + ], + }, + { + text: "アイテム", + collapsed: true, + items: [ + { text: "概要", link: "/ja_jp/developer/items/" }, + { text: "アイテムの追加", link: "/ja_jp/developer/items/adding-items" }, + ], + }, + { + text: "エンティティ", + collapsed: true, + items: [ + { text: "概要と階層", link: "/ja_jp/developer/entities/" }, + { text: "スポーンと追跡", link: "/ja_jp/developer/entities/spawning-and-tracking" }, + { text: "Mob AI とゴール", link: "/ja_jp/developer/entities/ai-and-mobs" }, + ], + }, + { + text: "コマンド", + collapsed: true, + items: [ + { text: "概要とディスパッチャー", link: "/ja_jp/developer/commands/" }, + { text: "コマンドの追加", link: "/ja_jp/developer/commands/adding-commands" }, + { text: "引数とサジェスト", link: "/ja_jp/developer/commands/arguments-and-suggestions" }, + { text: "実行とコンテキスト", link: "/ja_jp/developer/commands/execution-and-context" }, + ], + }, + { + text: "プラグインエンジン", + collapsed: true, + items: [ + { text: "概要", link: "/ja_jp/developer/plugins/" }, + { text: "プラグインローダーの追加", link: "/ja_jp/developer/plugins/loaders" }, + { text: "WASM 署名と検証", link: "/ja_jp/developer/plugins/wasm-signing" }, + ], + }, + { text: "ワールドエンジン", link: "/ja_jp/developer/world" }, { text: "モバイル開発", link: "/ja_jp/developer/mobile" }, ], }, @@ -69,9 +215,35 @@ export const jaJP = defineConfig({ text: "はじめに", link: "/ja_jp/plugin-dev/introduction", }, + { + text: "Bukkit からの移行", + collapsed: true, + items: [ + { + text: "概要", + link: "/ja_jp/plugin-dev/migrating-from-bukkit/", + }, + { + text: "コマンド", + link: "/ja_jp/plugin-dev/migrating-from-bukkit/commands", + }, + { + text: "イベント", + link: "/ja_jp/plugin-dev/migrating-from-bukkit/events", + }, + { + text: "インベントリ & GUI", + link: "/ja_jp/plugin-dev/migrating-from-bukkit/inventories", + }, + { + text: "設定とデータ", + link: "/ja_jp/plugin-dev/migrating-from-bukkit/configuration", + }, + ], + }, { text: "Rust", - collapsed: false, + collapsed: true, items: [ { text: "プロジェクトの作成", @@ -98,11 +270,15 @@ export const jaJP = defineConfig({ }, ], }, + { + text: "ライセンスとアップデート", + link: "/ja_jp/plugin-dev/rust/plugin-utils", + }, ], }, { text: "Python", - collapsed: false, + collapsed: true, items: [ { text: "クイックスタート", @@ -112,6 +288,10 @@ export const jaJP = defineConfig({ text: "基本ロジック", link: "/ja_jp/plugin-dev/python/basic-logic", }, + { + text: "最初のコマンド", + link: "/ja_jp/plugin-dev/python/first-command", + }, { text: "イベント", link: "/ja_jp/plugin-dev/python/events", @@ -120,27 +300,35 @@ export const jaJP = defineConfig({ }, { text: "C#", - collapsed: false, + collapsed: true, items: [ { text: "クイックスタート", link: "/ja_jp/plugin-dev/csharp/quick-start", }, + { + text: "最初のコマンド", + link: "/ja_jp/plugin-dev/csharp/first-command", + }, ], }, { text: "C", - collapsed: false, + collapsed: true, items: [ { text: "クイックスタート", link: "/ja_jp/plugin-dev/c/quick-start", }, + { + text: "最初のコマンド", + link: "/ja_jp/plugin-dev/c/first-command", + }, ], }, { text: "Go", - collapsed: false, + collapsed: true, items: [ { text: "クイックスタート", @@ -150,6 +338,34 @@ export const jaJP = defineConfig({ text: "基本ロジック", link: "/ja_jp/plugin-dev/go/basic-logic", }, + { + text: "最初のコマンド", + link: "/ja_jp/plugin-dev/go/first-command", + }, + ], + }, + { + text: "Kotlin", + collapsed: true, + items: [ + { + text: "クイックスタート", + link: "/ja_jp/plugin-dev/kotlin/quick-start", + }, + { + text: "最初のコマンド", + link: "/ja_jp/plugin-dev/kotlin/first-command", + }, + ], + }, + { + text: "D", + collapsed: true, + items: [ + { + text: "クイックスタート", + link: "/ja_jp/plugin-dev/d/quick-start", + }, ], }, ], @@ -159,7 +375,7 @@ export const jaJP = defineConfig({ items: [ { text: "よくある問題", - link: "/ja_jp/troubleshooting/common_issues.md", + link: "/ja_jp/troubleshooting/common_issues", }, ], }, @@ -172,16 +388,16 @@ export const jaJP = defineConfig({ logo: "/assets/favicon.ico", footer: { - message: "MIT ライセンスの下で公開されています。", + message: "MIT ライセンスの下でリリースされています。", copyright: `Copyright © 2024-${new Date().getFullYear()} Aleksandr Medvedev`, }, editLink: { pattern: "https://github.com/Pumpkin-MC/Pumpkin-Docs/blob/master/docs/:path", - text: "GitHub でこのページを編集する", + text: "GitHub でこのページを編集", }, lastUpdated: { - text: "最終更新", + text: "最終更新日", formatOptions: { dateStyle: "medium", timeStyle: "medium", diff --git a/docs/.vitepress/nl.ts b/docs/.vitepress/nl.ts index cb5ecbc..74ded7e 100644 --- a/docs/.vitepress/nl.ts +++ b/docs/.vitepress/nl.ts @@ -5,14 +5,26 @@ export const nl = defineConfig({ description: "Een Minecraft-serversoftware met hoge prestaties, geschreven in Rust", themeConfig: { - // https://vitepress.dev/reference/default-theme-config + siteTitle: "Pumpkin Docs", search: { provider: "local", }, nav: [ { - text: "Documentatie", - link: "/nl/about/introduction", + text: "Beheer", + link: "/nl/admin/introduction", + }, + { + text: "Bijdragers", + link: "/nl/developer/introduction", + }, + { + text: "Plugins", + link: "/nl/plugin-dev/introduction", + }, + { + text: "Website", + link: "https://pumpkinmc.org/", }, ], sidebar: [ @@ -23,36 +35,176 @@ export const nl = defineConfig({ ], }, { - text: "Configuratie", + text: "Beheer", + collapsed: false, items: [ - { text: "Introductie", link: "/nl/config/introduction" }, - { text: "Basis", link: "/nl/config/basic" }, - { text: "Proxy", link: "/nl/config/proxy" }, - { text: "Authenticatie", link: "/nl/config/authentication" }, - { text: "Compressie", link: "/nl/config/compression" }, - { text: "Resource Pack", link: "/nl/config/resource-pack" }, - { text: "Commando's", link: "/nl/config/commands" }, - { text: "RCON", link: "/nl/config/rcon" }, - { text: "PVP", link: "/nl/config/pvp" }, - { text: "Logbestanden", link: "/nl/config/logging" }, - { text: "Query", link: "/nl/config/query" }, - { text: "LAN Broadcast", link: "/nl/config/lan-broadcast" }, + { text: "Overzicht", link: "/nl/admin/introduction" }, + { text: "Migreren van Bukkit", link: "/nl/admin/migrating-from-bukkit" }, + { + text: "Configuratie", + collapsed: true, + items: [ + { text: "Introductie", link: "/nl/config/introduction" }, + { text: "Basis", link: "/nl/config/basic" }, + { text: "Wereld", link: "/nl/config/world" }, + { text: "Chat & Anti-Spam", link: "/nl/config/chat" }, + { text: "Commando's", link: "/nl/config/commands" }, + { text: "Bedrock & NetherNet", link: "/nl/config/bedrock" }, + { text: "Proxy", link: "/nl/config/proxy" }, + { text: "Authenticatie", link: "/nl/config/authentication" }, + { text: "Pakketlimiet", link: "/nl/config/packet-limiter" }, + { text: "Compressie", link: "/nl/config/compression" }, + { text: "Resource Pack", link: "/nl/config/resource-pack" }, + { text: "Plugins", link: "/nl/config/plugins" }, + { text: "Serverlinks", link: "/nl/config/server-links" }, + { text: "Spelergegevens", link: "/nl/config/player-data" }, + { text: "Logbestanden", link: "/nl/config/logging" }, + { text: "Query", link: "/nl/config/query" }, + { text: "RCON", link: "/nl/config/rcon" }, + { text: "PVP", link: "/nl/config/pvp" }, + { text: "LAN Broadcast", link: "/nl/config/lan-broadcast" }, + ], + }, ], }, { - text: "Ontwikkelaars", + text: "Bijdragers", items: [ - { text: "Bijdragen", link: "/nl/developer/contributing" }, { text: "Introductie", link: "/nl/developer/introduction" }, + { text: "Bijdragegids", link: "/nl/developer/contributing" }, + { text: "Architectuur", link: "/nl/developer/architecture" }, + { text: "Gegevensextractor", link: "/nl/developer/extractor" }, + { text: "Codegeneratie", link: "/nl/developer/codegen" }, + { text: "Versies overzetten", link: "/nl/developer/porting-versions" }, { text: "Netwerken", - link: "/nl/developer/networking/networking", + collapsed: true, items: [ - { text: "Authenticatie", link: "/nl/developer/networking/authentication" }, - { text: "RCON", link: "/nl/developer/networking/rcon" }, - ] + { + text: "Introductie", + link: "/nl/developer/networking/", + }, + { + text: "Metadata & Gesynchroniseerde Gegevens", + link: "/nl/developer/networking/metadata", + }, + { + text: "Java Edition", + collapsed: true, + items: [ + { + text: "Overzicht", + link: "/nl/developer/networking/java/overview", + }, + { + text: "Pakket toevoegen", + link: "/nl/developer/networking/java/adding-packets", + }, + { + text: "Authenticatie", + link: "/nl/developer/networking/java/authentication", + }, + ], + }, + { + text: "Bedrock Edition", + collapsed: true, + items: [ + { + text: "Overzicht", + link: "/nl/developer/networking/bedrock/overview", + }, + { + text: "Pakket toevoegen", + link: "/nl/developer/networking/bedrock/adding-packets", + }, + { + text: "Authenticatie", + link: "/nl/developer/networking/bedrock/authentication", + }, + { + text: "NetherNet", + link: "/nl/developer/networking/bedrock/nethernet", + }, + ], + }, + { + text: "Proxy-doorsturing", + collapsed: true, + items: [ + { + text: "Overzicht", + link: "/nl/developer/networking/proxy/", + }, + { + text: "BungeeCord", + link: "/nl/developer/networking/proxy/bungeecord", + }, + { + text: "Velocity", + link: "/nl/developer/networking/proxy/velocity", + }, + { + text: "Vine", + link: "/nl/developer/networking/proxy/vine", + }, + ], + }, + { + text: "Query (GameSpy4)", + link: "/nl/developer/networking/query", + }, + { + text: "RCON", + link: "/nl/developer/networking/rcon", + }, + ], }, - { text: "Wereld", link: "/nl/developer/world" }, + { + text: "Blokken", + collapsed: true, + items: [ + { text: "Overzicht", link: "/nl/developer/blocks/" }, + { text: "Blok toevoegen", link: "/nl/developer/blocks/adding-blocks" }, + ], + }, + { + text: "Items", + collapsed: true, + items: [ + { text: "Overzicht", link: "/nl/developer/items/" }, + { text: "Item toevoegen", link: "/nl/developer/items/adding-items" }, + ], + }, + { + text: "Entities", + collapsed: true, + items: [ + { text: "Overzicht & Hiërarchie", link: "/nl/developer/entities/" }, + { text: "Spawning & Tracking", link: "/nl/developer/entities/spawning-and-tracking" }, + { text: "Mob-AI & Doelen", link: "/nl/developer/entities/ai-and-mobs" }, + ], + }, + { + text: "Commando's", + collapsed: true, + items: [ + { text: "Overzicht & Dispatcher", link: "/nl/developer/commands/" }, + { text: "Commando toevoegen", link: "/nl/developer/commands/adding-commands" }, + { text: "Argumenten & Suggesties", link: "/nl/developer/commands/arguments-and-suggestions" }, + { text: "Uitvoering & Context", link: "/nl/developer/commands/execution-and-context" }, + ], + }, + { + text: "Plugin-engine", + collapsed: true, + items: [ + { text: "Overzicht", link: "/nl/developer/plugins/" }, + { text: "Plugin-loader toevoegen", link: "/nl/developer/plugins/loaders" }, + { text: "WASM-ondertekening & Verificatie", link: "/nl/developer/plugins/wasm-signing" }, + ], + }, + { text: "Wereld-engine", link: "/nl/developer/world" }, { text: "Mobiele ontwikkeling", link: "/nl/developer/mobile" }, ], }, @@ -63,9 +215,35 @@ export const nl = defineConfig({ text: "Introductie", link: "/nl/plugin-dev/introduction", }, + { + text: "Migreren van Bukkit", + collapsed: true, + items: [ + { + text: "Overzicht", + link: "/nl/plugin-dev/migrating-from-bukkit/", + }, + { + text: "Commando's", + link: "/nl/plugin-dev/migrating-from-bukkit/commands", + }, + { + text: "Events", + link: "/nl/plugin-dev/migrating-from-bukkit/events", + }, + { + text: "Inventarissen & GUI's", + link: "/nl/plugin-dev/migrating-from-bukkit/inventories", + }, + { + text: "Configuratie & Gegevens", + link: "/nl/plugin-dev/migrating-from-bukkit/configuration", + }, + ], + }, { text: "Rust", - link: "/nl/plugin-dev/rust/introduction", + collapsed: true, items: [ { text: "Project aanmaken", @@ -76,23 +254,44 @@ export const nl = defineConfig({ link: "/nl/plugin-dev/rust/basic-logic", }, { - text: "Join Event", - link: "/nl/plugin-dev/rust/join-event", + text: "Events", + link: "/nl/plugin-dev/rust/events", + }, + { + text: "Commando's", + items: [ + { + text: "Eerste commando", + link: "/nl/plugin-dev/rust/command/first-command", + }, + { + text: "Steen-papier-schaar", + link: "/nl/plugin-dev/rust/command/rock-paper-scissors", + }, + ], + }, + { + text: "Licenties & Updates", + link: "/nl/plugin-dev/rust/plugin-utils", }, ], }, { text: "Python", - collapsed: false, + collapsed: true, items: [ { text: "Quick Start", link: "/nl/plugin-dev/python/quick-start", }, { - text: "Basic Logic", + text: "Basislogica", link: "/nl/plugin-dev/python/basic-logic", }, + { + text: "Eerste commando", + link: "/nl/plugin-dev/python/first-command", + }, { text: "Events", link: "/nl/plugin-dev/python/events", @@ -101,22 +300,72 @@ export const nl = defineConfig({ }, { text: "C#", - collapsed: false, + collapsed: true, items: [ { text: "Quick Start", link: "/nl/plugin-dev/csharp/quick-start", }, + { + text: "Eerste commando", + link: "/nl/plugin-dev/csharp/first-command", + }, ], }, { text: "C", - collapsed: false, + collapsed: true, items: [ { - text: "Snelstart", + text: "Quick Start", link: "/nl/plugin-dev/c/quick-start", }, + { + text: "Eerste commando", + link: "/nl/plugin-dev/c/first-command", + }, + ], + }, + { + text: "Go", + collapsed: true, + items: [ + { + text: "Quick Start", + link: "/nl/plugin-dev/go/quick-start", + }, + { + text: "Basislogica", + link: "/nl/plugin-dev/go/basic-logic", + }, + { + text: "Eerste commando", + link: "/nl/plugin-dev/go/first-command", + }, + ], + }, + { + text: "Kotlin", + collapsed: true, + items: [ + { + text: "Quick Start", + link: "/nl/plugin-dev/kotlin/quick-start", + }, + { + text: "Eerste commando", + link: "/nl/plugin-dev/kotlin/first-command", + }, + ], + }, + { + text: "D", + collapsed: true, + items: [ + { + text: "Quick Start", + link: "/nl/plugin-dev/d/quick-start", + }, ], }, ], @@ -126,7 +375,7 @@ export const nl = defineConfig({ items: [ { text: "Veelvoorkomende problemen", - link: "/nl/troubleshooting/common_issues.md", + link: "/nl/troubleshooting/common_issues", }, ], }, @@ -155,5 +404,5 @@ export const nl = defineConfig({ }, }, outline: "deep", - } + }, }); diff --git a/docs/.vitepress/pt.ts b/docs/.vitepress/pt.ts index 7cb5106..3dbf0e9 100644 --- a/docs/.vitepress/pt.ts +++ b/docs/.vitepress/pt.ts @@ -1,18 +1,30 @@ import { defineConfig } from "vitepress"; export const pt = defineConfig({ - lang: "pt-BR", - description: "Um servidor Minecraft de alto desempenho escrito em Rust", + lang: "pt", + description: "Um software de servidor Minecraft de alta performance escrito em Rust", themeConfig: { - // https://vitepress.dev/reference/default-theme-config + siteTitle: "Pumpkin Docs", search: { provider: "local", }, nav: [ { - text: "Documentação", - link: "/pt/about/introduction", + text: "Administração", + link: "/pt/admin/introduction", + }, + { + text: "Contribuidores", + link: "/pt/developer/introduction", + }, + { + text: "Plugins", + link: "/pt/plugin-dev/introduction", + }, + { + text: "Site", + link: "https://pumpkinmc.org/", }, ], sidebar: [ @@ -23,36 +35,176 @@ export const pt = defineConfig({ ], }, { - text: "Configuração", + text: "Administração", + collapsed: false, items: [ - { text: "Introdução", link: "/pt/config/introduction" }, - { text: "Básico", link: "/pt/config/basic" }, - { text: "Proxy", link: "/pt/config/proxy" }, - { text: "Autenticação", link: "/pt/config/authentication" }, - { text: "Compressão", link: "/pt/config/compression" }, - { text: "Pacote de Recursos", link: "/pt/config/resource-pack" }, - { text: "Comandos", link: "/pt/config/commands" }, - { text: "RCON", link: "/pt/config/rcon" }, - { text: "PVP", link: "/pt/config/pvp" }, - { text: "Log", link: "/pt/config/logging" }, - { text: "Query", link: "/pt/config/query" }, - { text: "Transmissão LAN", link: "/pt/config/lan-broadcast" }, + { text: "Visão Geral", link: "/pt/admin/introduction" }, + { text: "Migrando do Bukkit", link: "/pt/admin/migrating-from-bukkit" }, + { + text: "Configuração", + collapsed: true, + items: [ + { text: "Introdução", link: "/pt/config/introduction" }, + { text: "Básico", link: "/pt/config/basic" }, + { text: "Mundo", link: "/pt/config/world" }, + { text: "Chat e Anti-Spam", link: "/pt/config/chat" }, + { text: "Comandos", link: "/pt/config/commands" }, + { text: "Bedrock e NetherNet", link: "/pt/config/bedrock" }, + { text: "Proxy", link: "/pt/config/proxy" }, + { text: "Autenticação", link: "/pt/config/authentication" }, + { text: "Limitador de Pacotes", link: "/pt/config/packet-limiter" }, + { text: "Compressão", link: "/pt/config/compression" }, + { text: "Pacote de Recursos", link: "/pt/config/resource-pack" }, + { text: "Plugins", link: "/pt/config/plugins" }, + { text: "Links do Servidor", link: "/pt/config/server-links" }, + { text: "Dados do Jogador", link: "/pt/config/player-data" }, + { text: "Registros (Logging)", link: "/pt/config/logging" }, + { text: "Query", link: "/pt/config/query" }, + { text: "RCON", link: "/pt/config/rcon" }, + { text: "PVP", link: "/pt/config/pvp" }, + { text: "Transmissão LAN", link: "/pt/config/lan-broadcast" }, + ], + }, ], }, { - text: "Desenvolvedores", + text: "Contribuidores", items: [ - { text: "Contribuindo", link: "/pt/developer/contributing" }, { text: "Introdução", link: "/pt/developer/introduction" }, + { text: "Guia de Contribuição", link: "/pt/developer/contributing" }, + { text: "Arquitetura", link: "/pt/developer/architecture" }, + { text: "Extrator de Dados", link: "/pt/developer/extractor" }, + { text: "Geração de Código", link: "/pt/developer/codegen" }, + { text: "Portando Versões", link: "/pt/developer/porting-versions" }, { text: "Rede", - link: "/pt/developer/networking/networking", + collapsed: true, items: [ - { text: "Autenticação", link: "/pt/developer/networking/authentication" }, - { text: "RCON", link: "/pt/developer/networking/rcon" }, - ] + { + text: "Introdução", + link: "/pt/developer/networking/", + }, + { + text: "Metadados e Dados Sincronizados", + link: "/pt/developer/networking/metadata", + }, + { + text: "Java Edition", + collapsed: true, + items: [ + { + text: "Visão Geral", + link: "/pt/developer/networking/java/overview", + }, + { + text: "Adicionando um Pacote", + link: "/pt/developer/networking/java/adding-packets", + }, + { + text: "Autenticação", + link: "/pt/developer/networking/java/authentication", + }, + ], + }, + { + text: "Bedrock Edition", + collapsed: true, + items: [ + { + text: "Visão Geral", + link: "/pt/developer/networking/bedrock/overview", + }, + { + text: "Adicionando um Pacote", + link: "/pt/developer/networking/bedrock/adding-packets", + }, + { + text: "Autenticação", + link: "/pt/developer/networking/bedrock/authentication", + }, + { + text: "NetherNet", + link: "/pt/developer/networking/bedrock/nethernet", + }, + ], + }, + { + text: "Encaminhamento de Proxy", + collapsed: true, + items: [ + { + text: "Visão Geral", + link: "/pt/developer/networking/proxy/", + }, + { + text: "BungeeCord", + link: "/pt/developer/networking/proxy/bungeecord", + }, + { + text: "Velocity", + link: "/pt/developer/networking/proxy/velocity", + }, + { + text: "Vine", + link: "/pt/developer/networking/proxy/vine", + }, + ], + }, + { + text: "Query (GameSpy4)", + link: "/pt/developer/networking/query", + }, + { + text: "RCON", + link: "/pt/developer/networking/rcon", + }, + ], }, - { text: "Mundo", link: "/pt/developer/world" }, + { + text: "Blocos", + collapsed: true, + items: [ + { text: "Visão Geral", link: "/pt/developer/blocks/" }, + { text: "Adicionando um Bloco", link: "/pt/developer/blocks/adding-blocks" }, + ], + }, + { + text: "Itens", + collapsed: true, + items: [ + { text: "Visão Geral", link: "/pt/developer/items/" }, + { text: "Adicionando um Item", link: "/pt/developer/items/adding-items" }, + ], + }, + { + text: "Entidades", + collapsed: true, + items: [ + { text: "Visão Geral e Hierarquia", link: "/pt/developer/entities/" }, + { text: "Spawning e Rastreamento", link: "/pt/developer/entities/spawning-and-tracking" }, + { text: "IA e Objetivos de Mobs", link: "/pt/developer/entities/ai-and-mobs" }, + ], + }, + { + text: "Comandos", + collapsed: true, + items: [ + { text: "Visão Geral e Despachante", link: "/pt/developer/commands/" }, + { text: "Adicionando um Comando", link: "/pt/developer/commands/adding-commands" }, + { text: "Argumentos e Sugestões", link: "/pt/developer/commands/arguments-and-suggestions" }, + { text: "Execução e Contexto", link: "/pt/developer/commands/execution-and-context" }, + ], + }, + { + text: "Motor de Plugins", + collapsed: true, + items: [ + { text: "Visão Geral", link: "/pt/developer/plugins/" }, + { text: "Adicionando um Carregador de Plugins", link: "/pt/developer/plugins/loaders" }, + { text: "Assinatura e Verificação WASM", link: "/pt/developer/plugins/wasm-signing" }, + ], + }, + { text: "Motor do Mundo", link: "/pt/developer/world" }, { text: "Desenvolvimento Mobile", link: "/pt/developer/mobile" }, ], }, @@ -64,59 +216,156 @@ export const pt = defineConfig({ link: "/pt/plugin-dev/introduction", }, { - text: "Rust", - link: "/pt/plugin-dev/rust/introduction", + text: "Migrando do Bukkit", + collapsed: true, items: [ { - text: "Criando um projeto", + text: "Visão Geral", + link: "/pt/plugin-dev/migrating-from-bukkit/", + }, + { + text: "Comandos", + link: "/pt/plugin-dev/migrating-from-bukkit/commands", + }, + { + text: "Eventos", + link: "/pt/plugin-dev/migrating-from-bukkit/events", + }, + { + text: "Inventários e GUIs", + link: "/pt/plugin-dev/migrating-from-bukkit/inventories", + }, + { + text: "Configuração e Dados", + link: "/pt/plugin-dev/migrating-from-bukkit/configuration", + }, + ], + }, + { + text: "Rust", + collapsed: true, + items: [ + { + text: "Criando o Projeto", link: "/pt/plugin-dev/rust/creating-project", }, { - text: "Lógica básica", + text: "Lógica Básica", link: "/pt/plugin-dev/rust/basic-logic", }, { - text: "Gerenciador de eventos", - link: "/pt/plugin-dev/rust/join-event", + text: "Eventos", + link: "/pt/plugin-dev/rust/events", + }, + { + text: "Comandos", + items: [ + { + text: "Primeiro Comando", + link: "/pt/plugin-dev/rust/command/first-command", + }, + { + text: "Pedra, Papel e Tesoura", + link: "/pt/plugin-dev/rust/command/rock-paper-scissors", + }, + ], + }, + { + text: "Licenciamento e Atualizações", + link: "/pt/plugin-dev/rust/plugin-utils", }, ], }, { text: "Python", - collapsed: false, + collapsed: true, items: [ { - text: "Quick Start", + text: "Início Rápido", link: "/pt/plugin-dev/python/quick-start", }, { - text: "Basic Logic", + text: "Lógica Básica", link: "/pt/plugin-dev/python/basic-logic", }, { - text: "Events", + text: "Primeiro Comando", + link: "/pt/plugin-dev/python/first-command", + }, + { + text: "Eventos", link: "/pt/plugin-dev/python/events", }, ], }, { text: "C#", - collapsed: false, + collapsed: true, items: [ { - text: "Quick Start", + text: "Início Rápido", link: "/pt/plugin-dev/csharp/quick-start", }, + { + text: "Primeiro Comando", + link: "/pt/plugin-dev/csharp/first-command", + }, ], }, { text: "C", - collapsed: false, + collapsed: true, items: [ { text: "Início Rápido", link: "/pt/plugin-dev/c/quick-start", }, + { + text: "Primeiro Comando", + link: "/pt/plugin-dev/c/first-command", + }, + ], + }, + { + text: "Go", + collapsed: true, + items: [ + { + text: "Início Rápido", + link: "/pt/plugin-dev/go/quick-start", + }, + { + text: "Lógica Básica", + link: "/pt/plugin-dev/go/basic-logic", + }, + { + text: "Primeiro Comando", + link: "/pt/plugin-dev/go/first-command", + }, + ], + }, + { + text: "Kotlin", + collapsed: true, + items: [ + { + text: "Início Rápido", + link: "/pt/plugin-dev/kotlin/quick-start", + }, + { + text: "Primeiro Comando", + link: "/pt/plugin-dev/kotlin/first-command", + }, + ], + }, + { + text: "D", + collapsed: true, + items: [ + { + text: "Início Rápido", + link: "/pt/plugin-dev/d/quick-start", + }, ], }, ], @@ -126,7 +375,7 @@ export const pt = defineConfig({ items: [ { text: "Problemas Comuns", - link: "/pt/troubleshooting/common_issues.md", + link: "/pt/troubleshooting/common_issues", }, ], }, @@ -139,13 +388,13 @@ export const pt = defineConfig({ logo: "/assets/favicon.ico", footer: { - message: "Distribuído sob a Licença MIT.", + message: "Lançado sob a licença MIT.", copyright: `Copyright © 2024-${new Date().getFullYear()} Aleksandr Medvedev`, }, editLink: { pattern: "https://github.com/Pumpkin-MC/Pumpkin-Docs/blob/master/docs/:path", - text: "Edite esta página no GitHub", + text: "Editar esta página no GitHub", }, lastUpdated: { text: "Atualizado em", @@ -155,5 +404,5 @@ export const pt = defineConfig({ }, }, outline: "deep", - } -}); \ No newline at end of file + }, +}); diff --git a/docs/.vitepress/tr_tr.ts b/docs/.vitepress/tr_tr.ts index ecb8361..051fed7 100644 --- a/docs/.vitepress/tr_tr.ts +++ b/docs/.vitepress/tr_tr.ts @@ -2,16 +2,28 @@ import { defineConfig } from "vitepress"; export const trTR = defineConfig({ lang: "tr-TR", - description: "Rust ile yazılmış yüksek performanslı bir Minecraft sunucu yazılımı", + description: "Rust ile yazılmış yüksek performanslı Minecraft sunucu yazılımı", themeConfig: { - // https://vitepress.dev/reference/default-theme-config + siteTitle: "Pumpkin Docs", search: { provider: "local", }, nav: [ { - text: "Ana Sayfa", + text: "Yönetim", + link: "/tr_TR/admin/introduction", + }, + { + text: "Geliştiriciler", + link: "/tr_TR/developer/introduction", + }, + { + text: "Eklentiler", + link: "/tr_TR/plugin-dev/introduction", + }, + { + text: "Web Sitesi", link: "https://pumpkinmc.org/", }, ], @@ -23,34 +35,124 @@ export const trTR = defineConfig({ ], }, { - text: "Yapılandırma", + text: "Yönetim", + collapsed: false, items: [ - { text: "Giriş", link: "/tr_TR/config/introduction" }, - { text: "Temel", link: "/tr_TR/config/basic" }, - { text: "Proxy", link: "/tr_TR/config/proxy" }, - { text: "Kimlik Doğrulama", link: "/tr_TR/config/authentication" }, - { text: "Sıkıştırma", link: "/tr_TR/config/compression" }, - { text: "Kaynak Paketi", link: "/tr_TR/config/resource-pack" }, - { text: "Komutlar", link: "/tr_TR/config/commands" }, - { text: "RCON", link: "/tr_TR/config/rcon" }, - { text: "PVP", link: "/tr_TR/config/pvp" }, - { text: "Günlükleme", link: "/tr_TR/config/logging" }, - { text: "Query", link: "/tr_TR/config/query" }, - { text: "LAN Yayını", link: "/tr_TR/config/lan-broadcast" }, + { text: "Genel Bakış", link: "/tr_TR/admin/introduction" }, + { text: "Bukkit'ten Geçiş", link: "/tr_TR/admin/migrating-from-bukkit" }, + { + text: "Yapılandırma", + collapsed: true, + items: [ + { text: "Giriş", link: "/tr_TR/config/introduction" }, + { text: "Temel", link: "/tr_TR/config/basic" }, + { text: "Dünya", link: "/tr_TR/config/world" }, + { text: "Sohbet & Anti-Spam", link: "/tr_TR/config/chat" }, + { text: "Komutlar", link: "/tr_TR/config/commands" }, + { text: "Bedrock & NetherNet", link: "/tr_TR/config/bedrock" }, + { text: "Proxy", link: "/tr_TR/config/proxy" }, + { text: "Kimlik Doğrulama", link: "/tr_TR/config/authentication" }, + { text: "Paket Sınırlayıcı", link: "/tr_TR/config/packet-limiter" }, + { text: "Sıkıştırma", link: "/tr_TR/config/compression" }, + { text: "Kaynak Paketi", link: "/tr_TR/config/resource-pack" }, + { text: "Eklentiler", link: "/tr_TR/config/plugins" }, + { text: "Sunucu Bağlantıları", link: "/tr_TR/config/server-links" }, + { text: "Oyuncu Verileri", link: "/tr_TR/config/player-data" }, + { text: "Günlük Kaydı (Logging)", link: "/tr_TR/config/logging" }, + { text: "Query", link: "/tr_TR/config/query" }, + { text: "RCON", link: "/tr_TR/config/rcon" }, + { text: "PVP", link: "/tr_TR/config/pvp" }, + { text: "LAN Yayını", link: "/tr_TR/config/lan-broadcast" }, + ], + }, ], }, { text: "Geliştiriciler", items: [ - { text: "Katkıda Bulunma", link: "/tr_TR/developer/contributing" }, { text: "Giriş", link: "/tr_TR/developer/introduction" }, + { text: "Katkı Kılavuzu", link: "/tr_TR/developer/contributing" }, + { text: "Mimari", link: "/tr_TR/developer/architecture" }, + { text: "Veri Çıkarıcı", link: "/tr_TR/developer/extractor" }, + { text: "Kod Üretimi", link: "/tr_TR/developer/codegen" }, + { text: "Sürüm Yükseltme", link: "/tr_TR/developer/porting-versions" }, { - text: "Ağ", - link: "/tr_TR/developer/networking/networking", + text: "Ağ İletişimi", + collapsed: true, items: [ { - text: "Kimlik Doğrulama", - link: "/tr_TR/developer/networking/authentication", + text: "Giriş", + link: "/tr_TR/developer/networking/", + }, + { + text: "Meta Veriler & Senkronize Veriler", + link: "/tr_TR/developer/networking/metadata", + }, + { + text: "Java Edition", + collapsed: true, + items: [ + { + text: "Genel Bakış", + link: "/tr_TR/developer/networking/java/overview", + }, + { + text: "Paket Ekleme", + link: "/tr_TR/developer/networking/java/adding-packets", + }, + { + text: "Kimlik Doğrulama", + link: "/tr_TR/developer/networking/java/authentication", + }, + ], + }, + { + text: "Bedrock Edition", + collapsed: true, + items: [ + { + text: "Genel Bakış", + link: "/tr_TR/developer/networking/bedrock/overview", + }, + { + text: "Paket Ekleme", + link: "/tr_TR/developer/networking/bedrock/adding-packets", + }, + { + text: "Kimlik Doğrulama", + link: "/tr_TR/developer/networking/bedrock/authentication", + }, + { + text: "NetherNet", + link: "/tr_TR/developer/networking/bedrock/nethernet", + }, + ], + }, + { + text: "Proxy Yönlendirme", + collapsed: true, + items: [ + { + text: "Genel Bakış", + link: "/tr_TR/developer/networking/proxy/", + }, + { + text: "BungeeCord", + link: "/tr_TR/developer/networking/proxy/bungeecord", + }, + { + text: "Velocity", + link: "/tr_TR/developer/networking/proxy/velocity", + }, + { + text: "Vine", + link: "/tr_TR/developer/networking/proxy/vine", + }, + ], + }, + { + text: "Query (GameSpy4)", + link: "/tr_TR/developer/networking/query", }, { text: "RCON", @@ -58,8 +160,52 @@ export const trTR = defineConfig({ }, ], }, - { text: "Dünya", link: "/tr_TR/developer/world" }, - { text: "Mobil geliştirme", link: "/tr_TR/developer/mobile" }, + { + text: "Bloklar", + collapsed: true, + items: [ + { text: "Genel Bakış", link: "/tr_TR/developer/blocks/" }, + { text: "Blok Ekleme", link: "/tr_TR/developer/blocks/adding-blocks" }, + ], + }, + { + text: "Eşyalar", + collapsed: true, + items: [ + { text: "Genel Bakış", link: "/tr_TR/developer/items/" }, + { text: "Eşya Ekleme", link: "/tr_TR/developer/items/adding-items" }, + ], + }, + { + text: "Varlıklar", + collapsed: true, + items: [ + { text: "Genel Bakış & Hiyerarşi", link: "/tr_TR/developer/entities/" }, + { text: "Oluşum & Takip", link: "/tr_TR/developer/entities/spawning-and-tracking" }, + { text: "Yaratık Yapay Zekası & Hedefler", link: "/tr_TR/developer/entities/ai-and-mobs" }, + ], + }, + { + text: "Komutlar", + collapsed: true, + items: [ + { text: "Genel Bakış & Dağıtıcı", link: "/tr_TR/developer/commands/" }, + { text: "Komut Ekleme", link: "/tr_TR/developer/commands/adding-commands" }, + { text: "Argümanlar & Öneriler", link: "/tr_TR/developer/commands/arguments-and-suggestions" }, + { text: "Çalıştırma & Bağlam", link: "/tr_TR/developer/commands/execution-and-context" }, + ], + }, + { + text: "Eklenti Motoru", + collapsed: true, + items: [ + { text: "Genel Bakış", link: "/tr_TR/developer/plugins/" }, + { text: "Eklenti Yükleyici Ekleme", link: "/tr_TR/developer/plugins/loaders" }, + { text: "WASM İmzalama & Doğrulama", link: "/tr_TR/developer/plugins/wasm-signing" }, + ], + }, + { text: "Dünya Motoru", link: "/tr_TR/developer/world" }, + { text: "Mobil Geliştirme", link: "/tr_TR/developer/mobile" }, ], }, { @@ -69,9 +215,35 @@ export const trTR = defineConfig({ text: "Giriş", link: "/tr_TR/plugin-dev/introduction", }, + { + text: "Bukkit'ten Geçiş", + collapsed: true, + items: [ + { + text: "Genel Bakış", + link: "/tr_TR/plugin-dev/migrating-from-bukkit/", + }, + { + text: "Komutlar", + link: "/tr_TR/plugin-dev/migrating-from-bukkit/commands", + }, + { + text: "Olaylar (Events)", + link: "/tr_TR/plugin-dev/migrating-from-bukkit/events", + }, + { + text: "Envanterler & Arayüzler", + link: "/tr_TR/plugin-dev/migrating-from-bukkit/inventories", + }, + { + text: "Yapılandırma & Veri", + link: "/tr_TR/plugin-dev/migrating-from-bukkit/configuration", + }, + ], + }, { text: "Rust", - link: "/tr_TR/plugin-dev/rust/introduction", + collapsed: true, items: [ { text: "Proje Oluşturma", @@ -82,60 +254,118 @@ export const trTR = defineConfig({ link: "/tr_TR/plugin-dev/rust/basic-logic", }, { - text: "Katılma Olayı", - link: "/tr_TR/plugin-dev/rust/join-event", + text: "Olaylar (Events)", + link: "/tr_TR/plugin-dev/rust/events", }, { text: "Komutlar", items: [ + { + text: "İlk Komut", + link: "/tr_TR/plugin-dev/rust/command/first-command", + }, { text: "Taş-Kağıt-Makas", link: "/tr_TR/plugin-dev/rust/command/rock-paper-scissors", }, - { - text: "Envanterler", - link: "/tr_TR/plugin-dev/rust/command/inventories", - }, ], }, + { + text: "Lisanslama & Güncellemeler", + link: "/tr_TR/plugin-dev/rust/plugin-utils", + }, ], }, { text: "Python", - collapsed: false, + collapsed: true, items: [ { - text: "Quick Start", + text: "Hızlı Başlangıç", link: "/tr_TR/plugin-dev/python/quick-start", }, { - text: "Basic Logic", + text: "Temel Mantık", link: "/tr_TR/plugin-dev/python/basic-logic", }, { - text: "Events", + text: "İlk Komut", + link: "/tr_TR/plugin-dev/python/first-command", + }, + { + text: "Olaylar (Events)", link: "/tr_TR/plugin-dev/python/events", }, ], }, { text: "C#", - collapsed: false, + collapsed: true, items: [ { text: "Hızlı Başlangıç", link: "/tr_TR/plugin-dev/csharp/quick-start", }, + { + text: "İlk Komut", + link: "/tr_TR/plugin-dev/csharp/first-command", + }, ], }, { text: "C", - collapsed: false, + collapsed: true, items: [ { text: "Hızlı Başlangıç", link: "/tr_TR/plugin-dev/c/quick-start", }, + { + text: "İlk Komut", + link: "/tr_TR/plugin-dev/c/first-command", + }, + ], + }, + { + text: "Go", + collapsed: true, + items: [ + { + text: "Hızlı Başlangıç", + link: "/tr_TR/plugin-dev/go/quick-start", + }, + { + text: "Temel Mantık", + link: "/tr_TR/plugin-dev/go/basic-logic", + }, + { + text: "İlk Komut", + link: "/tr_TR/plugin-dev/go/first-command", + }, + ], + }, + { + text: "Kotlin", + collapsed: true, + items: [ + { + text: "Hızlı Başlangıç", + link: "/tr_TR/plugin-dev/kotlin/quick-start", + }, + { + text: "İlk Komut", + link: "/tr_TR/plugin-dev/kotlin/first-command", + }, + ], + }, + { + text: "D", + collapsed: true, + items: [ + { + text: "Hızlı Başlangıç", + link: "/tr_TR/plugin-dev/d/quick-start", + }, ], }, ], @@ -145,7 +375,7 @@ export const trTR = defineConfig({ items: [ { text: "Yaygın Sorunlar", - link: "/tr_TR/troubleshooting/common_issues.md", + link: "/tr_TR/troubleshooting/common_issues", }, ], }, @@ -158,16 +388,16 @@ export const trTR = defineConfig({ logo: "/assets/favicon.ico", footer: { - message: "MIT Lisansı altında yayımlanmıştır.", + message: "MIT Lisansı altında yayınlandı.", copyright: `Copyright © 2024-${new Date().getFullYear()} Aleksandr Medvedev`, }, editLink: { pattern: "https://github.com/Pumpkin-MC/Pumpkin-Docs/blob/master/docs/:path", - text: "Bu sayfayı GitHub'da düzenle", + text: "Bu sayfayı GitHub'da düzenleyin", }, lastUpdated: { - text: "Güncellenme", + text: "Son güncelleme", formatOptions: { dateStyle: "medium", timeStyle: "medium", diff --git a/docs/.vitepress/zh_cn.ts b/docs/.vitepress/zh_cn.ts index a2557ce..00ddfd3 100644 --- a/docs/.vitepress/zh_cn.ts +++ b/docs/.vitepress/zh_cn.ts @@ -2,17 +2,29 @@ import { defineConfig } from "vitepress"; export const zhCN = defineConfig({ lang: "zh-CN", - description: "一款用 Rust 编写的高性能 Minecraft 服务器软件", + description: "使用 Rust 编写的高性能 Minecraft 服务器软件", themeConfig: { - // https://vitepress.dev/reference/default-theme-config + siteTitle: "Pumpkin Docs", search: { provider: "local", }, nav: [ { - text: "文档", - link: "/zh_cn/about/introduction", + text: "管理", + link: "/zh_cn/admin/introduction", + }, + { + text: "贡献者", + link: "/zh_cn/developer/introduction", + }, + { + text: "插件开发", + link: "/zh_cn/plugin-dev/introduction", + }, + { + text: "官网", + link: "https://pumpkinmc.org/", }, ], sidebar: [ @@ -23,34 +35,124 @@ export const zhCN = defineConfig({ ], }, { - text: "配置", + text: "管理", + collapsed: false, items: [ - { text: "介绍", link: "/zh_cn/config/introduction" }, - { text: "基础配置", link: "/zh_cn/config/basic" }, - { text: "代理", link: "/zh_cn/config/proxy" }, - { text: "身份验证", link: "/zh_cn/config/authentication" }, - { text: "压缩", link: "/zh_cn/config/compression" }, - { text: "资源包", link: "/zh_cn/config/resource-pack" }, - { text: "命令", link: "/zh_cn/config/commands" }, - { text: "RCON", link: "/zh_cn/config/rcon" }, - { text: "PVP", link: "/zh_cn/config/pvp" }, - { text: "日志", link: "/zh_cn/config/logging" }, - { text: "查询", link: "/zh_cn/config/query" }, - { text: "局域网广播", link: "/zh_cn/config/lan-broadcast" }, + { text: "概述", link: "/zh_cn/admin/introduction" }, + { text: "从 Bukkit 迁移", link: "/zh_cn/admin/migrating-from-bukkit" }, + { + text: "配置", + collapsed: true, + items: [ + { text: "介绍", link: "/zh_cn/config/introduction" }, + { text: "基础配置", link: "/zh_cn/config/basic" }, + { text: "世界", link: "/zh_cn/config/world" }, + { text: "聊天与反垃圾", link: "/zh_cn/config/chat" }, + { text: "命令", link: "/zh_cn/config/commands" }, + { text: "基岩版与 NetherNet", link: "/zh_cn/config/bedrock" }, + { text: "代理", link: "/zh_cn/config/proxy" }, + { text: "身份验证", link: "/zh_cn/config/authentication" }, + { text: "数据包限制器", link: "/zh_cn/config/packet-limiter" }, + { text: "压缩", link: "/zh_cn/config/compression" }, + { text: "资源包", link: "/zh_cn/config/resource-pack" }, + { text: "插件", link: "/zh_cn/config/plugins" }, + { text: "服务器链接", link: "/zh_cn/config/server-links" }, + { text: "玩家数据", link: "/zh_cn/config/player-data" }, + { text: "日志", link: "/zh_cn/config/logging" }, + { text: "Query", link: "/zh_cn/config/query" }, + { text: "RCON", link: "/zh_cn/config/rcon" }, + { text: "PVP", link: "/zh_cn/config/pvp" }, + { text: "局域网广播", link: "/zh_cn/config/lan-broadcast" }, + ], + }, ], }, { - text: "开发者", + text: "贡献者", items: [ - { text: "贡献指南", link: "/zh_cn/developer/contributing" }, { text: "介绍", link: "/zh_cn/developer/introduction" }, + { text: "贡献指南", link: "/zh_cn/developer/contributing" }, + { text: "架构", link: "/zh_cn/developer/architecture" }, + { text: "数据提取器", link: "/zh_cn/developer/extractor" }, + { text: "代码生成", link: "/zh_cn/developer/codegen" }, + { text: "版本迁移", link: "/zh_cn/developer/porting-versions" }, { text: "网络", - link: "/zh_cn/developer/networking/networking", + collapsed: true, items: [ { - text: "身份验证", - link: "/zh_cn/developer/networking/authentication", + text: "介绍", + link: "/zh_cn/developer/networking/", + }, + { + text: "元数据与同步数据", + link: "/zh_cn/developer/networking/metadata", + }, + { + text: "Java Edition", + collapsed: true, + items: [ + { + text: "概述", + link: "/zh_cn/developer/networking/java/overview", + }, + { + text: "添加数据包", + link: "/zh_cn/developer/networking/java/adding-packets", + }, + { + text: "身份验证", + link: "/zh_cn/developer/networking/java/authentication", + }, + ], + }, + { + text: "Bedrock Edition", + collapsed: true, + items: [ + { + text: "概述", + link: "/zh_cn/developer/networking/bedrock/overview", + }, + { + text: "添加数据包", + link: "/zh_cn/developer/networking/bedrock/adding-packets", + }, + { + text: "身份验证", + link: "/zh_cn/developer/networking/bedrock/authentication", + }, + { + text: "NetherNet", + link: "/zh_cn/developer/networking/bedrock/nethernet", + }, + ], + }, + { + text: "代理转发", + collapsed: true, + items: [ + { + text: "概述", + link: "/zh_cn/developer/networking/proxy/", + }, + { + text: "BungeeCord", + link: "/zh_cn/developer/networking/proxy/bungeecord", + }, + { + text: "Velocity", + link: "/zh_cn/developer/networking/proxy/velocity", + }, + { + text: "Vine", + link: "/zh_cn/developer/networking/proxy/vine", + }, + ], + }, + { + text: "Query (GameSpy4)", + link: "/zh_cn/developer/networking/query", }, { text: "RCON", @@ -58,7 +160,51 @@ export const zhCN = defineConfig({ }, ], }, - { text: "世界", link: "/zh_cn/developer/world" }, + { + text: "方块", + collapsed: true, + items: [ + { text: "概述", link: "/zh_cn/developer/blocks/" }, + { text: "添加方块", link: "/zh_cn/developer/blocks/adding-blocks" }, + ], + }, + { + text: "物品", + collapsed: true, + items: [ + { text: "概述", link: "/zh_cn/developer/items/" }, + { text: "添加物品", link: "/zh_cn/developer/items/adding-items" }, + ], + }, + { + text: "实体", + collapsed: true, + items: [ + { text: "概述与层级", link: "/zh_cn/developer/entities/" }, + { text: "生成与追踪", link: "/zh_cn/developer/entities/spawning-and-tracking" }, + { text: "生物 AI 与目标", link: "/zh_cn/developer/entities/ai-and-mobs" }, + ], + }, + { + text: "命令", + collapsed: true, + items: [ + { text: "概述与调度器", link: "/zh_cn/developer/commands/" }, + { text: "添加命令", link: "/zh_cn/developer/commands/adding-commands" }, + { text: "参数与建议", link: "/zh_cn/developer/commands/arguments-and-suggestions" }, + { text: "执行与上下文", link: "/zh_cn/developer/commands/execution-and-context" }, + ], + }, + { + text: "插件引擎", + collapsed: true, + items: [ + { text: "概述", link: "/zh_cn/developer/plugins/" }, + { text: "添加插件加载器", link: "/zh_cn/developer/plugins/loaders" }, + { text: "WASM 签名与校验", link: "/zh_cn/developer/plugins/wasm-signing" }, + ], + }, + { text: "世界引擎", link: "/zh_cn/developer/world" }, { text: "移动端开发", link: "/zh_cn/developer/mobile" }, ], }, @@ -69,9 +215,35 @@ export const zhCN = defineConfig({ text: "介绍", link: "/zh_cn/plugin-dev/introduction", }, + { + text: "从 Bukkit 迁移", + collapsed: true, + items: [ + { + text: "概述", + link: "/zh_cn/plugin-dev/migrating-from-bukkit/", + }, + { + text: "命令", + link: "/zh_cn/plugin-dev/migrating-from-bukkit/commands", + }, + { + text: "事件", + link: "/zh_cn/plugin-dev/migrating-from-bukkit/events", + }, + { + text: "物品栏与 GUI", + link: "/zh_cn/plugin-dev/migrating-from-bukkit/inventories", + }, + { + text: "配置与数据", + link: "/zh_cn/plugin-dev/migrating-from-bukkit/configuration", + }, + ], + }, { text: "Rust", - collapsed: false, + collapsed: true, items: [ { text: "创建项目", @@ -83,65 +255,117 @@ export const zhCN = defineConfig({ }, { text: "事件", - items: [ - { text: "加入事件", link: "/zh_cn/plugin-dev/rust/join-event" }, - ] + link: "/zh_cn/plugin-dev/rust/events", }, { text: "命令", items: [ { - text: "第一个命令", - link: "/zh_cn/plugin-dev/rust/command/first-command" + text: "首个命令", + link: "/zh_cn/plugin-dev/rust/command/first-command", }, { text: "剪刀石头布", - link: "/zh_cn/plugin-dev/rust/command/rock-paper-scissors" + link: "/zh_cn/plugin-dev/rust/command/rock-paper-scissors", }, - ] + ], }, { - text: "背包", - link: "/zh_cn/plugin-dev/rust/inventories", + text: "许可证与更新", + link: "/zh_cn/plugin-dev/rust/plugin-utils", }, ], }, { text: "Python", - collapsed: false, + collapsed: true, items: [ { - text: "Quick Start", + text: "快速入门", link: "/zh_cn/plugin-dev/python/quick-start", }, { - text: "Basic Logic", + text: "基本逻辑", link: "/zh_cn/plugin-dev/python/basic-logic", }, { - text: "Events", + text: "首个命令", + link: "/zh_cn/plugin-dev/python/first-command", + }, + { + text: "事件", link: "/zh_cn/plugin-dev/python/events", }, ], }, { text: "C#", - collapsed: false, + collapsed: true, items: [ { text: "快速入门", link: "/zh_cn/plugin-dev/csharp/quick-start", }, + { + text: "首个命令", + link: "/zh_cn/plugin-dev/csharp/first-command", + }, ], }, { text: "C", - collapsed: false, + collapsed: true, items: [ { text: "快速入门", link: "/zh_cn/plugin-dev/c/quick-start", }, + { + text: "首个命令", + link: "/zh_cn/plugin-dev/c/first-command", + }, + ], + }, + { + text: "Go", + collapsed: true, + items: [ + { + text: "快速入门", + link: "/zh_cn/plugin-dev/go/quick-start", + }, + { + text: "基本逻辑", + link: "/zh_cn/plugin-dev/go/basic-logic", + }, + { + text: "首个命令", + link: "/zh_cn/plugin-dev/go/first-command", + }, + ], + }, + { + text: "Kotlin", + collapsed: true, + items: [ + { + text: "快速入门", + link: "/zh_cn/plugin-dev/kotlin/quick-start", + }, + { + text: "首个命令", + link: "/zh_cn/plugin-dev/kotlin/first-command", + }, + ], + }, + { + text: "D", + collapsed: true, + items: [ + { + text: "快速入门", + link: "/zh_cn/plugin-dev/d/quick-start", + }, ], }, ], @@ -151,7 +375,7 @@ export const zhCN = defineConfig({ items: [ { text: "常见问题", - link: "/zh_cn/troubleshooting/common_issues.md", + link: "/zh_cn/troubleshooting/common_issues", }, ], }, @@ -164,8 +388,8 @@ export const zhCN = defineConfig({ logo: "/assets/favicon.ico", footer: { - message: "基于 MIT 许可证发布。", - copyright: `版权所有 © 2024-${new Date().getFullYear()} Aleksandr Medvedev`, + message: "基于 MIT 许可发布。", + copyright: `Copyright © 2024-${new Date().getFullYear()} Aleksandr Medvedev`, }, editLink: { pattern: @@ -173,7 +397,7 @@ export const zhCN = defineConfig({ text: "在 GitHub 上编辑此页面", }, lastUpdated: { - text: "更新于", + text: "最后更新于", formatOptions: { dateStyle: "medium", timeStyle: "medium", diff --git a/docs/de/admin/introduction.md b/docs/de/admin/introduction.md new file mode 100644 index 0000000..e20fb27 --- /dev/null +++ b/docs/de/admin/introduction.md @@ -0,0 +1,25 @@ +# Administrationsübersicht + +Willkommen im **Admin-Bereich** für Pumpkin-Serveradministratoren. Pumpkin ist ein hochperformanter, multithreaded Minecraft-Server, der in Rust geschrieben wurde und darauf ausgelegt ist, riesige Spielerzahlen bei minimalem Ressourcenverbrauch zu unterstützen. + +--- + +## Hauptfunktionen für Server-Admins + +- **Extremes Multithreading**: Von Grund auf für moderne Mehrkernprozessoren entwickelt. +- **WASM-Plugin-Architektur**: Sichere, isolierte (sandboxed) Plugin-Ausführung in Rust, Python, Kotlin, C#, Go oder C ohne JVM-Overhead. +- **Nativer Cross-Proxy-Support**: Standardmäßige Unterstützung für Velocity, BungeeCord und moderne Proxy-Weiterleitungen. +- **TOML-basierte Konfiguration**: Übersichtliche, menschenlesbare Konfigurationsdateien in `pumpkin.toml` und funktionsspezifischen TOML-Konfigurationen. + +--- + +## Admin-Leitfäden & Themen + +Entdecke die folgenden Administrationsleitfäden: + +- [Migration von Bukkit- / Paper- / Spigot-Servern](./migrating-from-bukkit) — Wesentliche Unterschiede in der Serveradministration, bei Plugins, Weltspeicherung und Performance. +- [Serverkonfiguration](../config/introduction) — Detaillierte Übersicht über die Einstellungen in `pumpkin.toml`. +- [Proxy-Einrichtung](../config/proxy) — Konfiguration der Weiterleitung für BungeeCord & Velocity. +- [Befehle & Berechtigungen](../config/commands) — Verwalten von In-Game-Operator-Befehlen und Berechtigungen. +- [Authentifizierung](../config/authentication) — Online-Modus vs. Offline-Modus und Yggdrasil-Einstellungen. +- [Fehlerbehebung & Häufige Probleme](../troubleshooting/common_issues) — Behebung von Port-Bindungs-, Speicher- und Plugin-Ladefehlern. diff --git a/docs/de/admin/migrating-from-bukkit.md b/docs/de/admin/migrating-from-bukkit.md new file mode 100644 index 0000000..c70a132 --- /dev/null +++ b/docs/de/admin/migrating-from-bukkit.md @@ -0,0 +1,126 @@ +# Migration von Bukkit / Spigot / Paper zu Pumpkin + +Dieser Leitfaden beschreibt die architektonischen und konfigurationsbezogenen Unterschiede beim Wechsel eines Servers von Bukkit, Spigot oder Paper zu Pumpkin. + +--- + +## 1. Architektonische Unterschiede + +| Aspekt | Bukkit / Spigot / Paper | Pumpkin | +| :--- | :--- | :--- | +| **Laufzeitumgebung** | Java Virtual Machine (erfordert Java 17/21+) | Native Binärdatei (kompiliertes Rust, kein Java erforderlich) | +| **Startbefehl** | `java [flags] -jar server.jar` | `./pumpkin` | +| **Tick-Loop** | Single-Thread-Tick-Loop mit asynchroner Aufgabenverteilung | Multithreaded Tick-Ausführung über mehrere CPU-Kerne | +| **Plugin-Format** | Java-Bytecode (`.jar`) für die Bukkit-API | WebAssembly (`.wasm`) oder `.jar` via PatchBukkit | +| **Plugin-Sprachen** | Java, Kotlin, Scala | Rust, Python, Kotlin, C#, Go, C | +| **Konfiguration** | `server.properties`, `paper.yml`, `spigot.yml` | `pumpkin.toml` (TOML-Format) | +| **Speicherverwaltung** | JVM Garbage Collection | Native OS-Speicherallokation | + +--- + +## 2. Plugins & Erweiterungen + +### WebAssembly-Plugins +Pumpkins primäres Plugin-Format ist WebAssembly (`.wasm`): +- **Isolation**: Plugins laufen in einer isolierten WebAssembly-Laufzeitumgebung (Sandbox). +- **Sprachunterstützung**: Plugins können aus Rust, Python, Go, C#, C oder Kotlin zu WASM kompiliert werden. +- **Eigene Plugins**: Hinweise zur Portierung eigener Bukkit-Plugins findest du im [Entwickler-Migrationsleitfaden](../plugin-dev/migrating-from-bukkit/index). + +### Bukkit-Plugin-Kompatibilität: PatchBukkit +Für Server, die auf bestehende Bukkit-/Spigot-`.jar`-Plugins angewiesen sind, bietet das Projekt **PatchBukkit** eine Kompatibilitätsschicht: +- **Architektur**: PatchBukkit bettet eine JVM in Pumpkin ein, implementiert Teile der Bukkit-API nach und vermittelt Aufrufe über JNI und FFI an Pumpkin. +- **Plugin-Verzeichnis**: Kompatible `.jar`-Plugins werden im Verzeichnis `patchbukkit/patchbukkit-plugins/` abgelegt. +- **Status & Einschränkungen**: PatchBukkit befindet sich in aktiver Entwicklung. Standardaufrufe der Bukkit-API werden unterstützt; Plugins, die stark auf NMS-Interna (`net.minecraft.server`), CraftBukkit-Reflexion oder Bytecode-Manipulation angewiesen sind, funktionieren möglicherweise noch nicht wie erwartet. +- **Java-Voraussetzung**: Die Nutzung von PatchBukkit setzt eine installierte Java-Laufzeitumgebung (JRE/JDK) auf dem Hostsystem voraus, um die eingebettete JVM auszuführen. + +--- + +## 3. Konfiguration & Eigenschaften + +Pumpkin verwendet TOML-Konfigurationsdateien anstelle von `server.properties` und YAML: + +| Paper / Spigot (`server.properties`) | Pumpkin (`pumpkin.toml`) | +| :--- | :--- | +| `server-port=25565` | `server_address = "0.0.0.0:25565"` | +| `motd=...` | `motd = "A Pumpkin Server"` | +| `max-players=20` | `max_players = 20` | +| `online-mode=true` | `online_mode = true` | +| `view-distance=10` | `view_distance = 10` | +| `simulation-distance=8` | `simulation_distance = 8` | + +--- + +## 4. Welt- & Datenspeicherung + +- **Formatkompatibilität**: Pumpkin liest das standardmäßige Anvil-Chunk-Format (`.mca`-Dateien). +- **Verzeichnisstruktur**: Achte beim Kopieren von Welten aus bestehenden Paper-/Spigot-Installationen darauf, dass die Dimensionsverzeichnisse (`world`, `world_nether`, `world_the_end`) der erwarteten Pumpkin-Struktur entsprechen. +- **Aktuellstes Welt- & Datapack-Format erforderlich**: Zumindest derzeit unterstützt Pumpkin nur das jeweils aktuellste Weltformat und Datapack-Format der unterstützten Minecraft-Version. Ältere Weltformate oder veraltete Datapack-Versionen werden zur Laufzeit nicht automatisch konvertiert. + - **Welten im Client aktualisieren**: Wenn du eine ältere Welt migrierst, aktualisiere sie vor dem Laden in Pumpkin. Du kannst eine bestehende Welt direkt im offiziellen Minecraft-Client upgraden: Öffne den Client in der passenden aktuellen Version, gehe auf **Einzelspieler**, wähle die Welt aus, klicke auf **Bearbeiten** und wähle **Welt optimieren**. Alternativ kannst du die Welt einmalig auf einem Vanilla-Server der aktuellen Version starten und speichern. + - **Datapacks**: Stelle sicher, dass alle Datapacks unter `world/datapacks` auf das von der Zielversion erwartete Format aktualisiert wurden. + +--- + +## 5. Serverstart & JVM-Flags + +### Keine Java-Installation erforderlich +Ein reiner Pumpkin-Server ist eine eigenständige native Binärdatei. Du benötigst keine Java-Laufzeitumgebung (JRE/JDK) auf deinem System oder im Container, um den Server auszuführen. + +### Startbefehle +Paper- und Spigot-Server nutzen üblicherweise JVM-Startskripte mit Heap-Allokation und Flags zur Steuerung der Garbage Collection (wie z. B. Aikars Flags): + +:::code-group + +```bash [Paper (JVM)] +# Typischer Paper-Startbefehl mit Aikars Flags: +java -Xms10G -Xmx10G \ + -XX:+UseG1GC \ + -XX:+ParallelRefProcEnabled \ + -XX:MaxGCPauseMillis=200 \ + -XX:+UnlockExperimentalVMOptions \ + -XX:+DisableExplicitGC \ + -XX:+AlwaysPreTouch \ + -XX:G1NewSizePercent=30 \ + -XX:G1MaxNewSizePercent=40 \ + -XX:G1HeapRegionSize=8M \ + -XX:G1ReservePercent=20 \ + -XX:G1HeapWastePercent=5 \ + -XX:G1MixedGCCountTarget=4 \ + -XX:InitiatingHeapOccupancyPercent=15 \ + -XX:G1MixedGCLiveThresholdPercent=90 \ + -XX:G1RSetUpdatingPauseTimePercent=5 \ + -XX:SurvivorRatio=32 \ + -XX:+PerfDisableSharedMem \ + -XX:MaxTenuringThreshold=1 \ + -Dusing.aikars.flags=https://mcflags.emc.gs \ + -Daikars.new.flags=true \ + -jar paper.jar --nogui +``` + +```bash [Pumpkin (Nativ)] +# Linux / macOS: +./pumpkin + +# Windows: +./pumpkin.exe + +# Docker: +docker run -p 25565:25565 -v ./data:/data pumpkinmc/pumpkin:latest +``` + +::: + +### Unterschiede im Speichermodell +- **Keine Garbage Collection**: Pumpkin ist in Rust geschrieben und nutzt deterministisches Speichermanagement zur Kompilierzeit (RAII). Es gibt keinen Laufzeit-Garbage-Collector und keine GC-Pausenoptimierung. +- **Keine feste Heap-Reservierung**: Flags wie `-Xms` und `-Xmx` existieren bei nativen Binärdateien nicht. Speicher wird bei Bedarf dynamisch vom Betriebssystem angefordert und beim Entladen von Chunks und Entities wieder freigegeben. +- **JVM-Flags nicht anwendbar**: Flags zur Steuerung der Garbage Collection, Generationsgrößen oder JVM-Interna sind nicht erforderlich und können dem Programm nicht übergeben werden. + +--- + +## Admin-Checkliste + +1. [ ] Sichere deine bestehenden Serverdaten und erstelle ein Backup, wobei Welt und Datapacks auf das aktuellste Format aktualisiert sein sollten (z. B. via „Welt optimieren“ im Client). +2. [ ] Übertrage Einstellungen aus `server.properties` in die `pumpkin.toml`. +3. [ ] Identifiziere benötigte Plugins und prüfe WebAssembly-Alternativen (`.wasm`) oder teste mit [PatchBukkit](#bukkit-plugin-kompatibilitat-patchbukkit). +4. [ ] Konfiguriere Proxy-Einstellungen (`Velocity` / `BungeeCord`) in der `pumpkin.toml`, falls ein Netzwerk betrieben wird. +5. [ ] Passe Startskripte an, um `./pumpkin` direkt ohne JVM-Argumente aufzurufen. +6. [ ] Starte den Server und überprüfe die Verbindung sowie die Protokolle. diff --git a/docs/de/config/bedrock.md b/docs/de/config/bedrock.md new file mode 100644 index 0000000..1394f2a --- /dev/null +++ b/docs/de/config/bedrock.md @@ -0,0 +1,50 @@ +# Bedrock & NetherNet + +Pumpkin bietet native Unterstützung für Minecraft Bedrock Edition-Clients, einschließlich des NetherNet-WebRTC/ICE-Transports und Anpassungen für Benutzernamen. + +## Konfiguration + +:::code-group + +```toml [pumpkin.toml] +[networking.bedrock] +enabled = true +online_mode = true +max_players = 1000 +view_distance = 16 +simulation_distance = 10 +motd = "A blazingly fast Pumpkin server!" +username_prefix = "" +replace_username_spaces = true +chunk_caching = true + +[networking.bedrock.nethernet] +enabled = true +address = "0.0.0.0:19132" +identity_key = "nethernet-key.der" +stun_servers = [] +``` + +::: + +### Bedrock-Optionen + +- **`enabled`**: Gibt an, ob Verbindungen von Bedrock-Edition-Clients akzeptiert werden (Standard: `true`). +- **`online_mode`**: Gibt an, ob die Xbox-Live-Authentifizierung für Bedrock-Spieler erzwungen wird (Standard: `true`). +- **`max_players`**: Maximale Anzahl gleichzeitiger Bedrock-Spieler (`0` deaktiviert das Limit, Standard: `1000`). +- **`view_distance`**: Maximale Chunk-Sichtweite, die an Bedrock-Clients gesendet wird (Standard: `16`). +- **`simulation_distance`**: Maximale Tick-Simulationsdistanz für Bedrock-Spieler (Standard: `10`). +- **`motd`**: „Message of the Day“, die in der Bedrock-Serverliste angezeigt wird. +- **`username_prefix`**: Optionales Präfix, das Bedrock-Spieler-Gamertags vorangestellt wird (z. B. `"."` oder `"*"`), um Namenskollisionen mit Java-Edition-Accounts auf Crossplay-Servern zu verhindern (Standard: `""`). +- **`replace_username_spaces`**: Gibt an, ob Leerzeichen in Bedrock-Gamertags automatisch durch Unterstriche `_` ersetzt werden, damit Spielernamen in Minecraft-Befehlen verwendet werden können (Standard: `true`). +- **`chunk_caching`**: Gibt an, ob clientseitiges Chunk-Blob-Caching aktiviert ist, um Netzwerkbandbreite zu sparen (Standard: `true`). + +### NetherNet-Transporteinstellungen + +NetherNet ist der moderne WebRTC/ICE-Netzwerktransport von Minecraft Bedrock: + +- **`enabled`**: Gibt an, ob sich Clients über NetherNet verbinden dürfen (Standard: `true`). +- **`address`**: Netzwerk-Socket-Adresse für TCP-Signalisierung und UDP-ICE-Multiplexing (Standard: `"0.0.0.0:19132"`). +- **`external_ip`**: Optionale öffentliche IP-Adresse, die angekündigt wird, wenn der Server hinter NAT betrieben wird. +- **`identity_key`**: Dateipfad zur privaten PKCS#8-P-384-Identitätsschlüsseldatei, die über Server-Neustarts hinweg für Trust On First Use (TOFU) des Clients erhalten bleibt (Standard: `"nethernet-key.der"`). +- **`stun_servers`**: Optionale Liste von STUN-Server-URLs für das ICE-NAT-Traversal. diff --git a/docs/de/config/chat.md b/docs/de/config/chat.md new file mode 100644 index 0000000..6783d61 --- /dev/null +++ b/docs/de/config/chat.md @@ -0,0 +1,39 @@ +# Chat & Anti-Spam + +Pumpkin bietet anpassbare In-Game-Chatformatierung und einen integrierten Anti-Spam-Schutz unter `[chat]` in `pumpkin.toml`. + +## Konfiguration + +:::code-group + +```toml [pumpkin.toml] +[chat] +format = "<{DISPLAYNAME}> {MESSAGE}" + +[chat.anti_spam] +enabled = true +spam_threshold = 200 +message_cost = 20 +decay_per_tick = 1 +ops_bypass = true +``` + +::: + +### Chat-Einstellungen + +- **`format`**: Das Format für Chatnachrichten (Standard: `"<{DISPLAYNAME}> {MESSAGE}"`). + - `{DISPLAYNAME}`: Anzeigename des Spielers. + - `{MESSAGE}`: Text der Chatnachricht. + > [!NOTE] + > Eine benutzerdefinierte Chatformatierung wird nicht angewendet, wenn sichere Chat-Meldungen (`allow_chat_reports = true`) aktiviert sind. + +### Anti-Spam-Schutz + +Pumpkin implementiert einen Leaky-Bucket-Spam-Zähler, um den Server vor spammenden Spielern und Bot-Floods zu schützen: + +- **`enabled`**: Gibt an, ob der Anti-Spam-Schutz aktiv ist (Standard: `true`). +- **`spam_threshold`**: Der Schwellenwert des Spam-Zählers in Ticks, ab dem ein Spieler gekickt wird (Standard: `200` Ticks). +- **`message_cost`**: Der Wert, der dem Spam-Zähler des Spielers für jede gesendete Chatnachricht oder jeden Befehl hinzugefügt wird (Standard: `20` Ticks). +- **`decay_per_tick`**: Der Wert, um den der Spam-Zähler des Spielers mit jedem Server-Tick abgebaut wird (Standard: `1` Tick). +- **`ops_bypass`**: Gibt an, ob Server-Operatoren von Anti-Spam-Prüfungen ausgenommen sind (Standard: `true`). diff --git a/docs/de/config/packet-limiter.md b/docs/de/config/packet-limiter.md new file mode 100644 index 0000000..64b29e2 --- /dev/null +++ b/docs/de/config/packet-limiter.md @@ -0,0 +1,30 @@ +# Paket-Begrenzer + +Pumpkin verfügt über einen integrierten Paketratenbegrenzer, um den Server vor Paket-Flooding, Spam-Angriffen und clientseitigen Exploits zu schützen. Paketratenlimits können für Java Edition- und Bedrock Edition-Clients unabhängig voneinander konfiguriert werden. + +## Konfiguration + +:::code-group + +```toml [pumpkin.toml] +[networking.java.packet_limiter] +enabled = true +max_packet_rate = 500.0 +burst_capacity = 500.0 +kick_message = "Kicked for spamming packets" + +[networking.bedrock.packet_limiter] +enabled = true +max_packet_rate = 500.0 +burst_capacity = 500.0 +kick_message = "Kicked for spamming packets" +``` + +::: + +### Konfigurationsoptionen + +- **`enabled`**: Gibt an, ob der Paketratenbegrenzer für diese Client-Edition aktiviert ist (Standard: `true`). +- **`max_packet_rate`**: Maximal erlaubte eingehende Pakete pro Sekunde und Client-Verbindung (Standard: `500.0`). Setze den Wert auf `<= 0.0`, um das Ratenlimit zu deaktivieren, ohne das Begrenzungsmodul zu deaktivieren. +- **`burst_capacity`**: Token-Bucket-Burst-Kapazität für kurze Spitzen beim Paketaufkommen des Clients (Standard: `500.0`). +- **`kick_message`**: Die Trennungsnachricht, die dem Spieler angezeigt wird, wenn seine Verbindung das Paketlimit überschreitet (Standard: `"Kicked for spamming packets"`). diff --git a/docs/de/config/player-data.md b/docs/de/config/player-data.md new file mode 100644 index 0000000..ab749e3 --- /dev/null +++ b/docs/de/config/player-data.md @@ -0,0 +1,41 @@ +# Spielerdaten & Gameplay + +Einstellungen zum Speichern persistenter Spielerdaten, Fortschritte (Advancements), Rezept-Synchronisation und saisonaler Späße werden in `pumpkin.toml` vorgenommen. + +## Konfiguration + +:::code-group + +```toml [pumpkin.toml] +[player_data] +save_player_data = true +save_player_cron_interval = 300 + +[advancement] +save_advancements = true + +[recipe] +send_recipes = true + +[fun] +april_fools = true +``` + +::: + +### Spielerdaten + +- **`save_player_data`**: Gibt an, ob persistente Spielerdaten (Inventar, Position, Gesundheit) auf der Festplatte gespeichert werden (Standard: `true`). +- **`save_player_cron_interval`**: Zeitintervall in Sekunden zwischen automatischen periodischen Speicherungen der Daten von Online-Spielern (Standard: `300` Sekunden / 5 Minuten). + +### Fortschritte (Advancements) + +- **`save_advancements`**: Gibt an, ob der Fortschritt von Spielern bei Fortschritten/Erfolgen erfasst und auf der Festplatte gespeichert wird (Standard: `true`). + +### Rezepte + +- **`send_recipes`**: Gibt an, ob Werk- und Schmelzrezepte mit verbundenen Clients synchronisiert werden, wodurch das clientseitige Rezeptbuch aktiviert wird (Standard: `true`). + +### Spaßfunktionen + +- **`april_fools`**: Gibt an, ob lustige und saisonale Aprilscherz-Easter-Eggs und Features aktiviert sind (Standard: `true`). diff --git a/docs/de/config/plugins.md b/docs/de/config/plugins.md new file mode 100644 index 0000000..afc0a94 --- /dev/null +++ b/docs/de/config/plugins.md @@ -0,0 +1,59 @@ +# Plugins + +Pumpkin bietet eine WebAssembly (WASM)-Plugin-Laufzeitumgebung mit fähigkeitenbasierter Sicherheit (Capability-based Security). Das Verhalten von Plugins, Signaturverifizierung und Berechtigungen werden unter `[plugins]` in `pumpkin.toml` konfiguriert. + +## Konfiguration + +:::code-group + +```toml [pumpkin.toml] +[plugins] +enabled = true +hot_reload = false +ask_permission_confirmation = true +allow_unsigned = true +allowed_permissions = [] +blocked_permissions = [] +inherit_env = false +loopback_only = false +verify_signatures = true + +# Optional per-plugin overrides +[plugins.overrides.my_plugin] +enabled = true +allow_unsigned = true +max_memory_mb = 128 +allowed_permissions = ["fs:read:data"] +blocked_permissions = ["network:outbound"] +loopback_only = true + +[plugins.overrides.my_plugin.environment] +API_KEY = "example_secret" +``` + +::: + +### Globale Plugin-Einstellungen + +- **`enabled`**: Hauptschalter zum Aktivieren oder Deaktivieren der Plugin-Laufzeitumgebung (Standard: `true`). +- **`hot_reload`**: Überwacht das `plugins/`-Verzeichnis und lädt geänderte Plugins zur Laufzeit automatisch neu (Standard: `false`). +- **`ask_permission_confirmation`**: Fragt in der Serverkonsole nach Bestätigung, wenn ein Plugin nicht vorab genehmigte Berechtigungen anfordert (Standard: `true`). +- **`allow_unsigned`**: Erlaubt das Laden von unsignierten WASM-Plugins (Standard: `true`). +- **`allowed_permissions`**: Liste von Berechtigungen, die global für alle Plugins vorab genehmigt sind, wodurch interaktive Konsolenabfragen übersprungen werden (Standard: `[]`). +- **`blocked_permissions`**: Liste von Berechtigungen, die für alle Plugins global verweigert werden (Standard: `[]`). +- **`inherit_env`**: Gibt an, ob Umgebungsvariablen des Hosts standardmäßig in die WASI-Sandboxen der Plugins vererbt werden (Standard: `false`). +- **`loopback_only`**: Gibt an, ob ausgehende Netzwerkverbindungen von Plugins auf `127.0.0.1` / localhost beschränkt sind (Standard: `false`). +- **`max_memory_mb`**: (Optional) Globales maximales Speicherlimit in Megabyte (MB) pro Plugin-Instanz. +- **`verify_signatures`**: Gibt an, ob Pumpkin kryptografische Signaturen von WASM-Plugins vor dem Laden überprüft (Standard: `true`). + +### Plugin-spezifische Überschreibungen + +Berechtigungen und Umgebungsvariablen für bestimmte Plugins können unter `[plugins.overrides.]` fein abgestimmt werden: + +- **`enabled`**: Aktiviert oder deaktiviert dieses spezifische Plugin (Standard: `true`). +- **`allow_unsigned`**: Überschreibt, ob dieses spezifische Plugin unsigniert ausgeführt werden darf (`true`/`false`). +- **`max_memory_mb`**: Maximales Speicherlimit in MB, das speziell für dieses Plugin zugewiesen wird. +- **`allowed_permissions`**: Vorab genehmigte Berechtigungen für dieses Plugin. +- **`blocked_permissions`**: Explizit blockierte Berechtigungen für dieses Plugin. +- **`loopback_only`**: Überschreibt die Loopback-Netzwerkbeschränkung für dieses Plugin. +- **`environment`**: Tabelle benutzerdefinierter Umgebungsvariablen, die direkt an die WASI-Umgebung des Plugins übergeben werden. diff --git a/docs/de/config/server-links.md b/docs/de/config/server-links.md new file mode 100644 index 0000000..9f19cd7 --- /dev/null +++ b/docs/de/config/server-links.md @@ -0,0 +1,50 @@ +# Server-Links + +Minecraft-Clients können (seit Version 1.21) anklickbare Server-Links im Pausenmenü und in den Spielmenüs anzeigen. Standardmäßige und benutzerdefinierte Server-Links werden unter `[server_links]` in `pumpkin.toml` konfiguriert. + +## Konfiguration + +:::code-group + +```toml [pumpkin.toml] +[server_links] +enabled = true +bug_report = "https://github.com/Pumpkin-MC/Pumpkin/issues" +support = "" +status = "" +feedback = "" +community = "" +website = "" +forums = "" +news = "" +announcements = "" + +[server_links.custom] +# "Store" = "https://store.example.com" +# "Discord" = "https://discord.gg/example" +``` + +::: + +### Konfigurationsoptionen + +- **`enabled`**: Hauptschalter zum Aktivieren der Übertragung von Server-Links an sich verbindende Clients (Standard: `true`). +- **`bug_report`**: Link zum Bug-Tracker des Servers oder Projekts (Standard: `"https://github.com/Pumpkin-MC/Pumpkin/issues"`). +- **`support`**: Link zum technischen Support oder Helpdesk. +- **`status`**: Link zur Server-Uptime- oder Statusseite. +- **`feedback`**: Link zu einer Seite für Spieler-Feedback oder Vorschläge. +- **`community`**: Link zu Community-Foren oder -Gruppen. +- **`website`**: Link zur offiziellen Webseite. +- **`forums`**: Link zu Diskussionsforen. +- **`news`**: Link zum Server-Blog oder zur News-Seite. +- **`announcements`**: Link zur Ankündigungsseite. + +### Benutzerdefinierte Links + +Beliebige Key-Value-Links, die in den Client-Menüs angezeigt werden, können unter `[server_links.custom]` definiert werden: + +```toml +[server_links.custom] +"Store" = "https://store.example.com" +"Discord" = "https://discord.gg/example" +``` diff --git a/docs/de/config/world.md b/docs/de/config/world.md new file mode 100644 index 0000000..f62a7db --- /dev/null +++ b/docs/de/config/world.md @@ -0,0 +1,41 @@ +# Welt + +Pumpkin ermöglicht die Feinabstimmung von Weltspeicherformaten, automatischen Speicherintervallen (Autosave) und Beleuchtungsmodi unter `[world]` in `pumpkin.toml`. + +## Konfiguration + +:::code-group + +```toml [pumpkin.toml] +[world] +lighting = "default" +autosave_ticks = 6000 + +[world.chunk] +type = "anvil" +write_in_place = false + +[world.chunk.compression] +algorithm = "LZ4" +level = 6 +``` + +::: + +### Welteinstellungen + +- **`lighting`**: Berechnungsmodus für die Lichtausbreitung der Beleuchtungs-Engine. + - `"default"`: Standardmäßige Vanilla-Minecraft-Lichtausbreitung. + - `"full"`: Durchgehend volles Himmelslicht überall ohne Schatten. + - `"dark"`: Vollständig dunkle Beleuchtung überall (kein Licht). +- **`autosave_ticks`**: Anzahl der Server-Ticks zwischen automatischen Weltspeicherungen (Standard: `6000`, entspricht 5 Minuten bei 20 TPS). Ein Wert von `0` deaktiviert das automatische Speichern der Welt. + +### Chunk-Speichereinstellungen + +- **`type`**: Das zu verwendende Chunk-Speicherformat: + - `"anvil"`: Standardmäßiges Minecraft-Anvil-Region-Dateiformat (`.mca`). + - `"linear"`: Linear-Region-Speicherformat mit schneller Kompression für einen geringeren Speicherplatzbedarf. + - `"pump"`: Pumpkins natives, optimiertes Weltformat. +- **`write_in_place`**: Gibt an, ob Chunks direkt an Ort und Stelle (in-place) in bestehende Regionsdateien überschrieben werden sollen, anstatt freie Chunks neu zuzuweisen (Standard: `false`). +- **`compression.algorithm`**: Kompressionsalgorithmus für Chunk-Daten (`"LZ4"`, `"ZLib"`, `"GZip"`, `"Custom"`). +- **`compression.level`**: Kompressionsstufe für die Chunk-Speicherung (Standard: `6`). diff --git a/docs/de/developer/architecture.md b/docs/de/developer/architecture.md new file mode 100644 index 0000000..19c26b1 --- /dev/null +++ b/docs/de/developer/architecture.md @@ -0,0 +1,139 @@ +# Pumpkin Architecture & Crates + +Pumpkin is organized as a modular Cargo workspace composed of specialized crates. Each crate has distinct responsibilities, strictly defined boundaries, and minimal cross-dependencies. + +This architecture enables high parallel compilation speeds, isolated testing, zero-cost data sharing, and clean separation between network protocols, game logic, and world management. + +--- + +## Workspace Crate Overview + +### Workspace Architecture at a Glance + +| Layer | Crates | Responsibilities | +| :--- | :--- | :--- | +| **Core Runtime** | `pumpkin` | Game loop, world ticking, player sessions, entity management, and event loops | +| **Networking & Protocols** | `pumpkin-protocol`, `pumpkin-auth`, `pumpkin-nbt` | Java/Bedrock packet serialization, Yggdrasil & Xbox Live authentication, SNBT | +| **World & Gameplay** | `pumpkin-world`, `pumpkin-inventory`, `pumpkin-command` | Region I/O (Anvil/Linear), terrain generation, block ticking, containers, Brigadier tree | +| **Extensibility** | `pumpkin-plugin-runtime`, `pumpkin-host-bindings`, `pumpkin-plugin-api`, `pumpkin-plugin-wit` | WebAssembly plugin execution via Wasmtime, WIT host APIs, native and WASM loaders | +| **Foundations** | `pumpkin-data`, `pumpkin-config`, `pumpkin-util` | Code-generated registries, typed configuration files, mathematical vectors, permissions | + +--- + +## Detailed Crate Breakdown + +### 1. `pumpkin` (Core Server) +- **Path**: `crates/pumpkin` +- **Purpose**: The main server binary and centralized orchestration engine. +- **Responsibilities**: + - Initializes configuration, logging (`tracing`), and telemetry. + - Spawns Tokio network listeners for Minecraft: Java Edition, Bedrock Edition, RCON, Query, and LAN broadcast. + - Manages the server tick loop (`Server::tick`), coordinating world updates, player actions, entity movements, and chunk broadcasting. + - Dispatches incoming client packets through `crates/pumpkin/src/net/java/` and `net/bedrock/`. + - Integrates the plugin manager, firing event handlers before and after packet processing. + +### 2. `pumpkin-protocol` (Network Protocol) +- **Path**: `crates/pumpkin-protocol` +- **Purpose**: High-speed network wire format serialization and deserialization for Minecraft Java and Bedrock editions. +- **Responsibilities**: + - Implements the `ClientPacket` (serialization) and `ServerPacket` (deserialization) traits. + - Handles protocol primitives: `VarInt`, `VarLong`, UUIDs, NBT compounds, and string codecs. + - Implements packets across all states: `Handshake`, `Status`, `Login`, `Config`, and `Play`. + - Provides packet compression (ZLib/Fast compression) and packet encryption (AES-128/CFB8). + - Encapsulates RCON and GameSpy4 Query protocols. + +### 3. `pumpkin-world` (World & Chunks) +- **Path**: `crates/pumpkin-world` +- **Purpose**: World loading, saving, terrain generation, and chunk management. +- **Responsibilities**: + - Supports multiple chunk storage formats: + - **Vanilla Anvil (`.mca`)**: Reading and writing standard region files. + - **Linear (`.linear`)**: High-efficiency zstd-compressed region format saving 50–95% disk space. + - **Slime (`.slime`)**: Single-file world format. + - **Pump**: Native Pumpkin chunk storage format. + - Implements world generation pipelines: Perlin noise heightmaps, 3D noise routers, biome climate parameters, placed features, carvers, and superflat terrain. + - Manages block lighting and sky lighting propagation. + - Coordinates chunk loading, unloading, and streaming via cylindrical chunk iterators. + +### 4. `pumpkin-data` (Minecraft Datasets & Registries) +- **Path**: `crates/pumpkin-data` +- **Purpose**: Compile-time static registries and data tables generated directly from vanilla Minecraft assets and reports. +- **Responsibilities**: + - Contains complete ID mappings, properties, and definitions for blocks, items, biomes, entities, sounds, particles, enchantments, and recipes. + - Packet ID lookups across multiple Minecraft protocol versions. + - Zero-cost lookup tables generated by `tools/pumpkin-codegen`. + +### 5. `pumpkin-config` (Configuration Management) +- **Path**: `crates/pumpkin-config` +- **Purpose**: TOML configuration parsing, serialization, and validation. +- **Responsibilities**: + - Loads and writes `pumpkin.toml`. + - Configures server ports, compression thresholds, proxy modes, resource packs, logging levels, world settings, and Bedrock integration. + - Provides safe defaults and schema validations. + +### 6. `pumpkin-inventory` (Inventory & Containers) +- **Path**: `crates/pumpkin-inventory` +- **Purpose**: Server-side container window handlers and inventory mechanics. +- **Responsibilities**: + - Manages player inventory, chest containers, crafting tables, furnaces, and villager trading screens. + - Validates client slot clicks, drag operations, item stacking, and item splitting. + - Coordinates window packet synchronization to ensure client inventory fidelity. + +### 7. `pumpkin-command` (Command Dispatcher) +- **Path**: `crates/pumpkin-command` +- **Purpose**: Brigadier-inspired hierarchical command tree dispatcher and parser. +- **Responsibilities**: + - Constructs modular command trees with typed arguments (`Integer`, `Float`, `Entity`, `BlockPos`, etc.). + - Executes permission-checked commands. + - Provides real-time autocompletion suggestions sent to connected clients. + +### 8. `pumpkin-auth` (Authentication & Session Verification) +- **Path**: `crates/pumpkin-auth` +- **Purpose**: Cryptographic authentication and player identity verification. +- **Responsibilities**: + - Validates client sessions with Mojang's Yggdrasil authentication servers. + - Handles RSA keypair generation and shared secret exchange during the Login state. + - Supports offline-mode UUID generation and custom authentication endpoints. + - Implements proxy forwarding protocols: [BungeeCord](https://github.com/SpigotMC/BungeeCord), [BungeeGuard](https://github.com/lucko/BungeeGuard), and [Velocity](https://github.com/PaperMC/Velocity) modern forwarding with HMAC-SHA256 signatures. + +### 9. `pumpkin-nbt` (Named Binary Tag Codec) +- **Path**: `crates/pumpkin-nbt` +- **Purpose**: High-throughput binary NBT parser and serializer. +- **Responsibilities**: + - Parses and encodes all standard NBT tag types (`Byte`, `Short`, `Int`, `Long`, `Float`, `Double`, `ByteArray`, `String`, `List`, `Compound`, `IntArray`, `LongArray`). + - Supports compression layers: Gzip, Zlib, and uncompressed Network NBT. + +### 10. `pumpkin-plugin-*` (Extensibility Infrastructure) +- **Paths**: + - `crates/pumpkin-plugin-api`: The Rust developer SDK for compiling native plugins. + - `crates/pumpkin-plugin-runtime`: Wasmtime-powered WebAssembly execution host. + - `crates/pumpkin-plugin-wit`: WebAssembly Interface Type (WIT) specifications. + - `crates/pumpkin-host-bindings`: Auto-generated host bindings connecting Wasmtime and the server core. +- **Purpose**: Enables safe, polyglot plugin execution (Rust, Python, C#, C, Go, Kotlin) with sandboxed memory and near-native performance. + +### 11. `pumpkin-util` & `pumpkin-macros` (Foundations) +- **Paths**: `crates/pumpkin-util`, `crates/pumpkin-macros`, `crates/pumpkin-api-macros` +- **Purpose**: Foundational types, shared algorithms, and procedural macros. +- **Responsibilities**: + - Mathematical utilities: `Vector2`, `Vector3`, `BoundingBox`, and angle conversions. + - Text and chat formatting: `TextComponent` and JSON component serialization. + - Procedural macros such as `#[java_packet(...)]` that bind packet structs to protocol IDs and packet traits. + +### 12. `tools/` (Developer Tooling) +- **`tools/pumpkin-codegen`**: Automated code generator that parses Minecraft version jars, data reports, and asset archives to emit Rust source files into `pumpkin-data`. +- **`tools/pumpkin-fuzzer`**: Fuzz testing suites for network packet parsers, NBT readers, and region file decoders to identify potential crash vectors or memory anomalies. + +--- + +## Concurrency & Threading Model + +| Component | Runtime / Pool | Primary Workload | Communication Mechanism | +| :--- | :--- | :--- | :--- | +| **Network & Socket I/O** | Tokio Async Runtime | TCP packet reads, Bedrock UDP packets, framing, session handshakes | Async tasks (`tokio::spawn`), non-blocking reactors | +| **Heavy Compute** | Rayon Thread Pool | Terrain generation, noise routers, light propagation, chunk compression | Worker threads via crossbeam / Rayon iterators | +| **Inter-Thread Bridge** | Tokio Channels | Dispatching completed chunks and encoded packets back to network tasks | Non-blocking `tokio::sync::mpsc` channels | + +Pumpkin strictly adheres to this separation of concerns: +- **I/O Bound**: Managed on Tokio asynchronous tasks. +- **CPU Bound**: Managed on Rayon thread pools. +- **Communication**: Coordinated via cross-thread channels (`tokio::sync::mpsc`) without ever blocking the Tokio reactor loop. diff --git a/docs/de/developer/blocks/adding-blocks.md b/docs/de/developer/blocks/adding-blocks.md new file mode 100644 index 0000000..0770ed8 --- /dev/null +++ b/docs/de/developer/blocks/adding-blocks.md @@ -0,0 +1,182 @@ +# Adding a Block Behavior + +This guide walks through implementing interactive behavior for a block in Pumpkin. + +--- + +## When Is a Behavior Needed? + +- **Non-Interactive Blocks** (e.g., stone, dirt, terracotta): Do **not** require any custom Rust code. Their breaking speeds, hardness, and placement properties are automatically derived from `pumpkin-data`. +- **Interactive or Functional Blocks** (e.g., doors, chests, levers, crops, campfires, TNT): Require an implementation of the `BlockBehaviour` trait to respond to player clicks, redstone signals, physics steps, or timer ticks. + +--- + +## Step 1: Create the Behavior Struct + +Create a new file under `crates/pumpkin/src/block/blocks//.rs` (or directly in `crates/pumpkin/src/block/blocks/`). + +Use the `#[pumpkin_block(...)]` macro to associate your behavior struct with one or more vanilla block identifiers: + +```rust +use std::sync::Arc; +use pumpkin_data::Block; +use pumpkin_data::block_properties::{DoorLikeProperties, Facing}; +use pumpkin_macros::pumpkin_block; +use pumpkin_util::math::position::BlockPos; +use crate::block::registry::BlockActionResult; +use crate::block::{ + BlockBehaviour, NormalUseArgs, OnNeighborUpdateArgs, PlacedArgs, UseWithItemArgs, +}; +use crate::world::World; + +/// Associates this behavior struct with one or multiple block IDs. +#[pumpkin_block([ + "minecraft:oak_door", + "minecraft:iron_door", + "minecraft:spruce_door", + "minecraft:birch_door" +])] +pub struct DoorBlock; + +impl BlockBehaviour for DoorBlock { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state_id = args.world.get_block_state(args.location).await; + + // Iron doors require redstone power and cannot be opened manually by hand + if args.block.id == pumpkin_data::Block::IRON_DOOR.id { + return BlockActionResult::Pass; + } + + // Toggle open state + let is_open = DoorLikeProperties::open(state_id); + let new_state = DoorLikeProperties::with_open(state_id, !is_open); + args.world.set_block_state(args.location, new_state).await; + + BlockActionResult::Consume + } + + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + // Handle neighbor redstone signals or support block destruction + } +} +``` + +--- + +## Step 2: `BlockBehaviour` Hook Reference + +The `BlockBehaviour` trait exposes lifecycle hooks modeled on vanilla mechanics: + +### Player & Item Interaction Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `normal_use` | `(&self, args: NormalUseArgs<'_>) -> BlockActionResult` | Triggered when right-clicking the block with an empty hand. | +| `use_with_item` | `(&self, args: UseWithItemArgs<'_>) -> BlockActionResult` | Triggered when right-clicking the block while holding an item. | +| `get_screen_handler_factory` | `(&self, args: GetScreenHandlerFactoryArgs<'_>) -> Option>` | Returns an inventory container factory to open a UI screen (e.g., Chests, Crafting Tables). | + +### World Lifecycle & Physics Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `placed` | `(&self, args: PlacedArgs<'_>)` | Triggered immediately after the block is placed in the world. | +| `broken` | `(&self, args: BrokenArgs<'_>)` | Triggered when broken by a player or game mechanics. | +| `on_entity_collision` | `(&self, args: OnEntityCollisionArgs<'_>)` | Triggered when an entity walks inside, steps on, or falls into the block. | +| `on_neighbor_update` | `(&self, args: OnNeighborUpdateArgs<'_>)` | Triggered when an adjacent block state updates or is destroyed. | +| `on_projectile_hit` | `(&self, args: OnProjectileHitArgs<'_>)` | Triggered when an arrow, snowball, or projectile strikes the block. | + +### Redstone & Power Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `emits_redstone_power` | `(&self, state_id: BlockStateId) -> bool` | Declares whether the block emits redstone power to neighbors. | +| `get_weak_redstone_power` | `(&self, ...) -> u8` | Returns emitted weak power level (0–15). | +| `get_strong_redstone_power` | `(&self, ...) -> u8` | Returns emitted strong direct power level (0–15). | + +### Foliage & Bonemeal Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `is_valid_bonemeal_target` | `(&self, args: BonemealArgs<'_>) -> bool` | Checks if bone meal can be applied (e.g., crop not fully grown). | +| `is_bonemeal_success` | `(&self, args: BonemealArgs<'_>) -> bool` | Evaluates random chance roll for bone meal success. | +| `perform_bonemeal` | `(&self, args: BonemealArgs<'_>)` | Advances growth stages or spawns adjacent flora. | + +--- + +## Step 3: Modifying Block States + +All block properties are strongly typed in `pumpkin_data::block_properties`: + +```rust +use pumpkin_data::block_properties::{BedLikeProperties, Part}; + +// 1. Read typed properties from the numeric BlockStateId +let part = BedLikeProperties::part(state_id); // Part::Head or Part::Foot +let is_occupied = BedLikeProperties::occupied(state_id); + +// 2. Generate a new BlockStateId with updated properties +let updated_state = BedLikeProperties::with_occupied(state_id, true); + +// 3. Commit the updated state to the world +world.set_block_state(pos, updated_state).await; +``` + +--- + +## Step 4: Adding a Block Entity (Tile Entity) + +If your block needs to store persistent custom state (inventories, custom text, timers): + +1. Create a block entity struct in `crates/pumpkin/src/block/entities/`: + ```rust + use pumpkin_util::math::position::BlockPos; + use crate::block::entities::BlockEntity; + + pub struct CustomBlockEntity { + pub pos: BlockPos, + pub custom_counter: u32, + } + + impl BlockEntity for CustomBlockEntity { + fn to_nbt(&self) -> pumpkin_nbt::compound::NbtCompound { + let mut compound = pumpkin_nbt::compound::NbtCompound::new(); + compound.put_int("Counter", self.custom_counter as i32); + compound + } + + fn from_nbt(&mut self, tag: &pumpkin_nbt::compound::NbtCompound) { + if let Some(val) = tag.get_int("Counter") { + self.custom_counter = val as u32; + } + } + + fn tick(&mut self, world: &std::sync::Arc, pos: &BlockPos) { + // Periodic logic per game tick + } + } + ``` + +2. Link the block entity in your `BlockBehaviour`: + ```rust + impl BlockBehaviour for CustomBlock { + fn create_block_entity(&self, pos: BlockPos) -> Option>> { + Some(Arc::new(Mutex::new(CustomBlockEntity::new(pos)))) + } + } + ``` + +--- + +## Step 5: Registering the Block + +1. Export your module in `crates/pumpkin/src/block/blocks/mod.rs`: + ```rust + pub mod doors; + ``` + +2. Register the behavior struct in `crates/pumpkin/src/block/registry.rs`: + ```rust + manager.register(DoorBlock); + ``` + +At startup, `manager.register` automatically reads the block IDs declared in `#[pumpkin_block(...)]` and routes interactions to your implementation. diff --git a/docs/de/developer/blocks/index.md b/docs/de/developer/blocks/index.md new file mode 100644 index 0000000..525a200 --- /dev/null +++ b/docs/de/developer/blocks/index.md @@ -0,0 +1,104 @@ +# Block Architecture & State System + +Blocks form the static and dynamic foundation of Minecraft worlds. In Pumpkin, block handling is cleanly bifurcated into two distinct layers: + +1. **Static Data Layer ([`pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data))**: Compile-time generated metadata including numeric block IDs, block state IDs, hardness, blast resistance, light emission, opacity, collision shapes, and block state properties generated by [`pumpkin-codegen`](/developer/codegen). +2. **Dynamic Behavior Layer ([`pumpkin::block`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/block))**: Server-side behavioral hooks—such as opening containers, toggling redstone signals, growing crops, breaking triggers, and collision effects—handled via the `BlockBehaviour` trait. + +--- + +## Static Blocks vs. Interactive Behaviors + +Because Minecraft contains thousands of block state combinations, Pumpkin avoids allocating runtime behavior structs for simple, non-interactive blocks: + +| Category | Description | Examples | Runtime Behavior Required? | +| :--- | :--- | :--- | :--- | +| **Static Blocks** | Rigid solid voxels without interactive logic or custom physics | `stone`, `dirt`, `cobblestone`, `bedrock`, `obsidian` | No — Placement, breaking times, and tool requirements are handled entirely by `pumpkin-data`. | +| **Interactive Blocks** | Blocks triggered by right-clicks or tool actions | `door`, `trapdoor`, `lever`, `button`, `crafting_table` | Yes — Implements `BlockBehaviour::normal_use` or `use_with_item`. | +| **Collision Blocks** | Blocks that modify entity movement or apply forces | `honey_block`, `slime_block`, `soul_sand`, `powder_snow` | Yes — Implements `on_entity_collision` or `on_landed_upon`. | +| **Redstone Components** | Power sources, conductors, and logic gates | `redstone_wire`, `repeater`, `comparator`, `observer` | Yes — Implements `emits_redstone_power` and `on_neighbor_update`. | +| **Block Entities** | Blocks with persistent, serialized custom states | `chest`, `furnace`, `sign`, `decorated_pot`, `spawner` | Yes — Implements `create_block_entity` and links a `BlockEntity`. | + +--- + +## Block IDs, States, and Properties + +Minecraft distinguishes between a **Block** type and a specific **Block State**: + +### 1. `BlockId` and `BlockStateId` + +- **`BlockId`**: An identifier representing a base block type (e.g., `minecraft:oak_door`). +- **`BlockStateId`**: A 16-bit integer representing a unique combination of block properties (e.g., `minecraft:oak_door[facing=north,half=lower,hinge=left,open=false,powered=false]`). + +In Pumpkin, block data is looked up from precomputed lookup tables in `pumpkin-data`: + +```rust +use pumpkin_data::Block; +use pumpkin_data::BlockStateId; + +// Access static properties directly from generated data +let door_block = Block::OAK_DOOR; +let default_state_id: BlockStateId = door_block.default_state_id; + +let hardness = door_block.hardness; +let blast_resistance = door_block.blast_resistance; +let is_air = door_block.is_air; +``` + +### 2. Generated State Property Helpers + +Rather than parsing raw strings or bitmasks manually, [`pumpkin-codegen`](/developer/codegen) generates strongly typed property helpers in `pumpkin_data::block_properties`: + +```rust +use pumpkin_data::block_properties::{DoorLikeProperties, DoubleBlockHalf, Facing}; + +// Read typed properties from a numeric BlockStateId +let is_open = DoorLikeProperties::open(state_id); +let half = DoorLikeProperties::half(state_id); +let facing = DoorLikeProperties::facing(state_id); + +// Generate a new BlockStateId with mutated properties +let toggled_state_id = DoorLikeProperties::with_open(state_id, !is_open); +``` + +--- + +## Physics, Bounding Boxes & Raycasting + +Pumpkin calculates collision and raycasting bounding boxes directly using pre-baked shapes in `pumpkin-data`: + +- **Solid Collisions**: During physics ticks, an entity's axis-aligned bounding box (AABB) is tested against surrounding block collision voxels. +- **Raycasting**: When a player targets a block to place or break, client raycasts are validated against the server-side block outline shapes. +- **Custom Movement Hooks**: Blocks like slime blocks or honey blocks override vertical rebound or horizontal velocity: + - `bounce_entity_after_fall`: Dampens fall damage and multiplies rebound velocity. + - `stop_vertical_movement_after_fall`: Zeros fall velocity upon contact. + +--- + +## Block Entities (Tile Entities) + +When a block must store arbitrary dynamic data that cannot fit into standard block state bits (such as container inventories, custom sign text, brewing timers, or mob spawner delays), it uses a **Block Entity**: + +| Component | Responsibility | Implementation File | +| :--- | :--- | :--- | +| `BlockEntity` Trait | Defines NBT serialization (`to_nbt`, `from_nbt`) and tick execution | [`crates/pumpkin/src/block/entities/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/block/entities) | +| `create_block_entity` | Hook in `BlockBehaviour` called when placing the block | Attached to relevant block behaviors | +| Chunk Entity Storage | Manages active block entities and coordinates within chunk sections | [`crates/pumpkin-world`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-world) | + +--- + +## The Block Registry & Dispatch System + +Block interaction events are routed through the central `BlockRegistry` in [`crates/pumpkin/src/block/registry.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/block/registry.rs): + +1. **Registration**: At server initialization, all behavior structs are registered into the `BlockManager`. +2. **Mapping**: The manager maps numeric `BlockId` keys to thread-safe behavior instances (`Arc`). +3. **Dispatch**: When the network layer receives block action packets (such as `CUseItemOn` or block dig packets), the server looks up the behavior in $O(1)$ time and invokes the relevant hook. + +--- + +## Next Steps + +To implement a new interactive block behavior, continue to the practical guide: + +- **[Adding a Block](/developer/blocks/adding-blocks)**: Complete walk-through of declaring attributes, implementing interaction hooks, creating block entities, and registering behaviors. diff --git a/docs/de/developer/codegen.md b/docs/de/developer/codegen.md new file mode 100644 index 0000000..3908d04 --- /dev/null +++ b/docs/de/developer/codegen.md @@ -0,0 +1,66 @@ +# Code Generation (`pumpkin-codegen`) + +Minecraft is a massive game with thousands of block states, items, packet IDs, sound events, recipes, and biome parameters. These registries frequently change between Minecraft updates. + +To maintain 100% fidelity without manually maintaining tens of thousands of error-prone constants, Pumpkin utilizes an automated code generation tool: [`tools/pumpkin-codegen`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/tools/pumpkin-codegen). + +--- + +## Purpose & Workflow + +`pumpkin-codegen` parses official Minecraft server data generator reports, asset files, and version specifications, and outputs zero-cost, strongly-typed Rust files directly into [`crates/pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data). + +The pipeline operates in three distinct stages: +1. **Ingestion**: Reads raw JSON assets and vanilla server reports from `assets/` and `assets/datapacks/`. +2. **Transformation**: Normalizes states, calculates bitsets, processes ViaVersion remappings, and optimizes lookup tables. +3. **Emission**: Emits compiled Rust constants and structs into [`crates/pumpkin-data/src/generated`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data/src/generated). + +--- + +## Generated Datasets + +`pumpkin-codegen` generates the following components in `pumpkin-data`: + +| Category | Description | Generated Source | +| :--- | :--- | :--- | +| **Packets** | Serverbound and clientbound packet IDs for all protocol states across supported Minecraft versions. | `packet.rs` | +| **Blocks** | Block definitions, default states, state properties (facing, powered, waterlogged, etc.), and block collision boxes. | `block.rs` | +| **Items** | Item definitions, stack sizes, durability, data components, food properties, and equipment slots. | `item.rs` | +| **Registries** | Dynamic registries for biomes, dimensions, chat types, damage types, trim materials, and banner patterns. | `registry.rs` | +| **Biomes & Noise** | Climate parameters, noise router configurations, 3D noise settings, and surface rules. | `biome.rs`, `noise_router.rs` | +| **Entities** | Entity types, default bounding boxes, attributes, and tracked entity metadata schemas. | `entity_type.rs`, `tracked_data.rs` | +| **Recipes & Crafting** | Shaped, shapeless, smelting, blasting, smoking, and stonecutting recipes. | `recipes.rs` | +| **Tags** | Block tags, item tags, fluid tags, and entity type tags (e.g., `#minecraft:mineable/pickaxe`). | `tag.rs` | +| **Enchantments & Potions** | Enchantment definitions, potion effects, and brewing stand recipes. | `enchantments.rs`, `potion.rs` | +| **Translations** | Official localization keys for chat messages and item names. | `translations.rs` | +| **Plugin WIT** | WebAssembly Component Model WIT files defining plugin interfaces. | `wit/` | + +--- + +## Running Code Generation + +To run the code generator locally: + +```bash +# Navigate to the workspace root +cd Pumpkin + +# Run the codegen tool +cargo run -p pumpkin-codegen +``` + +### When Should You Run Codegen? + +- **Upgrading to a New Minecraft Version**: When porting Pumpkin to a new minor or major Minecraft release. +- **Adding Protocol Mappings**: When adding support for an older or newer client protocol version. +- **Updating WIT Definitions**: When adding new functions or types to the WebAssembly plugin API. + +--- + +## Data Sources + +`pumpkin-codegen` reads data from several primary sources: + +1. **Minecraft Data Generator Reports**: Generated by running `java -DbundlerMainClass=net.minecraft.data.Main -jar server.jar --reports`. +2. **Minecraft Client Assets**: Extracted from client `.jar` files for translation keys, model shapes, and sound mappings. +3. **Minecraft Protocol Wiki**: Cross-referenced with protocol specifications for packet IDs. diff --git a/docs/de/developer/commands/adding-commands.md b/docs/de/developer/commands/adding-commands.md new file mode 100644 index 0000000..eda739a --- /dev/null +++ b/docs/de/developer/commands/adding-commands.md @@ -0,0 +1,246 @@ +# Adding a Command + +This guide explains how to implement and register a new command in the Pumpkin server codebase. + +Commands in Pumpkin are implemented in the `crates/pumpkin/src/command/commands/` directory and registered into the central [`CommandDispatcher`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/dispatcher.rs) during server initialization. + +--- + +## Anatomy of a Pumpkin Command + +A typical command file contains: +1. **Metadata Constants**: Command description, permission node identifier, and argument names. +2. **Permission Registration**: Declaring default OP requirements in the server's [`PermissionRegistry`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-util/src/permission.rs). +3. **Command Executors**: Structs implementing the [`CommandExecutor`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/mod.rs) trait for each terminal node or subcommand. +4. **Registration Function**: Constructing the argument tree using fluent builders (`command`, `literal`, `argument`). + +--- + +## Step-by-Step Implementation: Creating `/heal` + +Let's implement a complete `/heal` command that can heal either the executing player or a set of target players: + +``` +/heal -> Heals the executing sender +/heal -> Heals target player(s) +/heal -> Heals target player(s) by a specified amount +``` + +### 1. Create the File + +Create `crates/pumpkin/src/command/commands/heal.rs`: + +```rust +use std::sync::Arc; + +use crate::command::argument_builder::{ArgumentBuilder, argument, command}; +use crate::command::argument_types::core::float::FloatArgumentType; +use crate::command::argument_types::entity::EntityArgumentType; +use crate::command::context::command_context::CommandContext; +use crate::command::errors::error_types::CommandErrorType; +use crate::command::node::dispatcher::CommandDispatcher; +use crate::command::node::{CommandExecutor, CommandExecutorResult}; +use crate::entity::EntityBase; +use pumpkin_data::translation; +use pumpkin_util::PermissionLvl; +use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry}; +use pumpkin_util::text::TextComponent; + +const DESCRIPTION: &str = "Restores health to target entities."; +const PERMISSION: &str = "pumpkin:command.heal"; + +const ARG_TARGETS: &str = "targets"; +const ARG_AMOUNT: &str = "amount"; + +const ERROR_NOT_PLAYER: CommandErrorType<0> = CommandErrorType::new( + translation::java::PERMISSIONS_REQUIRES_PLAYER, + translation::java::PERMISSIONS_REQUIRES_PLAYER, +); +``` + +### 2. Implement the Executors + +Command execution logic is encapsulated in structs implementing [`CommandExecutor`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/mod.rs): + +```rust +/// Heals the executing player to full health. +struct SelfHealExecutor; + +impl CommandExecutor for SelfHealExecutor { + fn execute(&self, context: &CommandContext) -> CommandExecutorResult { + let player = context + .source + .output + .as_player() + .ok_or_else(|| ERROR_NOT_PLAYER.create_without_context())?; + + // Restore player health + player.living_entity.set_health(20.0); + + context.source.send_feedback( + TextComponent::text("Fully restored your health!"), + true, // Broadcast to operators if configured + ); + + // Return number of affected entities + Ok(1) + } +} + +/// Heals specified target players. +struct TargetsHealExecutor { + has_amount: bool, +} + +impl CommandExecutor for TargetsHealExecutor { + fn execute(&self, context: &CommandContext) -> CommandExecutorResult { + let targets = EntityArgumentType::get_players(context, ARG_TARGETS)?; + + let amount = if self.has_amount { + FloatArgumentType::get(context, ARG_AMOUNT)? + } else { + 20.0 + }; + + let count = targets.len(); + for target in &targets { + let current = target.living_entity.health.load(); + target.living_entity.set_health((current + amount).min(20.0)); + } + + let msg = if count == 1 { + TextComponent::text(format!("Healed {} by {} HP", targets[0].gameprofile.name, amount)) + } else { + TextComponent::text(format!("Healed {} players by {} HP", count, amount)) + }; + + context.source.send_feedback(msg, true); + + Ok(count as i32) + } +} +``` + +### 3. Build the Command Tree + +Use the fluent builder functions `command`, `literal`, and `argument` to assemble the syntax tree and register the permission: + +```rust +pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) { + // 1. Register permission node with default requirement (OP level 2) + registry.register_permission_or_panic(Permission::new( + PERMISSION, + DESCRIPTION, + PermissionDefault::Op(PermissionLvl::Two), + )); + + // 2. Assemble command tree + dispatcher.register( + command("heal", DESCRIPTION) + .requires(PERMISSION) + .executes(SelfHealExecutor) + .then( + argument(ARG_TARGETS, EntityArgumentType::Players) + .executes(TargetsHealExecutor { has_amount: false }) + .then( + argument(ARG_AMOUNT, FloatArgumentType::new(1.0, 100.0)) + .executes(TargetsHealExecutor { has_amount: true }), + ), + ), + ); +} +``` + +--- + +## Command Structure Patterns + +### Adding Subcommand Literals +Use `literal("subcommand_name")` to create keyword branches: + +```rust +use crate::command::argument_builder::{argument, command, literal}; + +dispatcher.register( + command("weather", "Set the world weather") + .requires("minecraft:command.weather") + .then(literal("clear").executes(ClearWeatherExecutor)) + .then(literal("rain").executes(RainWeatherExecutor)) + .then(literal("thunder").executes(ThunderWeatherExecutor)) +); +``` + +### Cross-Platform Translation Feedback +Pumpkin servers serve both Java and Bedrock Edition players simultaneously. Whenever possible, use vanilla translation keys with `TextComponent::translate_cross` so messages render localized on the client: + +```rust +use pumpkin_data::translation; +use pumpkin_util::text::TextComponent; + +let msg = TextComponent::translate_cross( + translation::java::COMMANDS_KILL_SUCCESS_SINGLE, + translation::bedrock::COMMANDS_KILL_SUCCESSFUL, + [target.get_display_name()], +); + +context.source.send_feedback(msg, true); +``` + +### Error Handling in Executors +Executors must return `CommandExecutorResult`, which resolves to `Result`. If validation fails, return an error cleanly without panicking: + +```rust +use crate::command::errors::error_types::CommandErrorType; + +const ERROR_INVALID_VALUE: CommandErrorType<0> = CommandErrorType::new( + "Amount must be greater than zero.", + "Amount must be greater than zero.", +); + +if amount <= 0.0 { + return Err(ERROR_INVALID_VALUE.create_without_context()); +} +``` + +--- + +## Registering in `default_dispatcher` + +Once your command is implemented, register it in `crates/pumpkin/src/command/commands/mod.rs`: + +1. **Declare the Module**: + ```rust + // In crates/pumpkin/src/command/commands/mod.rs + mod heal; + ``` + +2. **Invoke `register` in `default_dispatcher`**: + ```rust + pub fn default_dispatcher( + permission_manager: &PermissionManager, + commands_config: &CommandsConfig, + ) -> CommandDispatcher { + let registry = &permission_manager.registry; + let mut dispatcher = CommandDispatcher::new(); + + // ... existing registrations ... + heal::register(&mut dispatcher, registry); + + dispatcher + } + ``` + +--- + +## Best Practices Checklist + +- [ ] **No Panics or Unwraps**: Use `?` or return descriptive `CommandSyntaxError`s when arguments are invalid. +- [ ] **Permission Nodes**: Prefix permission strings with appropriate namespaces (`minecraft:command.` for vanilla commands, `pumpkin:command.` for server-specific commands). +- [ ] **Return Success Counts**: The returned `i32` represents the command's exit code or number of affected objects for redstone command blocks and `/execute store result`. +- [ ] **Respect GameRules**: When notifying players other than the sender, check if `send_command_feedback` is enabled: + ```rust + let server = context.source.server(); + if server.level_info.load().game_rules.send_command_feedback { + target.send_system_message(&feedback); + } + ``` diff --git a/docs/de/developer/commands/arguments-and-suggestions.md b/docs/de/developer/commands/arguments-and-suggestions.md new file mode 100644 index 0000000..6794ae1 --- /dev/null +++ b/docs/de/developer/commands/arguments-and-suggestions.md @@ -0,0 +1,159 @@ +# Argument Types & Suggestions + +Pumpkin provides a comprehensive suite of type-safe command arguments through `pumpkin-command` and the main `pumpkin` server crate. Arguments deserialize raw token streams into structured Rust types, validate ranges, and furnish tab-completion suggestions to clients. + +--- + +## Standard Argument Types + +Argument parsers implement the [`ArgumentType`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/argument_types/argument_type.rs) trait. The following built-in types are available for command builders: + +### 1. Primitive & Scalar Types + +| Argument Type | Import Path | Deserialized Type | Description | +| :--- | :--- | :--- | :--- | +| **`IntegerArgumentType`** | `...::core::integer` | `i32` | Standard 32-bit integer with optional `[min, max]` bounds. | +| **`FloatArgumentType`** | `...::core::float` | `f32` | 32-bit floating point number with bounds. | +| **`DoubleArgumentType`** | `...::core::double` | `f64` | 64-bit floating point number with bounds. | +| **`LongArgumentType`** | `...::core::long` | `i64` | 64-bit integer with bounds. | +| **`BoolArgumentType`** | `...::core::bool` | `bool` | Boolean value (`true` or `false`). | +| **`StringArgumentType`** | `...::core::string` | `String` | String tokens based on quoting rules. | + +#### String Argument Behaviors +- **`StringArgumentType::Word`**: Parses a single word without whitespace. +- **`StringArgumentType::Quotable`**: Parses either a single word or a quoted phrase (`"hello world"`). +- **`StringArgumentType::Greedy`**: Consumes all remaining characters in the input stream (ideal for chat/broadcast commands like `/say` and `/me`). + +--- + +## Coordinate Argument Types + +Minecraft coordinates support absolute positions, relative offsets (`~`), and local caret directions (`^`): + +```rust +use crate::command::argument_builder::argument; +use crate::command::argument_types::coordinates::block_pos::BlockPosArgumentType; +use crate::command::argument_types::coordinates::vec3::Vec3ArgumentType; + +// Block position (integer BlockPos) +argument("pos", BlockPosArgumentType) + +// Precise entity coordinates (f64 Vector3) +argument("location", Vec3ArgumentType) +``` + +### Retrieving Parsed Coordinates +Inside an executor, coordinate arguments resolve relative offsets against the sender's current position and orientation in the [`CommandSource`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/command/context/command_source.rs): + +```rust +// Extracts centered f64 coordinates (handling ~ and ^ relative to sender) +let pos: Vector3 = Vec3ArgumentType::get_coordinates(context, "location")?; + +// Extracts integer block position +let block_pos: BlockPos = BlockPosArgumentType::get_block_pos(context, "pos")?; +``` + +--- + +## Entity Selectors (`EntityArgumentType`) + +Entity selectors allow players and command blocks to target entities using player names, UUIDs, or vanilla target selectors (`@p`, `@r`, `@a`, `@e`, `@s`). + +In Pumpkin, entity selectors are accessed via [`EntityArgumentType`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/command/argument_types/entity.rs): + +```rust +pub enum EntityArgumentType { + /// Matches exactly one player + Player, + /// Matches zero or more players + Players, + /// Matches exactly one entity + Entity, + /// Matches zero or more entities + Entities, +} +``` + +### Resolution in Executors + +```rust +// 1. Target single player +let player: Arc = EntityArgumentType::get_player(context, "target")?; + +// 2. Target multiple players +let players: Vec> = EntityArgumentType::get_players(context, "targets")?; + +// 3. Target single living entity or mob +let entity: Arc = EntityArgumentType::get_entity(context, "target")?; + +// 4. Target multiple entities +let entities: Vec> = EntityArgumentType::get_entities(context, "targets")?; +``` + +### Supported Selector Syntaxes +Pumpkin's entity selector parser (`crates/pumpkin/src/command/argument_types/entity_selector/`) parses and validates: +- `@p`: Nearest player relative to execution coordinates. +- `@r`: Random player. +- `@a`: All online players. +- `@e`: All entities in loaded world chunks. +- `@s`: Executing entity (`context.source.entity`). +- **Bracket Arguments**: Filter queries like `@e[type=zombie,distance=..10,limit=5,sort=nearest]`. + +--- + +## Registry & Game Mechanic Types + +Pumpkin provides specialized argument parsers for Minecraft data types: + +- **`GameModeArgumentType`**: Resolves `survival`, `creative`, `adventure`, `spectator` or numeric IDs `0`–`3`. +- **`BlockArgumentType`**: Parses block identifiers and block states (e.g., `minecraft:chest[facing=north]`). +- **`ItemArgumentType`**: Parses item identifiers and optional data components (e.g., `minecraft:diamond_sword`). +- **`NbtArgumentType`**: Parses SNBT (Stringified NBT) tags into Pumpkin's NBT representation. +- **`TimeArgumentType`**: Parses durations with suffixes (e.g., `10s`, `20t`, `1d`). + +--- + +## Suggestion Providers & Tab Completion + +Arguments can supply dynamic tab-completion candidates to connected players using the [`SuggestionProvider`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/suggestion/mod.rs) trait: + +```rust +use crate::command::context::command_context::CommandContext; +use crate::command::suggestion::provider::SuggestionProvider; +use pumpkin_command::suggestion::suggestions::{Suggestions, SuggestionsBuilder}; + +struct CustomSuggestionProvider; + +impl SuggestionProvider for CustomSuggestionProvider { + fn suggest( + &self, + context: &CommandContext, + mut builder: SuggestionsBuilder, + ) -> Suggestions { + // Retrieve already typed token prefix + let remaining = builder.remaining(); + + let candidates = ["alpha", "bravo", "charlie"]; + for candidate in candidates { + if candidate.starts_with(remaining) { + builder.suggest(candidate); + } + } + + builder.build() + } +} +``` + +### Attaching Suggestions to Arguments +Use the `.suggests(...)` method on `RequiredArgumentBuilder`: + +```rust +argument("code", StringArgumentType::Word) + .suggests(CustomSuggestionProvider) + .executes(MyExecutor) +``` + +### Client Completion Protocol +- **Java Edition**: When a player presses `Tab`, the client transmits a `ServerboundCommandSuggestion` packet. Pumpkin matches the cursor position within the `CommandDispatcher`, executes the target node's `SuggestionProvider`, and responds with `ClientboundCommandSuggestions` containing completion ranges and tooltip tooltips. +- **Bedrock Edition**: Suggestion options for literals and enums are serialized ahead of time in the `CAvailableCommands` packet as dynamic enum value tables. diff --git a/docs/de/developer/commands/execution-and-context.md b/docs/de/developer/commands/execution-and-context.md new file mode 100644 index 0000000..6f5f044 --- /dev/null +++ b/docs/de/developer/commands/execution-and-context.md @@ -0,0 +1,118 @@ +# Execution & Context + +In Pumpkin, command execution separates the **physical origin** of a command from its **runtime context**. This design enables complex command routing, dimension shifts, conditional execution, and vanilla-compliant `/execute` chaining. + +--- + +## `CommandSender` vs. `CommandSource` + +Understanding the distinction between `CommandSender` and `CommandSource` is fundamental to developing commands in Pumpkin: + +| Attribute | `CommandSender` | `CommandSource` | +| :--- | :--- | :--- | +| **Concept** | The physical origin and network destination | The mutable runtime execution environment | +| **Mutability** | Immutable throughout command execution | Cloned and modified across `/execute` branches | +| **Key Fields** | Terminal socket, connection handle, RCON buffer | World, position coordinates, rotation, entity, silent flag | +| **Creation** | Instantiated on network connection or terminal input | Constructed via `sender.into_source(&server)` | + +### 1. `CommandSender` (Physical Caller) +Represents the entity or socket that initiated the command and receives its output: + +```rust +pub enum CommandSender { + Console, + Rcon(Arc>>), + Player(Arc), + CommandBlock(Arc, Arc), + Dummy, +} +``` + +- **Output Target**: Determines where messages formatted with `send_message` are routed (e.g., standard out, RCON buffer, player packet, or block entity NBT). +- **Hard Permissions**: Holds fixed base permissions (e.g., Console has level 4 bypass; Command Blocks have level 2). + +### 2. `CommandSource` (Execution Environment) +Represents the mutable runtime state during command evaluation: + +```rust +pub struct CommandSource { + pub output: CommandSender, + pub world: Option>, + pub entity: Option>, + pub position: Vector3, + pub rotation: Vector2, + pub name: String, + pub display_name: TextComponent, + pub server: Option>, + pub silent: bool, + pub command_result_taker: ResultValueTaker, + pub entity_anchor: EntityAnchor, +} +``` + +A sender converts into a `CommandSource` via `sender.into_source(&server)`. + +--- + +## The `/execute` Command & Context Redirection + +The `/execute` command manipulates `CommandSource` properties before passing control to a downstream command. + +### Branching Modifiers +- **`as `**: Changes `context.source.entity` to target entities without altering position or rotation. +- **`at `**: Updates `context.source.world`, `position`, and `rotation` to match the target entity. +- **`positioned `**: Modifies `context.source.position` to new coordinates. +- **`in `**: Changes `context.source.world` to the specified dimension (Overworld, Nether, The End). +- **`anchored (feet|eyes)`**: Adjusts coordinate offsets for line-of-sight and facing checks. + +### Context Forking & Chaining +When `/execute` matches multiple targets (e.g., `/execute as @e run ...`), `pumpkin-command` uses **forking**: +1. The dispatcher forks the execution context once per matched target. +2. The child command executes independently for each cloned `CommandSource`. +3. The cumulative success count is aggregated across all executions. + +--- + +## Result Values & Success Counts + +Vanilla commands communicate results back through integer exit codes and return values. + +### The Return Value Callback +Executors return `CommandExecutorResult` (`Result`). The returned integer signifies: +- **0**: Failure or zero entities affected. +- **> 0**: Number of entities affected, blocks changed, or items cleared. + +For Command Blocks, this value is persisted into the block entity's `SuccessCount`: + +```rust +pub fn set_success_count(&self, count: u32) { + if let Self::CommandBlock(c, _) = self { + c.success_count.store(count, Ordering::SeqCst); + } +} +``` + +### `/execute store` +When chained with `/execute store (result|success) ...`, Pumpkin intercepts the return value via `ReturnValueCallback` and writes it to scoreboards, bossbars, or entity NBT tags. + +--- + +## Feedback & GameRules Integration + +Sending output to the command sender is governed by server configuration and world game rules: + +```rust +context.source.send_feedback( + TextComponent::text("Command executed successfully!"), + true, // Broadcast to server operators +); +``` + +### Output Rules Matrix + +| Condition | Feedback Behavior | +| :--- | :--- | +| `silent == true` | Output is suppressed completely. | +| `send_command_feedback == false` | Player senders do not receive success feedback (errors are still displayed). | +| `command_block_output == false` | Command block execution output is not broadcast to online operators. | +| `broadcast_console_to_ops == true` | Server console and RCON commands emit a copy of their feedback to all online OP level 2+ players. | diff --git a/docs/de/developer/commands/index.md b/docs/de/developer/commands/index.md new file mode 100644 index 0000000..3b31eb9 --- /dev/null +++ b/docs/de/developer/commands/index.md @@ -0,0 +1,142 @@ +# Commands Architecture & Dispatcher + +Pumpkin features a high-performance, pure-Rust implementation of the **Brigadier** command tree specification through the dedicated `pumpkin-command` crate. It coordinates command parsing, requirement resolution, type-safe argument deserialization, execution branching, and dual-edition client tree synchronization for both **Minecraft: Java Edition** and **Minecraft: Bedrock Edition**. + +### Command Processing Pipeline + +Every command executed in Pumpkin flows through the following discrete stages: + +1. **Input Normalization**: Strips single leading slashes while preserving plugin multi-slash conventions (such as `//set`). +2. **Availability Check**: Rejects commands disabled via server configuration with standard `unknown command` syntax errors. +3. **Permission Evaluation**: Validates requirements (`PermissionLvl` or custom registry nodes) against the executing sender. +4. **Tokenization & Parsing**: `StringReader` recursively walks the `Tree`, matching literal keywords and parsing typed argument parameters. +5. **Context Dispatch**: Invokes the matched `CommandExecutor` callback with a typed `CommandContext`. +6. **Feedback & Result Consumption**: Formats cross-platform translation components, notifies observers, and writes exit codes back to the caller. + +--- + +## The `pumpkin-command` Crate + +Pumpkin decouples command parsing and tree representation from the main server runtime into the modular `pumpkin-command` crate (`crates/pumpkin-command`). + +Key components of `pumpkin-command` include: +- **`Tree`**: Contiguous vector-backed storage of attached command nodes (`AttachedNode`). +- **`CommandDispatcher`**: The central registry and dispatch engine. It processes raw command strings, parses tokens with `StringReader`, traverses the command tree, and executes target callbacks. +- **`ArgumentBuilder`**: Fluent builder API (`command`, `literal`, `argument`) modeled after Mojang's Brigadier library. +- **`CommandSource`**: Generic execution state representing the current position, rotation, dimension, entity, and feedback output stream. + +--- + +## The Command Node Hierarchy + +Commands in Pumpkin are modeled as directed graphs of nodes. To optimize memory allocations and avoid circular reference issues during building, the system separates nodes into **detached** and **attached** states: + +### 1. Detached Nodes (`DetachedNode`) +Constructed using fluent builders (`CommandArgumentBuilder`, `LiteralArgumentBuilder`, `RequiredArgumentBuilder`) before registration: +- **`CommandDetachedNode`**: Represents a root command entrypoint with an identifier and a description. +- **`LiteralDetachedNode`**: Represents a literal keyword in a subcommand branch (e.g., `set` in `/gamemode creative`). +- **`ArgumentDetachedNode`**: Represents a typed parameter parsed by an `ArgumentType` (e.g., coordinates, entity selectors, integer ranges). + +### 2. Attached Nodes (`AttachedNode`) +Once registered into the `CommandDispatcher`, detached trees are flattened into an indexed `Tree` structure: +```rust +pub enum AttachedNode { + Root(RootAttachedNode), + Literal(LiteralAttachedNode), + Argument(ArgumentAttachedNode), + Command(CommandAttachedNode), +} +``` +Each attached node has: +- **`NodeId`**: A zero-cost typed index (`NonZero`) into the contiguous node arena. +- **`children`**: Fast lookup map (`FxHashMap`) for literals and child arguments. +- **`requirements`**: Predicates determining whether a sender has permission to view or execute the node. +- **`command`**: Optional `CommandExecutor` callback invoked when parsing terminates on this node. +- **`redirect`**: Optional `Redirection` pointer to loop back or redirect to another sub-tree (used heavily by `/execute`). + +--- + +## Dispatch & Execution Pipeline + +When a command arrives at the server, `CommandDispatcher::handle_command` processes it through several stages: + +```rust +pub fn handle_command<'a>(&'a self, source: &S, mut input: &'a str) { + // 1. Strip single leading slash (preserving double-slash commands like //set) + if let Some(sliced) = input.strip_prefix('/') { + let first_token = input.split_whitespace().next().unwrap_or(""); + let sliced_token = sliced.split_whitespace().next().unwrap_or(""); + if !self.has_command(first_token) + && (self.has_command(sliced_token) || !first_token.starts_with("//")) + { + input = sliced; + } + } + + // 2. Check disabled commands from configuration + if self.is_disabled(Self::command_name(input)) { + let reader = StringReader::new(input); + Self::send_error_to_source(source, DISPATCHER_UNKNOWN_COMMAND.create(&reader), input); + return; + } + + // 3. Parse and execute + let output = self.execute_input(input, source); + if let Err(error) = output { + Self::send_error_to_source(source, error, input); + } +} +``` + +### Syntax Error Formatting +When parsing encounters invalid syntax, unexpected tokens, or out-of-range arguments, Pumpkin constructs a `CommandSyntaxError`. If context is available, the dispatcher formats a visual pointer highlighting exactly where the parser failed: + +``` +Unknown or incomplete command, see below for error +/gamemode creatve<--[HERE] +``` + +In the Java Edition client, this error text is annotated with a `ClickEvent::SuggestCommand`, allowing the player to click the error in chat to copy the failed command into their input box for quick editing. + +--- + +## Dual-Edition Network Synchronization + +Unlike vanilla servers which only serialize command graphs for Java Edition, Pumpkin dynamically synchronizes the registered command tree across both Java and Bedrock clients: + +### 1. Java Edition: `CCommands` Packet +When a player joins or their permission level updates (e.g., via `/op` or `/deop`), Pumpkin builds and sends a `CCommands` packet (`pumpkin_protocol::java::client::play::CCommands`): +- Converts `AttachedNode`s into a flat array of `ProtoNode` entries. +- Filters out disabled commands and nodes where `requirements.evaluate(source)` returns `false`. +- Serializes argument parsers (`brigadier:string`, `brigadier:integer`, `minecraft:entity`, etc.) and client-side suggestion identifiers. + +### 2. Bedrock Edition: `CAvailableCommands` Packet +Bedrock Edition uses an overload-and-enum format rather than Brigadier's recursive graph. In `client_suggestions.rs`, Pumpkin traverses the `AttachedNode` tree to synthesize: +- **`CommandData`**: Root command name, description, permission flags, and aliases. +- **`OverloadData`**: Flattened permutations of argument chains leading to executable endpoints. +- **`ParamData`**: Bedrock parameter types, bitflags (`ARG_FLAG_VALID`, `ARG_FLAG_ENUM`), and dynamically collected enum value tables. + +--- + +## Universal Execution Targets + +Pumpkin commands can be executed across diverse senders: + +| Sender | Source Representation | Output Destination | Default Permission | +| :--- | :--- | :--- | :--- | +| **Player** | `CommandSender::Player(Arc)` | Client system chat packet | Loaded from permission level / permissions file | +| **Server Console** | `CommandSender::Console` | Standard stdout (`tracing::info!`) | Level 4 (Full administrative bypass) | +| **RCON Console** | `CommandSender::Rcon(Arc>>)` | Network response packet payload | Level 4 | +| **Command Block** | `CommandSender::CommandBlock(Arc, Arc)` | NBT `LastOutput` field | Level 2 | +| **Datapack Function** | `CommandSender` inherited from caller | Inherited | Inherited | +| **WASM Plugin** | Invoked through WIT `execute_command` host call | Captured via `ResultDeferrer` | Configurable | + +--- + +## Command Configuration & Disabling + +Pumpkin allows server administrators to disable individual commands via `config/commands.toml` (`CommandsConfig`). When a command is disabled: +1. It is omitted from the `CCommands` packet for Java clients. +2. It is omitted from the `CAvailableCommands` packet for Bedrock clients. +3. Tab completion ignores the command entirely. +4. Any attempt to invoke the command returns `DISPATCHER_UNKNOWN_COMMAND`. diff --git a/docs/de/developer/entities/ai-and-mobs.md b/docs/de/developer/entities/ai-and-mobs.md new file mode 100644 index 0000000..bb1652f --- /dev/null +++ b/docs/de/developer/entities/ai-and-mobs.md @@ -0,0 +1,111 @@ +# Mob AI & Goal System + +Pumpkin implements a modular, priority-driven Artificial Intelligence (AI) goal system for computer-controlled mobs, located in [`crates/pumpkin/src/entity/ai/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity/ai) and [`crates/pumpkin/src/entity/mob/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity/mob). + +--- + +## The Goal System Architecture + +Mob behaviors are decomposed into independent modular tasks called **Goals**. Each mob possesses a **GoalSelector** that maintains an ordered collection of goals evaluated on every server tick. + +### Goal Priority Hierarchy + +Goals are assigned an integer priority (where lower numbers represent higher precedence). During each tick, higher-priority actions preempt lower-priority background tasks: + +| Priority | Goal Type | Evaluation Condition | Behavior | +| :--- | :--- | :--- | :--- | +| **1 (Highest)** | `SwimGoal` | Entity is submerged in water | Jump repeatedly to swim to the water surface and avoid drowning. | +| **2** | `MeleeAttackGoal` | Valid hostile target in sight/range | Navigate towards target entity and execute melee attacks. | +| **3** | `WanderAroundGoal` | No active combat target | Pick a random nearby navigable voxel and walk to it. | +| **4 (Lowest)** | `LookAtEntityGoal` | Player or mob within observation radius | Rotate head and body pitch/yaw towards the target entity. | + +### Execution Flow + +1. **Evaluation**: On every game tick, `GoalSelector::tick()` iterates through registered goals in order of priority. +2. **Activation**: If an inactive goal satisfies `can_start()`, it is activated via `start()`. +3. **Execution**: Active goals run their per-tick logic in `tick()`. +4. **Preemption & Termination**: If `should_continue()` returns `false`, or a mutually exclusive goal with higher priority activates, `stop()` is called and the goal resets. + +### The `Goal` Lifecycle + +Every AI behavior implements the `Goal` trait: + +```rust +pub trait Goal: Send + Sync { + /// Evaluates preconditions to determine if this goal can activate. + fn can_start(&self) -> bool; + + /// Called once when the goal transitions from inactive to active. + fn start(&mut self) {} + + /// Evaluates whether the goal should continue executing on subsequent ticks. + fn should_continue(&self) -> bool { + self.can_start() + } + + /// Executes the continuous behavior on each server tick. + fn tick(&mut self) {} + + /// Called when the goal completes, fails, or is preempted by a higher priority goal. + fn stop(&mut self) {} +} +``` + +--- + +## Standard Built-in Goals + +Pumpkin provides standard vanilla AI behaviors in `crates/pumpkin/src/entity/ai/`: + +| Goal | Description | Target Mobs | +| :--- | :--- | :--- | +| **`SwimGoal`** | Jumps repeatedly while submerged in water to prevent drowning. | Almost all land mobs | +| **`MeleeAttackGoal`** | Navigates towards an attack target and executes a strike when within reach. | Zombies, Spiders, Iron Golems | +| **`RangedAttackGoal`** | Stays at range and shoots projectiles (arrows, snowballs). | Skeletons, Pillagers, Blazes | +| **`FleeSunGoal`** | Seeks shade or water during daytime to prevent combustion. | Zombies, Skeletons | +| **`PanicGoal`** | Sprints in random directions when taking damage. | Cows, Pigs, Sheep, Chickens | +| **`WanderAroundGoal`** | Selects random accessible coordinates nearby when idle. | Passive & neutral mobs | +| **`LookAtEntityGoal`** | Rotates head to face nearby players within sensory range. | Villagers, Mobs | + +--- + +## Navigation & Pathfinding + +Mobs navigate the block world using Pumpkin's 3D grid pathfinder: + +1. **Path Calculation**: Resolves a path from the mob's origin to target coordinates using an A* algorithm optimized for Minecraft block geometries. +2. **Obstacle Avoidance**: + - Avoids hazardous blocks (lava, fire, sweet berry bushes). + - Prevents walking off ledges higher than the mob's fall threshold. + - Computes jumping steps over 1-block obstacles. +3. **Waypoint Steering**: The navigation engine updates the mob's velocity vectors per tick to smoothly guide the entity along waypoints. + +--- + +## Entity Attributes System + +Mob capabilities and stats are governed by the **Attributes** system in [`crates/pumpkin/src/entity/attributes.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/entity/attributes.rs): + +```rust +use pumpkin_data::entity::attribute::EntityAttribute; + +// Common vanilla attributes +EntityAttribute::GENERIC_MAX_HEALTH; // Base hit points (e.g. 20.0 for Zombies) +EntityAttribute::GENERIC_MOVEMENT_SPEED; // Base walking velocity (e.g. 0.23) +EntityAttribute::GENERIC_ATTACK_DAMAGE; // Melee attack damage (e.g. 3.0) +EntityAttribute::GENERIC_FOLLOW_RANGE; // Sensory detection range in blocks (e.g. 35.0) +EntityAttribute::GENERIC_KNOCKBACK_RESISTANCE; // Resistance against knockback (0.0 to 1.0) +``` + +### Modifiers +Attributes support dynamic modifiers with additive or multiplicative calculations (e.g., speed bonuses from Swiftness potions or armor attribute boosts). + +--- + +## Ageable & Passive Entities + +Passive animals (cows, sheep, pigs) implement the `Ageable` trait: + +- **Baby State**: Renders with smaller collision boxes and oversized heads. +- **Growing Age**: A negative tick timer counting upward to `0`. When it reaches `0`, the entity matures into an adult. +- **Breeding Cooldown**: Positive tick timer tracking cooldowns between successful matings. diff --git a/docs/de/developer/entities/index.md b/docs/de/developer/entities/index.md new file mode 100644 index 0000000..53623ee --- /dev/null +++ b/docs/de/developer/entities/index.md @@ -0,0 +1,77 @@ +# Entity Architecture & Hierarchy + +Entities in Minecraft represent all dynamic, living and non-living objects that exist within a world—ranging from dropped item stacks and arrows to zombies, villagers, and players. + +In Pumpkin, entity systems are implemented in [`crates/pumpkin/src/entity/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity). The architecture leverages object-oriented trait composition on top of concurrency-safe, cache-friendly data structures. + +--- + +## Entity Trait Hierarchy + +Pumpkin structures entities into an inheritance hierarchy using Rust traits: + +| Trait / Type | Parent Trait | Core Capabilities & State | +| :--- | :--- | :--- | +| `Entity` | *(None / Base Struct)* | Entity ID, UUID, atomic position and velocity vectors, bounding box, pose | +| `EntityBase` | *(Root Trait)* | Dynamic runtime downcasting (`get_living_entity`, etc.), world tick, damage, teleportation | +| `LivingEntity` | `EntityBase` | Health, absorption, potion effects, equipment slots, attribute containers | +| `Mob` | `LivingEntity` | AI goal selector, navigation, pathfinding, target tracking, death loot | +| `Player` | `LivingEntity` | Network client session, inventory, crafting screens, hunger, advancements | + +### 1. `Entity` (Base Struct) +Contains fundamental physics, geometry, and spatial state common to all entities: +- **`entity_id`**: Fast 32-bit unique integer identifier used across network packets. +- **`uuid`**: 128-bit persistent UUID. +- **`position` & `velocity`**: Thread-safe vectors (`AtomicCell>`) allowing concurrent reading without lock contention. +- **`bounding_box`**: 3D axis-aligned bounding box (AABB) used for collision resolution and raycast targeting. +- **`pose`**: Current pose (`Standing`, `Crouching`, `Sleeping`, `FallFlying`, etc.). + +### 2. `EntityBase` (Foundation Trait) +The root dynamic trait implemented by every entity in the server: +- Supports safe runtime downcasting via helper methods (`get_living_entity()`, `get_mob()`, `get_player()`). +- Provides hooks for world interaction: `tick()`, `damage()`, `teleport()`, `on_lightning_strike()`, `is_immune_to_explosion()`. + +### 3. `LivingEntity` (Biological Trait) +Extends `EntityBase` for entities with health, combat, and animation states: +- Manages health bars, absorption, invulnerability ticks, and fall damage. +- Tracks active potion effects (`crates/pumpkin/src/entity/effect/`). +- Handles equipment slots (`MainHand`, `OffHand`, `Helmet`, `Chestplate`, `Leggings`, `Boots`). +- Encapsulates attribute containers (max health, movement speed, knockback resistance). + +### 4. `Mob` (AI Trait) +Extends `LivingEntity` for computer-controlled mobs: +- Manages AI goal selectors (attacking, wandering, fleeing, looking around). +- Controls navigation and pathfinding over blocks. +- Spawns configured loot tables upon death. + +### 5. `Player` (Human Client) +Represents a human player connected through a game client: +- Links to network socket handlers (`JavaClient` or `BedrockClient`). +- Manages container screen handlers, ender chests, player inventory, and crafting. +- Tracks hunger, saturation, exhaustion, experience levels, and advancement criteria. + +--- + +## Physics & Movement Simulation + +On every server tick, Pumpkin simulates entity physics through a deterministic sequence: + +1. **Apply Velocity & Gravity**: Evaluates current velocity and adds downward gravitational acceleration unless `has_gravity()` is false or the entity is flying. +2. **Resolve Block Collisions**: Checks the entity's 3D axis-aligned bounding box (`bounding_box`) against solid block voxels in the surrounding chunk; velocity along colliding axes is dampened or zeroed. +3. **Apply Fluid Drag & Buoyancy**: Evaluates whether the entity intersects water or lava fluids, applying drag coefficients and buoyant upward force. +4. **Synchronize & Broadcast**: Persists the updated coordinates in atomic position cells and broadcasts relative delta movement packets to observing players. +5. **Void Death**: Non-player entities falling below $Y = -128$ are automatically despawned. + +--- + +## Combat & Damage System + +Damage calculations reside in [`crates/pumpkin/src/entity/combat.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/entity/combat.rs): + +1. **Damage Types**: Damage is categorized by `DamageType` (e.g., `GENERIC`, `PLAYER_ATTACK`, `FALL`, `FIRE`, `MAGIC`, `EXPLOSION`, `LIGHTNING_BOLT`). +2. **Damage Scaling**: + - Armor defense points mitigate incoming physical damage according to the vanilla formula: + $$\text{damage} = \text{damage} \times \left(1 - \frac{\min(20, \max(\text{armor} / 5, \text{armor} - \frac{\text{damage}}{2 + \text{toughness} / 4}))}{25}\right)$$ + - Resistance status effects and Protection enchantments apply secondary reductions. +3. **Knockback**: When damage is dealt, a directional impulse vector is added to the victim's velocity, modulated by their knockback resistance attribute. +4. **Hurt Animations**: Triggers hurt sounds and broadcasts red-tint flash metadata to all observers. diff --git a/docs/de/developer/entities/spawning-and-tracking.md b/docs/de/developer/entities/spawning-and-tracking.md new file mode 100644 index 0000000..82a3d7b --- /dev/null +++ b/docs/de/developer/entities/spawning-and-tracking.md @@ -0,0 +1,95 @@ +# Spawning & Network Tracking + +To maintain high tick rates with hundreds of active entities, Pumpkin utilizes a chunk-based entity tracking and streaming system. + +Entities are held in chunk-level collections inside [`crates/pumpkin-world`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-world) and synchronized across clients via edition-specific network packets. + +--- + +## The Spawning Pipeline + +Spawning an entity involves creating an instance, allocating its network IDs, registering it with the world, and broadcasting spawn packets to chunk observers: + +1. **Construct Entity Instance**: Instantiate the specific entity struct (such as `ZombieEntity`, `ArrowEntity`, or `ItemEntity`) initialized with its starting world and spatial coordinates. +2. **Allocate Unique Network Identifiers**: Assign an atomic 32-bit `entity_id` for network protocol packets and a 128-bit persistent UUID. +3. **Register in World & Chunk Map**: Insert the entity into the world's entity storage and index it within its corresponding chunk coordinate entry. +4. **Query Chunk Observers**: Look up all connected players within tracking range whose view distance includes the containing chunk. +5. **Broadcast Edition-Specific Spawn Packets**: Transmit the relevant spawn and metadata packets tailored to each observing client's edition (Java or Bedrock). + +### Spawning via the World API + +```rust +use std::sync::Arc; +use pumpkin_data::entity::EntityType; +use pumpkin_util::math::vector3::Vector3; +use crate::entity::Entity; +use crate::entity::mob::zombie::ZombieEntity; + +// 1. Create base entity with target world and position +let position = Vector3::new(100.5, 64.0, -200.5); +let base_entity = Entity::new(world.clone(), position, &EntityType::ZOMBIE); + +// 2. Wrap in specific entity implementation +let zombie = Arc::new(ZombieEntity::new(base_entity)); + +// 3. Register into the world +world.spawn_entity(zombie); +``` + +--- + +## Network Packet Synchronization + +When a player enters a chunk containing entities—or when an entity moves into an observed chunk—Pumpkin dispatches spawn packets tailored to the client's edition: + +### Java Edition Spawning +1. **`CSpawnEntity`**: Declares the entity ID, UUID, entity type, position, and initial pitch/yaw. +2. **`CSetEntityMetadata`**: Sends initial visual states (custom name tags, burning flags, baby/adult state). +3. **`CEntityVelocity`**: Synchronizes initial momentum. +4. **`CUpdateEntityRot` & `CHeadRot`**: Synchronizes head orientation independently from body yaw. + +### Bedrock Edition Spawning +1. **`CAddActor`**: Declares the runtime ID, actor type identifier, position, rotation, and embedded `SyncedActorDataList`. +2. **`CSetActorMotion`**: Sets initial velocity vector. + +--- + +## Movement & Position Synchronization + +Transmitting absolute 64-bit coordinates every tick would congest network bandwidth. Instead, Pumpkin dynamically switches between compact relative delta updates and authoritative full synchronization based on movement distance: + +| Metric / Scenario | Threshold | Java Protocol Packet | Bedrock Protocol Packet | Bandwidth Impact | +| :--- | :--- | :--- | :--- | :--- | +| **Incremental Movement** | $\Delta < 8$ blocks per tick | `CUpdateEntityPos` / `CUpdateEntityPosRot` | `CMoveActorDelta` | Minimal (fixed-point byte deltas) | +| **Teleport / Long Distance** | $\Delta \ge 8$ blocks or chunk warp | `CTeleportEntity` / `CEntityPositionSync` | `CMoveActorAbsolute` | Full 64-bit float coordinate payload | +| **Rotation Only** | Position unchanged | `CUpdateEntityRot` & `CHeadRot` | `CMoveActorDelta` (rot flags only) | Minimal (byte angles) | + +### 1. Relative Delta Movement +If the entity moved less than 8 blocks in a single tick, Pumpkin sends delta packets using fixed-point encoded increments: +- **Java**: `CUpdateEntityPos` (XYZ delta), `CUpdateEntityRot` (yaw/pitch delta), or `CUpdateEntityPosRot` (both). +- **Bedrock**: `CMoveActorDelta` with bitflags indicating which coordinates changed (`MOVE_ACTOR_DELTA_FLAG_HAS_X`, `HAS_Y`, `HAS_Z`). + +### 2. Full Position Synchronization +When an entity teleports, crosses chunk boundaries, or accumulates floating-point drift: +- **Java**: Sends `CEntityPositionSync` containing absolute coordinates, velocities, and on-ground flags. +- **Bedrock**: Sends `CMoveActorAbsolute`. + +--- + +## Passenger & Vehicle Mounting + +When an entity mounts another entity (e.g., a player mounting a horse, boat, or minecart): + +- **Java Edition**: Sends `CSetPassengers` containing the vehicle's `VarInt` entity ID and an array of passenger entity IDs. +- **Bedrock Edition**: Sends `CSetActorLink` defining the rider and vehicle runtime IDs and link type (`Rider`, `Passenger`). + +--- + +## Despawning & Entity Removal + +When an entity dies, is removed from the world, or steps out of a player's tracking radius: + +- **Java Edition**: Sends `CRemoveEntities` with an array of entity IDs. +- **Bedrock Edition**: Sends `CRemoveActor` with the entity's runtime ID. + +The client immediately frees its local rendering and audio memory for the removed entity. diff --git a/docs/de/developer/extractor.md b/docs/de/developer/extractor.md new file mode 100644 index 0000000..2b09f8f --- /dev/null +++ b/docs/de/developer/extractor.md @@ -0,0 +1,144 @@ +# Vanilla Data Extractor + +The [Pumpkin Extractor](https://github.com/Pumpkin-MC/Extractor) is a dedicated Fabric mod written in Kotlin that introspects a running vanilla Minecraft server or client to dump authoritative game registries and runtime metadata into structured JSON files. + +These JSON dumps are consumed by [`pumpkin-codegen`](/developer/codegen) to generate strongly-typed Rust code in `pumpkin-data` and `pumpkin-world`. + +--- + +## Datapacks vs. Extractor: Our Data Philosophy + +Before creating a new extractor, it is crucial to understand Pumpkin's data hierarchy: + +> [!IMPORTANT] +> **We preferably use vanilla datapacks whenever possible.** +> If vanilla Minecraft already exposes a registry or system through official datapack JSON files, we consume the datapack directly rather than writing custom extractor code. + +### When to Use Datapacks vs. When to Use the Extractor + +| Source | When to Use | Examples | +| :--- | :--- | :--- | +| **Vanilla Datapacks** *(Preferred)* | Any data officially serialized as JSON by Mojang in `data/minecraft/`. | Recipes, tags (`blocks`, `items`, `fluids`), biomes, dimension types, damage types, trim materials, worldgen noise parameters. | +| **Fabric Extractor** *(Only when necessary)* | Game data **hardcoded in Java bytecode** that cannot be exported from raw datapack files. | Block collision shapes, AABB bounding boxes, block hardness, packet ID-to-state mappings, entity tracked data IDs, fuel burn durations, composter chances, and flower pot conversion tables. | + +--- + +## Extractor Mod Architecture + +The Extractor runs on top of the Fabric loader using official Fabric Yarn mappings: + +- **Mod Entry Point (`src/main/kotlin/de/snowii/extractor/Extractor.kt`)**: Registers with Fabric's `ServerLifecycleEvents.SERVER_STARTED` event. When the headless server finishes booting, it invokes all registered extractors and dumps files to `run/pumpkin_extractor_output/`. +- **Extractor Interface**: Every data extractor implements the `Extractor.Extractor` interface: + ```kotlin + interface Extractor { + fun fileName(): String + + @Throws(Exception::class) + fun extract(server: MinecraftServer): JsonElement + + // Returns true if output belongs in tests/ rather than the root output folder + fun isTest(): Boolean = false + } + ``` +- **Output Directory**: JSON files are saved to `pumpkin_extractor_output/` encoded in UTF-8 without HTML escaping. + +--- + +## How to Add a New Extractor + +Follow this step-by-step workflow when you need to extract new hardcoded vanilla mechanics: + +### Step 1: Verify the Data Cannot Be Sourced from Datapacks +Inspect the official Minecraft client/server JAR or Mojang's data reports: +- Check `data/minecraft/` for existing registries, tags, or JSON definitions. +- If the data is present in the datapack, add it directly to `Pumpkin/assets/datapacks/` instead of writing an extractor. + +### Step 2: Create the Extractor Class +If the data is hardcoded in Java methods, create a new Kotlin class in: +```text +src/main/kotlin/de/snowii/extractor/extractors/.kt +``` + +Implement the `Extractor.Extractor` interface: + +```kotlin +package de.snowii.extractor.extractors + +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import de.snowii.extractor.Extractor +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.server.MinecraftServer +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack + +class Fuels : Extractor.Extractor { + override fun fileName(): String { + return "fuels.json" + } + + override fun extract(server: MinecraftServer): JsonElement { + val fuelsJson = JsonObject() + + // Query vanilla runtime methods + server.fuelValues().fuelItems().forEach { fuel -> + val itemId = BuiltInRegistries.ITEM.getKey(fuel).toString() + val burnDuration = server.fuelValues().burnDuration(ItemStack(fuel)) + fuelsJson.add(itemId, JsonPrimitive(burnDuration)) + } + + return fuelsJson + } +} +``` + +### Step 3: Register the Extractor +Open `src/main/kotlin/de/snowii/extractor/Extractor.kt` and add an instance of your class to the `extractors` array: + +```kotlin +val extractors = arrayOf( + // ... existing extractors ... + Blocks(), + Items(), + Entities(), + Fuels(), // [!code ++] +) +``` + +### Step 4: Run the Extractor +Execute the headless server task in the Extractor repository: + +```bash +./gradlew runServer +``` + +Once the extraction finishes, locate your new JSON file in: +```text +run/pumpkin_extractor_output/.json +``` + +### Step 5: Integrate with `pumpkin-codegen` +1. Move the generated JSON file into Pumpkin: + ```bash + cp run/pumpkin_extractor_output/fuels.json ../Pumpkin/assets/ + ``` +2. In the `Pumpkin` repository, open `tools/pumpkin-codegen`: + - Create a parser for `.json`. + - Write a code generation template to produce the corresponding Rust structs or static lookup tables in `crates/pumpkin-data`. +3. Run code generation: + ```bash + cargo run -p pumpkin-codegen + ``` +4. Verify that `pumpkin-data` compiles and passes all unit tests: + ```bash + cargo test -p pumpkin-data + ``` + +--- + +## Best Practices for Extractor Contributions + +- **Sort Keys Deterministically**: When generating JSON objects or lists, sort entries by numeric registry ID or alphabetically by identifier (`minecraft:stone`, `minecraft:dirt`). This prevents noisy git diffs when re-running extraction across updates. +- **Prefer Standard Types**: Use vanilla identifiers (`net.minecraft.resources.Identifier`) as string keys instead of arbitrary integers where possible. +- **Access Wideners**: If private vanilla fields or methods need to be accessed, declare an access widener in `src/main/resources/extractor.accesswidener` rather than resorting to brittle runtime reflection. diff --git a/docs/de/developer/items/adding-items.md b/docs/de/developer/items/adding-items.md new file mode 100644 index 0000000..6335d14 --- /dev/null +++ b/docs/de/developer/items/adding-items.md @@ -0,0 +1,121 @@ +# Adding an Item Behavior + +This guide details how to implement custom interactive mechanics for an item in Pumpkin. + +--- + +## When Is an Item Behavior Needed? + +- **Passive Items** (e.g., diamonds, gold ingots, sticks): Do **not** require custom Rust code. Their stack sizes, tool categories, and crafting recipes are handled automatically by `pumpkin-data` and `pumpkin-inventory`. +- **Interactive Items** (e.g., snowballs, ender pearls, hoes, bows, potions): Require an implementation of `ItemBehaviour` to handle right-click events, cooldowns, animations, and entity spawns. + +--- + +## Step 1: Define the Item Struct & Metadata + +Create a new file under `crates/pumpkin/src/item/items/.rs` (e.g., `items/snowball.rs`): + +```rust +use std::sync::Arc; +use pumpkin_data::entity::EntityType; +use pumpkin_data::item::Item; +use pumpkin_data::sound::{Sound, SoundCategory}; +use crate::entity::Entity; +use crate::entity::player::Player; +use crate::entity::projectile::snowball::SnowballEntity; +use crate::item::{ItemBehaviour, ItemMetadata}; + +pub struct SnowBallItem; + +// 1. Declare the associated numeric Item ID(s) +impl ItemMetadata for SnowBallItem { + fn ids() -> Box<[u16]> { + [Item::SNOWBALL.id].into() + } +} + +// 2. Implement the interaction behavior +impl ItemBehaviour for SnowBallItem { + fn normal_use(&self, _item: &Item, player: &Player) { + let position = player.position(); + let world = player.world(); + + // Play throw sound effect + world.play_sound( + Sound::EntitySnowballThrow, + SoundCategory::Neutral, + &position, + ); + + // Construct and launch the projectile entity + let entity = Entity::new(world.clone(), position, &EntityType::SNOWBALL); + let snowball = SnowballEntity::new_shot(entity, player.get_entity()); + let (yaw, pitch) = player.rotation(); + snowball.thrown.set_velocity_from(pitch, yaw, 0.0, 1.5, 1.0); + world.spawn_entity(Arc::new(snowball)); + + // Safely decrement held item unless in Creative mode + let mut held = player.inventory.held_item(); + held.decrement_unless_creative(player.gamemode.load(), 1); + player.inventory.set_held_item(held); + } +} +``` + +--- + +## Step 2: `ItemBehaviour` Hook Reference + +The `ItemBehaviour` trait provides comprehensive hooks for different interaction models: + +### Instant Single-Use Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `normal_use` | `(&self, item: &Item, player: &Player)` | Triggered on right-clicking into open air. | +| `normal_use_with_rotation` | `(&self, item: &Item, player: &Player, yaw: f32, pitch: f32)` | Provides client-reported view angles for precise raycasting and aiming. | +| `use_on_block` | `(&self, item: &mut ItemStack, player: &Player, location: BlockPos, face: BlockDirection, cursor_pos: Vector3, block: &Block, server: &Server) -> BlockActionResult` | Triggered when right-clicking a block face (e.g., stripping logs with an axe, tilling farmland with a hoe). | +| `use_on_entity` | `(&self, item: &mut ItemStack, player: &Player, entity: Arc)` | Triggered when right-clicking another living entity (e.g., shearing sheep, nametags). | + +### Channeled & Timed Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `get_use_duration` | `(&self) -> i32` | Duration in game ticks required to complete the action (e.g., `32` ticks for food, `72000` ticks for bows). | +| `on_use_tick` | `(&self, stack: &ItemStack, player: &Player, remaining_use_ticks: i32)` | Executed on each tick while right-click is held. | +| `on_stopped_using` | `(&self, stack: &ItemStack, player: &Player)` | Triggered when the player releases right-click (e.g., firing a charged bow). | + +--- + +## Step 3: Durability & Cooldowns + +### Decrementing Stacks Safely +Always use `decrement_unless_creative` to ensure survival players consume items while creative players retain them: + +```rust +item_stack.decrement_unless_creative(player.gamemode.load(), 1); +``` + +### Applying Cooldowns +To prevent action spamming (e.g., Ender Pearls, Chorus Fruit): + +```rust +// Start a 20-tick (1-second) cooldown on Ender Pearls +player.start_cooldown(Item::ENDER_PEARL.registry_key.to_string(), 20); +``` + +--- + +## Step 4: Registering the Item + +1. Export your module in `crates/pumpkin/src/item/items/mod.rs`: + ```rust + pub mod snowball; + ``` + +2. Register the struct in `crates/pumpkin/src/item/registry.rs`: + ```rust + items.register(SnowBallItem); + ``` + +When the server initializes, `items.register` maps the IDs declared in `ItemMetadata::ids()` to your behavior instance. diff --git a/docs/de/developer/items/index.md b/docs/de/developer/items/index.md new file mode 100644 index 0000000..70f9f9b --- /dev/null +++ b/docs/de/developer/items/index.md @@ -0,0 +1,71 @@ +# Item Architecture & Data Components + +Items represent portable game objects held in player inventories, hotbars, armor slots, and world containers. In Pumpkin, item handling is divided into two primary subsystems: + +1. **Static Data Layer ([`pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data))**: Compile-time generated metadata including item IDs, max stack sizes, max durability, tool tier properties, food nutrition, and data components generated by [`pumpkin-codegen`](/developer/codegen). +2. **Dynamic Behavior Layer ([`pumpkin::item`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/item))**: Interactive right-click actions, projectile launching, eating durations, bow charging, and block interactions handled via the `ItemBehaviour` trait. + +--- + +## Static Items vs. Actionable Items + +| Item Type | Description | Examples | Runtime Behavior Struct Needed? | +| :--- | :--- | :--- | :--- | +| **Passive Items** | Items without active right-click effects | `diamond`, `stick`, `iron_ingot`, `feather` | No — Stacking, moving, crafting, and dropping are handled automatically by `pumpkin-inventory`. | +| **Standard Block Items** | Items corresponding directly to placeable blocks | `stone`, `oak_planks`, `glass` | No — Placement logic queries the target block's data. | +| **Projectiles & Throwables** | Items spawned into physical entities when thrown | `snowball`, `egg`, `ender_pearl`, `splash_potion` | Yes — Implements `ItemBehaviour::normal_use`. | +| **Channeled / Charged Items** | Items with use durations or charging animations | `bow`, `crossbow`, `spyglass`, `trident` | Yes — Implements `get_use_duration`, `on_use_tick`, `on_stopped_using`. | +| **Tools with Block Interactions**| Items modifying existing blocks in the world | `hoe` (tilling), `axe` (stripping), `shovel` (pathmaking) | Yes — Implements `ItemBehaviour::use_on_block`. | + +--- + +## The `ItemStack` Structure + +An `ItemStack` represents an instance of an item in an inventory container. It encapsulates: + +- **`item`**: Reference to the static [`Item`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-data/src/item.rs) definition. +- **`item_count`**: Current stack size ($1 \le \text{count} \le \text{max\_stack\_size}$). +- **Data Components**: Dynamic properties attached to the item stack (custom names, lore, damage values, enchantments, potion effects). + +```rust +use pumpkin_data::item::Item; +use pumpkin_data::item_stack::ItemStack; + +// Create a new item stack +let mut stack = ItemStack::new(1, Item::DIAMOND_SWORD); + +// Safely decrement stack count, respecting creative mode +stack.decrement_unless_creative(player.gamemode.load(), 1); +``` + +--- + +## Data Components System + +Modern Minecraft represents item properties via modular **Data Components**: + +| Component | Responsibility | Description | +| :--- | :--- | :--- | +| `FoodImpl` | Nutrition & Saturation | Controls hunger points restored and saturation ratio. | +| `ConsumableImpl` | Consumption Mechanics | Controls eating duration, sound effects, and animation types. | +| `Damageable` | Durability | Tracks current damage points and breaks when max durability is reached. | +| `UseCooldown` | Cooldown Group | Prevents item re-use for a specified duration in game ticks. | +| `Enchantments` | Enchantment Levels | Key-value mapping of applied enchantments and levels. | + +--- + +## The Item Registry & Dispatch Architecture + +Item interactions are managed by the `ItemRegistry` in [`crates/pumpkin/src/item/registry.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/item/registry.rs): + +1. **Registration**: At server startup, all `ItemBehaviour` implementations are registered along with their target numeric item IDs. +2. **Interaction Routing**: When a client sends use packets (`CUseItem`, `CUseItemOn`), Pumpkin evaluates cooldowns and routes execution to the registered `ItemBehaviour`. +3. **Cooldown System**: Players track active cooldowns keyed by cooldown group strings. If an item is on cooldown, interactions are canceled before calling the behavior. + +--- + +## Next Steps + +To implement interactive mechanics for a new item, proceed to the practical guide: + +- **[Adding an Item](/developer/items/adding-items)**: Step-by-step instructions for implementing `ItemMetadata`, `ItemBehaviour`, managing cooldowns, and registering items. diff --git a/docs/de/developer/networking/bedrock/adding-packets.md b/docs/de/developer/networking/bedrock/adding-packets.md new file mode 100644 index 0000000..f68b888 --- /dev/null +++ b/docs/de/developer/networking/bedrock/adding-packets.md @@ -0,0 +1,182 @@ +# Adding a Bedrock Edition Packet + +This guide explains how to implement, encode, decode, and handle a new Minecraft: Bedrock Edition packet in Pumpkin. + +--- + +## Naming Conventions + +- **Clientbound packets** (Server → Client) must start with **`C`** (e.g., `CChunkRadiusUpdated`, `CContainerOpen`). +- **Serverbound packets** (Client → Server) must start with **`S`** (e.g., `SRequestChunkRadius`, `SPlayerAction`). + +--- + +## Step 1: Identifying the Packet ID + +In the Bedrock protocol, packet IDs are fixed integer constants assigned by Mojang. Verify the numeric ID from the official protocol documentation or network packet traces. + +For example: +- `Disconnect` packet ID: `5` +- `RequestChunkRadius` packet ID: `69` + +--- + +## Step 2: Implementing a Clientbound Packet + +Clientbound packets implement the `PacketWrite` trait from `pumpkin-protocol::serial`. + +### 1. Create the Packet File +Place the file in `crates/pumpkin-protocol/src/bedrock/client/.rs`: + +```rust +use std::io::{Error, Write}; +use pumpkin_macros::packet; +use crate::{codec::var_int::VarInt, serial::PacketWrite}; + +#[packet(5)] +pub struct CDisconnect { + pub reason: VarInt, + pub skip_message: bool, + pub message: String, + pub filtered_message: String, +} + +impl CDisconnect { + #[must_use] + pub const fn new(reason: i32, message: String) -> Self { + Self { + reason: VarInt(reason), + skip_message: message.is_empty(), + message, + filtered_message: String::new(), + } + } +} + +impl PacketWrite for CDisconnect { + fn write(&self, writer: &mut W) -> Result<(), Error> { + self.reason.write(writer)?; + self.skip_message.write(writer)?; + if !self.skip_message { + self.message.write(writer)?; + self.filtered_message.write(writer)?; + } + Ok(()) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/bedrock/client/mod.rs`: + +```rust +pub mod disconnect; +pub use disconnect::*; +``` + +--- + +## Step 3: Implementing a Serverbound Packet + +Serverbound packets implement the `PacketRead` trait. Pumpkin provides a procedural derive macro `#[derive(PacketRead)]` for automatic deserialization when fields implement `PacketRead`. + +### 1. Create the Packet File +Place the file in `crates/pumpkin-protocol/src/bedrock/server/.rs`: + +```rust +use pumpkin_macros::packet; +use crate::{codec::var_int::VarInt, serial::PacketRead}; + +#[derive(PacketRead, Debug)] +#[packet(69)] +pub struct SRequestChunkRadius { + pub chunk_radius: VarInt, + pub max_chunk_radius: u8, +} +``` + +If a packet requires custom parsing logic, implement `PacketRead` manually: + +```rust +use std::io::{Error, Read}; +use crate::serial::PacketRead; + +impl PacketRead for SMyCustomPacket { + fn read(reader: &mut R) -> Result { + let chunk_radius = VarInt::read(reader)?; + let max_chunk_radius = u8::read(reader)?; + Ok(Self { chunk_radius, max_chunk_radius }) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/bedrock/server/mod.rs`: + +```rust +pub mod request_chunk_radius; +pub use request_chunk_radius::*; +``` + +--- + +## Step 4: Dispatching Incoming Packets in `pumpkin` + +Incoming packets are dispatched in `crates/pumpkin/src/net/bedrock/mod.rs`: + +```rust +// Inside BedrockClient::handle_game_packet +match packet.id { + SRequestChunkRadius::PACKET_ID => { + let packet = SRequestChunkRadius::read(reader)?; + self.handle_request_chunk_radius(player, &packet); + } + _ => {} +} +``` + +--- + +## Step 5: Implementing the Handler Function + +Implement the handler logic in `crates/pumpkin/src/net/bedrock/play/.rs`: + +```rust +use super::*; +use pumpkin_protocol::bedrock::server::request_chunk_radius::SRequestChunkRadius; + +impl BedrockClient { + pub fn handle_request_chunk_radius(&self, player: &Arc, packet: &SRequestChunkRadius) { + let requested_radius = packet.chunk_radius.0; + + // Clamp view distance within server configuration limits + let clamped = requested_radius.clamp(2, 16); + + // Notify client that view radius was accepted + self.try_enqueue_client_packet(&CChunkRadiusUpdated { + chunk_radius: VarInt(clamped), + }); + } +} +``` + +Remember to register the new module in `crates/pumpkin/src/net/bedrock/play/mod.rs`: + +```rust +pub mod request_chunk_radius; +``` + +--- + +## Step 6: Sending Outgoing Packets + +To send a Bedrock packet to a connected client: + +```rust +// Enqueues the packet into the client's outgoing batch queue +self.try_enqueue_client_packet(&CChunkRadiusUpdated { + chunk_radius: VarInt(view_distance), +}); +``` + +Batched packets are automatically compressed, framed with `0xfe`, and transmitted over the RakNet/NetherNet socket. diff --git a/docs/de/developer/networking/bedrock/authentication.md b/docs/de/developer/networking/bedrock/authentication.md new file mode 100644 index 0000000..1571a73 --- /dev/null +++ b/docs/de/developer/networking/bedrock/authentication.md @@ -0,0 +1,61 @@ +# Bedrock Edition Authentication + +Minecraft: Bedrock Edition implements a certificate chain authentication protocol based on JSON Web Tokens (JWT) cryptographically anchored to Microsoft / Xbox Live root public keys. + +Authentication handling is powered by the [`pumpkin-auth`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-auth) crate and processed during the Bedrock login handshake in `crates/pumpkin/src/net/bedrock/login/`. + +--- + +## The Handshake Flow + +When a Bedrock client initiates connection, it sends an `SLogin` packet containing the client's protocol version, an **Identity Chain**, and **Client Data**: + +1. **Token Acquisition**: The Bedrock client signs into Xbox Live, obtaining a chained JSON Web Token (JWT) certificate signed by Microsoft's CA. +2. **Login Transmission**: The client packages this identity chain alongside a separate `ClientData` JWT into `SLogin`. +3. **Chain Validation**: Pumpkin walks the certificate chain, verifying each token's signature against Mojang's public root keys and extracting the player's Xbox User ID (XUID), Gamertag, and ephemeral session public key. +4. **Hardware Verification**: Pumpkin verifies the `ClientData` payload using the client's ephemeral public key, extracting geometry, skins, and device OS telemetry. +5. **Session Authorization**: Once validated, Pumpkin emits `CPlayStatus` (Login Success) and progresses to resource pack negotiation. + +--- + +## Token Payloads + +### 1. Identity Chain (`Chain`) +An array of JWT tokens forming an unbroken chain of trust: +- **Root Token**: Cryptographically signed by Mojang's official public root certificate authority. +- **Intermediate Tokens**: Issued by Xbox Live services, verifying the player's Xbox Live User ID (**XUID**), gamertag, and generating an ephemeral ECDSA public key for the session. +- **Claims**: Includes `extraData` with `displayName`, `identity` (UUID), `XUID`, and `titleId`. + +### 2. Client Data (`ClientData`) +A separate JWT signed with the client's ephemeral private key corresponding to the public key in the identity chain: +- **Hardware & Environment**: `DeviceOS`, `DeviceModel`, `CurrentInputMode`, `DefaultInputMode`, `ClientRandomId`. +- **Game Version**: `GameVersion` string. +- **Player Cosmetics**: Full skin texture bitmaps, cape data, geometry names, and persona definitions. + +--- + +## Verification in `pumpkin-auth` + +Pumpkin verifies token signatures using **NIST P-384 ECDSA** cryptography: + +```rust +// In crates/pumpkin/src/net/bedrock/login/mod.rs +match AuthPayload::verify(&jwt_payload) { + Ok(profile) => { + // Successfully verified against Mojang public keys + info!("Authenticated Bedrock player: {} (XUID: {})", profile.name, profile.xuid); + } + Err(LoginError::ChainValidationFailed(err)) => { + warn!("Bedrock JWT chain validation failed: {err}"); + self.try_kick(DisconnectReason::Kicked, "Authentication failed".into()); + } + Err(LoginError::SelfSignedNotAllowed) => { + warn!("Rejected unauthenticated self-signed Bedrock client"); + self.try_kick(DisconnectReason::Kicked, "Server enforces Xbox Live authentication".into()); + } +} +``` + +### Online vs. Offline Mode +- **Online Mode** (`online_mode = true` in `pumpkin.toml`): The chain must successfully validate against Mojang's public keys. Self-signed or unsigned tokens are immediately rejected. +- **Offline Mode** (`online_mode = false`): Pumpkin accepts self-signed client chains, generating offline UUIDs while extracting cosmetic skins and device metadata. diff --git a/docs/de/developer/networking/bedrock/nethernet.md b/docs/de/developer/networking/bedrock/nethernet.md new file mode 100644 index 0000000..88c92f0 --- /dev/null +++ b/docs/de/developer/networking/bedrock/nethernet.md @@ -0,0 +1,61 @@ +# NetherNet WebRTC Transport + +**NetherNet** is Mojang's modern, WebRTC-based transport protocol for Minecraft: Bedrock Edition. Designed for high-resilience peer-to-peer and relayed communication, NetherNet enables seamless cross-platform multiplayer (mobile, console, desktop) without requiring server operators or players to configure router port-forwarding. + +Pumpkin provides a native, zero-dependency NetherNet implementation in [`crates/pumpkin/src/net/bedrock/nethernet.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/bedrock/nethernet.rs). + +--- + +## Architectural Layers + +| Layer | Implementation | Responsibility | +| :--- | :--- | :--- | +| **HTTP Signaling** | Axum HTTP Router (`ice_router.rs`) | Handles SDP Offer (`POST /offer`), SDP Answer (`GET /answer`), and ICE Candidates (`POST /candidate`) | +| **NAT Traversal** | ICE Agent | Coordinates STUN/TURN binding discovery and hole punching | +| **Transport Security** | DTLS Handshake | Generates ephemeral session keys and secures the WebRTC transport using ECDSA P-384 keys | +| **Data Delivery** | WebRTC DataChannels | Unordered/ordered datagram channels streaming standard Bedrock game batches to the dispatcher | + +--- + +## Core Components in Pumpkin + +Pumpkin's NetherNet subsystem is divided into four distinct modules: + +### 1. Cryptographic Identity (`nethernet-key.der`) +- Pumpkin generates and loads a **NIST P-384 ECDSA private key** (`nethernet-key.der`). +- Used to cryptographically sign session descriptions (SDP offers and answers), authenticating the server's identity to connecting clients. +- Encoded using standard PKCS#8 DER formatting. + +### 2. HTTP Signaling Router (`ice_router.rs`) +Signaling is handled over HTTP via an [Axum](https://github.com/tokio-rs/axum) router: + +- **`POST /offer`**: The client submits an SDP offer describing its WebRTC codecs, transport profiles, and network interfaces. +- **`GET /answer`**: Pumpkin generates a corresponding local SDP answer containing its negotiated capabilities. +- **`POST /candidate`**: Both endpoints exchange trickle ICE candidates to discover the fastest direct network path (host, server-reflexive, or relay). + +### 3. Local Network Discovery (`discovery.rs`) +- Listens and broadcasts availability on the local subnet via UDP multicast. +- Allows Bedrock clients on the same Wi-Fi or LAN to discover and connect to the Pumpkin server automatically without entering an IP address. + +### 4. DataChannel Packet Streaming +- Once the WebRTC peer connection transitions to the `Connected` state, an ordered, reliable **WebRTC DataChannel** is opened. +- Bedrock batch packets (`0xfe`) flow bidirectionally through the DataChannel directly into Pumpkin's `incoming_game_packet_send` channel. + +--- + +## Configuration in `pumpkin.toml` + +NetherNet is configured under `[networking.bedrock.nethernet]` in `pumpkin.toml`: + +```toml +[networking.bedrock] +enabled = true +address = "0.0.0.0:19132" + +[networking.bedrock.nethernet] +enabled = true +signaling_address = "0.0.0.0:19133" +ice_servers = [ + "stun:stun.l.google.com:19302" +] +``` diff --git a/docs/de/developer/networking/bedrock/overview.md b/docs/de/developer/networking/bedrock/overview.md new file mode 100644 index 0000000..ae0d615 --- /dev/null +++ b/docs/de/developer/networking/bedrock/overview.md @@ -0,0 +1,80 @@ +# Bedrock Edition Networking Overview + +Pumpkin features native support for **Minecraft: Bedrock Edition** clients (iOS, Android, Windows, Xbox, PlayStation, Switch). + +Bedrock protocol structures and wire codecs reside in `pumpkin-protocol::bedrock`, while connection management and gameplay handlers reside in `pumpkin::net::bedrock`. + +--- + +## Transport Layers: RakNet & NetherNet + +Unlike Java Edition, which communicates exclusively over TCP streams, Bedrock Edition operates over UDP using two primary transport layers: + +1. **RakNet (Port 19132)**: + - A reliable, sequenced UDP networking library originally developed by Jenkins Software. + - Provides connection handshakes, packet acknowledgment (ACK/NACK), MTU negotiation, and packet splitting for payloads larger than the network MTU. +2. **NetherNet**: + - Modern peer-to-peer and relayed transport protocol used by modern Bedrock versions. + - Integrated in Pumpkin via `crates/pumpkin/src/net/bedrock/nethernet.rs`. + +--- + +## Packet Framing & Batching (`0xfe`) + +Bedrock does not send individual game packets over the network directly. Instead, multiple game packets are bundled into a single **batch packet**: + +1. **Header Byte**: Every Bedrock game packet batch starts with `0xfe`. +2. **Compression**: The payload following `0xfe` is compressed using **Deflate** (or ZLib) above a negotiated threshold. +3. **Sub-packet Framing**: When decompressed, the batch contains one or more sequential game packets, each prefixed by its length encoded as a `VarUInt`. + +In Pumpkin, batch decoding and assembly are managed by `BedrockBatchDecoder` and `BedrockBatchEncoder` in `crates/pumpkin-protocol/src/bedrock/`. + +--- + +## Connection Lifecycle & State Progression + +Bedrock does not have explicit protocol states like Java Edition. Instead, it follows a strict sequence of packets: + +| Step | Packet Exchanged | Purpose | +| :--- | :--- | :--- | +| **1. Network Settings** | `SRequestNetworkSettings` -> `CNetworkSettings` | Negotiates compression algorithm (Deflate/Snappy) and payload threshold | +| **2. Authentication** | `SLogin` -> `CPlayStatus` | Transmits JWT identity chain and client hardware data; verifies Xbox Live tokens | +| **3. Resource Negotiation** | `CResourcePacksInfo` -> `SResourcePackClientResponse` | Negotiates server resource packs and download behavior | +| **4. World Initialization** | `CStartGame` + `CItemRegistry` + `CCreativeContent` | Streams world spawn position, dimension settings, runtime player entity ID, and item palettes | +| **5. Ready to Spawn** | `SSetLocalPlayerAsInitialized` -> `CPlayStatus` | Signals client loading completion and transitions to active in-game play | + +--- + +## Packet Dispatching in `pumpkin` + +Inside `crates/pumpkin/src/net/bedrock/mod.rs`, decompressed sub-packets are dispatched based on their numeric packet ID: + +```rust +match packet.id { + SPlayerAuthInput::PACKET_ID => { + let packet = SPlayerAuthInput::read(reader)?; + self.handle_player_auth_input(player, packet, server); + } + SRequestChunkRadius::PACKET_ID => { + let packet = SRequestChunkRadius::read(reader)?; + self.handle_request_chunk_radius(player, &packet); + } + SInventoryTransaction::PACKET_ID => { + let packet = SInventoryTransaction::read(reader)?; + self.handle_inventory_action(player, packet); + } + _ => { + trace!("Unhandled Bedrock packet ID: {}", packet.id); + } +} +``` + +--- + +## Authentication & NetherNet + +- **JWT Authentication**: Bedrock verifies player identities via certificate chains signed by Microsoft / Xbox Live root authorities. +- **NetherNet WebRTC**: High-performance peer-to-peer transport using WebRTC DataChannels, ICE NAT traversal, and P-384 ECDSA keys. + +For complete implementation details, see **[Bedrock Authentication & NetherNet](/developer/networking/bedrock/authentication)**. + diff --git a/docs/de/developer/networking/index.md b/docs/de/developer/networking/index.md new file mode 100644 index 0000000..0685c45 --- /dev/null +++ b/docs/de/developer/networking/index.md @@ -0,0 +1,49 @@ +# Networking Overview + +Pumpkin is built with a high-performance, asynchronous networking stack capable of servicing both **Minecraft: Java Edition** and **Minecraft: Bedrock Edition** clients simultaneously. + +All protocol definitions, serialization traits, and wire-level codecs are isolated in the [`pumpkin-protocol`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-protocol) crate, while client connection management, session state machines, and gameplay packet dispatching are managed in [`pumpkin/src/net`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/net). + +--- + +## Supported Editions + +Pumpkin separates network implementations by edition due to their fundamentally distinct transport layers, packet encodings, and state models: + +| Feature | Minecraft: Java Edition | Minecraft: Bedrock Edition | +| :--- | :--- | :--- | +| **Transport Layer** | TCP socket streams | UDP via RakNet & NetherNet | +| **Framing** | VarInt length-prefixed frames | Batched packets encapsulated in `0xfe` headers | +| **Encryption** | AES-128 / CFB8 stream cipher | Cryptographic session handshake | +| **Compression** | Per-packet ZLib (above configurable threshold) | Per-batch Deflate / ZLib compression | +| **State Model** | Explicit states: Handshake, Status, Login, Config, Play | Packet-driven state progression | +| **Protocol Crate** | `pumpkin-protocol::java` | `pumpkin-protocol::bedrock` | +| **Server Net Module** | `pumpkin::net::java` | `pumpkin::net::bedrock` | + +--- + +## Networking Architecture + +The networking pipeline routes incoming connections through specialized transport and codec layers before delegating to the unified server game loop: + +1. **Transport Listeners**: Independent listeners bind to their respective ports—Tokio TCP for Java Edition (`:25565`) and UDP/RakNet/NetherNet for Bedrock Edition (`:19132`). +2. **Session Abstraction**: Each accepted connection spawns an isolated session task (`JavaClient` or `BedrockClient`) managing wire framing and encryption. +3. **Codec Deserialization**: Wire bytes are deserialized into typed packet structs via `pumpkin-protocol::java` (`ClientPacket`/`ServerPacket`) and `pumpkin-protocol::bedrock` (`PacketWrite`/`PacketRead`). +4. **Unified Gameplay Dispatch**: Decoded packets are dispatched into the unified game loop, updating player entity states and streaming world chunks. + +--- + +## Navigating the Networking Docs + +Explore the documentation for each edition: + +- **[Entity Metadata & Synced Data](/developer/networking/metadata)**: Synchronization of visual entity states (DataWatchers in Java, Actor Data in Bedrock). +- **[Java Edition](/developer/networking/java/overview)**: + - **[Architecture & Lifecycle](/developer/networking/java/overview)**: State machine, `JavaClient` vs. `Player`, and multi-version negotiation. + - **[How to Add a Packet](/developer/networking/java/adding-packets)**: Guide to defining, serializing, and dispatching Java Edition packets. + - **[Authentication](/developer/networking/java/authentication)**: Mojang Yggdrasil validation, GameProfiles, and proxy forwarding. +- **[Bedrock Edition](/developer/networking/bedrock/overview)**: + - **[Architecture & Transports](/developer/networking/bedrock/overview)**: RakNet and NetherNet integration, batching, and session handling. + - **[How to Add a Packet](/developer/networking/bedrock/adding-packets)**: Guide to defining and handling Bedrock Edition packets. + - **[Authentication & NetherNet](/developer/networking/bedrock/authentication)**: JWT certificate chain verification, WebRTC DataChannels, P-384 keys, and signaling. +- **[RCON](/developer/networking/rcon)**: Implementation of Valve's Remote Console protocol for administrative management. diff --git a/docs/de/developer/networking/java/adding-packets.md b/docs/de/developer/networking/java/adding-packets.md new file mode 100644 index 0000000..1800d06 --- /dev/null +++ b/docs/de/developer/networking/java/adding-packets.md @@ -0,0 +1,205 @@ +# Adding a Java Edition Packet + +This guide walks you through implementing, serializing, dispatching, and sending a new Java Edition packet in Pumpkin. + +--- + +## Naming Conventions + +To keep packet definitions uniform across the codebase, follow these naming conventions: + +- **Clientbound packets** (Server → Client) must start with **`C`** (e.g., `CBlockUpdate`, `CPlayDisconnect`). +- **Serverbound packets** (Client → Server) must start with **`S`** (e.g., `SChatCommand`, `SPlayerPosition`). +- If a packet exists in multiple states, prefix the state name before the packet action (e.g., `CLoginDisconnect`, `CConfigDisconnect`, and `CPlayDisconnect`). + +--- + +## Step 1: Find the Packet ID in `pumpkin-data` + +Packet IDs across all supported Minecraft versions are maintained in `pumpkin-data`. Import the corresponding constant: + +```rust +// Clientbound Play state disconnect +use pumpkin_data::packet::clientbound::play::DISCONNECT; + +// Serverbound Play state player position +use pumpkin_data::packet::serverbound::play::MOVE_PLAYER_POS; +``` + +--- + +## Step 2: Implementing a Clientbound Packet + +Clientbound packets are sent from Pumpkin to the Minecraft client. They implement the `ClientPacket` trait from `pumpkin-protocol`. + +### 1. Create the Struct File +Place the file in `crates/pumpkin-protocol/src/java/client//.rs` (e.g., `play/disconnect.rs`): + +```rust +use std::io::Write; +use pumpkin_data::packet::clientbound::play::DISCONNECT; +use pumpkin_macros::java_packet; +use pumpkin_protocol::ClientPacket; +use pumpkin_protocol::ser::{NetworkWriteExt, WritingError}; +use pumpkin_util::text::TextComponent; +use pumpkin_util::version::JavaMinecraftVersion; + +/// Displays a disconnect screen with a message to the player in Play state. +#[java_packet(DISCONNECT)] +pub struct CPlayDisconnect<'a> { + pub reason: &'a TextComponent, +} + +impl<'a> CPlayDisconnect<'a> { + #[must_use] + pub const fn new(reason: &'a TextComponent) -> Self { + Self { reason } + } +} + +impl ClientPacket for CPlayDisconnect<'_> { + fn write_packet_data( + &self, + mut write: impl Write, + version: &JavaMinecraftVersion, + ) -> Result<(), WritingError> { + // Use NetworkWriteExt methods to serialize fields + write.write_component(self.reason, version) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/java/client//mod.rs`: + +```rust +mod disconnect; +pub use disconnect::*; +``` + +--- + +## Step 3: Implementing a Serverbound Packet + +Serverbound packets are received by Pumpkin from the game client. They implement the `ServerPacket` trait from `pumpkin-protocol`. + +### 1. Create the Struct File +Place the file in `crates/pumpkin-protocol/src/java/server//.rs` (e.g., `play/player_position.rs`): + +```rust +use pumpkin_data::packet::serverbound::play::MOVE_PLAYER_POS; +use pumpkin_macros::java_packet; +use pumpkin_protocol::ServerPacket; +use pumpkin_protocol::ser::{NetworkReadExt, ReadingError}; +use pumpkin_util::math::vector3::Vector3; +use pumpkin_util::version::JavaMinecraftVersion; + +#[java_packet(MOVE_PLAYER_POS)] +pub struct SPlayerPosition { + pub position: Vector3, + pub collision: u8, +} + +impl<'a> ServerPacket<'a> for SPlayerPosition { + fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result { + let x = bytebuf.get_f64_be()?; + let y = bytebuf.get_f64_be()?; + + // Handle version-specific fields if necessary + if *version <= JavaMinecraftVersion::V_1_7_6 { + let _stance = bytebuf.get_f64_be()?; + } + + let z = bytebuf.get_f64_be()?; + let collision = bytebuf.get_u8()?; + + Ok(Self { + position: Vector3::new(x, y, z), + collision, + }) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/java/server//mod.rs`: + +```rust +mod player_position; +pub use player_position::*; +``` + +--- + +## Step 4: Network Codec Extension Helpers + +`NetworkWriteExt` and `NetworkReadExt` provide helper methods for standard Minecraft types: + +| Data Type | Reading Method | Writing Method | +| :--- | :--- | :--- | +| **`VarInt`** | `bytebuf.get_var_int()?` | `write.write_var_int(val)?` | +| **`VarLong`** | `bytebuf.get_var_long()?` | `write.write_var_long(val)?` | +| **`String`** | `bytebuf.get_string()?` | `write.write_string(val)?` | +| **`UUID`** | `bytebuf.get_uuid()?` | `write.write_uuid(&val)?` | +| **`TextComponent`** | `bytebuf.get_component()?` | `write.write_component(val, version)?` | +| **`Position`** | `bytebuf.get_block_pos()?` | `write.write_block_pos(val)?` | +| **`NBT`** | `bytebuf.get_nbt()?` | `write.write_nbt(&val)?` | + +--- + +## Step 5: Handling Incoming Packets in `pumpkin` + +Incoming serverbound packets must be handled in the appropriate state handler in `crates/pumpkin/src/net/java/`. + +For packets in the **Play state**, open `crates/pumpkin/src/net/java/mod.rs` and update `handle_play_packet`: + +```rust +// In crates/pumpkin/src/net/java/mod.rs +pub fn handle_play_packet( + &self, + player: &Arc, + server: &Arc, + packet: &RawPacket, +) -> Result<(), Box> { + let version = self.version.load(); + let mut payload = &packet.payload[..]; + + match packet.id { + // Match against version-specific packet ID + id if id == SPlayerPosition::to_id(version) => { + let packet = SPlayerPosition::read(&mut payload, &version)?; + self.handle_player_position(player, &packet); + } + _ => {} + } + Ok(()) +} +``` + +Then implement the handler function: + +```rust +impl JavaClient { + pub fn handle_player_position(&self, player: &Arc, packet: &SPlayerPosition) { + // Validate coordinates, update player position, check collisions + player.set_position(packet.position); + } +} +``` + +--- + +## Step 6: Sending Outgoing Packets + +To send a clientbound packet to a player: + +```rust +// 1. Send directly to a player's connection +player.client.send_packet(&CPlayDisconnect::new(&reason)); + +// 2. Broadcast to all players watching a chunk +world.broadcast_packet_except( + &[player.gameprofile.id], + &CBlockUpdate::new(block_pos, block_id), +).await; +``` diff --git a/docs/de/developer/networking/java/authentication.md b/docs/de/developer/networking/java/authentication.md new file mode 100644 index 0000000..861da51 --- /dev/null +++ b/docs/de/developer/networking/java/authentication.md @@ -0,0 +1,83 @@ +# Java Edition Authentication + +Authentication verifies the identity of players connecting to Pumpkin to prevent username spoofing and account impersonation. + +Pumpkin handles authentication in the [`pumpkin-auth`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-auth) crate and orchestrates it during the `Login` state in `crates/pumpkin/src/net/java/login/`. + +--- + +## Why Authentication Matters + +Offline (non-authenticated) accounts generate player UUIDs strictly based on their offline username string (`UUID::new_v3(...)`). Without verification, any user can join using the username of an operator or administrator and gain privileged access. + +In online mode (`online_mode = true` in `pumpkin.toml`), Pumpkin cryptographically verifies with Mojang's session servers that the connecting client owns the requested account. + +--- + +## How Yggdrasil Authentication Works + +The authentication exchange between the client, Pumpkin, and Mojang's Yggdrasil servers follows these steps: + +1. **Initiation**: The client sends `SLoginStart` declaring its desired username and optional offline UUID. +2. **Key Exchange**: Pumpkin replies with `CEncryptionBegin`, providing a random 4-byte verify token, the server ID string, and Pumpkin's 1024-bit RSA public key. +3. **Session Join**: The client generates a random 16-byte shared secret (`AES-128`), computes a SHA-1 server hash, and calls Mojang's session server (`POST /session/minecraft/join`) using its authenticated Microsoft access token. +4. **Verification**: The client sends `SEncryptionResponse` containing the RSA-encrypted shared secret and encrypted verify token. Pumpkin decrypts the token with its RSA private key, recalculates the SHA-1 digest, and queries Mojang (`GET /session/minecraft/hasJoined`). +5. **Profile Retrieval**: Mojang validates that the client and server hashes match, returning the authenticated player's UUID, username, and signed skin/cape texture properties (`GameProfile`). +6. **Encryption Activation**: Pumpkin activates full-stream symmetric encryption (`AES-128/CFB8`) and completes the login sequence by sending `CLoginSuccess`. + +--- + +## Game Profile Structure + +When Mojang's `hasJoined` endpoint validates a session, it returns a `GameProfile` containing the player's authenticated UUID, username, and signed texture properties: + +```rust +pub struct GameProfile { + pub id: Uuid, + pub name: String, + pub properties: Vec, + pub profile_actions: Option>, +} + +pub struct Property { + pub name: String, + pub value: String, // Base64 encoded texture data + pub signature: Option, // Cryptographic signature verifying authenticity +} +``` + +Pumpkin verifies that skin and cape signatures are cryptographically signed with Mojang's public key to prevent clients from injecting malicious payloads or unauthorized textures. + +--- + +## Custom Authentication Endpoints + +For private networks or enterprise environments running custom Yggdrasil-compatible authentication servers, endpoints can be customized in `pumpkin.toml`: + +```toml +[networking.java.authentication] +enabled = true +fallbacks = [] +connect_timeout = 5000 +read_timeout = 5000 +prevent_proxy_connections = false + +[networking.java.authentication.player_profile] +allow_banned_players = false +allowed_actions = ["FORCED_NAME_CHANGE", "USING_BANNED_SKIN"] + +[networking.java.authentication.textures] +enabled = true +allowed_url_schemes = ["http", "https"] +allowed_url_domains = [".minecraft.net", ".mojang.com"] +``` + +--- + +## Proxy Authentication Forwarding + +When running Pumpkin behind proxies, client connections terminate at the proxy. Pumpkin supports the three primary proxy forwarding standards: + +- **Velocity Modern Forwarding**: Validates player data forwarded with HMAC-SHA256 secret tokens (`[networking.proxy.velocity]`). +- **BungeeGuard**: Token-based authentication header validation for BungeeCord setups. +- **Legacy BungeeCord**: Handshake forwarding appending IP, UUID, and properties to the server host string. diff --git a/docs/de/developer/networking/java/overview.md b/docs/de/developer/networking/java/overview.md new file mode 100644 index 0000000..e79753c --- /dev/null +++ b/docs/de/developer/networking/java/overview.md @@ -0,0 +1,74 @@ +# Java Edition Networking Overview + +The Java Edition network stack handles standard Minecraft PC clients connecting over TCP. Wire-level structures and serialization logic reside in `pumpkin-protocol::java`, while connection management and gameplay packet handlers reside in `pumpkin::net::java`. + +--- + +## The Five Protocol States + +The Minecraft Java protocol operates as an explicit finite state machine. Every connection begins in the `Handshake` state and transitions according to client requests and server responses: + +| State | Transition Trigger | Purpose & Next State | +| :--- | :--- | :--- | +| **Handshake** | Client initiates connection | Inspects protocol version and transitions to `Status`, `Login`, or `Transfer` | +| **Status** | `NextState = 1` | Streams MOTD, active player counts, favicon, and ping response | +| **Login** | `NextState = 2` | Authenticates account (Yggdrasil), negotiates encryption (AES-128), transitions to `Config` | +| **Transfer** | `NextState = 3` | Modern proxy / server-transfer negotiation | +| **Config** | Authentication success | Exchanges dynamic registries, data tags, feature flags, and resource packs | +| **Play** | Configuration acknowledged | Primary bidirectional gameplay loop (chunks, movement, combat, chat) | + +1. **Handshake (`0x00`)**: Always the first packet sent by the client. It declares the client's protocol version, server address, port, and desired next state (`Status`, `Login`, or `Transfer`). +2. **Status**: Lightweight query state. The client requests a server list ping, and Pumpkin responds with the server version, player counts, favicon, and MOTD. +3. **Login**: Authenticates the player. If `online_mode` is enabled, Pumpkin performs an encryption handshake (AES-128/CFB8) and verifies the player's account with Mojang's Yggdrasil session servers. +4. **Config**: Introduced in modern Minecraft (1.20.2+). The server sends feature flags, known registries, resource packs, and custom server links before the player joins the physical world. +5. **Play**: The primary gameplay state. Used for all in-game actions including block breaking, entity movement, combat, chat, and chunk streaming. + +--- + +## Connection Representation: `JavaClient` vs. `Player` + +To keep memory usage minimal and prevent unnecessary allocations, Pumpkin cleanly separates pre-play connections from active players: + +### `JavaClient` +- **Scope**: Exists across `Handshake`, `Status`, `Login`, and `Config` states. +- **Location**: `crates/pumpkin/src/net/java/mod.rs`. +- **Characteristics**: + - Contains connection metadata: client protocol version, remote address, encryption/compression state. + - Has no world coordinates, no physics, no inventory, and no living entity. + - Very low memory footprint. + +### `Player` +- **Scope**: Exists solely during the `Play` state. +- **Location**: `crates/pumpkin/src/entity/player/`. +- **Characteristics**: + - Implements `EntityBase`, representing a living entity inside a `World` and `Level`. + - Holds inventory screens, health, hunger, velocities, and chunk view subscription queues. + - Contains an `Arc` reference for network transmission. + +--- + +## Multi-Version Support (`JavaMinecraftVersion`) + +Pumpkin supports multiple Minecraft client versions. Protocol differences are handled cleanly via `pumpkin_util::version::JavaMinecraftVersion`: + +- Packet IDs may differ across Minecraft versions. The `#[java_packet(ID)]` macro works with `pumpkin-data` to resolve the correct ID for the target version. +- When reading or writing packets, methods accept a `&JavaMinecraftVersion` reference to conditionally include or omit fields that changed in specific Minecraft updates. + +```rust +if *version <= JavaMinecraftVersion::V_1_7_6 { + // Read legacy stance coordinate present in early Minecraft versions + let _stance = bytebuf.get_f64_be()?; +} +``` + +--- + +## The Packet Pipeline + +Incoming Java packets pass through a structured pipeline: + +1. **Length Framing**: `tokio_util::codec` decodes VarInt frame lengths. +2. **Decompression & Decryption**: If enabled, the raw buffer is decrypted via AES/CFB8 and decompressed via ZLib. +3. **Plugin Event Interception**: A `PacketReceivedEvent` is dispatched to the plugin manager. Plugins can inspect or cancel the packet before server processing. +4. **ID Dispatching**: The packet ID is matched against version-specific constants in `crates/pumpkin/src/net/java/mod.rs` (or the respective state module). +5. **Handling**: The packet struct is deserialized and processed by state-specific handler functions. diff --git a/docs/de/developer/networking/metadata.md b/docs/de/developer/networking/metadata.md new file mode 100644 index 0000000..9def4ae --- /dev/null +++ b/docs/de/developer/networking/metadata.md @@ -0,0 +1,123 @@ +# Entity Metadata & Synced Data + +Minecraft synchronizes persistent entity visual states, animation flags, poses, and properties via **Entity Metadata** (historically called *DataWatchers* in Java Edition and *Actor Data* in Bedrock Edition). + +Instead of transmitting full state packets whenever an entity crouches, catches fire, or takes damage, the server broadcasts compact metadata updates to nearby observers. + +--- + +## Edition Comparison + +| Feature | Minecraft: Java Edition | Minecraft: Bedrock Edition | +| :--- | :--- | :--- | +| **Packet Name** | `CSetEntityMetadata` (`SET_ENTITY_DATA`) | `CSetActorData` (Packet ID `39`) | +| **Storage Structure** | `TrackedData` list terminating with `0xFF` | `SyncedActorDataList` (`HashMap`) | +| **Key Type** | Index (`u8`) mapped to typed `MetaDataType` | Enum keys (`entity_data_key::*`) | +| **Flags** | Bitfield byte (fire, crouching, sprinting, glowing) | Bitfield int64 (`entity_data_flag::*`) | +| **Dynamic Properties** | N/A (baked into tracked indices) | `PropertySyncData` (client/server sync) | +| **Crate Location** | `crates/pumpkin-protocol/src/java/client/play/entity_metadata.rs` | `crates/pumpkin-protocol/src/bedrock/client/set_actor_data.rs` | + +--- + +## 1. Java Edition: `CSetEntityMetadata` + +In Java Edition, entity metadata consists of numbered index entries. Each entry defines an index, a `MetaDataType` ID, and the encoded value. The list terminates with a single `0xFF` byte. + +### Key Index Hierarchy + +Entity metadata follows an object-oriented inheritance model: + +1. **Entity (Base)**: + - `0`: State bitfield (flags: bit `0` = on fire, bit `1` = crouching, bit `3` = sprinting, bit `5` = invisible, bit `6` = glowing). + - `1`: Air supply (`VarInt`). + - `2`: Custom name (optional `TextComponent`). + - `3`: Custom name visible (`bool`). + - `4`: Silent (`bool`). + - `5`: No gravity (`bool`). + - `6`: Pose (`Standing`, `FallFlying`, `Sleeping`, `Swimming`, `SpinAttack`, `Crouching`, `Dying`). +2. **LivingEntity (extends Entity)**: + - `8`: Hand states (using item, active hand). + - `9`: Health (`f32`). + - `10`: Potion effect ambient color. +3. **Player (extends LivingEntity)**: + - Additional hearts, skin customization bitmask, main hand indicator. + +### Sending Metadata Updates in Pumpkin + +Pumpkin encodes entity metadata in `pumpkin-protocol`: + +```rust +use pumpkin_protocol::VarInt; +use pumpkin_protocol::java::client::play::CSetEntityMetadata; + +// Construct metadata update for entity with ID 42 +let packet = CSetEntityMetadata::new( + VarInt(entity.entity_id), + serialized_metadata_bytes, +); + +// Broadcast to chunk observers +world.broadcast_packet_all(&packet).await; +``` + +--- + +## 2. Bedrock Edition: `CSetActorData` + +Bedrock Edition uses `CSetActorData` (`packet(39)`), storing properties in a dictionary mapping `u32` keys to strongly-typed `MetadataValue` variants. + +### Key & Flag Constants + +Bedrock exposes flags as a 64-bit integer bitfield under `entity_data_key::FLAGS`: + +- `entity_data_flag::ON_FIRE` (`0`): Entity is burning. +- `entity_data_flag::SNEAKING` (`1`): Player is sneaking. +- `entity_data_flag::RIDING` (`2`): Entity is mounted on a vehicle. +- `entity_data_flag::SPRINTING` (`3`): Player is sprinting. +- `entity_data_flag::USING_ITEM` (`4`): Player is actively charging a bow, eating food, or using a shield. +- `entity_data_flag::INVISIBLE` (`5`): Entity is invisible. +- `entity_data_flag::GLOWING` (`26`): Entity has the glowing effect outline. + +### Constructing and Updating Actor Data in Pumpkin + +In `crates/pumpkin/src/entity/living.rs`: + +```rust +use pumpkin_protocol::bedrock::client::set_actor_data::{ + CSetActorData, MetadataValue, PropertySyncData, SyncedActorDataList, + entity_data_flag, entity_data_key, +}; + +let mut actor_data = SyncedActorDataList::new(); + +// Set entity flags (e.g. actively using an item) +let mut flags: i64 = 0; +if is_using_item { + flags |= 1i64 << entity_data_flag::USING_ITEM; +} +actor_data.set(entity_data_key::FLAGS, MetadataValue::Int64(flags)); + +// Set air supply +actor_data.set(entity_data_key::AIR_SUPPLY, MetadataValue::Short(air as i16)); + +// Set custom name tag +actor_data.set(entity_data_key::NAME, MetadataValue::String(name)); + +let packet = CSetActorData { + target_runtime_id: VarULong(entity_id), + actor_data, + synced_properties: PropertySyncData::default(), + tick: VarULong(current_tick), +}; + +client.try_enqueue_client_packet(&packet); +``` + +--- + +## Multi-Version Compatibility + +Because Mojang frequently shifts metadata indices between Minecraft releases, `pumpkin-protocol` leverages translation tables from `pumpkin-data`: + +- For Java Edition, `pumpkin_data::meta_data_type::MetaDataType` remaps indices dynamically based on the client's `JavaMinecraftVersion`. +- For Bedrock Edition, `SyncedActorDataList` validates keys against the active Bedrock protocol version to prevent client crashes. diff --git a/docs/de/developer/networking/proxy/bungeecord.md b/docs/de/developer/networking/proxy/bungeecord.md new file mode 100644 index 0000000..cb06ff5 --- /dev/null +++ b/docs/de/developer/networking/proxy/bungeecord.md @@ -0,0 +1,73 @@ +# BungeeCord Forwarding + +Pumpkin provides native support for BungeeCord and Waterfall proxy networks, including **BungeeGuard** authentication token verification. + +Implementation is located in [`crates/pumpkin/src/net/proxy/bungeecord.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/bungeecord.rs). + +--- + +## How BungeeCord Forwarding Operates + +Unlike modern protocols that use custom login channels, BungeeCord operates by encoding client metadata directly into the `server_address` field of the initial `SHandshake` packet. + +The string uses null character (`\0`) delimiters: + +```text +[original_host]\0[player_ip:port]\0[player_uuid]\0[properties_json] +``` + +1. **`original_host`**: The domain name the player originally typed into their client. +2. **`player_ip:port`**: The genuine IP address and ephemeral source port of the client. +3. **`player_uuid`**: The player's Mojang UUID (32 hex characters without hyphens or 36 characters with hyphens). +4. **`properties_json`**: A JSON array containing skin textures, cape textures, and Mojang cryptographic signatures. + +--- + +## The Security Risk: Proxy Bypasses + +Because BungeeCord's handshake string is unauthenticated plain text, an attacker could connect directly to an exposed backend port, inject an arbitrary UUID and operator username, and gain full server control. + +Traditionally, this required strict Linux firewall rules (`iptables` / `ufw`) to restrict port access exclusively to the proxy IP. + +--- + +## BungeeGuard Protection + +To eliminate proxy bypasses without relying solely on network firewalls, Pumpkin integrates **BungeeGuard** token verification: + +1. The BungeeCord proxy injects a shared secret into the player's profile properties under the key `bungeeguard-token`. +2. When Pumpkin processes the handshake in `bungeecord_login`: + - It checks whether `secret` is configured in `pumpkin.toml`. + - It inspects the forwarded properties for `bungeeguard-token`. + - If the token matches, Pumpkin **strips the secret property** from the player's profile (so other plugins cannot read it) and completes the login. + - If the token is missing or incorrect, Pumpkin rejects the connection with `BungeeCordError::MissingToken` or `BungeeCordError::InvalidToken`. + +```rust +// In crates/pumpkin/src/net/proxy/bungeecord.rs +if !secret.is_empty() { + let Some(token_index) = properties.iter().position(|p| p.name == BUNGEEGUARD_TOKEN_PROPERTY) else { + return Err(BungeeCordError::MissingToken); + }; + + if properties[token_index].value != secret { + return Err(BungeeCordError::InvalidToken); + } + properties.remove(token_index); +} +``` + +--- + +## Configuration in `pumpkin.toml` + +To enable BungeeCord forwarding with BungeeGuard: + +```toml +[networking.proxy] +enabled = true + +[networking.proxy.bungeecord] +enabled = true +# Optional: Configure the BungeeGuard shared secret to block direct connections +secret = "YOUR_BUNGEEGUARD_SECRET_HERE" +``` diff --git a/docs/de/developer/networking/proxy/index.md b/docs/de/developer/networking/proxy/index.md new file mode 100644 index 0000000..300c89e --- /dev/null +++ b/docs/de/developer/networking/proxy/index.md @@ -0,0 +1,46 @@ +# Proxy Forwarding Overview + +In multi-server Minecraft networks, players connect through a front-facing reverse proxy (such as Velocity, BungeeCord, or Vine) that routes connections between lobbies and sub-servers without requiring players to reconnect. + +Because backend servers receive TCP connections directly from the proxy machine rather than the player, the server must be informed of the player's genuine **IP address**, **UUID**, **username**, and **skin properties**. + +Pumpkin provides native support for all three major proxy forwarding protocols in [`crates/pumpkin/src/net/proxy/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/net/proxy). + +--- + +## The Proxy Forwarding Problem + +Without secure proxy forwarding: +1. **IP Loss**: Every connected player appears with the proxy's loopback or internal IP (breaking IP bans and geo-location). +2. **UUID Spoofing & Bypasses**: Malicious users could connect directly to the backend server's port with any spoofed username or operator identity. + +To prevent proxy bypasses, Pumpkin validates cryptographic secrets, tokens, or digital signatures sent by the proxy. + +--- + +## Comparison of Supported Protocols + +| Protocol | Protocol Transport | Security / Cryptography | Replay Protection | Crate Module | +| :--- | :--- | :--- | :--- | :--- | +| **[BungeeCord](/developer/networking/proxy/bungeecord)** | `SHandshake` server address string | Optional token in profile properties (`bungeeguard-token`) | None | `pumpkin::net::proxy::bungeecord` | +| **[Velocity](/developer/networking/proxy/velocity)** | Login plugin message (`velocity:player_info`) | Symmetric HMAC-SHA256 signature | None | `pumpkin::net::proxy::velocity` | +| **[Vine](/developer/networking/proxy/vine)** | Login plugin message (`vine:player_info`) | Asymmetric **Ed25519** digital signatures | 16-byte nonce challenge + 30s timestamp window | `pumpkin::net::proxy::vine` | + +--- + +## Lifecycle Architecture + +1. **Player Authentication**: The player connects to the proxy (BungeeCord, Velocity, or Vine) and authenticates against Mojang's session servers. +2. **Backend Connection**: The proxy connects to Pumpkin over TCP and sends `SHandshake`. +3. **Data Forwarding**: + - **BungeeCord Mode**: The proxy injects the player's remote IP, UUID, and properties directly into the `SHandshake` hostname string (separated by null bytes `\0`). + - **Modern Mode (Velocity / Vine)**: Pumpkin sends `CLoginPluginRequest` on a dedicated channel (`velocity:player_info` or `vine:player_info`). The proxy signs the player profile using HMAC-SHA256 (Velocity) or Ed25519 (Vine) and replies with `SLoginPluginResponse`. +4. **Validation & Session Initialization**: Pumpkin validates the signature, binds the real IP and GameProfile to the `JavaClient`, and sends `CLoginSuccess`. + +--- + +## Proxy Documentation + +- **[BungeeCord](/developer/networking/proxy/bungeecord)**: Legacy host string injection and optional BungeeGuard token validation. +- **[Velocity Modern Forwarding](/developer/networking/proxy/velocity)**: HMAC-SHA256 signed player info during the Login state. +- **[Vine Modern Forwarding](/developer/networking/proxy/vine)**: Next-generation proxy forwarding with Ed25519 signatures and replay attack prevention. diff --git a/docs/de/developer/networking/proxy/velocity.md b/docs/de/developer/networking/proxy/velocity.md new file mode 100644 index 0000000..b0333e7 --- /dev/null +++ b/docs/de/developer/networking/proxy/velocity.md @@ -0,0 +1,88 @@ +# Velocity Modern Forwarding + +[Velocity](https://papermc.io/software/velocity) is a high-performance proxy created by PaperMC. It uses modern plugin messaging during the `Login` state to securely transmit player data with **HMAC-SHA256** message authentication codes. + +Pumpkin implements Velocity modern forwarding in [`crates/pumpkin/src/net/proxy/velocity.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/velocity.rs). + +--- + +## How Velocity Forwarding Operates + +Unlike BungeeCord, which injects unauthenticated data into the handshake string, Velocity operates during the **Login** state over a dedicated plugin channel: + +1. **Handshake & Login**: The player authenticates with Velocity. Velocity initiates a standard TCP connection to Pumpkin and transmits `SHandshake` and `SLoginStart`. +2. **Channel Query**: Recognizing that Velocity modern forwarding is enabled, Pumpkin pauses the login sequence and dispatches a `CLoginPluginRequest` on channel `velocity:player_info`. +3. **Payload Generation**: Velocity bundles the player's remote address, UUID, username, and skin textures. It calculates a 32-byte HMAC-SHA256 signature across this payload using the shared secret. +4. **Signature Verification**: Velocity sends `SLoginPluginResponse`. Pumpkin validates the HMAC-SHA256 digest against its configured secret. If valid, Pumpkin overwrites the connection's network address and GameProfile and transitions to `Config` / `Play`. + +--- + +## Wire Payload Structure + +The payload in `SLoginPluginResponse` is structured as follows: + +```text +┌─────────────────────────────────────────────────────────────┐ +│ 32-byte HMAC-SHA256 Signature │ +├─────────────────────────────────────────────────────────────┤ +│ Forwarding Protocol Version (VarInt: 1 - 4) │ +├─────────────────────────────────────────────────────────────┤ +│ Client Remote IP Address (String / IPv4 / IPv6) │ +├─────────────────────────────────────────────────────────────┤ +│ Player UUID (128-bit / UUID) │ +├─────────────────────────────────────────────────────────────┤ +│ Player Username (String) │ +├─────────────────────────────────────────────────────────────┤ +│ Profile Properties Count (VarInt) │ +│ ├─ Property Name (String) │ +│ ├─ Property Value (String) │ +│ └─ Optional Signature (String) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Cryptographic Validation in Pumpkin + +Inside `crates/pumpkin/src/net/proxy/velocity.rs`: + +```rust +use hmac::{Hmac, KeyInit, Mac}; +use sha2::Sha256; + +type HmacSha256 = Hmac; + +// Extract signature and payload +let (signature, data_to_verify) = data.split_at(32); + +// Initialize HMAC with secret from pumpkin.toml +let mut mac = HmacSha256::new_from_slice(secret.as_bytes()) + .map_err(|_| VelocityError::FailedVerifyIntegrity)?; + +mac.update(data_to_verify); + +// Constant-time verification prevents timing attacks +mac.verify_slice(signature) + .map_err(|_| VelocityError::FailedVerifyIntegrity)?; +``` + +If the signature does not match (e.g., if an attacker attempts to connect directly to the backend without knowing the secret), Pumpkin drops the connection immediately. + +--- + +## Configuration in `pumpkin.toml` + +1. Open `velocity.toml` on your Velocity proxy and set: + ```toml + player-info-forwarding-mode = "modern" + forwarding-secret-file = "forwarding.secret" + ``` +2. Copy the generated secret string from `forwarding.secret` into `pumpkin.toml`: + ```toml + [networking.proxy] + enabled = true + + [networking.proxy.velocity] + enabled = true + secret = "YOUR_VELOCITY_FORWARDING_SECRET_HERE" + ``` diff --git a/docs/de/developer/networking/proxy/vine.md b/docs/de/developer/networking/proxy/vine.md new file mode 100644 index 0000000..fed2a8a --- /dev/null +++ b/docs/de/developer/networking/proxy/vine.md @@ -0,0 +1,112 @@ +# Vine Modern Forwarding + +**Vine** is a next-generation, high-performance Minecraft proxy written in Rust. Designed for ultra-low latency, multi-threading, and enterprise-grade security, Vine introduces modern cryptographic forwarding based on **Ed25519 asymmetric signatures** and **replay attack protection**. + +Pumpkin provides native support for Vine forwarding in [`crates/pumpkin/src/net/proxy/vine.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/vine.rs). + +--- + +## Why Vine Forwarding? + +While Velocity's HMAC-SHA256 forwarding represents an improvement over legacy BungeeCord, it relies on a **symmetric shared secret**. If one backend server in a network is compromised, an attacker obtains the shared secret and can forge authenticated player logins across the entire server cluster. + +Vine solves this with: +1. **Asymmetric Public-Key Cryptography (Ed25519)**: The proxy signs player data using its private key. Backend servers only store the proxy's **Public Key** (`public_key`). Even if a backend server is compromised, the attacker cannot forge identities to other servers. +2. **Replay Attack Protection (16-byte Challenge Nonce)**: Pumpkin generates a cryptographic random 16-byte nonce for each connection. The proxy must sign and return this nonce. Replaying a captured login packet is impossible. +3. **Timestamp Drift Window**: Transmits a Unix timestamp that must be within ±30 seconds of the backend's system clock (`MAX_TIMESTAMP_DRIFT_SECS = 30`). + +--- + +## The Handshake Sequence + +1. **Connection**: The player connects and authenticates with Vine. Vine opens a backend connection to Pumpkin and sends `SHandshake` and `SLoginStart`. +2. **Challenge Query**: Pumpkin generates a cryptographically random 16-byte nonce and sends `CLoginPluginRequest` on the `vine:player_info` channel. +3. **Asymmetric Signing**: Vine serializes the version, timestamp, the returned 16-byte nonce, the client IP address, and the player GameProfile. It signs this byte stream with its Ed25519 private key. +4. **Tri-fold Validation**: Vine replies with `SLoginPluginResponse`. Pumpkin verifies: + - **Clock Skew**: The embedded timestamp is within ±30 seconds of UTC time. + - **Nonce Check**: The signed nonce exactly matches the 16-byte challenge generated in step 2. + - **Ed25519 Signature**: The signature is cryptographically valid against the configured `public_key`. +5. **Session Success**: If all three checks pass, Pumpkin applies the verified identity and emits `CLoginSuccess`. + +--- + +## Wire Payload Structure + +The `SLoginPluginResponse` data layout: + +```text +┌─────────────────────────────────────────────────────────────┐ +│ 64-byte Ed25519 Digital Signature │ +├─────────────────────────────────────────────────────────────┤ +│ Forwarding Protocol Version (i32: 1) │ +├─────────────────────────────────────────────────────────────┤ +│ Unix Timestamp in Seconds (i64) │ +├─────────────────────────────────────────────────────────────┤ +│ 16-byte Challenge Nonce │ +├─────────────────────────────────────────────────────────────┤ +│ Client Remote IP Address (String / IPv4 / IPv6) │ +├─────────────────────────────────────────────────────────────┤ +│ Player UUID (128-bit / UUID) │ +├─────────────────────────────────────────────────────────────┤ +│ Player Username (String) │ +├─────────────────────────────────────────────────────────────┤ +│ Profile Properties Count (VarInt) │ +│ ├─ Property Name (String) │ +│ ├─ Property Value (String) │ +│ └─ Optional Signature (String) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Verification in Pumpkin + +In `crates/pumpkin/src/net/proxy/vine.rs`: + +```rust +use ed25519_dalek::{Signature, Verifier, VerifyingKey}; + +// 1. Validate payload length (64-byte signature + 25-byte headers minimum) +if data.len() < 89 { + return Err(VineError::DataTooShort); +} + +// 2. Extract signature and signed payload +let (sig_bytes, signed_data) = data.split_at(64); +let signature = Signature::from_slice(sig_bytes) + .map_err(|_| VineError::InvalidSignature)?; + +// 3. Verify timestamp skew (30 second maximum drift) +let skew = (server_now - timestamp).abs(); +if skew > MAX_TIMESTAMP_DRIFT_SECS { + return Err(VineError::TimestampExpired(skew, MAX_TIMESTAMP_DRIFT_SECS)); +} + +// 4. Verify challenge nonce matches connection state +if received_nonce != expected_nonce { + return Err(VineError::ChallengeMismatch); +} + +// 5. Verify Ed25519 signature with proxy's public key +verifying_key.verify(signed_data, &signature) + .map_err(|_| VineError::InvalidSignature)?; +``` + +--- + +## Configuration in `pumpkin.toml` + +Configure Vine forwarding in `pumpkin.toml`: + +```toml +[networking.proxy] +enabled = true + +[networking.proxy.vine] +enabled = true +# 64-character hex-encoded Ed25519 public key of the Vine proxy +public_key = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + +# Alternatively, if secret is supplied, the public key is derived automatically +secret = "" +``` diff --git a/docs/de/developer/networking/query.md b/docs/de/developer/networking/query.md new file mode 100644 index 0000000..7d97a9c --- /dev/null +++ b/docs/de/developer/networking/query.md @@ -0,0 +1,97 @@ +# GameSpy4 Query Protocol + +Pumpkin includes native support for the **GameSpy4 Query** protocol over UDP. External monitoring systems, Discord bots, and server listing platforms use Query to poll real-time player counts, MOTD, server versions, and active player lists with minimal network overhead. + +Query wire encoding is implemented in [`crates/pumpkin-protocol/src/query.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-protocol/src/query.rs), and the asynchronous UDP listener is implemented in [`crates/pumpkin/src/net/query.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/query.rs). + +--- + +## Why Query? (UDP vs. Status Ping) + +While standard Minecraft clients ping servers via the TCP `Status` state (SLP), Query operates over stateless **UDP**: + +- **No TCP Handshake**: Avoids TCP connection overhead and connection state tables. +- **Detailed Server State**: Returns comprehensive key-value metadata and a full list of connected player usernames (not available via basic TCP ping). +- **Amplification Attack Protection**: Enforces cryptographic challenge tokens so malicious actors cannot abuse the UDP port for denial-of-service reflection attacks. + +--- + +## The Handshake & Challenge Token Flow + +To prevent UDP spoofing and reflection DDoS attacks, Query requires a two-step handshake: + +1. **Handshake Challenge**: The client transmits `SHandshake` (`0xFEFD`, type `0x09`, and a 32-bit session ID). Pumpkin generates a cryptographic 32-bit integer challenge token tied to the client's `SocketAddr` and responds with `CHandshake`. +2. **Status Query**: The client sends `SStatusRequest` containing the received challenge token: + - **Basic Status** (no padding): Returns MOTD, game type (`SMP`), map name, online player count, and max player capacity. + - **Full Status** (4-byte padding): Returns detailed server metadata key-value pairs (`hostname`, `gametype`, `game_id`, `version`, `plugins`, `map`, `numplayers`, `maxplayers`, `hostport`, `hostip`) followed by an array of online player usernames. + +### Challenge Token Invalidation +In `crates/pumpkin/src/net/query.rs`, challenge tokens are stored in a thread-safe map (`Arc>>`). A background Tokio timer purges all challenge tokens every 30 seconds to prevent replay abuse: + +```rust +// Tokens are strictly bound to the client's IP and port +let valid_challenge_tokens = Arc::new(RwLock::new(HashMap::new())); + +// Background expiration loop +tokio::spawn(async move { + let mut interval = time::interval(Duration::from_secs(30)); + loop { + interval.tick().await; + valid_challenge_tokens.write().await.clear(); + } +}); +``` + +--- + +## Packet Formats + +Every incoming Query packet starts with the 2-byte magic header `0xFEFD` followed by a 1-byte packet type: + +| Type Byte | Name | Direction | Description | +| :--- | :--- | :--- | :--- | +| `0x09` | Handshake Request | Client → Server | Client asks for a challenge token. | +| `0x09` | Handshake Response | Server → Client | Returns the numeric token encoded as an ASCII string. | +| `0x00` | Stat Request | Client → Server | Requests basic or full status with challenge token. | +| `0x00` | Basic Stat Response | Server → Client | 11-byte short summary. | +| `0x00` | Full Stat Response | Server → Client | Key-value dictionary + null-separated player list. | + +--- + +## Response Payloads + +### 1. Basic Status (`CBasicStatus`) +Contains essential server fields: +- **`motd`**: Server message of the day (stripped of color codes). +- **`gametype`**: Fixed string `"SMP"`. +- **`map`**: Active world/level name (e.g., `"world"`). +- **`numplayers`**: Online player count. +- **`maxplayers`**: Maximum configured player capacity. +- **`hostport`**: Port number (`u16` Little Endian). +- **`hostip`**: Server IP string. + +### 2. Full Status (`CFullStatus`) +Returns two null-terminated sections: + +1. **Key-Value Metadata Map**: + - `hostname`: Server description / MOTD. + - `gametype`: `"SMP"`. + - `game_id`: `"MINECRAFT"`. + - `version`: Server protocol / Minecraft version string. + - `plugins`: Installed server software and plugin names. + - `map`: Level name. + - `numplayers` / `maxplayers`: Capacity stats. + - `hostport` / `hostip`: Network bindings. +2. **Player List**: + - An array of null-terminated UTF-8 strings containing the usernames of all currently connected players. + - Concludes with a terminal null byte (`\0`). + +--- + +## Configuration in `pumpkin.toml` + +```toml +[networking.query] +enabled = true +address = "0.0.0.0:25565" +``` diff --git a/docs/de/developer/plugins/index.md b/docs/de/developer/plugins/index.md new file mode 100644 index 0000000..8cc993f --- /dev/null +++ b/docs/de/developer/plugins/index.md @@ -0,0 +1,56 @@ +# Plugin Engine Architecture + +Pumpkin features a modular, multi-runtime plugin engine designed for performance, safety, and cross-language interoperability. + +Unlike traditional Minecraft servers that rely on a single JVM class loader, Pumpkin isolates plugin execution through dedicated loaders supporting both **native high-performance Rust binaries** and **sandboxed WebAssembly components** compiled from Rust, Python, C#, C, Go, or Kotlin. + +--- + +## Architectural Overview + +### Architectural Breakdown + +| Subsystem | Primary Crate | Responsibility | +| :--- | :--- | :--- | +| **Plugin Manager** | `crates/pumpkin/src/plugin` | Lifecycle orchestration (load, enable, disable), event dispatching, and loader registration | +| **Native Loader** | `crates/pumpkin/src/plugin/loader/native` | Direct dynamic library loading (`.so`, `.dll`, `.dylib`) for zero-overhead Rust extensions | +| **WASM Loader** | `crates/pumpkin/src/plugin/loader/wasm` | Wasmtime execution engine for sandboxed components compiled from Rust, Python, Go, C#, C, and Kotlin | +| **Interface Specifications** | `crates/pumpkin-plugin-wit` | WebAssembly Interface Types (WIT) defining typed functions, types, and host imports | +| **Security & Verification** | `crates/pumpkin/src/plugin/loader/wasm/sign` | Cryptographic signature verification via W3C wasmsign2 / Ed25519 public keys | + +--- + +## The Dual Execution Model + +| Feature | Native Plugins | WebAssembly (Wasmtime) Plugins | +| :--- | :--- | :--- | +| **File Extension** | `.so` (Linux), `.dll` (Windows), `.dylib` (macOS) | `.wasm` (Component Model) | +| **Languages** | Rust (compiled dynamically) | Rust, Python, C#, C, Go, Kotlin | +| **Performance** | Native CPU execution (zero overhead) | JIT-compiled with near-native speed | +| **Sandboxing** | None (runs in host address space) | Memory-isolated sandboxed execution | +| **Security & Signing** | OS-level permissions | Cryptographic W3C Wasm-Sign verification | +| **Hot Unloading** | Limited (OS shared library constraints) | Fully supported memory reclamation | + +--- + +## Event Lifecycle & Dispatching + +Plugins interact with Pumpkin primarily through events: + +1. **Event Dispatch**: + - `server.plugin_manager.fire_blocking(&server, &mut event)`: Dispatches events synchronously on the executing thread (e.g., cancelling player block placement or modifying packet payloads). + - Asynchronous events run on non-blocking Tokio tasks. +2. **Cancellable Events**: + - Handlers can set `event.cancelled = true` to abort the default game logic (such as cancelling player movement, interactions, or packet processing). + +--- + +## Plugin Engine Documentation + +Explore the internal mechanics of Pumpkin's plugin system: + +- **[Adding a Plugin Loader](/developer/plugins/loaders)**: Step-by-step guide on implementing the `PluginLoader` trait for new scripting languages and runtimes. +- **[WASM Signing & Verification](/developer/plugins/wasm-signing)**: Detailed specification of Ed25519 cryptographic signing, W3C `wasmsign2` custom sections, and marketplace license verification. + +> [!NOTE] +> Looking to develop third-party plugins rather than working on Pumpkin's internal plugin engine? Check out the **[Plugin Development Guide](/plugin-dev/introduction)**. diff --git a/docs/de/developer/plugins/loaders.md b/docs/de/developer/plugins/loaders.md new file mode 100644 index 0000000..7e0f070 --- /dev/null +++ b/docs/de/developer/plugins/loaders.md @@ -0,0 +1,161 @@ +# Adding a Plugin Loader + +Pumpkin's plugin architecture is runtime-agnostic. The server does not assume all plugins run in WebAssembly or native shared libraries; instead, all execution runtimes implement the unified [`PluginLoader`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/plugin/loader/mod.rs) trait. + +This guide explains how `PluginLoader` operates and demonstrates how to implement a custom loader for new script runtimes or virtual machines (such as Lua, JavaScript/Boa, or GraalVM). + +--- + +## The `PluginLoader` Trait + +All plugin loaders implement the `PluginLoader` trait located in `crates/pumpkin/src/plugin/loader/mod.rs`: + +```rust +use std::{any::Any, path::Path, pin::Pin, sync::Arc}; +use crate::plugin::{PluginMetadata, api::Plugin}; + +pub type PluginLoadFuture<'a> = Pin< + Box< + dyn Future< + Output = Result< + (Arc, PluginMetadata, Box), + LoaderError, + >, + > + Send + + 'a, + >, +>; + +pub type PluginUnloadFuture<'a> = + Pin> + Send + 'a>>; + +pub trait PluginLoader: Send + Sync { + /// Determines whether this loader handles the target file. + fn can_load(&self, path: &Path) -> bool; + + /// Loads, parses, and initializes a plugin instance asynchronously. + fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a>; + + /// Unloads the plugin and reclaims associated resources. + fn unload(&self, data: Box) -> PluginUnloadFuture<'_>; + + /// Indicates whether the runtime supports safe unloading without memory leaks. + fn can_unload(&self) -> bool; +} +``` + +--- + +## Existing Loaders in Pumpkin + +Pumpkin currently provides two official loaders: + +1. **`NativePluginLoader` (`loader/native.rs`)**: + - Matches dynamic library files (`.so` on Linux, `.dll` on Windows, `.dylib` on macOS). + - Dynamically links the library using [`libloading`](https://docs.rs/libloading/). + - Locates and invokes the extern entrypoint function (`pumpkin_plugin_init`). + - `can_unload()` returns `false` due to OS-level shared library unloading hazards. +2. **`WasmPluginLoader` (`loader/wasm/mod.rs`)**: + - Matches `.wasm` files. + - Verifies cryptographic signatures (see [WASM Signing](/developer/plugins/wasm-signing)). + - JIT-compiles WebAssembly components using [Wasmtime](https://wasmtime.dev/). + - Links host WIT exports (`pumpkin-host-bindings`). + - `can_unload()` returns `true` because Wasmtime safely drops guest memory. + +--- + +## Step-by-Step: Adding a Custom Plugin Loader + +Here is how to create a custom loader (e.g., for a scripting runtime): + +### Step 1: Define the Loader Struct +Create a new file in `crates/pumpkin/src/plugin/loader/my_loader.rs`: + +```rust +use std::{any::Any, path::Path, sync::Arc}; +use crate::plugin::loader::{LoaderError, PluginLoadFuture, PluginLoader, PluginUnloadFuture}; +use crate::plugin::{PluginMetadata, api::Plugin}; + +pub struct MyCustomLoader; + +impl MyCustomLoader { + #[must_use] + pub fn new() -> Self { + Self + } +} +``` + +### Step 2: Implement File Recognition (`can_load`) +Check the file extension or inspect header bytes: + +```rust +impl PluginLoader for MyCustomLoader { + fn can_load(&self, path: &Path) -> bool { + path.extension() + .and_then(|ext| ext.to_str()) + .is_some_and(|ext| ext.eq_ignore_ascii_case("script")) + } +``` + +### Step 3: Implement Asynchronous Loading (`load`) +The `load` method reads the script, initializes the VM, registers server host callbacks, and returns a tuple containing: +1. `Arc`: The generic plugin handle. +2. `PluginMetadata`: Name, author, version, description. +3. `Box`: Loader-specific context (e.g., VM state) retained for unloading. + +```rust + fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a> { + Box::pin(async move { + let script_content = tokio::fs::read_to_string(path).await + .map_err(|e| LoaderError::InitializationFailed(e.to_string()))?; + + // 1. Parse plugin metadata from header or accompanying manifest + let metadata = PluginMetadata { + name: "ExampleScript".into(), + version: "1.0.0".into(), + authors: vec!["Author".into()], + description: Some("Custom script plugin".into()), + }; + + // 2. Initialize runtime instance + let plugin_instance = Arc::new(MyScriptPluginWrapper::new(script_content)); + let runtime_state: Box = Box::new(()); + + Ok((plugin_instance, metadata, runtime_state)) + }) + } +``` + +### Step 4: Implement Clean Unloading (`unload` & `can_unload`) +If your runtime can cleanly terminate without leaking resources: + +```rust + fn can_unload(&self) -> bool { + true + } + + fn unload(&self, data: Box) -> PluginUnloadFuture<'_> { + Box::pin(async move { + // Drop runtime state and disconnect event listeners + drop(data); + Ok(()) + }) + } +} +``` + +### Step 5: Register the Loader in `PluginManager` + +Open `crates/pumpkin/src/plugin/mod.rs` and register your loader in `PluginManager::new()`: + +```rust +// In crates/pumpkin/src/plugin/mod.rs +let mut loaders: Vec> = Vec::new(); + +loaders.push(Box::new(NativePluginLoader::new())); +loaders.push(Box::new(WasmPluginLoader::new())); +loaders.push(Box::new(MyCustomLoader::new())); // Add custom loader +``` + +During server startup, Pumpkin iterates through all registered loaders in sequence when scanning the `plugins/` folder, dispatching each file to the first loader whose `can_load` returns `true`. diff --git a/docs/de/developer/plugins/wasm-signing.md b/docs/de/developer/plugins/wasm-signing.md new file mode 100644 index 0000000..40a36eb --- /dev/null +++ b/docs/de/developer/plugins/wasm-signing.md @@ -0,0 +1,86 @@ +# WASM Plugin Signing & Verification + +Pumpkin implements a cryptographic signing and license verification system for WebAssembly plugins. This ensures plugin integrity, authenticates author identity, and protects verified or marketplace plugins against unauthorized tampering. + +The signing subsystem is implemented in [`crates/pumpkin/src/plugin/loader/wasm/wasm_host/signature.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/plugin/loader/wasm/wasm_host/signature.rs) based on the **W3C `wasmsign2`** specification using custom WebAssembly sections. + +--- + +## Why Sign WebAssembly Plugins? + +- **Tamper Protection**: Ensures that compiled bytecode has not been injected with malicious logic after compilation. +- **Author Identity**: Authenticates that a plugin was built and released by a verified developer. +- **Marketplace Licensing**: Validates license keys and entitlements for plugins distributed through the official [Pumpkin Marketplace](https://market.pumpkinmc.org). + +--- + +## Custom Sections in the WASM Binary + +Pumpkin embeds signing and verification metadata directly into two standard WebAssembly custom sections: +- **`pumpkin.metadata`**: Stores developer, publication, and license claims in JSON format. +- **`wasm_signature`**: Stores the cryptographic signature envelope compliant with the W3C `wasmsign2` standard. + +### 1. `pumpkin.metadata` Section +Contains a JSON string holding publication and license claims: + +```json +{ + "marketplace_url": "https://market.pumpkinmc.org", + "plugin_id": 1024, + "plugin_name": "EconomyPlus", + "version": "1.2.0", + "dev_id": 42, + "dev_name": "DevName", + "is_paid": true, + "user_id": 1001, + "license_key": "XXXX-XXXX-XXXX-XXXX", + "issued_at": "2026-09-14T05:00:00Z" +} +``` + +### 2. `wasm_signature` Section +Follows the W3C `wasmsign2` envelope structure: + +```json +{ + "version": 1, + "algorithm": "Ed25519", + "public_key_hex": "4a7b...", + "signature_hex": "8e1c..." +} +``` + +--- + +## The Signing Pipeline + +The signing process guarantees integrity by hashing and signing the clean WebAssembly bytecode combined with the metadata claims: + +### Signing Algorithm Steps +1. **Sanitization (`strip_pumpkin_sections`)**: Uses [`wasmparser`](https://docs.rs/wasmparser/) to strip any preexisting `pumpkin.metadata` or `wasm_signature` custom sections to avoid recursion. +2. **Payload Construction**: + ```rust + let mut sign_payload = Vec::new(); + sign_payload.extend_from_slice(&clean_wasm); + sign_payload.extend_from_slice(&metadata_json); + ``` +3. **Ed25519 Signature**: The payload is signed using [`ed25519-dalek`](https://docs.rs/ed25519-dalek/) with the developer's 32-byte secret key. +4. **Encoding**: Uses [`wasm-encoder`](https://docs.rs/wasm-encoder/) to append the `pumpkin.metadata` and `wasm_signature` custom sections to the clean binary. + +--- + +## The Verification Pipeline + +When Pumpkin boots and loads a WebAssembly plugin, the verification sequence executes before the plugin is instantiated: + +1. **Custom Section Extraction**: The `WasmPluginLoader` scans the binary for `pumpkin.metadata` and `wasm_signature`. +2. **Key Retrieval**: + - If the plugin is marked as marketplace-verified, Pumpkin verifies the public key against the Marketplace Public Key Endpoint (`https://market.pumpkinmc.org/api/v1/rest/public-key`). + - If signed by an independent developer, the key is matched against trusted developer keys configured in `pumpkin.toml`. +3. **Bytecode Verification**: + - Pumpkin strips the custom sections from the loaded binary to recover the original `clean_wasm`. + - Reconstructs the payload `(clean_wasm + metadata_json)`. + - Verifies the signature using `verifying_key.verify(&payload, &signature)`. +4. **License & Integrity Validation**: + - If verification succeeds, Wasmtime instantiates the plugin safely. + - If verification fails or the signature is invalid, loading is aborted with an error, protecting the server from running compromised code. diff --git a/docs/de/developer/porting-versions.md b/docs/de/developer/porting-versions.md new file mode 100644 index 0000000..0a5014a --- /dev/null +++ b/docs/de/developer/porting-versions.md @@ -0,0 +1,287 @@ +# Porting to Newer Minecraft Versions + +Upgrading Pumpkin to support a newer Minecraft release—whether a minor patch (e.g., `1.21.4` to `1.21.5`) or a major update (e.g., `1.21` to `26.1`/`26.2`)—is a structured, multi-step process. + +> [!IMPORTANT] +> **The Extractor mod must be ported first!** +> Pumpkin cannot be ported in isolation. The primary source for Pumpkin's assets (`blocks.json`, `items.json`, `entities.json`, `packets.json`, `tracked_data.json`, `synced_registries.json`, etc.) is our dedicated Fabric mod: [Pumpkin-MC/Extractor](https://github.com/Pumpkin-MC/Extractor). You **must first port and run the Extractor on the target Minecraft version** before running `pumpkin-codegen` or updating server crates. + +### Porting Lifecycle at a Glance + +| Stage | Responsibility | Primary Crates / Tools | +| :--- | :--- | :--- | +| **1. Port Extractor** | Extract vanilla block states, entities, items, packets, and registries | `Pumpkin-MC/Extractor` (Fabric mod) | +| **2. Update Datapacks** | Ingest new data packs, tags, recipes, and Bedrock mappings | `assets/datapacks/`, `assets/bedrock` | +| **3. Version Declarations** | Register protocol version and ViaVersion/ViaBackwards remap nodes | `pumpkin-util`, `tools/pumpkin-codegen` | +| **4. Code Generation** | Regenerate zero-cost Rust data tables and registries | `pumpkin-codegen` -> `pumpkin-data` | +| **5. Compiler Fixes** | Update packet layouts, codecs, and data components | `pumpkin-protocol`, `pumpkin-inventory` | +| **6. Gameplay Logic** | Implement new block behaviors, item mechanics, and mob AI | `crates/pumpkin/src/block`, `item`, `entity` | +| **7. Multi-Version QA** | Validate backward compatibility, test suites, and client joins | `cargo test`, `pumpkin-gametest` | + +--- + +## Phase 1: Port the Extractor Mod First (`Pumpkin-MC/Extractor`) + +The [Extractor](https://github.com/Pumpkin-MC/Extractor) is a Kotlin-based Fabric mod that boots an actual Minecraft client or server instance to introspect live game registries and extract data into the exact JSON formats Pumpkin expects. + +### 1. Clone or Update the Extractor Repository +```bash +git clone https://github.com/Pumpkin-MC/Extractor +cd Extractor +``` + +### 2. Update `gradle.properties` +Bump the dependency versions to match the target Minecraft version, Yarn mappings, and Fabric ecosystem: + +```properties +minecraft_version=26.2 +yarn_mappings=26.2+build.1:v2 +loader_version=0.16.10 +fabric_version=0.115.0+26.2 +``` + +### 3. Resolve Extractor Compilation Errors +Minecraft and Fabric internal APIs often change between releases. Check and fix any broken extractors in `src/main/kotlin/de/snowii/extractor/extractors/`: +- **`Blocks.kt`**: Extracts block states, hardness, resistance, luminance, and collision box offsets. +- **`Items.kt`**: Extracts item IDs, max stack sizes, and default components. +- **`Entities.kt`**: Extracts entity IDs, dimensions (width, height), and eye heights. +- **`Packets.kt`**: Extracts packet IDs for handshake, status, login, configuration, and play states. +- **`SyncedRegistries.kt`**: Extracts dynamic registries synchronized to clients. +- **`TrackedData.kt`**: Extracts entity tracked data indices and serializers. +- **`MultiNoise.kt` & `Biome.kt`**: Extracts world generation biome trees and noise parameters. + +### 4. Run the Extractor +Execute the server runner (or client runner for translation/screen dumps): + +```bash +./gradlew runServer +``` + +Once the extraction finishes, all dumped JSON files will be located in `run/pumpkin_extractor_output/`. + +### 5. Replace the Old Asset Files in Pumpkin +Copy the newly generated JSON files from `run/pumpkin_extractor_output/` and use them to **replace the old asset files** in Pumpkin: +- Replace the core game assets in `Pumpkin/assets/` (`blocks.json`, `items.json`, `entities.json`, `packets.json`, `properties.json`, `synced_registries.json`, `tracked_data.json`, `en_us.json`, etc.). +- Replace the world generation test files in `Pumpkin/crates/pumpkin-world/assets/`. + +--- + +## Phase 2: Update Assets & Datapacks + +Once the raw extractor assets are in place, update the versioned datapacks and supplemental files in Pumpkin: + +### 1. Add the Versioned Datapack +Extract the internal vanilla data pack from the new Minecraft server JAR and place it under `assets/datapacks//`: + +``` +assets/datapacks// +└── data/ + ├── minecraft/ + │ ├── advancement/ + │ ├── chat_type/ + │ ├── damage_type/ + │ ├── dimension_type/ + │ ├── enchantment/ + │ ├── item_modifier/ + │ ├── loot_table/ + │ ├── recipe/ + │ ├── tags/ + │ └── worldgen/ + └── c/ + └── tags/ (Conventional / Common Tags) +``` + +### 2. Update Versioned Assets +Update or generate the corresponding versioned metadata files: +- `assets/registry/_synced_registries.json`: Dynamic registries streamed to the client during the `Configuration` network state. +- `assets/tags/_tags.json`: Tag collections. +- `assets/tracked_data/_tracked_data.json`: Entity metadata indices. + +### 3. Update Bedrock Mappings +If porting Bedrock Edition support simultaneously, update the files in `assets/bedrock/` using the latest mappings from [GeyserMC](https://github.com/GeyserMC/Geyser) and Bedrock Dedicated Server (BDS). + +### 4. Decompile Sources for Reverse Engineering (Optional but Recommended) +For complex changes—such as worldgen noise router formulas and custom block collision shapes—decompile the matching vanilla JAR into `minecraft-sources-/` (using Vineflower or Fabric Loom with official Mojang mappings) for reference. + +--- + +## Phase 3: Register Version Constants & ViaVersion Mappings + +Pumpkin supports multi-version clients through automated protocol remapping powered by **ViaBackwards** / **ViaRewind** mapping tables. + +### 1. Declare Version in `pumpkin-util` +Open `crates/pumpkin-util/src/version.rs` and add the new version to [`JavaMinecraftVersion`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-util/src/version.rs): + +```rust +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum JavaMinecraftVersion { + // ... existing versions ... + V_1_21_4, + V_1_21_11, + V_26_2, // New target version +} + +impl JavaMinecraftVersion { + pub const fn protocol_version(&self) -> i32 { + match self { + // ... + Self::V_26_2 => 770, // Protocol version number + } + } + + pub const fn version_name(&self) -> &'static str { + match self { + // ... + Self::V_26_2 => "26.2", + } + } +} +``` + +### 2. Declare Version in `pumpkin-codegen` +Open `tools/pumpkin-codegen/src/version.rs` and add the identical enum variant to the codegen AST. + +### 3. Add ViaVersion / ViaBackwards Mappings +To enable clients on older versions to connect to the updated server (e.g., a `1.21.4` client connecting to a `26.2` server): +1. Obtain the NBT mapping file from the [ViaBackwards repository](https://github.com/ViaVersion/ViaBackwards): + `mappings-to.nbt` +2. Save it to: + `assets/viabackwards/data/mappings-to.nbt` +3. In `tools/pumpkin-codegen/src/remap/mod.rs`, link the new node into the `remap_nodes!` macro: + +```rust +let node_26_2 = $crate::remap::MappingNode { + version: $crate::version::JavaMinecraftVersion::V_26_2, + value: "../../assets/viabackwards/data/mappings-26.2to26.1.nbt", + child: Some(&node_26_1), +}; +$remapper.process(&node_26_2) +``` + +4. Check `tools/pumpkin-codegen/src/remap/` to ensure specific remap builders (`block_state.rs`, `item_id.rs`, `entity_id.rs`, `sound_id.rs`, `argument_type.rs`) correctly process any new remapping structures introduced by ViaVersion. + +--- + +## Phase 4: Run Code Generation (`pumpkin-codegen`) + +With all extractor assets, datapacks, and mapping nodes in place, execute the codegen tool from the workspace root: + +```bash +cargo run -p pumpkin-codegen +``` + +### What Gets Regenerated? +`pumpkin-codegen` generates zero-cost Rust data tables into `crates/pumpkin-data/src/generated/`: +- **`block.rs`**: All block IDs, properties, state masks, and collision shapes. +- **`item.rs`**: Items, default components, and tool tiers. +- **`entity_type.rs` & `tracked_data.rs`**: Entity type IDs, default bounding boxes, and metadata serializers. +- **`packet.rs`**: Serverbound and clientbound packet ID constants across protocol states. +- **`recipes.rs`**: All crafting, cooking, and stonecutting recipes parsed from the new datapack. +- **`tag.rs`**: Tag lookups (`#minecraft:mineable/axe`, `#c:iron_ingots`). +- **`remap/*.rs`**: State remap lookup tables (`block_state_remap.rs`, `item_id_remap.rs`, `entity_id_remap.rs`, `sound_id_remap.rs`). + +--- + +## Phase 5: Resolve Workspace Compilation Errors + +After regenerating `pumpkin-data`, the Rust compiler will flag breaking changes across the workspace. Run: + +```bash +cargo check --workspace +``` + +### Common Compiler Breakages & Fixes: + +#### 1. Packet ID & Layout Changes (`pumpkin-protocol`) +Mojang frequently adds, moves, or alters packet IDs and payloads: +- Update `pumpkin-protocol/src/java/client/` and `pumpkin-protocol/src/java/server/`. +- Inspect packets like `LoginSuccess`, `PlayLogin`, `Respawn`, and `PlayerSpawnData` for newly added fields (e.g., new game mode flags, dimension attributes, or teleportation IDs). +- For Bedrock Edition, check `StartGame`, `NetworkItem`, and container transaction packets in `pumpkin-protocol/src/bedrock/`. + +#### 2. Data Component Changes +If items switched from older NBT structures to new Data Components (or if components were renamed/restructured): +- Update `crates/pumpkin-data/src/data_component.rs`. +- Update serialization and deserialization in `pumpkin-inventory`. + +#### 3. Registry Synchronization +During the `Configuration` phase, servers send dynamic registry contents to Java clients. If new registries were added to vanilla Minecraft (e.g., trim materials, banner patterns, instrument sounds): +- Verify `crates/pumpkin-data/src/generated/registry.rs`. +- Ensure `pumpkin/src/net/java/config.rs` streams all required registry codecs to the client. + +--- + +## Phase 6: Implement Logic for New Blocks, Items & Entities + +Once the server compiles, any newly introduced gameplay content must be implemented. + +### 1. New Blocks +1. Locate new block identifiers in `pumpkin-data::Block`. +2. Create a behavior module under `crates/pumpkin/src/block/blocks/.rs` implementing [`BlockBehaviour`](/developer/blocks/adding-blocks): + ```rust + pub struct NewBlockBehaviour; + + impl BlockBehaviour for NewBlockBehaviour { + fn on_use(&self, block: &Block, player: &Arc, location: BlockPos, world: &Arc) { + // Interaction logic + } + } + ``` +3. Register the behavior in the block registry in `crates/pumpkin/src/block/mod.rs`. + +### 2. New Items +1. Locate new item identifiers in `pumpkin-data::Item`. +2. If the item has unique right-click or use behavior (e.g., weapons, projectiles, instruments), create an item behavior in `crates/pumpkin/src/item/items/.rs` implementing [`ItemBehaviour`](/developer/items/adding-items). +3. If the item places entities (e.g., spawn eggs, boats, minecarts), configure entity spawning logic in `pumpkin/src/item/items/spawn_egg.rs`. +4. Register the behavior in `crates/pumpkin/src/item/mod.rs`. + +### 3. New Entities +1. Check `pumpkin-data::entity_type::EntityType` for new entity IDs. +2. Create an entity struct in `crates/pumpkin/src/entity/mob/` or `living/` extending [`LivingEntity`](/developer/entities/). +3. Implement `TrackedData` fields to match vanilla client expectations (e.g., poses, animations, glowing states). +4. Assign AI goals and navigation tasks using the [Mob AI & Goals system](/developer/entities/ai-and-mobs). +5. Add spawn packet construction in `crates/pumpkin/src/entity/spawning.rs`. + +--- + +## Phase 7: Multi-Version & ViaVersion Verification + +Ensure that older clients can still connect seamlessly: + +1. **Verify Generated Remap Tables**: + Check `crates/pumpkin-data/src/generated/block_state_remap.rs` and `item_id_remap.rs`. Ensure that novel block states fall back gracefully to compatible equivalents on older client versions (e.g., new wood variants mapping to oak or spruce). +2. **Packet Translation**: + Verify that packet writers in `pumpkin-protocol` apply `remap` functions when serializing data for a connection whose negotiated `protocol_version` is older than the server's native version. + +--- + +## Phase 8: Testing & Verification Checklist + +Before opening a Pull Request for a version port, complete the following QA checklist: + +- [ ] **Automated Tests**: + ```bash + cargo test --workspace + ``` +- [ ] **Game Test Suite**: + ```bash + cargo test -p pumpkin-gametest + ``` +- [ ] **Strict Linter Rules**: + ```bash + cargo clippy --workspace --all-targets -- -D warnings + ``` +- [ ] **Spellcheck**: + ```bash + typos + ``` +- [ ] **In-Game Java Client Test**: + - Connect with the native new Minecraft client version. + - Test login, world loading, chunk generation, and player movement. + - Place and break new blocks; verify collision boxes and redstone reactivity. + - Use and equip new items; test attack speed, durability, and animations. + - Spawn and fight new entities; test animations, hurt sounds, and drops. +- [ ] **In-Game Older Version Client Test**: + - Connect with an older supported client version (e.g., 1.21.x). + - Verify that blocks, items, and entities translate without causing client crashes or desyncs. +- [ ] **In-Game Bedrock Client Test**: + - Verify that Bedrock clients can authenticate, join, load world chunks, and view custom items. diff --git a/docs/de/plugin-dev/c/first-command.md b/docs/de/plugin-dev/c/first-command.md new file mode 100644 index 0000000..3ac3ebd --- /dev/null +++ b/docs/de/plugin-dev/c/first-command.md @@ -0,0 +1,90 @@ +# Creating Your First Command + +Writing commands in C for Pumpkin is low-overhead, fast, and uses C WIT bindings. + +--- + +## 1. Quick Example + +Here is a complete C plugin file (`main.c`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```c [main.c] +#include +#include "pumpkin.h" + +void plugin_on_load(pumpkin_context_t ctx) { + // 1. Register permission node + pumpkin_permission_t perm = { + .node = "my_c_plugin:hello", + .description = "Allows executing the /hello command", + .default_access = PERMISSION_DEFAULT_ALLOW + }; + pumpkin_register_permission(ctx, &perm); + + // 2. Build and register command tree + const char* names[] = {"hello", "hi"}; + pumpkin_command_t cmd = pumpkin_command_new(names, 2, "Greets the player"); + + pumpkin_register_command(ctx, cmd, "my_c_plugin:hello"); +} + +int32_t plugin_handle_command(uint32_t command_id, pumpkin_sender_t sender, pumpkin_server_t server) { + pumpkin_sender_send_message(sender, "Hello from C Plugin!"); + return 1; +} + +pumpkin_metadata_t plugin_get_metadata() { + pumpkin_metadata_t meta = { + .name = "my_c_plugin", + .version = "0.1.0", + .description = "My first Pumpkin C plugin" + }; + return meta; +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Struct +Populate a `pumpkin_permission_t` struct and call `pumpkin_register_permission`: +```c +pumpkin_permission_t perm = { + .node = "my_c_plugin:hello", + .description = "Allows executing the /hello command", + .default_access = PERMISSION_DEFAULT_ALLOW +}; +pumpkin_register_permission(ctx, &perm); +``` + +### Step 2: Build the Command Tree +Create the command tree by passing name array and description: +```c +const char* names[] = {"hello", "hi"}; +pumpkin_command_t cmd = pumpkin_command_new(names, 2, "Greets the player"); +``` + +### Step 3: Register Command & Handle Execution +Pass the command handle to `pumpkin_register_command` and handle responses inside `plugin_handle_command`: +```c +pumpkin_register_command(ctx, cmd, "my_c_plugin:hello"); +``` + +::: + +--- + +## Next Steps + +- Check out the [C Quick Start Guide](./quick-start) to set up your compiler toolchain. diff --git a/docs/de/plugin-dev/csharp/first-command.md b/docs/de/plugin-dev/csharp/first-command.md new file mode 100644 index 0000000..c2a34ca --- /dev/null +++ b/docs/de/plugin-dev/csharp/first-command.md @@ -0,0 +1,110 @@ +# Creating Your First Command + +Registering custom commands in Pumpkin using C# provides a strongly-typed, clean structure. + +--- + +## 1. Quick Example + +Here is a complete C# plugin file (`MyPlugin.cs`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```csharp [MyPlugin.cs] +using PluginWorld; +using PluginWorld.wit.Exports.pumpkin.plugin.v0_1_0; +using PluginWorld.wit.Imports.pumpkin.plugin.v0_1_0; + +namespace MyPumpkinPlugin; + +public class MyPlugin : IPluginWorldExports, IMetadataExports +{ + public static void InitPlugin() { } + + public static void OnLoad(IContextImports.Context context) + { + // 1. Register permission node + var perm = new IPermissionImports.Permission( + "my_csharp_plugin:hello", + "Allows executing the /hello command", + IPermissionImports.PermissionDefault.Allow, + [] + ); + IContextImports.RegisterPermission(context, perm); + + // 2. Build and register command tree + var command = ICommandImports.Command.New(["hello", "hi"], "Greets the player"); + IContextImports.RegisterCommand(context, command, "my_csharp_plugin:hello"); + } + + public static void OnUnload(IContextImports.Context context) { } + + public static IEventImports.Event HandleEvent(uint eventId, IServerImports.Server server, IEventImports.Event @event) + { + return @event; + } + + public static int HandleCommand(uint commandId, ICommandImports.CommandSender sender, IServerImports.Server server, ICommandImports.ConsumedArgs args) + { + ICommandImports.SendMessage(sender, "Hello from C# Plugin!"); + return 1; + } + + public static void HandleTask(uint handlerId, IServerImports.Server server) { } + + public static IMetadataExports.PluginMetadata Metadata() + { + return new IMetadataExports.PluginMetadata( + "my_csharp_plugin", + "0.1.0", + "My first Pumpkin C# plugin", + ["Developer"], + [] + ); + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Node +Construct an `IPermissionImports.Permission` record and register it via `IContextImports.RegisterPermission`: +```csharp +var perm = new IPermissionImports.Permission( + "my_csharp_plugin:hello", + "Allows executing the /hello command", + IPermissionImports.PermissionDefault.Allow, + [] +); +IContextImports.RegisterPermission(context, perm); +``` + +### Step 2: Build the Command Tree +Instantiate your command with primary names and description: +```csharp +var command = ICommandImports.Command.New(["hello", "hi"], "Greets the player"); +``` + +### Step 3: Register and Handle +Register the command with `IContextImports.RegisterCommand` and process incoming executions inside `HandleCommand`: +```csharp +IContextImports.RegisterCommand(context, command, "my_csharp_plugin:hello"); +``` + +::: + +--- + +## Next Steps + +- Follow the [C# Quick Start Guide](./quick-start) to configure `.csproj` and build WebAssembly outputs. diff --git a/docs/de/plugin-dev/d/quick-start.md b/docs/de/plugin-dev/d/quick-start.md new file mode 100644 index 0000000..4781cbd --- /dev/null +++ b/docs/de/plugin-dev/d/quick-start.md @@ -0,0 +1,130 @@ +# Schnellstart + +Dieser Leitfaden hilft dir beim Einstieg in die Entwicklung von Pumpkin-Server-Plugins mit der Programmiersprache [D](https://dlang.org/). + +D-Plugins für Pumpkin werden mithilfe der offiziellen Bindings [`pumpkin-api-d`](https://github.com/Pumpkin-MC/pumpkin-api-d) als WebAssembly-Komponenten (Wasm) kompiliert. + +--- + +## Voraussetzungen + +Bevor du D-Plugins für Pumpkin erstellst, stelle sicher, dass folgende Software installiert ist: + +- **[LDC](https://github.com/ldc-developers/ldc)**: Version 1.43 oder neuer (LLVM-basierter D-Compiler). +- **`addon-wasi`**: Das WASI-Zielpaket für LDC. +- **[DUB](https://dub.pm/)**: Der D-Paket- und Build-Manager (in der Regel in LDC enthalten). + +--- + +## 1. Projekt einrichten + +Erstelle ein neues Verzeichnis für dein Plugin-Projekt und lege eine `dub.json`-Konfigurationsdatei an: + +```bash +mkdir my-d-plugin +cd my-d-plugin +``` + +Erstelle die `dub.json` im Projektstammverzeichnis: + +```json [dub.json] +{ + "name": "my-d-plugin", + "description": "Ein minimales Pumpkin-Plugin in D.", + "license": "proprietary", + "authors": ["DeinName"], + "copyright": "Copyright © 2026, DeinName", + + "dependencies": { + "pumpkin-api-d": "~>0.1.0" + }, + + "targetType": "executable", + "dflags": ["-Xcc=-mexec-model=reactor"], + "buildTypes": { + "debug": { + "buildOptions": ["debugMode", "debugInfo"] + }, + "release": { + "buildOptions": ["optimize", "inline"], + "dflags": ["-L-S"] + } + } +} +``` + +> [!NOTE] +> Das Flag `-Xcc=-mexec-model=reactor` wird vom WebAssembly-Linker benötigt, damit das Plugin als Reaktor (eine Komponente mit exportierten Einstiegspunkten) anstelle eines eigenständigen CLI-Programms mit Beendigungspunkt behandelt wird. + +--- + +## 2. Das Plugin schreiben + +Erstelle ein `source`-Verzeichnis und lege deinen Code in `source/app.d` ab: + +```d [source/app.d] +module plugin; + +import wit.pumpkin.plugin.plugin; +import wit.common; +import pumpkin.register; + +struct MyPlugin { + @GetMetadata PluginMetadata getMetadata() + { + immutable(WitString)[$] authors = [ + "DeinName".witList + ]; + + return (immutable PluginMetadata( + name: "my-d-plugin".witList, + version_: "0.1.0".witList, + authors: authors.witList, + description: "Ein Beispiel-Plugin in D.".witList, + dependencies: WitList!WitString(), + permissions: WitList!WitString() + )).witClone; + } + + @OnLoad + Result!(void, WitString) onLoad(Context ctx) + { + scope (exit) + ctx.witDrop; // Eingehende Ressourcen müssen nach der Nutzung freigegeben werden + + log(Level.info, "Hallo vom D-Plugin!".witList); + return ok!WitString; + } +} + +mixin RegisterPlugin!MyPlugin; +``` + +--- + +## 3. Plugin kompilieren + +Kompiliere dein Plugin zu einer WebAssembly-Komponente mit dem Ziel `wasm32-wasip2`: + +```bash +dub build -b release -a wasm32-wasip2 +``` + +Dadurch wird `my-d-plugin.wasm` im Projektstammverzeichnis erzeugt. + +--- + +## 4. Plugin ausführen + +1. Kopiere die kompilierte `.wasm`-Datei in das `plugins/`-Verzeichnis deines Pumpkin-Servers: + ```bash + cp my-d-plugin.wasm /pfad/zu/pumpkin/plugins/ + ``` +2. Starte oder starte den Pumpkin-Server neu: + ```bash + ./pumpkin + ``` +3. Prüfe die Server-Logs auf die Erfolgsmeldung: + ```text + [INFO] Hallo vom D-Plugin! + ``` diff --git a/docs/de/plugin-dev/go/basic-logic.md b/docs/de/plugin-dev/go/basic-logic.md new file mode 100644 index 0000000..7e0e4a6 --- /dev/null +++ b/docs/de/plugin-dev/go/basic-logic.md @@ -0,0 +1,60 @@ +# Writing the basic logic + +## Plugin Entry Point + +In Go, your plugin must implement the `api.Plugin` interface and register itself in the `init()` function. You also need to import `wit_exports` for WebAssembly compatibility. + +:::code-group + +```go [main.go] +package main + +import ( + "github.com/Pumpkin-MC/pumpkin-api-go/api" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_context" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_logging" + _ "github.com/Pumpkin-MC/pumpkin-api-go/pkg/wit_exports" // Required for WASM exports +) + +type MyPlugin struct { + api.DefaultPlugin +} + +func (p *MyPlugin) Metadata() api.Metadata { + return api.Metadata{ + Name: "my-go-plugin", + Version: "0.1.0", + Authors: []string{"you"}, + } +} + +func (p *MyPlugin) OnLoad(ctx *pumpkin_plugin_context.Context) { + pumpkin_plugin_logging.Log(pumpkin_plugin_logging.LevelInfo(), "Go plugin loaded!") +} + +func init() { + api.RegisterPlugin(&MyPlugin{}) +} + +func main() {} +``` + +::: + +## Compiling the plugin + +To compile your plugin to WebAssembly, you must use **TinyGo**. The standard Go compiler does not yet support the specific WASI targets required by Pumpkin in the same way. + +Run the following command in your project folder: + +```bash +tinygo build -o my_plugin.wasm -target=wasi main.go +``` + +This will generate a `my_plugin.wasm` file that can be loaded by Pumpkin. + +## Testing the plugin + +Installing a plugin is as simple as putting the plugin binary (`.wasm`) into the `plugins/` folder of your Pumpkin server! + +When you start up the server and run the `/plugins` command, you should see your plugin listed. diff --git a/docs/de/plugin-dev/go/first-command.md b/docs/de/plugin-dev/go/first-command.md new file mode 100644 index 0000000..f20ffdd --- /dev/null +++ b/docs/de/plugin-dev/go/first-command.md @@ -0,0 +1,103 @@ +# Creating Your First Command + +Registering commands in Go with Pumpkin is built on standard Go idiom using TinyGo WASM compilation. + +--- + +## 1. Quick Example + +Here is a complete Go plugin file (`main.go`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```go [main.go] +package main + +import ( + "github.com/Pumpkin-MC/pumpkin-api-go/api" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_command" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_context" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_permission" + _ "github.com/Pumpkin-MC/pumpkin-api-go/pkg/wit_exports" +) + +type MyPlugin struct { + api.DefaultPlugin +} + +func (p *MyPlugin) Metadata() api.Metadata { + return api.Metadata{ + Name: "my_go_plugin", + Version: "0.1.0", + Authors: []string{"Developer"}, + Description: "My first Pumpkin Go plugin", + } +} + +func (p *MyPlugin) OnLoad(ctx *pumpkin_plugin_context.Context) { + // 1. Register permission node + ctx.RegisterPermission(pumpkin_plugin_permission.Permission{ + Node: "my_go_plugin:hello", + Description: "Allows executing the /hello command", + Default: pumpkin_plugin_permission.PermissionDefaultAllow(), + }) + + // 2. Build and register command tree + cmd := pumpkin_plugin_command.New([]string{"hello", "hi"}, "Greets the player") + ctx.RegisterCommand(cmd, "my_go_plugin:hello") +} + +func (p *MyPlugin) HandleCommand(cmdID uint32, sender pumpkin_plugin_command.CommandSender) int32 { + sender.SendMessage("Hello from Go Plugin!") + return 1 +} + +func init() { + api.RegisterPlugin(&MyPlugin{}) +} + +func main() {} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Register Permission +Call `ctx.RegisterPermission` inside `OnLoad`: +```go +ctx.RegisterPermission(pumpkin_plugin_permission.Permission{ + Node: "my_go_plugin:hello", + Description: "Allows executing the /hello command", + Default: pumpkin_plugin_permission.PermissionDefaultAllow(), +}) +``` + +### Step 2: Build the Command Tree +Instantiate the command tree with `pumpkin_plugin_command.New`: +```go +cmd := pumpkin_plugin_command.New([]string{"hello", "hi"}, "Greets the player") +``` + +### Step 3: Register & Execute +Register the command with `ctx.RegisterCommand` and handle execution in `HandleCommand`: +```go +ctx.RegisterCommand(cmd, "my_go_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Follow the [Go Quick Start Guide](./quick-start) to set up TinyGo compilation. +- Learn more about [Basic Logic](./basic-logic). diff --git a/docs/de/plugin-dev/go/quick-start.md b/docs/de/plugin-dev/go/quick-start.md new file mode 100644 index 0000000..90bacb4 --- /dev/null +++ b/docs/de/plugin-dev/go/quick-start.md @@ -0,0 +1,35 @@ +# Creating a new project + +Pumpkin plugins use the [Go](https://go.dev/) programming language and are compiled to WebAssembly. + +## Prerequisites + +Before we can start, make sure you have the following installed: +- [Go](https://go.dev/doc/install) (latest version recommended) +- [TinyGo](https://tinygo.org/getting-started/install/) (required for WASM compilation) + +## Initializing a new module + +First, create a new directory for your project and initialize a Go module: + +```bash +mkdir hello-pumpkin +cd hello-pumpkin +go mod init github.com/yourname/hello-pumpkin +``` + +Next, add the Pumpkin Go API as a dependency: + +```bash +go get github.com/Pumpkin-MC/pumpkin-api-go +``` + +## Project Structure + +Your project should have at least a `main.go` file. The structure should look like this: + +```text +├── go.mod +├── go.sum +└── main.go +``` diff --git a/docs/de/plugin-dev/introduction.md b/docs/de/plugin-dev/introduction.md index 73b8053..15ecaf7 100644 --- a/docs/de/plugin-dev/introduction.md +++ b/docs/de/plugin-dev/introduction.md @@ -15,3 +15,5 @@ Die Pumpkin Plugin API orientiert sich in vielen Bereichen an der Spigot/Bukkit * [C# API](https://github.com/Pumpkin-MC/pumpkin-api-cs) * [Go API](https://github.com/Pumpkin-MC/pumpkin-api-go) * [C API](https://github.com/Pumpkin-MC/pumpkin-api-c) +* [Kotlin API](https://github.com/Pumpkin-MC/pumpkin-api-kt) +* [D API](https://github.com/Pumpkin-MC/pumpkin-api-d) diff --git a/docs/de/plugin-dev/kotlin/first-command.md b/docs/de/plugin-dev/kotlin/first-command.md new file mode 100644 index 0000000..12831f5 --- /dev/null +++ b/docs/de/plugin-dev/kotlin/first-command.md @@ -0,0 +1,94 @@ +# Creating Your First Command + +Registering custom commands in Pumpkin using Kotlin is concise, idiomatic, and strongly typed. + +--- + +## 1. Quick Example + +Here is a complete Kotlin plugin (`Plugin.kt`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```kotlin [src/wasmWasiMain/kotlin/plugin/Plugin.kt] +package plugin + +import pumpkin.plugin.context.Context +import pumpkin.plugin.command.Command +import pumpkin.plugin.permission.Permission +import pumpkin.plugin.permission.PermissionDefault + +class MyPlugin { + fun onLoad(ctx: Context) { + // 1. Register permission node + ctx.registerPermission( + Permission( + node = "my_kotlin_plugin:hello", + description = "Allows executing the /hello command", + default = PermissionDefault.ALLOW, + children = emptyList() + ) + ) + + // 2. Build and register command tree + val cmd = Command( + names = listOf("hello", "hi"), + description = "Greets the player" + ) + ctx.registerCommand(cmd, "my_kotlin_plugin:hello") + } + + fun handleCommand(sender: pumpkin.plugin.command.CommandSender): Int { + sender.sendMessage("Hello from Kotlin Plugin!") + return 1 + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Node +Construct a `Permission` object and pass it to `ctx.registerPermission`: +```kotlin +ctx.registerPermission( + Permission( + node = "my_kotlin_plugin:hello", + description = "Allows executing the /hello command", + default = PermissionDefault.ALLOW, + children = emptyList() + ) +) +``` + +### Step 2: Build the Command Tree +Instantiate `Command` with primary names and aliases: +```kotlin +val cmd = Command( + names = listOf("hello", "hi"), + description = "Greets the player" +) +``` + +### Step 3: Register with Context +Register the command with `ctx.registerCommand` and handle calls inside `handleCommand`: +```kotlin +ctx.registerCommand(cmd, "my_kotlin_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Follow the [Kotlin Quick Start Guide](./quick-start) to configure your `Makefile` and Kotlin/Wasm build tools. diff --git a/docs/de/plugin-dev/kotlin/quick-start.md b/docs/de/plugin-dev/kotlin/quick-start.md new file mode 100644 index 0000000..acefd9c --- /dev/null +++ b/docs/de/plugin-dev/kotlin/quick-start.md @@ -0,0 +1,81 @@ +# Quick Start + +This guide will help you get started with writing Pumpkin server plugins using Kotlin. + +:::warning +Until the Kotlin + Wasm component toolchain matures more, there will be some oddity and inconvenience. + +Bugs are also expected. +::: + +## Prerequisites + +Before you start, ensure you have the following installed: +- JDK 17 or later + - To run Gradle 9.4 +- [Rust](https://rust-lang.org/) + - This is required as a key component (wit-bindgen) is written and Rust must be built from a particular Kotlin-enabled fork. + - You only need a default Rust install for your host platform. NOT for any WebAssembly targets +- [wasm-tools](https://github.com/bytecodealliance/wasm-tools) + - To bundle the Wasm Kotlin produces into a component +- Make (e.g. [GNU Make](https://www.gnu.org/software/make/)) + - To run the convenience `Makefile`. You may opt to do without and perform the contained steps manually. + +## Setting up the project + +Unlike most of the other APIs packages available, [pumpkin-api-kt](https://github.com/Pumpkin-MC/pumpkin-api-kt) is a TEMPLATE, not e.g. a Maven package. + +To start off, clone the template (and rename it as you please): +```sh +git clone --recurse-submodules https://github.com/Pumpkin-MC/pumpkin-api-kt +mv pumpkin-api-kt my_kotlin_plugin +cd my_kotlin_plugin +``` + +Next, we'll want to update the `wit` submodule so we are using the latest version of the Pumpkin plugin API. + +```sh +cd wit +git pull origin master +cd .. +``` + +Finally, rename the Gradle project. Change the `rootProject.name` in `settings.gradle.kts`, AND the `PROJECT_NAME` in `Makefile`. They must both match. Whatever you name the project, is going to be the filename of the Wasm produced. + +## Creating your first plugin + +As part of the template, a basic plugin is implemented in `src/wasmWasiMain/kotlin/plugin/Plugin.kt`. + +Feel free to modify the metadata at the bottom. However, before changing anything else, it's recommended that you build the plugin (see next section) first so that the bindings are generated and IDE completion works for `pumpkin`. + +## Building the plugin + +To build your plugin into a WebAssembly component: + +```sh +make +``` + +The compiled `.wasm` file will be located in `build`. You can place this file in the `plugins` folder of your Pumpkin server. + +Note that doing this the first time might take awhile, while it builds `wit-bindgen` from Rust source. + +Running `make` will check for `wit-bindgen` updates each time. You may want to run instead `make componentify` after the initial setup to avoid this. + +## Troubleshooting + +### Linker errors +If you start getting errors like +``` +main ThreadId(01) pumpkin::plugin: Failed to load plugin from +"./plugins/my_plugin.wasm": Wasm plugin initialization error: plugin failed +to load with error: component imports instance 'pumpkin:plugin/gui@0.1.0', but +a matching implementation was not found in the linker +``` +or other "linker" errors when loading your plugin into Pumpkin, update the `wit` submodule. + +```sh +cd wit +git pull origin master +cd .. +``` diff --git a/docs/de/plugin-dev/migrating-from-bukkit/commands.md b/docs/de/plugin-dev/migrating-from-bukkit/commands.md new file mode 100644 index 0000000..ec9ddc7 --- /dev/null +++ b/docs/de/plugin-dev/migrating-from-bukkit/commands.md @@ -0,0 +1,171 @@ +# Befehle von Bukkit migrieren + +In Bukkit werden Befehle in der `plugin.yml` definiert und über `CommandExecutor`-Klassen oder die Brigadier-Bibliothek verarbeitet. + +In Pumpkin werden Befehle mithilfe eines **von Brigadier inspirierten Befehlsbaums** erstellt und programmatisch über `context.register_command` registriert. Es werden keine YAML-Konfigurationsdateien verwendet. + +--- + +## Wesentliche Unterschiede + +| Merkmal | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Deklaration** | In der `plugin.yml` unter `commands:` deklariert | Im Code über `Command::new(...)` erstellt | +| **Berechtigungen** | In `plugin.yml` verknüpft oder via `player.hasPermission()` geprüft | Expliziter Berechtigungsknoten bei `register_command()` gebunden | +| **Tab-Vervollständigung** | Manuelles String-Matching in `TabCompleter` | Automatische baumbasierte clientseitige Tab-Vervollständigung | +| **Unterbefehle (Subcommands)** | Manuelles `if (args[0].equalsIgnoreCase("..."))` | Verschachtelte `Command`-Baumknoten | + +--- + +## Code-Vergleich: Einfacher Befehl + +Vergleich der Registrierung eines `/feed`-Befehls in Bukkit vs. Pumpkin: + +### 1. Bukkit-Implementierung (Java) + +```java [FeedCommand.java] +// 1. Registered in plugin.yml: +// commands: +// feed: +// description: Feeds the player +// permission: myplugin.feed + +public class FeedCommand implements CommandExecutor { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("Only players can execute this command!"); + return true; + } + Player player = (Player) sender; + if (!player.hasPermission("myplugin.feed")) { + player.sendMessage("No permission!"); + return true; + } + player.setFoodLevel(20); + player.sendMessage("Your hunger has been satisfied!"); + return true; + } +} +``` + +### 2. Pumpkin-Implementierungen + +::: code-group + +```rust [Rust] +use pumpkin_plugin_api::{ + command::{CommandHandler, CommandSender, ConsumedArgs, CommandError, Command}, + permission::{Permission, PermissionDefault}, + Context, Plugin, PluginMetadata, Server, +}; + +struct FeedExecutor; + +impl CommandHandler for FeedExecutor { + fn handle(&self, sender: CommandSender, _server: Server, _args: ConsumedArgs) -> Result { + sender.send_message("Your hunger has been satisfied!"); + Ok(1) + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { MyPlugin } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "feed_plugin".into(), + version: "1.0.0".into(), + authors: vec!["Developer".into()], + description: "Feed command plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Register permission node (Replaces plugin.yml permission) + context.register_permission(&Permission { + node: "feed_plugin:feed".to_string(), + description: "Allows executing /feed".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), + })?; + + // 2. Build command tree + let command = Command::new(&["feed".to_string(), "eat".to_string()], "Feeds the player") + .execute(FeedExecutor); + + // 3. Register with context + context.register_command(command, "feed_plugin:feed")?; + + Ok(()) + } +} +``` + +```python [Python] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + permission, command, context, server +) + +class FeedExecutor: + def handle(self, sender: command.CommandSender, srv: server.Server, args: command.ConsumedArgs) -> int: + sender.send_message("Your hunger has been satisfied!") + return 1 + +class FeedPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata(name="feed_plugin", version="1.0.0", authors=["Dev"], description="Feed command") + + def on_load(self, ctx: context.Context) -> None: + ctx.register_permission(permission.Permission( + node="feed_plugin:feed", + description="Allows executing /feed", + default=permission.PermissionDefault.ALLOW, + children=[] + )) + + cmd = command.Command(["feed", "eat"], "Feeds the player") + cmd.execute(FeedExecutor()) + ctx.register_command(cmd, "feed_plugin:feed") + + register_plugin(FeedPlugin) +``` + +```kotlin [Kotlin] +package plugin + +import pumpkin.plugin.context.Context +import pumpkin.plugin.command.Command +import pumpkin.plugin.command.CommandSender +import pumpkin.plugin.permission.Permission +import pumpkin.plugin.permission.PermissionDefault + +class FeedPlugin { + fun onLoad(ctx: Context) { + ctx.registerPermission( + Permission( + node = "feed_plugin:feed", + description = "Allows executing /feed", + default = PermissionDefault.ALLOW, + children = emptyList() + ) + ) + + val cmd = Command( + names = listOf("feed", "eat"), + description = "Feeds the player" + ) + ctx.registerCommand(cmd, "feed_plugin:feed") + } + + fun handleCommand(sender: CommandSender): Int { + sender.sendMessage("Your hunger has been satisfied!") + return 1 + } +} +``` + +::: diff --git a/docs/de/plugin-dev/migrating-from-bukkit/configuration.md b/docs/de/plugin-dev/migrating-from-bukkit/configuration.md new file mode 100644 index 0000000..8fcc90c --- /dev/null +++ b/docs/de/plugin-dev/migrating-from-bukkit/configuration.md @@ -0,0 +1,69 @@ +# Konfiguration & Datenspeicherung von Bukkit migrieren + +Bukkit-Plugins stützen sich stark auf `config.yml`, `YamlConfiguration` und das Serialisierungssystem von Bukkit. + +In Pumpkin laufen WebAssembly-Plugins in einer sicheren Sandbox-Umgebung, in der Konfigurationsdateien nativ über Standardformate wie **TOML**, **JSON** oder **YAML** verarbeitet werden können. + +--- + +## Wesentliche Unterschiede + +| Merkmal | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Konfigurationsdateiformat** | `config.yml` (YAML-Format) | Flexibel: TOML, JSON oder eigene Dateien | +| **Generierung von Standard-Konfigurationen** | `saveDefaultConfig()` | Lesen der Konfiguration aus dem Plugin-Verzeichnis oder eingebetteten WASM-Assets | +| **Daten-Parsing** | `getConfig().getString(...)` | Native Deserialisierung über Serde / JSON / TOML | +| **Dateisystem-Sandboxing** | Beliebiger direkter Dateisystemzugriff | Dem WASM-Plugin zugewiesener, isolierter Verzeichniszugriff | + +--- + +## Code-Vergleich: Laden der Konfiguration + +### 1. Bukkit-Implementierung (Java) + +```java [ConfigExample.java] +public class ConfigExample extends JavaPlugin { + @Override + public void onEnable() { + saveDefaultConfig(); // Saves config.yml from jar resources + FileConfiguration config = getConfig(); + + String welcomeMsg = config.getString("welcome-message", "Default welcome!"); + int maxItems = config.getInt("max-items", 10); + getLogger().info("Loaded message: " + welcomeMsg); + } +} +``` + +### 2. Pumpkin-Implementierung (Rust) + +In Pumpkin kannst du `serde::Deserialize` ableiten, um saubere TOML-Konfigurationsdateien einzulesen: + +```rust [config.rs] +use serde::Deserialize; +use std::fs; + +#[derive(Deserialize, Debug)] +pub struct PluginConfig { + pub welcome_message: String, + pub max_items: i32, +} + +impl Default for PluginConfig { + fn default() -> Self { + Self { + welcome_message: "Default welcome!".to_string(), + max_items: 10, + } + } +} + +pub fn load_config() -> PluginConfig { + let config_path = "plugins/my_plugin/config.toml"; + if let Ok(content) = fs::read_to_string(config_path) { + toml::from_str(&content).unwrap_or_default() + } else { + PluginConfig::default() + } +} +``` diff --git a/docs/de/plugin-dev/migrating-from-bukkit/events.md b/docs/de/plugin-dev/migrating-from-bukkit/events.md new file mode 100644 index 0000000..ec6170b --- /dev/null +++ b/docs/de/plugin-dev/migrating-from-bukkit/events.md @@ -0,0 +1,117 @@ +# Events von Bukkit migrieren + +In Bukkit werden Events verarbeitet, indem Klassen erstellt werden, die `Listener` implementieren, und Methoden mit `@EventHandler` annotiert werden. + +In Pumpkin sind Event-Handler Strukturen (Structs) oder Objekte, die explizit über `context.register_event_handler(...)` registriert werden. Zudem unterscheidet Pumpkin zwischen **blockierenden** und **nicht-blockierenden** Events, um die Nebenläufigkeit (Concurrency) des Servers zu maximieren. + +--- + +## Wesentliche Unterschiede + +| Merkmal | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Listener-Deklaration** | `@EventHandler`-Annotation an Methoden | Implementierung des Traits / Interface `EventHandler` | +| **Registrierung** | `pm.registerEvents(listener, plugin)` | `context.register_event_handler(handler, priority, is_blocking)` | +| **Ausführungsmodell** | Ausführung auf dem Haupt-Thread (`EventPriority`) | Wahl zwischen **blockierend** (sequenziell/abbrechbar) und **nicht-blockierend** (nebenläufig) | +| **Abbrechen von Events** | `event.setCancelled(true)` | `event.cancel()` bei blockierenden Events | + +--- + +## Blockierende vs. Nicht-blockierende Events in Pumpkin + +Im Gegensatz zu Bukkit, wo alle Event-Handler sequenziell auf dem Haupt-Server-Thread laufen, kannst du in Pumpkin festlegen, ob ein Event-Handler **blockierend** ist: + +```rust +// Registrierungssignatur in Rust: +context.register_event_handler(handler, priority, is_blocking)?; +``` + +- **Blockierend (`is_blocking = true`)**: Wird in sequenzieller Prioritätsreihenfolge ausgeführt. Kann Event-Daten ändern (z. B. die Beitrittsnachricht bearbeiten) oder das Event abbrechen. +- **Nicht-blockierend (`is_blocking = false`)**: Wird nebenläufig über Worker-Threads hinweg ausgeführt. Ideal für Logging, Metriken oder externe Benachrichtigungen, bei denen kein Abbruch des Events erforderlich ist. + +--- + +## Code-Vergleich: Spieler-Beitritts-Event + +### 1. Bukkit-Implementierung (Java) + +```java [JoinListener.java] +public class JoinListener implements Listener { + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + event.setJoinMessage("Welcome " + player.getName() + " to the server!"); + } +} + +// In JavaPlugin: +// getServer().getPluginManager().registerEvents(new JoinListener(), this); +``` + +### 2. Pumpkin-Implementierungen + +::: code-group + +```rust [Rust] +use pumpkin_plugin_api::{ + Context, Plugin, PluginMetadata, Server, + events::{EventData, EventHandler, EventPriority, PlayerJoinEvent}, + text::TextComponent, +}; + +struct JoinHandler; + +impl EventHandler for JoinHandler { + fn handle<'a>( + &'a self, + _server: Server, + mut event: EventData, + ) -> EventData { + event.join_message = TextComponent::text("Welcome to the Pumpkin server!"); + event + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { MyPlugin } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "join_plugin".into(), + version: "1.0.0".into(), + authors: vec!["Developer".into()], + description: "Join event handler".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Registriere blockierenden Event-Handler mit Normal-Priorität + context.register_event_handler(JoinHandler, EventPriority::Normal, true)?; + Ok(()) + } +} +``` + +```python [Python] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + event, server, context +) + +class MyPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata(name="join_plugin", version="1.0.0", authors=["Dev"], description="Join event") + + def on_load(self, ctx: context.Context) -> None: + self.register_event(ctx, event.EventType.PLAYER_JOIN_EVENT, self.on_player_join) + + def on_player_join(self, srv: server.Server, evt: event.PlayerJoinEventData) -> event.PlayerJoinEventData: + print(f"Player joined!") + return evt + +register_plugin(MyPlugin) +``` + +::: diff --git a/docs/de/plugin-dev/migrating-from-bukkit/index.md b/docs/de/plugin-dev/migrating-from-bukkit/index.md new file mode 100644 index 0000000..2a96425 --- /dev/null +++ b/docs/de/plugin-dev/migrating-from-bukkit/index.md @@ -0,0 +1,27 @@ +# Migrationsübersicht & Architektur + +Der Wechsel von der Plugin-Entwicklung für **Bukkit / Spigot / Paper** zu Pumpkin erfordert den Übergang von einem Java-zentrierten, objektorientierten Modell hin zu einem kompilierten, mehrsprachigen **WebAssembly (WASM)**-Komponentenmodell. + +--- + +Die folgende Tabelle stellt das bewährte Bukkit-/Paper-Paradigma Pumpkins moderner WebAssembly-Architektur gegenüber: + +| Konzept | Bukkit / Spigot / Paper | Pumpkin | +| :--- | :--- | :--- | +| **Sprachunterstützung** | Java / Kotlin / Scala (JVM) | Rust, Python, Kotlin, C#, Go, C | +| **Binärausgabe** | `.jar` Java-Archiv | `.wasm` WebAssembly-Komponente | +| **Plugin-Deskriptor** | `plugin.yml`-Datei | Programmatische `PluginMetadata`-Struktur | +| **Lebenszyklus-Hooks** | `onEnable()` / `onDisable()` | `on_load(context)` / `on_unload(context)` | +| **Sicherheit & Isolation** | Uneingeschränkte JVM-Reflection | Isoliertes WASM-Fähigkeitenmodell (Capability Model) | +| **Nebenläufigkeit (Concurrency)** | Single-Thread-Tick-Loop (`BukkitScheduler`) | Multithreaded native Ausführung mit Async-Laufzeitumgebung | + +--- + +## Detaillierte Migrationsthemen + +Erkunde ausführliche Leitfäden zur Migration der einzelnen zentralen Plugin-Subsysteme: + +- [Befehle migrieren](./commands) — Übergang von `getCommand().setExecutor()` und `plugin.yml` zu Brigadier-Befehlsbäumen. +- [Events migrieren](./events) — Ersetzen von `@EventHandler` und `Listener`-Schnittstellen durch Pumpkins blockierendes bzw. nicht-blockierendes Event-System. +- [Inventare & GUIs migrieren](./inventories) — Wechsel von `Bukkit.createInventory()` zu Pumpkins Container- und Fenster-Handlern. +- [Konfiguration & Daten migrieren](./configuration) — Ersetzt `getConfig()` / `config.yml` durch natives TOML, JSON oder benutzerdefinierte Speicherlösungen. diff --git a/docs/de/plugin-dev/migrating-from-bukkit/inventories.md b/docs/de/plugin-dev/migrating-from-bukkit/inventories.md new file mode 100644 index 0000000..7012bff --- /dev/null +++ b/docs/de/plugin-dev/migrating-from-bukkit/inventories.md @@ -0,0 +1,72 @@ +# Inventare & GUIs von Bukkit migrieren + +Das Erstellen von Inventar-GUIs (wie Kisten-Menüs, Shop-Oberflächen oder benutzerdefinierten Spielerfenstern) ist eine alltägliche Aufgabe bei der Minecraft-Plugin-Entwicklung. + +In Bukkit werden benutzerdefinierte GUIs über `Bukkit.createInventory()` erstellt und via `InventoryClickEvent` verarbeitet. In Pumpkin werden Inventarfenster über **Window-APIs** und Container-Klickpakete verwaltet. + +--- + +## Wesentliche Unterschiede + +| Merkmal | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Inventar-Erstellung** | `Bukkit.createInventory(owner, size, title)` | Fenster-Container-Definitionen (`WindowType`) | +| **Öffnen von Fenstern** | `player.openInventory(inv)` | `player.open_window(window)` | +| **Klicks abfangen** | Verarbeitung via `InventoryClickEvent` | Callback-Handler für Fensterklicks | +| **Item-Stacking** | `ItemStack` mit `ItemMeta` | `ItemStack` mit Komponenten / NBT | + +--- + +## Code-Vergleich: Öffnen eines benutzerdefinierten GUI-Menüs + +### 1. Bukkit-Implementierung (Java) + +```java [MenuGUI.java] +public class MenuGUI implements Listener { + + public void openMenu(Player player) { + Inventory inv = Bukkit.createInventory(null, 9, Component.text("Custom Menu")); + + ItemStack item = new ItemStack(Material.DIAMOND); + ItemMeta meta = item.getItemMeta(); + meta.displayName(Component.text("Click Me!")); + item.setItemMeta(meta); + + inv.setItem(4, item); + player.openInventory(inv); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (event.getView().getTitle().equals("Custom Menu")) { + event.setCancelled(true); + if (event.getSlot() == 4) { + event.getWhoClicked().sendMessage("Diamond clicked!"); + } + } + } +} +``` + +### 2. Pumpkin-Implementierung (Rust) + +```rust [gui.rs] +use pumpkin_plugin_api::{ + player::Player, + item::ItemStack, + window::{Window, WindowType}, + text::TextComponent, +}; + +pub fn open_custom_menu(player: &Player) { + // 1. Erstelle ein Truhenfenster mit 9 Slots + let mut window = Window::new(WindowType::Generic9x1, TextComponent::text("Custom Menu")); + + // 2. Setze Gegenstand in Slot 4 + let item = ItemStack::new("minecraft:diamond", 1); + window.set_item(4, Some(item)); + + // 3. Öffne Fenster für den Spieler + player.open_window(window); +} +``` diff --git a/docs/de/plugin-dev/python/first-command.md b/docs/de/plugin-dev/python/first-command.md new file mode 100644 index 0000000..00058f4 --- /dev/null +++ b/docs/de/plugin-dev/python/first-command.md @@ -0,0 +1,95 @@ +# Creating Your First Command + +Registering and handling custom commands in Pumpkin with Python is designed to be straightforward and expressive. + +--- + +## 1. Quick Example + +Here is a complete, minimal Python plugin (`main.py`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```python [main.py] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + permission, command, context, server +) + +class HelloCommand: + def handle(self, sender: command.CommandSender, srv: server.Server, args: command.ConsumedArgs) -> int: + sender.send_message("Hello from Python Plugin!") + return 1 + +class MyPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata( + name="my_python_plugin", + version="0.1.0", + authors=["Developer"], + description="My first Pumpkin Python plugin" + ) + + def on_load(self, ctx: context.Context) -> None: + # 1. Register permission node + ctx.register_permission(permission.Permission( + node="my_python_plugin:hello", + description="Allows executing the /hello command", + default=permission.PermissionDefault.ALLOW, + children=[] + )) + + # 2. Build the command tree with aliases & executor + cmd = command.Command(["hello", "hi"], "Greets the player") + cmd.execute(HelloCommand()) + + # 3. Register the command + ctx.register_command(cmd, "my_python_plugin:hello") + +register_plugin(MyPlugin) +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission +Commands in Pumpkin require a permission node. Register it with `permission.PermissionDefault.ALLOW` to allow execution by default: +```python +ctx.register_permission(permission.Permission( + node="my_python_plugin:hello", + description="Allows executing the /hello command", + default=permission.PermissionDefault.ALLOW, + children=[] +)) +``` + +### Step 2: Build the Command Tree +Create a `command.Command` instance with primary names and aliases, then attach your handler with `.execute(...)`: +```python +cmd = command.Command(["hello", "hi"], "Greets the player") +cmd.execute(HelloCommand()) +``` + +### Step 3: Register with Context +Pass the command tree and permission node string to `ctx.register_command`: +```python +ctx.register_command(cmd, "my_python_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Check out [Events](./events) to handle player actions and server events in Python. +- Learn more about [Basic Logic](./basic-logic). diff --git a/docs/de/plugin-dev/rust/command/first-command.md b/docs/de/plugin-dev/rust/command/first-command.md new file mode 100644 index 0000000..9840346 --- /dev/null +++ b/docs/de/plugin-dev/rust/command/first-command.md @@ -0,0 +1,115 @@ +# Creating Your First Command + +Registering and handling custom commands in Pumpkin is designed to be developer-friendly, fast, and structured like Mojang's Brigadier system. + +--- + +## 1. Quick Example + +Here is a complete, minimal Rust plugin that registers a `/hello` command with permission checks and sends a response back to the player. + +```rust [src/lib.rs] +use pumpkin_plugin_api::{ + command::{CommandHandler, CommandSender, ConsumedArgs, CommandError, Command}, + permission::{Permission, PermissionDefault}, + Context, Plugin, PluginMetadata, Server, +}; + +struct HelloExecutor; + +impl CommandHandler for HelloExecutor { + fn handle( + &self, + sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + sender.send_message("Hello from Pumpkin!"); + Ok(1) + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { + MyPlugin + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "my_plugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Developer".into()], + description: "My first Pumpkin plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Register permission node + context.register_permission(&Permission { + node: "my_plugin:hello".to_string(), + description: "Allows executing the /hello command".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), + })?; + + // 2. Build the command tree with aliases & executor + let names = ["hello".to_string(), "hi".to_string()]; + let command = Command::new(&names, "Greets the player").execute(HelloExecutor); + + // 3. Register the command + context.register_command(command, "my_plugin:hello")?; + + Ok(()) + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission +Commands in Pumpkin require a permission node. Using `PermissionDefault::Allow` allows all players to execute it by default: +```rust +context.register_permission(&Permission { + node: "my_plugin:hello".to_string(), + description: "Allows executing the /hello command".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), +})?; +``` + +### Step 2: Build the Command Tree +Use `Command::new` to define primary names and aliases, then attach your `CommandHandler` struct with `.execute(...)`: +```rust +let names = ["hello".to_string(), "hi".to_string()]; +let command = Command::new(&names, "Greets the player") + .execute(HelloExecutor); +``` + +### Step 3: Register with Context +Pass the command tree and the permission node string to `context.register_command`: +```rust +context.register_command(command, "my_plugin:hello")?; +``` + +::: + +--- + +## Next Steps + +- Explore complex subcommands and argument parsing in the [Rock-Paper-Scissors Tutorial](./rock-paper-scissors). +- Learn how to handle inventory and GUI interactions in plugin logic. diff --git a/docs/de/plugin-dev/rust/command/inventories.md b/docs/de/plugin-dev/rust/command/inventories.md deleted file mode 100644 index b5c2269..0000000 --- a/docs/de/plugin-dev/rust/command/inventories.md +++ /dev/null @@ -1,347 +0,0 @@ -# Ein benutzerdefiniertes Inventar öffnen - -## Überblick - -Das Inventarsystem in Pumpkin bietet einen flexiblen Weg zur Verwaltung und Manipulation von Items. Dieses Dokument erklärt, wie du eigene Inventare erstellst und implementierst. - -## Inhaltsverzeichnis - -- [Ein benutzerdefiniertes Inventar öffnen](#ein-benutzerdefiniertes-inventar-öffnen) - - [Überblick](#überblick) - - [Inhaltsverzeichnis](#inhaltsverzeichnis) - - [Einfache Inventar-Implementierung](#einfache-inventar-implementierung) - - [Erforderliche Traits](#erforderliche-traits) - - [Inventory Trait](#inventory-trait) - - [Clearable Trait](#clearable-trait) - - [Screen Handler](#screen-handler) - - [Generische Screen Handler verwenden](#generische-screen-handler-verwenden) - - [Eigene Screen Handler erstellen](#eigene-screen-handler-erstellen) - - [Best Practices](#best-practices) - - [Beispiele](#beispiele) - - [Einfacher Inventar-Einsatz](#einfacher-inventar-einsatz) - -## Einfache Inventar-Implementierung - -Die Struktur `BasicInventory` bietet eine Standard‑Implementierung mit 27 Slots. So implementieren Sie Ihr eigenes Inventar: - -```rust -use pumpkin_world::{ - inventory::{ - split_stack, {Clearable, Inventory}, - }, - item::ItemStack, -}; - -#[derive(Debug)] -pub struct BasicInventory { - pub items: [Arc>; 27], -} -``` - -## Erforderliche Traits - -### Inventory Trait - -Das Trait `Inventory` definiert Kernfunktionalitäten die alle Inventare implementieren müssen: - -```rust -impl Inventory for BasicInventory { - // Get the total number of slots in the inventory - fn size(&self) -> usize { - self.items.len() - } - - // Check if the inventory is completely empty - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - for slot in self.items.iter() { - if !slot.lock().await.is_empty() { - return false; - } - } - - true - }) - } - - // Get a reference to an item stack in a specific slot - fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc>> { - Box::pin(async move { self.items[slot].clone() }) - } - - // Remove and return the entire stack from a slot - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut removed = ItemStack::EMPTY.clone(); - let mut guard = self.items[slot].lock().await; - std::mem::swap(&mut removed, &mut *guard); - removed - }) - } - - // Remove a specific amount of items from a stack - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { split_stack(&self.items, slot, amount).await }) - } - - // Set the contents of a specific slot - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - *self.items[slot].lock().await = stack; - }) - } -} -``` - -### Clearable Trait - -Das `Clearable`-Trait bietet die Möglichkeit, ein Inventar zu leeren. Dies muss für das `Inventory`-Trait implementiert werden: - -```rust -impl Clearable for YourInventory { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - for item in self.items.iter() { - *item.lock().await = ItemStack::EMPTY.clone(); - } - }) - } -} -``` - -## Screen Handler - -Bildschirmhandler dienen der Erstellung und Verwaltung der Benutzeroberfläche für Inventare. Sie definieren, wie Gegenstände zwischen Slots verschoben werden können und wie das Inventar mit dem Inventar des Spielers interagiert. - -### Generische Screen Handler verwenden - -Pumpkin bietet einen generischen Bildschirmhandler für gängige Inventarlayouts. So verwenden Sie ihn: - -```rust -use pumpkin_inventory::generic_container_screen_handler::create_generic_9x3; -use pumpkin_inventory::player::player_inventory::PlayerInventory; -use pumpkin_inventory::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerFactory}; - -struct MyScreenFactory { - inventory: Arc, -} - -impl ScreenHandlerFactory for MyScreenFactory { - fn create_screen_handler( - &self, - sync_id: u8, - player_inventory: &Arc, - _player: &dyn InventoryPlayer, - ) -> Option>> { - Some(Arc::new(Mutex::new(create_generic_9x3( - sync_id, - player_inventory, - self.inventory.clone(), - )))) - } - - fn get_display_name(&self) -> TextComponent { - TextComponent::translate("container.barrel", vec![]) - } -} - -// Erstellen Sie eine Factory für den Bildschirm -let factory = MyScreenFactory { - inventory: inventory.clone(), -}; - -// Öffne das Inventar eines Spieler -player.open_handled_screen(factory).await; - -// Der Bildschirm wird folgendes automatisch verarbeiten: -// - Item-Bewegungen zwischen Slots -// - Schnellbewegungsfunktion -// - Spieler Inventar Interaktion -// - Bildschirm schließen -``` - -### Eigene Screen Handler erstellen - -Du kannst benutzerdefinierte Bildschirmhandler erstellen, indem du das `ScreenHandler`-Trait implementierst. Das bietet mehr Flexibilität und ermöglicht es, Bildschirme für andere Zwecke als nur die Inventarverwaltung zu erstellen. - -Um einen benutzerdefinierten Bildschirmhandler zu erstellen, kannst du eine neue Struktur anlegen, die das `Slot`-Trait implementiert. `NormalSlot` ist ein Slot, der mit Pumpkin mitgeliefert wird und lediglich als Index für ein Inventar dient und keine Einschränkungen hat. - -TODO: Beispiel für einen benutzerdefinierten Slot - -Hier ist ein Beispiel für den Quellcode des generischen Bildschirmhandlers: - -```rust -use std::{any::Any, sync::Arc}; - - -use pumpkin_data::screen::WindowType; -use pumpkin_world::{inventory::Inventory, item::ItemStack}; - -use pumpkin_world::{ - player::player_inventory::PlayerInventory, - screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour}, - slot::NormalSlot, -}; - -pub fn create_generic_9x3( - sync_id: u8, - player_inventory: &Arc, - inventory: Arc, -) -> GenericContainerScreenHandler { - GenericContainerScreenHandler::new( - WindowType::Generic9x3, - sync_id, - player_inventory, - inventory, - 3, - ) -} - -pub struct GenericContainerScreenHandler { - pub inventory: Arc, - pub rows: u8, - behaviour: ScreenHandlerBehaviour, -} - -impl GenericContainerScreenHandler { - fn new( - screen_type: WindowType, - sync_id: u8, - player_inventory: &Arc, - inventory: Arc, - rows: u8, - ) -> Self { - let mut handler = Self { - inventory, - rows, - behaviour: ScreenHandlerBehaviour::new(sync_id, Some(screen_type)), - }; - - handler.add_inventory_slots(); - let player_inventory: Arc = player_inventory.clone(); - handler.add_player_slots(&player_inventory); - - handler - } - - fn add_inventory_slots(&mut self) { - for i in 0..self.rows { - for j in 0..9 { - self.add_slot(Arc::new(NormalSlot::new( - self.inventory.clone(), - (j + i * 9) as usize, - ))); - } - } - } -} - -impl ScreenHandler for GenericContainerScreenHandler { - fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.default_on_closed(player).await; - self.inventory.on_close().await; - }) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn get_behaviour(&self) -> &ScreenHandlerBehaviour { - &self.behaviour - } - - fn get_behaviour_mut(&mut self) -> &mut ScreenHandlerBehaviour { - &mut self.behaviour - } - - fn quick_move<'a>( - &'a mut self, - _player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ItemStackFuture<'a> { - Box::pin(async move { - let mut stack_left = ItemStack::EMPTY.clone(); - // Assuming bounds check passed for slot_index by caller or within quick_move spec - let slot = self.get_behaviour().slots[slot_index as usize].clone(); - - if slot.has_stack().await { - let slot_stack_lock = slot.get_stack().await; - let slot_stack_guard = slot_stack_lock.lock().await; - stack_left = slot_stack_guard.clone(); - // Release the guard before calling insert_item which needs its own lock - drop(slot_stack_guard); - - // Re-acquire lock for insert_item (which expects &mut ItemStack) - let mut slot_stack_mut = slot_stack_lock.lock().await; - - if slot_index < (self.rows * 9) as i32 { - // Move from inventory to player area (end) - if !self - .insert_item( - &mut slot_stack_mut, - (self.rows * 9).into(), - self.get_behaviour().slots.len() as i32, - true, - ) - .await - { - return ItemStack::EMPTY.clone(); - } - } else if !self - .insert_item(&mut slot_stack_mut, 0, (self.rows * 9).into(), false) - .await - { - // Move from player area to inventory (start) - return ItemStack::EMPTY.clone(); - } - - // Check the resulting state of the slot stack after insert_item - if slot_stack_mut.is_empty() { - drop(slot_stack_mut); // Release lock - slot.set_stack(ItemStack::EMPTY.clone()).await; - } else { - drop(slot_stack_mut); // Release lock - slot.mark_dirty().await; - } - } - - stack_left - }) - } -} -``` - -## Best Practices - -1. **Thread-Sicherheit** - - Implementiere eine korrekte Fehlerbehandlung für die Slot-Grenzenprüfung. - - Denk daran, Stack-Sperren vor der Verwendung von `inventory.set_stack()` oder `slot.get_cloned_stack()` aufzuheben, um Deadlocks zu vermeiden. - -2. **Inventarverwaltung** - - Verwende die Konstante `ItemStack::EMPTY` zum Leeren von Slots oder zum Initialisieren leerer Inventare. - -3. **Implementierung der Bildschirmbehandlung** - - Behandel das Schließen des Inventars korrekt, um den Verlust von Gegenständen zu verhindern. - - Stelle sicher, dass die Anzahl der Slots der Anzahl der Slots im jeweiligen Fenstertyp entspricht. - -## Beispiele - -### Grundlegende Inventarnutzung - -```rust -// Erstellt ein neues Inventar -let inventory = BasicInventory { - items: [(); 27].map(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), -}; - -// Fügt ein Item dem Slot hinzu -inventory.set_stack(0, ItemStack::new(1, &Items::OAK_LOG)).await; - -// Entfernt Items aus einem Slot -let removed = inventory.remove_stack(0).await; - -// Prüft ob das Inventar leer ist -let is_empty = inventory.is_empty().await; -``` diff --git a/docs/de/plugin-dev/rust/join-event.md b/docs/de/plugin-dev/rust/events.md similarity index 100% rename from docs/de/plugin-dev/rust/join-event.md rename to docs/de/plugin-dev/rust/events.md diff --git a/docs/de/plugin-dev/rust/introduction.md b/docs/de/plugin-dev/rust/introduction.md deleted file mode 100644 index 5d40115..0000000 --- a/docs/de/plugin-dev/rust/introduction.md +++ /dev/null @@ -1,11 +0,0 @@ -# Ein Beispiel‑Plugin schreiben - -Dieses Tutorial führt dich durch das Erstellen eines neuen Plugins. - -Du solltest etwas Erfahrung mit Rust und der Kommandozeile mitbringen. - -Inhalte dieses Tutorials: - -- Die Join‑Nachricht anpassen -- Einen Schere‑Stein‑Papier‑Befehl erstellen -- Plugin‑Daten im Plugin‑Ordner speichern (bald) diff --git a/docs/de/plugin-dev/rust/plugin-utils.md b/docs/de/plugin-dev/rust/plugin-utils.md new file mode 100644 index 0000000..3adfbbe --- /dev/null +++ b/docs/de/plugin-dev/rust/plugin-utils.md @@ -0,0 +1,348 @@ +# Licensing & Updates (`pumpkin-plugin-utils`) + +`pumpkin-plugin-utils` is an official utility crate for Pumpkin Rust plugins. It provides built-in tools for **licensing checks**, **cryptographic signature verification**, and **update checks** against the [Pumpkin Marketplace](https://market.pumpkinmc.org). + +## Features + +- **Offline Ed25519 Signature Verification**: Verifies plugin integrity and embedded marketplace metadata in `< 1ms` on startup without blocking the server. +- **Global Metadata Caching**: Call `init(&context)` once in your plugin's `on_load` lifecycle method to cache verified metadata globally. +- **Online License Verification**: Query the marketplace `/api/v1/rest/check-license` endpoint with automatic 7-day lease caching (`license_lease.json`). +- **Offline Grace Period**: Resilient offline lease evaluation ensures your plugin keeps working during marketplace maintenance or network outages. +- **Zero-Argument Update Checking**: Easily check if newer plugin releases are available on the marketplace. +- **Dynamic Public Key Resolution**: Resolves the marketplace public key dynamically via host WIT imports or HTTPS fallback without hardcoding keys. + +--- + +## Adding the Dependency + +Add `pumpkin-plugin-utils` alongside `pumpkin-plugin-api` in your plugin's `Cargo.toml`: + +:::code-group + +```toml [Cargo.toml] +[package] +name = "my-plugin" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +pumpkin-plugin-api = "0.1.0-dev+26.2-26.45" +pumpkin-plugin-utils = "0.1.0-dev+26.2-26.45" +tracing = "0.1" +``` + +::: + +--- + +## Initialization + +Before performing license or update checks, initialize the utility crate inside your plugin's `on_load` method using the plugin's `Context`: + +```rust +use pumpkin_plugin_api::{Context, Plugin, PluginMetadata}; +use tracing::{info, warn}; + +struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { + Self + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "MyPlugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Author".into()], + description: "Plugin with license and update checks".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Initialize pumpkin-plugin-utils (verifies signature & caches metadata) + let metadata = pumpkin_plugin_utils::init(&context) + .map_err(|e| format!("Initialization failed: {e}"))?; + + info!( + "Loaded plugin '{}' v{} (Dev: {})", + metadata.plugin_name, metadata.version, metadata.dev_name + ); + + Ok(()) + } +} +``` + +::: info What happens during `init`? +1. Locates the plugin's `.wasm` binary in the server directory. +2. Extracts the `pumpkin.metadata` and `wasm_signature` custom sections. +3. Cryptographically verifies the Ed25519 signature over the WASM binary and metadata. +4. Caches the verified `PumpkinMetadata` globally for subsequent calls. +::: + +--- + +## Checking for Updates + +`pumpkin-plugin-utils` provides both zero-argument automatic checking and custom manual checking. + +### 1. Automatic Update Check (Recommended) + +Once initialized, call `pumpkin_plugin_utils::check_for_updates()` without arguments. It automatically uses the cached plugin name, current version, and marketplace endpoint: + +```rust +use tracing::{info, warn}; + +fn check_updates() { + match pumpkin_plugin_utils::check_for_updates() { + Ok(update) => { + if update.update_available { + info!( + "A new update is available: {}!", + update.latest_version.as_deref().unwrap_or("unknown") + ); + } else { + info!("Plugin is up to date."); + } + } + Err(err) => { + warn!("Failed to check for updates: {err}"); + } + } +} +``` + +### 2. Manual Update Check (`UpdateChecker`) + +If you want to manually specify the plugin name, version, or a custom marketplace URL, use `UpdateChecker`: + +```rust +use pumpkin_plugin_utils::UpdateChecker; + +let checker = UpdateChecker::new(); +let response = checker.check_for_updates( + "my-plugin", + "1.0.0", + "https://market.pumpkinmc.org", +)?; + +if response.update_available { + println!("Update found: {:?}", response.latest_version); +} +``` + +### Update Response Structure + +The update check returns a `CheckUpdateResponse`: + +```rust +pub struct CheckUpdateResponse { + /// Whether a newer stable release exists on the marketplace. + pub update_available: bool, + /// The latest stable version string, if available. + pub latest_version: Option, +} +``` + +--- + +## Checking and Verifying Licenses + +`pumpkin-plugin-utils` supports online verification against the marketplace API as well as offline lease management with configurable grace periods. + +### 1. Online License Check + +To verify a license against the marketplace (`GET /api/v1/rest/check-license`): + +```rust +// Pass `None` to use the embedded license key from metadata +let response = pumpkin_plugin_utils::check_license_online(None) + .map_err(|e| format!("License verification failed: {e}"))?; + +if response.valid { + info!("License is valid (status: {})", response.status); +} else { + warn!("License is invalid: {}", response.status); + return Err(format!("Invalid license status: {}", response.status).into()); +} +``` + +#### Providing a Custom License Key Override +If your plugin allows server owners to configure a license key in a `config.toml` file, pass `Some(&key)`: + +```rust +let config_key = "LIC-CUSTOM-KEY-12345"; +let response = pumpkin_plugin_utils::check_license_online(Some(config_key))?; +``` + +::: tip 7-Day Lease Caching +Successful online checks automatically save a verified lease file (`license_lease.json`) into the plugin's data folder with a 7-day expiration. +::: + +### 2. Evaluating License with Grace Period + +To keep paid plugins functional when the server is offline or the marketplace is unreachable, use `evaluate_license`: + +```rust +use pumpkin_plugin_utils::LicenseStatus; +use tracing::{info, warn}; + +// Specify grace period duration in days (e.g., 7 days) +let grace_period_days = 7; + +match pumpkin_plugin_utils::evaluate_license(grace_period_days) { + LicenseStatus::Valid(meta) => { + info!("License valid for buyer (User ID: {})", meta.user_id); + } + LicenseStatus::GracePeriod { metadata, days_remaining, reason } => { + warn!( + "Running in offline grace period ({} days remaining). Reason: {}", + days_remaining, reason + ); + } + LicenseStatus::Invalid(reason) => { + return Err(format!("Plugin license invalid or tampered: {reason}").into()); + } + LicenseStatus::Unsigned => { + info!("Plugin is running in unsigned / development mode."); + } +} +``` + +### `LicenseStatus` Enum + +| Variant | Description | +| :--- | :--- | +| `LicenseStatus::Valid(PumpkinMetadata)` | License is valid, verified, or the plugin is free / open-source. | +| `LicenseStatus::GracePeriod { metadata, days_remaining, reason }` | Operating offline with a previously validated lease that is within the allowable grace period. | +| `LicenseStatus::Invalid(String)` | Signature is invalid, expired, revoked, or metadata was tampered with. | +| `LicenseStatus::Unsigned` | Binary does not contain marketplace metadata/signature (useful during local development). | + +--- + +## Accessing Plugin Metadata + +You can access the verified plugin metadata anywhere in your codebase using `get_metadata` or `metadata()`: + +```rust +if let Some(meta) = pumpkin_plugin_utils::get_metadata() { + println!("Plugin Name: {}", meta.plugin_name); + println!("Plugin Version: {}", meta.version); + println!("Is Paid: {}", meta.is_paid); + println!("License Key: {:?}", meta.license_key); + println!("Buyer User ID: {}", meta.user_id); + println!("Developer: {} (ID: {})", meta.dev_name, meta.dev_id); + println!("Marketplace URL: {}", meta.marketplace_url); + println!("Issued At: {}", meta.issued_at); +} +``` + +### `PumpkinMetadata` Fields + +| Field | Type | Description | +| :--- | :--- | :--- | +| `plugin_name` | `String` | Canonical plugin name on the marketplace. | +| `version` | `String` | SemVer version string of the plugin binary. | +| `plugin_id` | `i64` | Unique plugin identifier on the marketplace. | +| `dev_id` | `i64` | Developer account ID. | +| `dev_name` | `String` | Developer display name. | +| `is_paid` | `bool` | `true` if this is a premium/commercial plugin. | +| `user_id` | `i64` | Buyer / licensee user ID (`0` for free plugins). | +| `license_key` | `Option` | Unique license key issued to the licensee. | +| `marketplace_url` | `String` | Base marketplace URL (`https://market.pumpkinmc.org`). | +| `issued_at` | `String` | ISO-8601 timestamp when the binary/license was issued. | + +--- + +## Complete Example + +Here is a full plugin implementation combining initialization, online licensing checks, offline grace periods, and update checking: + +:::code-group + +```rust:line-numbers [src/lib.rs] +use pumpkin_plugin_api::{Context, Plugin, PluginMetadata}; +use pumpkin_plugin_utils::LicenseStatus; +use tracing::{error, info, warn}; + +struct CommercialPlugin; + +impl Plugin for CommercialPlugin { + fn new() -> Self { + Self + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "CommercialPlugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Developer".into()], + description: "A commercial Pumpkin plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Initialize plugin-utils (verifies Ed25519 signature & caches metadata) + let meta = pumpkin_plugin_utils::init(&context).map_err(|err| { + error!("Signature verification failed: {err}"); + format!("Security verification error: {err}") + })?; + + info!("Loaded {} v{}", meta.plugin_name, meta.version); + + // 2. Validate license (offline evaluation + online check) + match pumpkin_plugin_utils::evaluate_license(7) { + LicenseStatus::Valid(_) => { + info!("Local license status: Valid"); + } + LicenseStatus::GracePeriod { days_remaining, .. } => { + warn!("Local license in grace period: {days_remaining} days remaining"); + } + LicenseStatus::Unsigned => { + info!("Running in development mode (unsigned binary)"); + } + LicenseStatus::Invalid(reason) => { + error!("Invalid license: {reason}"); + return Err(format!("License verification failed: {reason}").into()); + } + } + + // 3. Online license check if paid + if meta.is_paid { + if let Ok(check) = pumpkin_plugin_utils::check_license_online(None) { + if !check.valid { + return Err(format!("Online license rejected: {}", check.status).into()); + } + info!("Online license check confirmed: {}", check.status); + } else { + warn!("Online license verification unreachable, falling back to local lease"); + } + } + + // 4. Check for updates + if let Ok(update) = pumpkin_plugin_utils::check_for_updates() { + if update.update_available { + info!( + "A new version is available on the marketplace: {}", + update.latest_version.as_deref().unwrap_or("unknown") + ); + } + } + + Ok(()) + } + + fn on_unload(&mut self, _context: Context) -> pumpkin_plugin_api::Result<()> { + info!("CommercialPlugin unloaded."); + Ok(()) + } +} + +pumpkin_plugin_api::register_plugin!(CommercialPlugin); +``` + +::: diff --git a/docs/de/troubleshooting/common_issues.md b/docs/de/troubleshooting/common_issues.md index 1cae33b..b8dda5b 100644 --- a/docs/de/troubleshooting/common_issues.md +++ b/docs/de/troubleshooting/common_issues.md @@ -7,3 +7,14 @@ **Ursache:** Das hängt mit der Authentifizierung zusammen, häufig mit der Einstellung `prevent_proxy_connections`. **Lösung:** Deaktiviere `prevent_proxy_connections` in `pumpkin.toml`. + +2. ## Verbindung über 0.0.0.0 schlägt fehl + + **Problem:** Spieler, die dem Server auf demselben PC beitreten möchten, können keine Verbindung herstellen (z. B. Fehler „Connection refused“), wenn sie `0.0.0.0` oder `0.0.0.0:25565` eingeben. + + **Ursache:** In `pumpkin.toml` weist `0.0.0.0` den Server an, auf allen lokalen Netzwerkschnittstellen zu lauschen. Es ist eine Bind-Adresse für den Server, aber keine gültige Zieladresse für den Minecraft-Client. + + **Lösung:** + - Wenn du vom **selben PC** aus beitrittst, verwende `localhost` oder `127.0.0.1`. + - Wenn du von einem **anderen Gerät im selben lokalen Netzwerk (LAN)** aus beitrittst, verwende die lokale IP-Adresse des Host-PCs (z. B. `192.168.x.x`). + - Wenn Spieler über das **Internet** beitreten sollen, müssen sie deine öffentliche IP-Adresse oder Domain verwenden (mit entsprechender Portweiterleitung im Router). diff --git a/docs/en/admin/migrating-from-bukkit.md b/docs/en/admin/migrating-from-bukkit.md index f799566..bed6b01 100644 --- a/docs/en/admin/migrating-from-bukkit.md +++ b/docs/en/admin/migrating-from-bukkit.md @@ -1,35 +1,45 @@ -# Migrating a Server from Bukkit / Spigot / Paper to Pumpkin +# Migrating from Bukkit / Spigot / Paper to Pumpkin -Switching your server environment from Paper/Spigot to Pumpkin brings massive performance gains, multithreaded tick execution, and reduced RAM usage. However, because Pumpkin is built in Rust rather than running on the Java Virtual Machine, there are fundamental architectural differences that server administrators need to know. +This guide covers architectural and configuration differences when transitioning a server from Bukkit, Spigot, or Paper to Pumpkin. --- -## 1. Major Server Differences At-a-Glance +## 1. Architectural Differences -| Feature / Aspect | Bukkit / Spigot / Paper | Pumpkin | +| Aspect | Bukkit / Spigot / Paper | Pumpkin | | :--- | :--- | :--- | -| **Runtime Environment** | Java Virtual Machine (JVM) | Native Binary (Rust) | -| **Main Thread Architecture** | Single-threaded tick loop with asynchronous offloading | Multithreaded tick execution across CPU cores | -| **Plugin Engine** | `.jar` files compiled for Java / Bukkit API | `.wasm` WebAssembly components running in sandboxed WASM runtime | -| **Plugin Languages** | Java / Kotlin / Scala | Rust, Python, Kotlin, C#, Go, C | -| **Configuration Format** | `server.properties` & `paper.yml` / `spigot.yml` | `pumpkin.toml` & structured TOML configuration files | -| **Memory Footprint** | High baseline (JVM garbage collection overhead) | Micro baseline (Native memory management) | +| **Runtime Environment** | Java Virtual Machine (requires Java 17/21+) | Native executable (compiled Rust, no Java required) | +| **Startup Command** | `java [flags] -jar server.jar` | `./pumpkin` | +| **Tick Loop** | Single-threaded tick loop with async worker offloading | Multithreaded tick execution across CPU cores | +| **Plugin Format** | Java bytecode (`.jar`) targeting Bukkit API | WebAssembly (`.wasm`), or `.jar` via PatchBukkit | +| **Plugin Languages** | Java, Kotlin, Scala | Rust, Python, Kotlin, C#, Go, C | +| **Configuration** | `server.properties`, `paper.yml`, `spigot.yml` | `pumpkin.toml` (TOML format) | +| **Memory Management** | JVM Garbage Collection | Native OS memory allocation | --- ## 2. Plugins & Extensions -- **Java `.jar` Plugins Cannot Run Directly**: Standard Bukkit/Spigot `.jar` plugins are bytecode compiled for the Java JVM and Bukkit API. They cannot be dropped into Pumpkin's `plugins/` directory. -- **WebAssembly Plugins**: Pumpkin uses WebAssembly (`.wasm`) for plugins. This sandboxes plugins for memory safety and security while allowing developers to write plugins in virtually any language. -- **Porting Plugins**: If you maintain custom internal plugins, consult our [Developer Migration Guide](../plugin-dev/migrating-from-bukkit/index) to convert Java logic to Pumpkin plugin bindings. +### WebAssembly Plugins +Pumpkin's primary plugin format is WebAssembly (`.wasm`): +- **Isolation**: Plugins run in a sandboxed WebAssembly runtime. +- **Language Support**: Plugins can be compiled to WASM from Rust, Python, Go, C#, C, or Kotlin. +- **Custom Plugins**: For porting custom Bukkit plugins to Pumpkin, see the [Developer Migration Guide](../plugin-dev/migrating-from-bukkit/index). + +### Bukkit Plugin Compatibility: PatchBukkit +For servers relying on existing Bukkit/Spigot `.jar` plugins, the **PatchBukkit** project provides a compatibility layer: +- **Architecture**: PatchBukkit embeds a JVM within Pumpkin and reimplements parts of the Bukkit API, bridging calls to Pumpkin via JNI and FFI. +- **Plugin Location**: Compatible `.jar` plugins are placed in `patchbukkit/patchbukkit-plugins/`. +- **Status & Limitations**: PatchBukkit is in active development. While standard Bukkit API calls are supported, plugins that depend on internal NMS (`net.minecraft.server`) classes, CraftBukkit internals, or bytecode manipulation may not function properly. +- **Java Requirement**: Running PatchBukkit requires a Java runtime (JRE/JDK) installed on the host system to run the embedded JVM. --- ## 3. Configuration & Properties -Instead of `server.properties` and YAML files, Pumpkin consolidates server configuration into clean TOML files: +Pumpkin uses TOML configuration files instead of `server.properties` and YAML: -| Paper / Spigot Setting | Pumpkin `pumpkin.toml` Equivalent | +| Paper / Spigot (`server.properties`) | Pumpkin (`pumpkin.toml`) | | :--- | :--- | | `server-port=25565` | `server_address = "0.0.0.0:25565"` | | `motd=...` | `motd = "A Pumpkin Server"` | @@ -42,31 +52,75 @@ Instead of `server.properties` and YAML files, Pumpkin consolidates server confi ## 4. World & Data Storage -- **Anvil Format Compatibility**: Pumpkin reads standard Minecraft chunk data format. -- **Dimension Directories**: Make sure `world`, `world_nether`, and `world_the_end` directory structures are mapped appropriately when copying existing worlds from Paper/Spigot server installations. +- **Format Compatibility**: Pumpkin reads standard Anvil chunk format (`.mca` files). +- **Directory Layout**: Ensure dimension directories (`world`, `world_nether`, `world_the_end`) match Pumpkin's expected layout when copying world folders from existing Paper/Spigot installations. +- **Latest World & Data Pack Format Required**: At least for now, Pumpkin only supports the latest world format and latest data pack format for its target Minecraft release. Older world formats or legacy data pack versions are not automatically converted at runtime. + - **Upgrading Worlds in the Client**: If migrating an older world, upgrade it first before loading it in Pumpkin. You can update a world directly in the official Minecraft client: open the client on the matching latest version, navigate to **Singleplayer**, select the world, click **Edit**, and choose **Optimize World**. Alternatively, you can run the world once on a vanilla server matching the latest version. + - **Data Packs**: Ensure all data packs located in `world/datapacks` are updated to the pack format expected by the target release. --- -## 5. Performance Tuning & Startup +## 5. Startup & JVM Flags -Unlike Paper servers which require complex JVM flags (`-XX:+UseG1GC`, `-Xms`, `-Xmx`), Pumpkin runs as a direct native binary: +### No Java Installation Needed +Pure Pumpkin is a self-contained native executable. You do not need a Java runtime (JRE/JDK) installed on your system or inside your container to run the server. -```bash -# Starting Pumpkin Server on Linux/macOS +### Startup Commands +Paper and Spigot servers often use JVM startup scripts with heap allocation and garbage collection flags (such as Aikar's Flags): + +:::code-group + +```bash [Paper (JVM)] +# Common Paper startup command with Aikar's Flags: +java -Xms10G -Xmx10G \ + -XX:+UseG1GC \ + -XX:+ParallelRefProcEnabled \ + -XX:MaxGCPauseMillis=200 \ + -XX:+UnlockExperimentalVMOptions \ + -XX:+DisableExplicitGC \ + -XX:+AlwaysPreTouch \ + -XX:G1NewSizePercent=30 \ + -XX:G1MaxNewSizePercent=40 \ + -XX:G1HeapRegionSize=8M \ + -XX:G1ReservePercent=20 \ + -XX:G1HeapWastePercent=5 \ + -XX:G1MixedGCCountTarget=4 \ + -XX:InitiatingHeapOccupancyPercent=15 \ + -XX:G1MixedGCLiveThresholdPercent=90 \ + -XX:G1RSetUpdatingPauseTimePercent=5 \ + -XX:SurvivorRatio=32 \ + -XX:+PerfDisableSharedMem \ + -XX:MaxTenuringThreshold=1 \ + -Dusing.aikars.flags=https://mcflags.emc.gs \ + -Daikars.new.flags=true \ + -jar paper.jar --nogui +``` + +```bash [Pumpkin (Native)] +# Linux / macOS: ./pumpkin -# Or running via Docker +# Windows: +./pumpkin.exe + +# Docker: docker run -p 25565:25565 -v ./data:/data pumpkinmc/pumpkin:latest ``` -No garbage collection tuning or JVM heap allocation tuning is needed. +::: + +### Memory Differences +- **No Garbage Collector**: Pumpkin is written in Rust and uses deterministic compile-time memory management (RAII). There is no runtime garbage collector or GC pause tuning. +- **No Fixed Heap Allocation**: Flags like `-Xms` and `-Xmx` do not exist in native binaries. Memory is allocated from the OS dynamically as chunks and entities load, and returned when they unload. +- **JVM Flags Do Not Apply**: Flags tuning garbage collection strategies, generational sizing, or JVM internals are not needed and cannot be passed to the binary. --- -## Admin Checklist for Migration +## Admin Checklist -1. [ ] Back up your existing Paper/Spigot server data and world files. -2. [ ] Map `server.properties` values into Pumpkin's `pumpkin.toml`. -3. [ ] Check if replacement WebAssembly plugins (`.wasm`) are available for your essential server utilities. -4. [ ] Configure Proxy support (`Velocity` / `BungeeCord`) if running behind a server network. -5. [ ] Launch `./pumpkin` and verify player connectivity and performance logs. +1. [ ] Back up existing server data and world files, and ensure the world and data packs are updated to the latest format (e.g., via the client's "Optimize World"). +2. [ ] Map `server.properties` settings to `pumpkin.toml`. +3. [ ] Identify required plugins and check for WebAssembly (`.wasm`) equivalents or test with [PatchBukkit](#bukkit-plugin-compatibility-patchbukkit). +4. [ ] Configure proxy settings (`Velocity` / `BungeeCord`) in `pumpkin.toml` if using a proxy network. +5. [ ] Update startup scripts to launch `./pumpkin` directly without JVM arguments. +6. [ ] Start the server and check connectivity and logs. diff --git a/docs/en/developer/commands/adding-commands.md b/docs/en/developer/commands/adding-commands.md index 1094675..eda739a 100644 --- a/docs/en/developer/commands/adding-commands.md +++ b/docs/en/developer/commands/adding-commands.md @@ -2,7 +2,7 @@ This guide explains how to implement and register a new command in the Pumpkin server codebase. -Commands in Pumpkin are implemented in the `crates/pumpkin/src/command/commands/` directory and registered into the central [`CommandDispatcher`](file:///home/alex/Documents/Development/Rust/Pumpkin/crates/pumpkin-command/src/node/dispatcher.rs) during server initialization. +Commands in Pumpkin are implemented in the `crates/pumpkin/src/command/commands/` directory and registered into the central [`CommandDispatcher`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/dispatcher.rs) during server initialization. --- @@ -10,8 +10,8 @@ Commands in Pumpkin are implemented in the `crates/pumpkin/src/command/commands/ A typical command file contains: 1. **Metadata Constants**: Command description, permission node identifier, and argument names. -2. **Permission Registration**: Declaring default OP requirements in the server's [`PermissionRegistry`](file:///home/alex/Documents/Development/Rust/Pumpkin/crates/pumpkin-util/src/permission.rs). -3. **Command Executors**: Structs implementing the [`CommandExecutor`](file:///home/alex/Documents/Development/Rust/Pumpkin/crates/pumpkin-command/src/node/mod.rs) trait for each terminal node or subcommand. +2. **Permission Registration**: Declaring default OP requirements in the server's [`PermissionRegistry`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-util/src/permission.rs). +3. **Command Executors**: Structs implementing the [`CommandExecutor`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/mod.rs) trait for each terminal node or subcommand. 4. **Registration Function**: Constructing the argument tree using fluent builders (`command`, `literal`, `argument`). --- @@ -60,7 +60,7 @@ const ERROR_NOT_PLAYER: CommandErrorType<0> = CommandErrorType::new( ### 2. Implement the Executors -Command execution logic is encapsulated in structs implementing [`CommandExecutor`](file:///home/alex/Documents/Development/Rust/Pumpkin/crates/pumpkin-command/src/node/mod.rs): +Command execution logic is encapsulated in structs implementing [`CommandExecutor`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/mod.rs): ```rust /// Heals the executing player to full health. diff --git a/docs/en/developer/commands/arguments-and-suggestions.md b/docs/en/developer/commands/arguments-and-suggestions.md index 1976c3f..6794ae1 100644 --- a/docs/en/developer/commands/arguments-and-suggestions.md +++ b/docs/en/developer/commands/arguments-and-suggestions.md @@ -6,7 +6,7 @@ Pumpkin provides a comprehensive suite of type-safe command arguments through `p ## Standard Argument Types -Argument parsers implement the [`ArgumentType`](file:///home/alex/Documents/Development/Rust/Pumpkin/crates/pumpkin-command/src/argument_types/argument_type.rs) trait. The following built-in types are available for command builders: +Argument parsers implement the [`ArgumentType`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/argument_types/argument_type.rs) trait. The following built-in types are available for command builders: ### 1. Primitive & Scalar Types @@ -43,7 +43,7 @@ argument("location", Vec3ArgumentType) ``` ### Retrieving Parsed Coordinates -Inside an executor, coordinate arguments resolve relative offsets against the sender's current position and orientation in the [`CommandSource`](file:///home/alex/Documents/Development/Rust/Pumpkin/crates/pumpkin/src/command/context/command_source.rs): +Inside an executor, coordinate arguments resolve relative offsets against the sender's current position and orientation in the [`CommandSource`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/command/context/command_source.rs): ```rust // Extracts centered f64 coordinates (handling ~ and ^ relative to sender) @@ -59,7 +59,7 @@ let block_pos: BlockPos = BlockPosArgumentType::get_block_pos(context, "pos")?; Entity selectors allow players and command blocks to target entities using player names, UUIDs, or vanilla target selectors (`@p`, `@r`, `@a`, `@e`, `@s`). -In Pumpkin, entity selectors are accessed via [`EntityArgumentType`](file:///home/alex/Documents/Development/Rust/Pumpkin/crates/pumpkin/src/command/argument_types/entity.rs): +In Pumpkin, entity selectors are accessed via [`EntityArgumentType`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/command/argument_types/entity.rs): ```rust pub enum EntityArgumentType { @@ -115,7 +115,7 @@ Pumpkin provides specialized argument parsers for Minecraft data types: ## Suggestion Providers & Tab Completion -Arguments can supply dynamic tab-completion candidates to connected players using the [`SuggestionProvider`](file:///home/alex/Documents/Development/Rust/Pumpkin/crates/pumpkin-command/src/suggestion/mod.rs) trait: +Arguments can supply dynamic tab-completion candidates to connected players using the [`SuggestionProvider`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/suggestion/mod.rs) trait: ```rust use crate::command::context::command_context::CommandContext; diff --git a/docs/en/developer/porting-versions.md b/docs/en/developer/porting-versions.md index 6b50ba0..0a5014a 100644 --- a/docs/en/developer/porting-versions.md +++ b/docs/en/developer/porting-versions.md @@ -110,7 +110,7 @@ For complex changes—such as worldgen noise router formulas and custom block co Pumpkin supports multi-version clients through automated protocol remapping powered by **ViaBackwards** / **ViaRewind** mapping tables. ### 1. Declare Version in `pumpkin-util` -Open `crates/pumpkin-util/src/version.rs` and add the new version to [`JavaMinecraftVersion`](file:///home/alex/Documents/Development/Rust/Pumpkin/crates/pumpkin-util/src/version.rs): +Open `crates/pumpkin-util/src/version.rs` and add the new version to [`JavaMinecraftVersion`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-util/src/version.rs): ```rust #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] diff --git a/docs/en/plugin-dev/d/quick-start.md b/docs/en/plugin-dev/d/quick-start.md new file mode 100644 index 0000000..6aba691 --- /dev/null +++ b/docs/en/plugin-dev/d/quick-start.md @@ -0,0 +1,135 @@ +# Quick Start + +This guide will help you get started with writing Pumpkin server plugins using the [D programming language](https://dlang.org/). + +D plugins for Pumpkin compile to WebAssembly (Wasm) components using the official [`pumpkin-api-d`](https://github.com/Pumpkin-MC/pumpkin-api-d) bindings. + +--- + +## Prerequisites + +Before building D plugins for Pumpkin, ensure you have the following installed: + +- **[LDC](https://github.com/ldc-developers/ldc)**: Version 1.43 or later (LLVM-based D compiler). +- **`addon-wasi`**: The WASI target package for LDC. +- **[DUB](https://dub.pm/)**: The D package and build tool (usually included with LDC). + +--- + +## 1. Setting Up the Project + +Create a new directory for your plugin project and add a `dub.json` configuration file: + +```bash +mkdir my-d-plugin +cd my-d-plugin +``` + +Create `dub.json` in the project root: + +```json [dub.json] +{ + "name": "my-d-plugin", + "description": "A minimal Pumpkin plugin built with D.", + "license": "proprietary", + "authors": ["You"], + "copyright": "Copyright © 2026, You", + + "dependencies": { + "pumpkin-api-d": "~>0.1.0" + }, + + "targetType": "executable", + "dflags": ["-Xcc=-mexec-model=reactor"], + "buildTypes": { + "debug": { + "buildOptions": ["debugMode", "debugInfo"] + }, + "release": { + "buildOptions": ["optimize", "inline"], + "dflags": ["-L-S"] + } + } +} +``` + +> [!NOTE] +> The flag `-Xcc=-mexec-model=reactor` is required by the WebAssembly linker so that the plugin is treated as a reactor (a component exposing entry points) rather than a command-line program with an exit point. + +--- + +## 2. Writing the Plugin + +Create a `source` directory and place your code in `source/app.d`: + +```d [source/app.d] +module plugin; + +import wit.pumpkin.plugin.plugin; +import wit.common; +import pumpkin.register; + +struct MyPlugin { + @GetMetadata PluginMetadata getMetadata() + { + immutable(WitString)[$] authors = [ + "You".witList + ]; + + return (immutable PluginMetadata( + name: "my-d-plugin".witList, + version_: "0.1.0".witList, + authors: authors.witList, + description: "An example plugin in D.".witList, + dependencies: WitList!WitString(), + permissions: WitList!WitString() + )).witClone; + } + + @OnLoad + Result!(void, WitString) onLoad(Context ctx) + { + scope (exit) + ctx.witDrop; // Incoming resources must be cleaned up after use + + log(Level.info, "Hello from D plugin!".witList); + return ok!WitString; + } +} + +mixin RegisterPlugin!MyPlugin; +``` + +### Key Elements: +- **`@GetMetadata`**: Defines plugin information (name, version, authors, description, permissions) requested by Pumpkin when loading the component. +- **`@OnLoad`**: Entry point invoked when Pumpkin initializes the plugin. Ensure incoming context resources (`ctx`) are freed with `ctx.witDrop`. +- **`mixin RegisterPlugin!MyPlugin;`**: Generates the necessary WebAssembly component exports and boilerplate required by Pumpkin's WASM loader. + +--- + +## 3. Building the Plugin + +Compile your plugin into a WebAssembly component targeting `wasm32-wasip2`: + +```bash +dub build -b release -a wasm32-wasip2 +``` + +This generates `my-d-plugin.wasm` in your project root. + +--- + +## 4. Running Your Plugin + +1. Copy the compiled `.wasm` file to your server's `plugins/` directory: + ```bash + cp my-d-plugin.wasm /path/to/pumpkin/plugins/ + ``` +2. Start or restart Pumpkin: + ```bash + ./pumpkin + ``` +3. Check the server logs to verify your plugin loaded: + ```text + [INFO] Hello from D plugin! + ``` diff --git a/docs/en/plugin-dev/introduction.md b/docs/en/plugin-dev/introduction.md index 918de96..dfa95d4 100644 --- a/docs/en/plugin-dev/introduction.md +++ b/docs/en/plugin-dev/introduction.md @@ -6,7 +6,7 @@ If you run into any issues please reach out on [our Discord server](https://disc ::: The Pumpkin Plugin API takes inspiration from the Spigot/Bukkit plugin API in many places, -so if you have previous experience with these and have experience with Rust, Python, C#, Go, C, or Kotlin development, +so if you have previous experience with these and have experience with Rust, Python, C#, Go, C, Kotlin, or D development, you should have a pretty easy time writing plugins for Pumpkin. :smile: * [Rust API](https://github.com/Pumpkin-MC/Pumpkin) @@ -15,3 +15,4 @@ you should have a pretty easy time writing plugins for Pumpkin. :smile: * [Go API](https://github.com/Pumpkin-MC/pumpkin-api-go) * [C API](https://github.com/Pumpkin-MC/pumpkin-api-c) * [Kotlin API](https://github.com/Pumpkin-MC/pumpkin-api-kt) +* [D API](https://github.com/Pumpkin-MC/pumpkin-api-d) diff --git a/docs/en/troubleshooting/common_issues.md b/docs/en/troubleshooting/common_issues.md index 81be6ce..973c4e9 100644 --- a/docs/en/troubleshooting/common_issues.md +++ b/docs/en/troubleshooting/common_issues.md @@ -7,3 +7,14 @@ **Cause:** This has to do with authentication, and usually with the `prevent_proxy_connections` setting. **Fix:** Disable `prevent_proxy_connections` under `[networking.java.authentication]` in `pumpkin.toml` + +2. ## Cannot connect using 0.0.0.0 + + **Issue:** Players attempting to join a server running on their own machine cannot connect (e.g., encountering a "Connection refused" error) when entering `0.0.0.0` or `0.0.0.0:25565`. + + **Cause:** In `pumpkin.toml`, `0.0.0.0` is a wildcard listener address that tells Pumpkin to bind and listen on all local network interfaces. It is not a valid destination address for Minecraft clients to connect to. + + **Fix:** + - When connecting from the **same machine**, enter `localhost` or `127.0.0.1` in the Minecraft client. + - When connecting from **another device on the same local network (LAN)**, use the server host's local IP address (e.g., `192.168.x.x`). + - When connecting over the **internet**, other players must connect using your public IP address or domain name (with port `25565` forwarded on your router). diff --git a/docs/ja_jp/admin/introduction.md b/docs/ja_jp/admin/introduction.md new file mode 100644 index 0000000..b955243 --- /dev/null +++ b/docs/ja_jp/admin/introduction.md @@ -0,0 +1,25 @@ +# サーバー管理の概要 + +Pumpkin サーバー管理者のための**管理者セクション**へようこそ。Pumpkin は Rust で書かれた高性能マルチスレッド Minecraft サーバーであり、最小限のリソース消費で膨大なプレイヤー数をサポートできるように設計されています。 + +--- + +## サーバー管理者向けの主な機能 + +- **高度なマルチスレッド化**: 最新のマルチコアプロセッサ向けにゼロから設計されています。 +- **WASM プラグインアーキテクチャ**: JVM のオーバーヘッドなしで、Rust、Python、Kotlin、C#、Go、C による安全でサンドボックス化されたプラグイン実行が可能です。 +- **ネイティブなクロスプロキシサポート**: Velocity、BungeeCord、最新のプロキシ転送を標準でサポートしています。 +- **TOML ベースの設定**: `pumpkin.toml` および機能ごとの TOML 設定ファイルによる、クリーンで人間が読みやすい設定です。 + +--- + +## 管理ガイドとトピック + +以下の管理ガイドをご覧ください: + +- [Bukkit / Paper / Spigot サーバーからの移行](./migrating-from-bukkit) — サーバー管理、プラグイン、ワールド保存、パフォーマンスに関する主な相違点。 +- [サーバー設定](../config/introduction) — `pumpkin.toml` 設定の詳細な解説。 +- [プロキシの設定](../config/proxy) — BungeeCord および Velocity プレイヤー転送の設定。 +- [コマンドと権限](../config/commands) — ゲーム内のオペレーターコマンドと権限の管理。 +- [認証](../config/authentication) — オンラインモードとオフラインモード、Yggdrasil の設定。 +- [トラブルシューティングと一般的な問題](../troubleshooting/common_issues) — ポートバインド、メモリ、プラグイン読み込み問題の解決。 diff --git a/docs/ja_jp/admin/migrating-from-bukkit.md b/docs/ja_jp/admin/migrating-from-bukkit.md new file mode 100644 index 0000000..3bc0e1e --- /dev/null +++ b/docs/ja_jp/admin/migrating-from-bukkit.md @@ -0,0 +1,126 @@ +# Bukkit / Spigot / Paper から Pumpkin への移行 + +このガイドでは、サーバーを Bukkit、Spigot、または Paper から Pumpkin に移行する際のアーキテクチャおよび設定の違いについて説明します。 + +--- + +## 1. アーキテクチャの違い + +| 項目 | Bukkit / Spigot / Paper | Pumpkin | +| :--- | :--- | :--- | +| **実行環境** | Java 仮想マシン (Java 17/21+ が必要) | ネイティブ実行ファイル (Rust コンパイル済み、Java 不要) | +| **起動コマンド** | `java [flags] -jar server.jar` | `./pumpkin` | +| **ティックループ** | 非同期タスクオフロードを伴うシングルスレッド | CPU コア全体にわたるマルチスレッド実行 | +| **プラグイン形式** | Bukkit API 向け Java バイトコード (`.jar`) | WebAssembly (`.wasm`)、または PatchBukkit 経由の `.jar` | +| **プラグイン言語** | Java, Kotlin, Scala | Rust, Python, Kotlin, C#, Go, C | +| **設定ファイル** | `server.properties`, `paper.yml`, `spigot.yml` | `pumpkin.toml` (TOML 形式) | +| **メモリ管理** | JVM ガベージコレクション (GC) | OS ネイティブメモリ割り当て | + +--- + +## 2. プラグインと拡張機能 + +### WebAssembly プラグイン +Pumpkin の主要なプラグイン形式は WebAssembly (`.wasm`) です: +- **分離性**: プラグインはサンドボックス化された WebAssembly ランタイム内で実行されます。 +- **言語サポート**: Rust、Python、Go、C#、C、または Kotlin から WASM へコンパイルできます。 +- **カスタムプラグイン**: 独自の Bukkit プラグインを移植する場合は、[開発者向け移行ガイド](../plugin-dev/migrating-from-bukkit/index)を参照してください。 + +### Bukkit プラグインの互換性: PatchBukkit +既存の Bukkit/Spigot `.jar` プラグインを必要とするサーバー向けに、**PatchBukkit** プロジェクトが互換レイヤーを提供しています: +- **アーキテクチャ**: PatchBukkit は Pumpkin 内に JVM を組み込み、Bukkit API の一部を再実装して、JNI および FFI 経由で Pumpkin に仲介します。 +- **プラグインの配置**: 互換性のある `.jar` プラグインは `patchbukkit/patchbukkit-plugins/` に配置します。 +- **状況と制限**: PatchBukkit は開発中です。標準的な Bukkit API 呼び出しはサポートされていますが、内部の NMS (`net.minecraft.server`) クラス、CraftBukkit リフレクション、またはバイトコード操作に依存するプラグインは正常に動作しない場合があります。 +- **Java の必要性**: PatchBukkit を実行するには、組み込み JVM を実行するためにホスト環境に Java ランタイム (JRE/JDK) が必要です。 + +--- + +## 3. 設定とプロパティ + +Pumpkin では `server.properties` や YAML の代わりに TOML 設定ファイルを使用します: + +| Paper / Spigot (`server.properties`) | Pumpkin (`pumpkin.toml`) | +| :--- | :--- | +| `server-port=25565` | `server_address = "0.0.0.0:25565"` | +| `motd=...` | `motd = "A Pumpkin Server"` | +| `max-players=20` | `max_players = 20` | +| `online-mode=true` | `online_mode = true` | +| `view-distance=10` | `view_distance = 10` | +| `simulation-distance=8` | `simulation_distance = 8` | + +--- + +## 4. ワールドとデータストレージ + +- **形式の互換性**: Pumpkin は標準的な Anvil チャンク形式 (`.mca` ファイル) を読み込みます。 +- **ディレクトリ配置**: 既存の Paper/Spigot からワールドをコピーする際は、ディメンションディレクトリ (`world`, `world_nether`, `world_the_end`) が Pumpkin の想定する配置と一致していることを確認してください。 +- **最新のワールドおよびデータパック形式が必要**: 少なくとも現時点では、Pumpkin は対象の Minecraft バージョンに対応する最新のワールド形式およびデータパック形式のみをサポートしています。古いワールド形式や以前のデータパックバージョンを起動時に自動変換する機能はありません。 + - **クライアントでのワールド更新**: 古いバージョンで生成されたワールドを移行する場合は、Pumpkin に読み込む前に更新してください。公式の Minecraft クライアントで直接ワールドをアップグレードできます。対応する最新バージョンのクライアントを起動し、**シングルプレイ**から対象ワールドを選択して**編集** -> **ワールドを最適化**を実行します。または、最新バージョンのバニラサーバーで一度ワールドを起動して保存することでも更新可能です。 + - **データパック**: `world/datapacks` 内のすべてのデータパックが、対象バージョンで要求されるパック形式に更新されていることを確認してください。 + +--- + +## 5. 起動と JVM フラグ + +### Java のインストールは不要 +純粋な Pumpkin は単体の自己完結型ネイティブ実行ファイルです。サーバーを実行するために OS やコンテナ内に Java ランタイム (JRE/JDK) をインストールする必要はありません。 + +### 起動コマンドの比較 +Paper や Spigot サーバーでは通常、ヒープ割り当てやガベージコレクション設定を含む JVM 起動スクリプト (Aikar's Flags など) を使用します: + +:::code-group + +```bash [Paper (JVM)] +# Aikar's Flags を用いた典型的な Paper 起動コマンド: +java -Xms10G -Xmx10G \ + -XX:+UseG1GC \ + -XX:+ParallelRefProcEnabled \ + -XX:MaxGCPauseMillis=200 \ + -XX:+UnlockExperimentalVMOptions \ + -XX:+DisableExplicitGC \ + -XX:+AlwaysPreTouch \ + -XX:G1NewSizePercent=30 \ + -XX:G1MaxNewSizePercent=40 \ + -XX:G1HeapRegionSize=8M \ + -XX:G1ReservePercent=20 \ + -XX:G1HeapWastePercent=5 \ + -XX:G1MixedGCCountTarget=4 \ + -XX:InitiatingHeapOccupancyPercent=15 \ + -XX:G1MixedGCLiveThresholdPercent=90 \ + -XX:G1RSetUpdatingPauseTimePercent=5 \ + -XX:SurvivorRatio=32 \ + -XX:+PerfDisableSharedMem \ + -XX:MaxTenuringThreshold=1 \ + -Dusing.aikars.flags=https://mcflags.emc.gs \ + -Daikars.new.flags=true \ + -jar paper.jar --nogui +``` + +```bash [Pumpkin (ネイティブ)] +# Linux / macOS: +./pumpkin + +# Windows: +./pumpkin.exe + +# Docker: +docker run -p 25565:25565 -v ./data:/data pumpkinmc/pumpkin:latest +``` + +::: + +### メモリモデルの違い +- **ガベージコレクターなし**: Pumpkin は Rust で記述されており、コンパイル時の決定論的なメモリ管理 (RAII) を使用します。ランタイムガベージコレクターはなく、GC 一時停止の調整もありません。 +- **固定ヒープ割り当てなし**: ネイティブバイナリには `-Xms` や `-Xmx` などのフラグは存在しません。メモリはチャンクやエンティティのロードに応じて動的に確保され、アンロード時に解放されます。 +- **JVM フラグは適用不可**: ガベージコレクション戦略、世代サイズ、または JVM 内部パラメータを調整するフラグは不要であり、バイナリに渡すことはできません。 + +--- + +## 管理者チェックリスト + +1. [ ] 既存のサーバーデータとワールドファイルのバックアップを作成し、ワールドとデータパックが最新形式に更新されていることを確認する (クライアントの「ワールドを最適化」など)。 +2. [ ] `server.properties` の設定を `pumpkin.toml` に反映する。 +3. [ ] 必要なプラグインを確認し、WebAssembly (`.wasm`) の代替を探すか、[PatchBukkit](#bukkit-プラグインの互換性patchbukkit) でテストする。 +4. [ ] プロキシネットワークを使用する場合は、`pumpkin.toml` でプロキシ設定 (`Velocity` / `BungeeCord`) を行う。 +5. [ ] 起動スクリプトを更新し、JVM 引数なしで `./pumpkin` を直接実行するように変更する。 +6. [ ] サーバーを起動し、接続性とログを確認する。 diff --git a/docs/ja_jp/config/authentication.md b/docs/ja_jp/config/authentication.md new file mode 100644 index 0000000..a37c7d7 --- /dev/null +++ b/docs/ja_jp/config/authentication.md @@ -0,0 +1,83 @@ +# Authentication + +Pumpkin verifies accounts with Mojang's session servers to ensure players use legitimate accounts. Authentication settings are configured under `[networking.java.authentication]` and `[networking.bedrock.authentication]` in `pumpkin.toml`. + +## Java Edition Authentication + +:::code-group + +```toml [pumpkin.toml] +[networking.java.authentication] +enabled = true +fallbacks = [] +profile_by_name_fallbacks = [] +profile_by_uuid_fallbacks = [] +connect_timeout = 5000 +read_timeout = 5000 +prevent_proxy_connections = false + +[networking.java.authentication.player_profile] +allow_banned_players = false +allowed_actions = ["FORCED_NAME_CHANGE", "USING_BANNED_SKIN"] + +[networking.java.authentication.textures] +enabled = true +allowed_url_schemes = ["http", "https"] +allowed_url_domains = [".minecraft.net", ".mojang.com"] + +[networking.java.authentication.textures.types] +skin = true +cape = true +elytra = true +``` + +::: + +### Configuration Options + +- **`enabled`**: Enables online authentication for Java clients (default: `true`). +- **`url`**: (Optional) Custom authentication URL to verify join requests (e.g., custom auth servers like Drasl). Supports `{username}` and `{server_hash}` parameters. +- **`fallbacks`**: List of fallback authentication URLs contacted if the primary/official session server is unreachable. +- **`profile_by_name_url`**: (Optional) Custom URL template to fetch player profiles by username (`{username}`). +- **`profile_by_name_fallbacks`**: Fallback URLs for looking up player profiles by username. +- **`profile_by_uuid_url`**: (Optional) Custom URL template to fetch player profiles by UUID (`{uuid}`). +- **`profile_by_uuid_fallbacks`**: Fallback URLs for looking up player profiles by UUID. +- **`connect_timeout`**: Connection timeout in milliseconds when contacting authentication servers (default: `5000`). +- **`read_timeout`**: Read timeout in milliseconds when contacting authentication servers (default: `5000`). +- **`prevent_proxy_connections`**: Whether to block connections coming from proxies or VPNs during Mojang authentication (default: `false`). +- **`prevent_proxy_connection_auth_url`**: (Optional) Custom authentication endpoint used specifically when proxy prevention is enabled. +- **`services_url`**: (Optional) Public Minecraft services URL (used for player certificates and keys). + +### Player Profile Settings + +- **`allow_banned_players`**: Whether to allow players flagged or banned by Mojang to connect (default: `false`). +- **`allowed_actions`**: Profile actions permitted when flagged players connect (e.g. `["FORCED_NAME_CHANGE", "USING_BANNED_SKIN"]`). + +### Textures Settings + +- **`enabled`**: Enables fetching and validating player skins, capes, and elytra textures (default: `true`). +- **`allowed_url_schemes`**: URL schemes permitted for texture downloads (default: `["http", "https"]`). +- **`allowed_url_domains`**: Domains permitted for texture downloads (default: `[".minecraft.net", ".mojang.com"]`). +- **`types.skin`**: Enable custom player skins (default: `true`). +- **`types.cape`**: Enable player capes (default: `true`). +- **`types.elytra`**: Enable player elytra textures (default: `true`). + +## Bedrock Edition Authentication + +:::code-group + +```toml [pumpkin.toml] +[networking.bedrock.authentication] +enabled = true +connect_timeout = 5000 +read_timeout = 5000 +``` + +::: + +### Configuration Options + +- **`enabled`**: Whether Xbox Live authentication is required for Bedrock clients (default: `true`). +- **`url`**: (Optional) Custom authentication or discovery endpoint URL. +- **`connect_timeout`**: Connection timeout in milliseconds (default: `5000`). +- **`read_timeout`**: Read timeout in milliseconds (default: `5000`). diff --git a/docs/ja_jp/config/basic.md b/docs/ja_jp/config/basic.md new file mode 100644 index 0000000..fc251d9 --- /dev/null +++ b/docs/ja_jp/config/basic.md @@ -0,0 +1,253 @@ +# 基本設定 + +Pumpkin は単一の `pumpkin.toml` ファイルを使用してサーバーのすべての側面を設定します。以下は、利用可能なすべてのセクションとデフォルト値を含む設定ファイルの完全な構造です。 + +## 完全なデフォルトの `pumpkin.toml` + +```toml +seed = "1790155153199293024" +default_difficulty = "Normal" +op_permission_level = 4 +allow_nether = true +allow_end = true +hardcore = false +tps = 20.0 +default_gamemode = "Survival" +force_gamemode = false +scrub_ips = true +use_favicon = true +default_level_name = "world" +allow_chat_reports = false +white_list = false +enforce_whitelist = false + +[logging] +enabled = true +level = "info" +threads = false +thread_ids = false +target = false +color = true +timestamp = true +timestamp_format = "[hour]:[minute]:[second]" +file = "latest.log" + +[resource_pack.java] +enabled = false +url = "" +sha1 = "" +prompt_message = "" +force = false + +[resource_pack.bedrock] +enabled = false +force = false +packs = [] + +[world] +lighting = "default" +autosave_ticks = 6000 + +[world.chunk] +type = "anvil" +write_in_place = false + +[world.chunk.compression] +algorithm = "LZ4" +level = 6 + +[networking.query] +enabled = false +address = "0.0.0.0:25565" + +[networking.rcon] +enabled = false +address = "0.0.0.0:25575" +password = "" +max_connections = 10 + +[networking.rcon.logging] +logged_successfully = true +wrong_password = true +commands = true +quit = true + +[networking.proxy] +enabled = false + +[networking.proxy.velocity] +enabled = false +secret = "" + +[networking.proxy.bungeecord] +enabled = false +secret = "" + +[networking.lan_broadcast] +enabled = false + +[networking.java] +enabled = true +address = "0.0.0.0:25565" +encryption = true +online_mode = true +max_players = 1000 +view_distance = 16 +simulation_distance = 10 +keep_alive_time = 15 +motd = "A blazingly fast Pumpkin server!" + +[networking.java.compression] +enabled = true +threshold = 256 +level = 4 + +[networking.java.authentication] +enabled = true +fallbacks = [] +profile_by_name_fallbacks = [] +profile_by_uuid_fallbacks = [] +connect_timeout = 5000 +read_timeout = 5000 +prevent_proxy_connections = false + +[networking.java.authentication.player_profile] +allow_banned_players = false +allowed_actions = ["FORCED_NAME_CHANGE", "USING_BANNED_SKIN"] + +[networking.java.authentication.textures] +enabled = true +allowed_url_schemes = ["http", "https"] +allowed_url_domains = [".minecraft.net", ".mojang.com"] + +[networking.java.authentication.textures.types] +skin = true +cape = true +elytra = true + +[networking.java.packet_limiter] +enabled = true +max_packet_rate = 500.0 +burst_capacity = 500.0 +kick_message = "Kicked for spamming packets" + +[networking.bedrock] +enabled = true +online_mode = true +max_players = 1000 +view_distance = 16 +simulation_distance = 10 +motd = "A blazingly fast Pumpkin server!" +username_prefix = "" +replace_username_spaces = true +chunk_caching = true + +[networking.bedrock.compression] +enabled = true +threshold = 256 +level = 4 + +[networking.bedrock.authentication] +enabled = true +connect_timeout = 5000 +read_timeout = 5000 + +[networking.bedrock.nethernet] +enabled = true +address = "0.0.0.0:19132" +identity_key = "nethernet-key.der" +stun_servers = [] + +[networking.bedrock.packet_limiter] +enabled = true +max_packet_rate = 500.0 +burst_capacity = 500.0 +kick_message = "Kicked for spamming packets" + +[commands] +use_console = true +use_tty = true +log_console = true +broadcast_console_to_ops = true +default_op_level = 0 + +[commands.overrides] + +[chat] +format = "<{DISPLAYNAME}> {MESSAGE}" + +[chat.anti_spam] +enabled = true +spam_threshold = 200 +message_cost = 20 +decay_per_tick = 1 +ops_bypass = true + +[pvp] +enabled = true +hurt_animation = true +protect_creative = true +knockback = true +swing = true + +[server_links] +enabled = true +bug_report = "https://github.com/Pumpkin-MC/Pumpkin/issues" +support = "" +status = "" +feedback = "" +community = "" +website = "" +forums = "" +news = "" +announcements = "" + +[server_links.custom] + +[player_data] +save_player_data = true +save_player_cron_interval = 300 + +[fun] +april_fools = true + +[recipe] +send_recipes = true + +[plugins] +enabled = true +hot_reload = false +ask_permission_confirmation = true +allow_unsigned = true +allowed_permissions = [] +blocked_permissions = [] +inherit_env = false +loopback_only = false +verify_signatures = true + +[plugins.overrides] + +[advancement] +save_advancements = true +``` + +## 設定項目の詳細 + +### トップレベル設定 + +- **`seed`**: ワールド生成用のシード値文字列。 +- **`default_difficulty`**: デフォルトの難易度 (`"Peaceful"`, `"Easy"`, `"Normal"`, `"Hard"`)。 +- **`op_permission_level`**: オペレーターに割り当てられるデフォルトの権限レベル (1〜4)。 +- **`allow_nether`**: ネザーディメンションを有効にするかどうか。 +- **`allow_end`**: ジ・エンドディメンションを有効にするかどうか。 +- **`hardcore`**: ハードコアモードを有効化 (サバイバルでリスポーン不可)。 +- **`tps`**: 目標 TPS (ticks per second、デフォルト: `20.0`)。 +- **`default_gamemode`**: デフォルトのゲームモード (`"Survival"`, `"Creative"`, `"Adventure"`, `"Spectator"`)。 +- **`force_gamemode`**: 参加時にプレイヤーへデフォルトのゲームモードを強制するかどうか。 +- **`scrub_ips`**: ログ内のプレイヤー IP アドレスを匿名化するかどうか。 +- **`use_favicon`**: サーバーアイコン (`icon.png`) を有効にするかどうか。 +- **`favicon_path`**: カスタムサーバーファビコンアイコンへの任意のファイルパス。 +- **`default_level_name`**: メインワールドディレクトリの名前 (デフォルト: `"world"`)。 +- **`allow_chat_reports`**: 署名付きチャット通報を許可するかどうか。 +- **`white_list`**: サーバーホワイトリストを有効にするかどうか。 +- **`enforce_whitelist`**: ホワイトリストが有効化された際に、ホワイトリストに登録されていないプレイヤーをキックするかどうか。 diff --git a/docs/ja_jp/config/bedrock.md b/docs/ja_jp/config/bedrock.md new file mode 100644 index 0000000..078534a --- /dev/null +++ b/docs/ja_jp/config/bedrock.md @@ -0,0 +1,50 @@ +# Bedrock と NetherNet + +Pumpkin は、NetherNet WebRTC/ICE トランスポートやユーザー名のカスタマイズを含め、Minecraft 統合版 (Bedrock Edition) クライアントをネイティブでサポートしています。 + +## 設定 + +:::code-group + +```toml [pumpkin.toml] +[networking.bedrock] +enabled = true +online_mode = true +max_players = 1000 +view_distance = 16 +simulation_distance = 10 +motd = "A blazingly fast Pumpkin server!" +username_prefix = "" +replace_username_spaces = true +chunk_caching = true + +[networking.bedrock.nethernet] +enabled = true +address = "0.0.0.0:19132" +identity_key = "nethernet-key.der" +stun_servers = [] +``` + +::: + +### Bedrock オプション + +- **`enabled`**: 統合版 (Bedrock Edition) クライアントからの接続を受け入れるかどうか (デフォルト: `true`)。 +- **`online_mode`**: Bedrock プレイヤーに対して Xbox Live 認証を強制するかどうか (デフォルト: `true`)。 +- **`max_players`**: 同時接続可能な Bedrock プレイヤーの最大数 (`0` で無制限、デフォルト: `1000`)。 +- **`view_distance`**: Bedrock クライアントに送信される最大チャンク描画距離 (デフォルト: `16`)。 +- **`simulation_distance`**: Bedrock プレイヤーの最大シミュレーション距離 (デフォルト: `10`)。 +- **`motd`**: Bedrock サーバーリストに表示される MOTD (Message of the Day)。 +- **`username_prefix`**: Bedrock プレイヤーのゲーマータグの先頭に付加する任意のプレフィックス (例: `"."` または `"*"` など)。クロスプレイサーバーでの Java 版アカウントとの名前衝突を防止します (デフォルト: `""`)。 +- **`replace_username_spaces`**: Bedrock ゲーマータグ内のスペースをアンダースコア `_` に自動置換するかどうか。Minecraft のスラッシュコマンドでプレイヤー名を指定できるようにします (デフォルト: `true`)。 +- **`chunk_caching`**: ネットワーク帯域を節約するために、クライアント側のチャンク blob キャッシュを有効にするかどうか (デフォルト: `true`)。 + +### NetherNet トランスポート設定 + +NetherNet は、Minecraft Bedrock の最新の WebRTC/ICE ネットワーキングトランスポートです: + +- **`enabled`**: クライアントが NetherNet を使用して接続できるかどうか (デフォルト: `true`)。 +- **`address`**: TCP シグナリングおよび UDP ICE 多重化用のネットワークソケットアドレス (デフォルト: `"0.0.0.0:19132"`)。 +- **`external_ip`**: サーバーが NAT 配下でホストされている場合に通知する任意のパブリック IP アドレス。 +- **`identity_key`**: クライアントの TOFU (Trust On First Use) のために再起動間でも保持される PKCS#8 P-384 秘密 ID キーファイルへのパス (デフォルト: `"nethernet-key.der"`)。 +- **`stun_servers`**: ICE NAT トラバーサル用の STUN サーバー URL の任意のリスト。 diff --git a/docs/ja_jp/config/chat.md b/docs/ja_jp/config/chat.md new file mode 100644 index 0000000..569a923 --- /dev/null +++ b/docs/ja_jp/config/chat.md @@ -0,0 +1,39 @@ +# チャットとスパム対策 + +Pumpkin では、`pumpkin.toml` 内の `[chat]` セクションで、ゲーム内チャットのフォーマットのカスタマイズや組み込みのスパム対策機能を提供しています。 + +## 設定 + +:::code-group + +```toml [pumpkin.toml] +[chat] +format = "<{DISPLAYNAME}> {MESSAGE}" + +[chat.anti_spam] +enabled = true +spam_threshold = 200 +message_cost = 20 +decay_per_tick = 1 +ops_bypass = true +``` + +::: + +### チャット設定 + +- **`format`**: チャットメッセージのフォーマット文字列 (デフォルト: `"<{DISPLAYNAME}> {MESSAGE}"`)。 + - `{DISPLAYNAME}`: プレイヤーの表示名。 + - `{MESSAGE}`: チャットメッセージの本文。 + > [!NOTE] + > 安全なチャット通報機能 (`allow_chat_reports = true`) が有効になっている場合、カスタムチャットフォーマットは適用されません。 + +### スパム対策 (Anti-Spam) + +Pumpkin はリーキーバケット (leaky-bucket) 方式のスパムカウンターを実装しており、スパム行為を行うプレイヤーや bot の大量接続からサーバーを保護します: + +- **`enabled`**: スパム対策を有効にするかどうか (デフォルト: `true`)。 +- **`spam_threshold`**: プレイヤーがキックされるスパムカウンターのしきい値 (単位: ティック、デフォルト: `200` ティック)。 +- **`message_cost`**: チャットメッセージまたはコマンドが送信されるたびにプレイヤーのスパムカウンターに追加される量 (デフォルト: `20` ティック)。 +- **`decay_per_tick`**: サーバーの 1 ティックごとにプレイヤーのスパムカウンターから減少する量 (デフォルト: `1` ティック)。 +- **`ops_bypass`**: サーバーオペレーターがスパムチェックをバイパスできるかどうか (デフォルト: `true`)。 diff --git a/docs/ja_jp/config/commands.md b/docs/ja_jp/config/commands.md new file mode 100644 index 0000000..0836a1f --- /dev/null +++ b/docs/ja_jp/config/commands.md @@ -0,0 +1,48 @@ +# コマンド + +Pumpkin は Minecraft コマンドをサポートしており、`pumpkin.toml` 内でコンソール、TTY、権限の挙動を設定できます。 + +## 設定 + +:::code-group + +```toml [pumpkin.toml] +[commands] +use_console = true +use_tty = true +log_console = true +broadcast_console_to_ops = true +default_op_level = 0 + +# 任意のコマンドごとのオーバーライド +[commands.overrides.gamemode] +permission_level = 4 + +[commands.overrides.tp] +enabled = false +``` + +::: + +### 設定オプション + +- **`use_console`**: コンソールからのコマンド入力を受け付けるかどうか。 +- **`use_tty`**: 対話型コンソール入力のために TTY サポート (rustyline) を有効にするかどうか。 +- **`log_console`**: プレイヤーが実行したコマンドをコンソールに記録するかどうか。 +- **`broadcast_console_to_ops`**: コンソールおよび RCON のコマンド出力をオンラインのオペレーターにブロードキャストするかどうか (バニラの `broadcast-console-to-ops` と同等)。 +- **`default_op_level`**: 非オペレーターに割り当てられる OP 権限レベル (0〜4、デフォルト: `0`)。 + +## コマンドオーバーライド + +`[commands.overrides.]` を使用して、個々のコマンドの権限をカスタマイズしたり、完全に無効化したりできます。キーには先頭のスラッシュを除いたコマンド名を指定します (例: `gamemode`, `tp`)。 + +### オーバーライドオプション + +- **`enabled`**: コマンドが有効かどうか (デフォルト: `true`)。`false` に設定すると、コマンドは完全に非表示になります。実行できず、`/help` にも表示されず、タブ補完にも現れません。実行を試みたプレイヤーには不明なコマンドのメッセージが表示されます。 +- **`permission_level`**: コマンドの実行に必要な最小権限レベル: + - `0`: 誰でも使用可能。 + - `2`: 通常のオペレーター (一般的なチートコマンド、例: `/gamemode`)。 + - `3`: 管理者 (モデレーションコマンド、例: `/kick`, `/ban`)。 + - `4`: サーバーオーナー (サーバー全体の管理、例: `/stop`, `/op`)。 + +コマンドのデフォルトの権限レベルを維持する場合は、`permission_level` を省略してください。 diff --git a/docs/ja_jp/config/compression.md b/docs/ja_jp/config/compression.md new file mode 100644 index 0000000..021caab --- /dev/null +++ b/docs/ja_jp/config/compression.md @@ -0,0 +1,54 @@ +# Compression + +Packet compression reduces bandwidth usage for both Java and Bedrock clients. In `pumpkin.toml`, compression is configured independently for Java and Bedrock networking, as well as world chunk compression. + +## Network Compression + +### Java Edition + +:::code-group + +```toml [pumpkin.toml] +[networking.java.compression] +enabled = true +threshold = 256 +level = 4 +``` + +::: + +### Bedrock Edition + +:::code-group + +```toml [pumpkin.toml] +[networking.bedrock.compression] +enabled = true +threshold = 256 +level = 4 +``` + +::: + +### Configuration Options + +- **`enabled`**: Enables network packet compression. +- **`threshold`**: Minimum packet payload size (in bytes) required before triggering compression. +- **`level`**: Compression level (0 to 9, where higher values trade CPU time for smaller packet sizes). + +## World Chunk Compression + +Chunk compression settings control how stored world chunk data is compressed on disk. + +:::code-group + +```toml [pumpkin.toml] +[world.chunk.compression] +algorithm = "LZ4" +level = 6 +``` + +::: + +- **`algorithm`**: Compression algorithm used for chunk data. Supported options are `"LZ4"`, `"ZLib"`, `"GZip"`, and `"Custom"` (default: `"LZ4"`). +- **`level`**: Compression level for chunk data (default: `6`). diff --git a/docs/ja_jp/config/introduction.md b/docs/ja_jp/config/introduction.md new file mode 100644 index 0000000..18b7f93 --- /dev/null +++ b/docs/ja_jp/config/introduction.md @@ -0,0 +1,24 @@ +### 設定 + +Pumpkin は強力な設定システムを備えており、外部プラグインに依存することなくサーバーの動作の様々な側面をカスタマイズできます。これにより、サーバー運用の柔軟性と制御性が高まります。 + +### 設定ファイル + +Pumpkin の設定は単一の `pumpkin.toml` ファイルに保存されます。 + +- `pumpkin.toml`: すべてのサーバー設定、機能、ネットワーキング、動作を一箇所で制御します。 + +Pumpkin を起動する前に `pumpkin.toml` を作成することも、初回起動後に編集することもできます。 + +### サーバーバージョン + +Pumpkin は最新の Minecraft バージョンをサポートすることを目指しています。他のバージョン向けに Pumpkin サーバーをホストしたい場合は、[ViaProxy](https://github.com/ViaVersion/ViaProxy) というプロジェクトがあります。 + +- プロキシ接続を許可するように設定してください。 +- Pumpkin と ViaProxy には直接の関係はありません。彼らのコードに関する issue を Pumpkin に送信しないでください。さらに、これはサードパーティ製のプロキシであり、Pumpkin はその動作や結果について一切の責任を負いません。 + +#### 主な特徴 + +- 広範なカスタマイズ: サーバー設定、プレイヤーの挙動、ワールド生成などを設定可能。 +- パフォーマンスの最適化: 設定の調整によるサーバーパフォーマンスの最適化。 +- プラグイン不要のカスタマイズ: 追加のプラグインを必要とせず、望み通りの変更を実現。 diff --git a/docs/ja_jp/config/lan-broadcast.md b/docs/ja_jp/config/lan-broadcast.md new file mode 100644 index 0000000..41e0a8f --- /dev/null +++ b/docs/ja_jp/config/lan-broadcast.md @@ -0,0 +1,22 @@ +# LAN Broadcast + +Pumpkin can broadcast server announcements on the local network so LAN clients can easily discover and join the server. + +## Configuration + +:::code-group + +```toml [pumpkin.toml] +[networking.lan_broadcast] +enabled = false +# motd = "My Custom LAN Server" +# port = 25565 +``` + +::: + +### Configuration Options + +- **`enabled`**: Enables or disables broadcasting server presence on the local network (default: `false`). +- **`motd`**: (Optional) Custom one-line Message of the Day shown in LAN client discovery. If omitted, defaults to the server's Java MOTD with newlines removed. +- **`port`**: (Optional) Port advertised in the LAN broadcast. Useful for port forwarding or predictable ports in containerized environments like Docker. diff --git a/docs/ja_jp/config/logging.md b/docs/ja_jp/config/logging.md new file mode 100644 index 0000000..e706f48 --- /dev/null +++ b/docs/ja_jp/config/logging.md @@ -0,0 +1,34 @@ +# Logging + +Pumpkin provides customizable logging options in `pumpkin.toml`. + +## Configuration + +:::code-group + +```toml [pumpkin.toml] +[logging] +enabled = true +level = "info" +threads = false +thread_ids = false +target = false +color = true +timestamp = true +timestamp_format = "[hour]:[minute]:[second]" +file = "latest.log" +``` + +::: + +### Configuration Options + +- **`enabled`**: Master switch to enable or disable server logging (default: `true`). +- **`level`**: Minimum log level for console and file output. Available levels are `"trace"`, `"debug"`, `"info"`, `"warn"`, `"error"`, and `"off"` (default: `"info"`). Can also be overridden with the `RUST_LOG` environment variable. +- **`threads`**: Whether to include thread names in log messages (default: `false`). +- **`thread_ids`**: Whether to include numeric thread IDs in log messages (default: `false`). +- **`target`**: Whether to include module and component targets in log entries (default: `false`). +- **`color`**: Whether to enable ANSI colored log output in the console (default: `true`). +- **`timestamp`**: Whether to include timestamps in log entries (default: `true`). +- **`timestamp_format`**: Timestamp format string using `time` format description syntax (default: `"[hour]:[minute]:[second]"`). +- **`file`**: File path to write log output to (default: `"latest.log"`). diff --git a/docs/ja_jp/config/packet-limiter.md b/docs/ja_jp/config/packet-limiter.md new file mode 100644 index 0000000..6ce063e --- /dev/null +++ b/docs/ja_jp/config/packet-limiter.md @@ -0,0 +1,30 @@ +# パケットリミッター + +Pumpkin には、パケットフラッド攻撃、スパム攻撃、クライアント側の悪用 (エクスプロイト) からサーバーを保護するための組み込みパケットレートリミッターが搭載されています。パケットレート制限は、Java Edition クライアントと Bedrock Edition クライアントで個別に設定できます。 + +## 設定 + +:::code-group + +```toml [pumpkin.toml] +[networking.java.packet_limiter] +enabled = true +max_packet_rate = 500.0 +burst_capacity = 500.0 +kick_message = "Kicked for spamming packets" + +[networking.bedrock.packet_limiter] +enabled = true +max_packet_rate = 500.0 +burst_capacity = 500.0 +kick_message = "Kicked for spamming packets" +``` + +::: + +### 設定オプション + +- **`enabled`**: このクライアントエディションでパケットレートリミッターを有効にするかどうか (デフォルト: `true`)。 +- **`max_packet_rate`**: クライアント接続ごとに 1 秒あたりに許可される受信パケットの最大数 (デフォルト: `500.0`)。リミッターモジュール自体を無効化せずにレート制限のみを無効にするには、`<= 0.0` に設定します。 +- **`burst_capacity`**: クライアントパケットの一時的なスパイクを許容するトークンバケットのバースト許容量 (デフォルト: `500.0`)。 +- **`kick_message`**: パケット制限を超過した際にプレイヤーに表示される切断メッセージ (デフォルト: `"Kicked for spamming packets"`)。 diff --git a/docs/ja_jp/config/player-data.md b/docs/ja_jp/config/player-data.md new file mode 100644 index 0000000..851aa57 --- /dev/null +++ b/docs/ja_jp/config/player-data.md @@ -0,0 +1,41 @@ +# プレイヤーデータとゲームプレイ + +永続的なプレイヤーデータの保存、進捗 (Advancements)、レシピの同期、および季節ごとの楽しい機能に関する設定は、`pumpkin.toml` で設定します。 + +## 設定 + +:::code-group + +```toml [pumpkin.toml] +[player_data] +save_player_data = true +save_player_cron_interval = 300 + +[advancement] +save_advancements = true + +[recipe] +send_recipes = true + +[fun] +april_fools = true +``` + +::: + +### プレイヤーデータ + +- **`save_player_data`**: 永続的なプレイヤーデータ (インベントリ、位置、体力など) をディスクに保存するかどうか (デフォルト: `true`)。 +- **`save_player_cron_interval`**: オンラインプレイヤーデータの自動定期保存を行う間隔 (秒単位、デフォルト: `300` 秒 / 5 分)。 + +### 進捗 (Advancements) + +- **`save_advancements`**: プレイヤーの進捗状況を追跡し、ディスクに保存するかどうか (デフォルト: `true`)。 + +### レシピ + +- **`send_recipes`**: クラフトや精錬のレシピを接続中のクライアントと同期し、クライアント側のレシピ本を有効にするかどうか (デフォルト: `true`)。 + +### お楽しみ機能 + +- **`april_fools`**: エイプリルフールのイースターエッグや季節限定の楽しい機能を有効にするかどうか (デフォルト: `true`)。 diff --git a/docs/ja_jp/config/plugins.md b/docs/ja_jp/config/plugins.md new file mode 100644 index 0000000..b00ab4f --- /dev/null +++ b/docs/ja_jp/config/plugins.md @@ -0,0 +1,59 @@ +# プラグイン + +Pumpkin は、ケーパビリティベースのセキュリティを備えた WebAssembly (WASM) プラグインランタイムを提供します。プラグインの挙動、署名検証、権限は `pumpkin.toml` 内の `[plugins]` で設定します。 + +## 設定 + +:::code-group + +```toml [pumpkin.toml] +[plugins] +enabled = true +hot_reload = false +ask_permission_confirmation = true +allow_unsigned = true +allowed_permissions = [] +blocked_permissions = [] +inherit_env = false +loopback_only = false +verify_signatures = true + +# 任意のプラグインごとのオーバーライド +[plugins.overrides.my_plugin] +enabled = true +allow_unsigned = true +max_memory_mb = 128 +allowed_permissions = ["fs:read:data"] +blocked_permissions = ["network:outbound"] +loopback_only = true + +[plugins.overrides.my_plugin.environment] +API_KEY = "example_secret" +``` + +::: + +### グローバルプラグイン設定 + +- **`enabled`**: プラグインランタイムを有効または無効にするマスタースイッチ (デフォルト: `true`)。 +- **`hot_reload`**: `plugins/` ディレクトリを監視し、実行時に変更されたプラグインを自動的に再読み込みするかどうか (デフォルト: `false`)。 +- **`ask_permission_confirmation`**: プラグインが未承認のケーパビリティ/権限を要求した際に、サーバーコンソールで確認を求めるかどうか (デフォルト: `true`)。 +- **`allow_unsigned`**: 署名されていない WASM プラグインの読み込みを許可するかどうか (デフォルト: `true`)。 +- **`allowed_permissions`**: すべてのプラグインに対してグローバルに事前承認され、対話型コンソールプロンプトをバイパスする権限のリスト (デフォルト: `[]`)。 +- **`blocked_permissions`**: すべてのプラグインに対してグローバルに拒否される権限のリスト (デフォルト: `[]`)。 +- **`inherit_env`**: ホストの環境変数をデフォルトでプラグインの WASI サンドボックスに継承するかどうか (デフォルト: `false`)。 +- **`loopback_only`**: プラグインからのアウトバウンドネットワーク接続を `127.0.0.1` / localhost のみに制限するかどうか (デフォルト: `false`)。 +- **`max_memory_mb`**: (任意) プラグインインスタンスごとのグローバル最大メモリ制限 (MB 単位)。 +- **`verify_signatures`**: 読み込み前に Pumpkin が WASM プラグインの暗号署名を検証するかどうか (デフォルト: `true`)。 + +### プラグインごとのオーバーライド + +`[plugins.overrides.]` を使用して、特定のプラグインの権限や環境変数を細かく調整できます: + +- **`enabled`**: この特定のプラグインを有効または無効にする (デフォルト: `true`)。 +- **`allow_unsigned`**: この特定のプラグインが署名なしで実行できるかどうかをオーバーライド (`true`/`false`)。 +- **`max_memory_mb`**: このプラグイン専用に割り当てられる最大メモリ制限 (MB 単位)。 +- **`allowed_permissions`**: このプラグインに対して事前承認された権限。 +- **`blocked_permissions`**: このプラグインに対して明示的にブロックされた権限。 +- **`loopback_only`**: このプラグインのループバックネットワーク制限をオーバーライド。 +- **`environment`**: プラグインの WASI 環境に直接渡されるカスタム環境変数のテーブル。 diff --git a/docs/ja_jp/config/proxy.md b/docs/ja_jp/config/proxy.md new file mode 100644 index 0000000..ede8187 --- /dev/null +++ b/docs/ja_jp/config/proxy.md @@ -0,0 +1,30 @@ +# プロキシ + +Pumpkin はネットワークサーバー構成向けのプロキシプロトコルをサポートしています。Velocity および BungeeCord のサポートは、`pumpkin.toml` 内の `[networking.proxy]` で設定します。 + +## 設定 + +:::code-group + +```toml [pumpkin.toml] +[networking.proxy] +enabled = false + +[networking.proxy.velocity] +enabled = false +secret = "" + +[networking.proxy.bungeecord] +enabled = false +secret = "" +``` + +::: + +### 設定オプション + +- **`[networking.proxy].enabled`**: プロキシサポートを有効にするマスタースイッチ。 +- **`[networking.proxy.velocity].enabled`**: Velocity の modern 転送プロトコルを有効化。 +- **`[networking.proxy.velocity].secret`**: Velocity プロキシ設定と一致する転送シークレット。 +- **`[networking.proxy.bungeecord].enabled`**: BungeeCord のプレイヤー情報転送プロトコルを有効化。 +- **`[networking.proxy.bungeecord].secret`**: BungeeGuard (`bungeeguard-token`) を介して BungeeCord プロキシからの接続を認証するための任意の共有シークレット。設定すると、受信接続はこのトークンを提供する必要があり、プレイヤーがプロキシをバイパスして直接接続するのを防ぎます。 diff --git a/docs/ja_jp/config/pvp.md b/docs/ja_jp/config/pvp.md new file mode 100644 index 0000000..b0e00a6 --- /dev/null +++ b/docs/ja_jp/config/pvp.md @@ -0,0 +1,26 @@ +# PVP + +PVP (プレイヤー対プレイヤーの戦闘) の挙動と戦闘メカニクスは、`pumpkin.toml` 内の `[pvp]` で設定します。 + +## 設定 + +:::code-group + +```toml [pumpkin.toml] +[pvp] +enabled = true +hurt_animation = true +protect_creative = true +knockback = true +swing = true +``` + +::: + +### 設定オプション + +- **`enabled`**: プレイヤー同士の戦闘 (PVP) を有効化。 +- **`hurt_animation`**: 攻撃を受けた際の被ダメージアニメーションを表示。 +- **`protect_creative`**: クリエイティブモードのプレイヤーが PVP ダメージを受けるのを防止。 +- **`knockback`**: ダメージを受けた際のノックバック効果を有効化。 +- **`swing`**: 攻撃時の腕を振るアニメーションを有効化。 diff --git a/docs/ja_jp/config/query.md b/docs/ja_jp/config/query.md new file mode 100644 index 0000000..5df19be --- /dev/null +++ b/docs/ja_jp/config/query.md @@ -0,0 +1,20 @@ +# Query + +The Query protocol allows external applications (such as server lists or monitoring systems) to request status details from Pumpkin. + +## Configuration + +:::code-group + +```toml [pumpkin.toml] +[networking.query] +enabled = false +address = "0.0.0.0:25565" +``` + +::: + +### Configuration Options + +- **`enabled`**: Enables or disables the GS4 Query listener (default: `false`). +- **`address`**: Network address and port to bind the Query listener to (default: `"0.0.0.0:25565"`). diff --git a/docs/ja_jp/config/rcon.md b/docs/ja_jp/config/rcon.md new file mode 100644 index 0000000..4917974 --- /dev/null +++ b/docs/ja_jp/config/rcon.md @@ -0,0 +1,37 @@ +# RCON + +RCON を使用すると、ネットワーク接続を介して Pumpkin サーバーをリモート管理できます。`pumpkin.toml` では、設定は `[networking.rcon]` の下に配置されます。 + +## 設定 + +:::code-group + +```toml [pumpkin.toml] +[networking.rcon] +enabled = false +address = "0.0.0.0:25575" +password = "" +max_connections = 10 + +[networking.rcon.logging] +logged_successfully = true +wrong_password = true +commands = true +quit = true +``` + +::: + +### RCON 設定 + +- **`enabled`**: RCON サービスを有効にするマスタースイッチ。 +- **`address`**: RCON サーバーをバインドする IP アドレスとポート。 +- **`password`**: RCON クライアントの認証に必要なパスワード。 +- **`max_connections`**: 許可される RCON クライアントの最大同時接続数。 + +### RCON ログ設定 + +- **`logged_successfully`**: クライアントの認証成功イベントをログに記録するかどうか。 +- **`wrong_password`**: 認証失敗 (パスワード間違い) の試行をログに記録するかどうか。 +- **`commands`**: RCON 経由で実行されたコマンドをログに記録するかどうか。 +- **`quit`**: クライアントの切断イベントをログに記録するかどうか。 diff --git a/docs/ja_jp/config/resource-pack.md b/docs/ja_jp/config/resource-pack.md new file mode 100644 index 0000000..31af9fa --- /dev/null +++ b/docs/ja_jp/config/resource-pack.md @@ -0,0 +1,62 @@ +# Resource Pack + +Pumpkin supports serving resource packs to Java and Bedrock clients. Resource pack options are configured under `[resource_pack.java]` and `[resource_pack.bedrock]` in `pumpkin.toml`. + +## Java Resource Pack + +:::code-group + +```toml [pumpkin.toml] +[resource_pack.java] +enabled = false +url = "" +sha1 = "" +prompt_message = "" +force = false +``` + +::: + +### Options + +- **`enabled`**: Enable serving resource pack to Java clients. +- **`url`**: Direct download URL for the Java resource pack zip. +- **`sha1`**: SHA-1 checksum of the resource pack zip file. +- **`prompt_message`**: Message shown to players when prompted to download. +- **`force`**: Disconnect players who decline the resource pack download. + +## Bedrock Resource Pack + +:::code-group + +```toml [pumpkin.toml] +[resource_pack.bedrock] +enabled = false +force = false +packs = [ + # { uuid = "00000000-0000-0000-0000-000000000000", version = "1.0.0", size = 1048576, download_url = "https://example.com/pack.mcpack" } +] +``` + +::: + +### Options + +- **`enabled`**: Enable resource packs for Bedrock clients. +- **`force`**: Force Bedrock clients to download required packs before joining. +- **`packs`**: Array of Bedrock resource pack definitions. + +### Bedrock Pack Fields + +Each pack in the `packs` array accepts the following properties: + +- **`uuid`**: (Required) Unique identifier (UUID) for the Bedrock pack matching its `manifest.json`. +- **`version`**: (Required) Version string of the pack (e.g. `"1.0.0"`). +- **`size`**: (Required) File size of the pack archive in bytes. +- **`download_url`**: (Required) Direct download URL for the `.mcpack` or archive file. +- **`content_key`**: (Optional) Decryption key for encrypted marketplace/custom packs. +- **`sub_pack_name`**: (Optional) Sub-pack name within the pack to activate. +- **`content_id`**: (Optional) Content identifier string. +- **`has_scripts`**: (Optional) Whether the pack contains client scripts (`true`/`false`, default: `false`). +- **`addon_pack`**: (Optional) Whether the pack is marked as an addon pack (default: `false`). +- **`rtx_enabled`**: (Optional) Whether ray tracing / RTX features are enabled for the pack (default: `false`). diff --git a/docs/ja_jp/config/server-links.md b/docs/ja_jp/config/server-links.md new file mode 100644 index 0000000..d72e15a --- /dev/null +++ b/docs/ja_jp/config/server-links.md @@ -0,0 +1,50 @@ +# サーバーリンク (Server Links) + +Minecraft クライアント (1.21 以降) では、ポーズメニューやゲームメニュー内にクリック可能なサーバーリンクを表示できます。標準リンクおよびカスタムサーバーリンクは、`pumpkin.toml` 内の `[server_links]` で設定します。 + +## 設定 + +:::code-group + +```toml [pumpkin.toml] +[server_links] +enabled = true +bug_report = "https://github.com/Pumpkin-MC/Pumpkin/issues" +support = "" +status = "" +feedback = "" +community = "" +website = "" +forums = "" +news = "" +announcements = "" + +[server_links.custom] +# "Store" = "https://store.example.com" +# "Discord" = "https://discord.gg/example" +``` + +::: + +### 設定オプション + +- **`enabled`**: 接続中のクライアントへのサーバーリンクの配信を有効にするマスタースイッチ (デフォルト: `true`)。 +- **`bug_report`**: サーバーまたはプロジェクトのバグトラッカーへのリンク (デフォルト: `"https://github.com/Pumpkin-MC/Pumpkin/issues"`)。 +- **`support`**: テクニカルサポートまたはヘルプデスクへのリンク。 +- **`status`**: サーバーの稼働状況 / ステータスページへのリンク。 +- **`feedback`**: プレイヤーのフィードバックまたは提案ページへのリンク。 +- **`community`**: コミュニティフォーラムまたはグループへのリンク。 +- **`website`**: 公式ウェブサイトへのリンク。 +- **`forums`**: ディスカッションフォーラムへのリンク。 +- **`news`**: サーバーブログまたはニュースページへのリンク。 +- **`announcements`**: お知らせページへのリンク。 + +### カスタムリンク + +`[server_links.custom]` の下に、クライアントメニューに表示する任意のキーと値のリンクを定義できます: + +```toml +[server_links.custom] +"Store" = "https://store.example.com" +"Discord" = "https://discord.gg/example" +``` diff --git a/docs/ja_jp/config/world.md b/docs/ja_jp/config/world.md new file mode 100644 index 0000000..3ade969 --- /dev/null +++ b/docs/ja_jp/config/world.md @@ -0,0 +1,41 @@ +# ワールド + +Pumpkin では、`pumpkin.toml` 内の `[world]` セクションで、ワールドのストレージ形式、自動保存の間隔、ライティングモードを細かく調整できます。 + +## 設定 + +:::code-group + +```toml [pumpkin.toml] +[world] +lighting = "default" +autosave_ticks = 6000 + +[world.chunk] +type = "anvil" +write_in_place = false + +[world.chunk.compression] +algorithm = "LZ4" +level = 6 +``` + +::: + +### ワールド設定 + +- **`lighting`**: ライティングエンジンの光伝搬計算モード。 + - `"default"`: 標準のバニラ Minecraft の光伝搬。 + - `"full"`: 影がなく、すべての場所が最大の空の明るさになります。 + - `"dark"`: すべての場所が完全に真っ暗になります (光レベル 0)。 +- **`autosave_ticks`**: ワールドが自動保存されるサーバーティックの間隔 (デフォルト: `6000`、20 TPS で 5 分に相当)。`0` に設定するとワールドの自動保存が無効になります。 + +### チャンクストレージ設定 + +- **`type`**: 使用するチャンクストレージ形式: + - `"anvil"`: 標準の Minecraft Anvil リージョンファイル形式 (`.mca`)。 + - `"linear"`: ディスク使用量を抑えるために高速圧縮を使用する Linear リージョンストレージ形式。 + - `"pump"`: Pumpkin ネイティブの最適化されたワールド形式。 +- **`write_in_place`**: 空きチャンクを再割り当てするのではなく、既存のリージョンファイル内の元の位置にチャンクを上書きするかどうか (デフォルト: `false`)。 +- **`compression.algorithm`**: チャンクデータに使用される圧縮アルゴリズム (`"LZ4"`, `"ZLib"`, `"GZip"`, `"Custom"`)。 +- **`compression.level`**: チャンクストレージの圧縮レベル (デフォルト: `6`)。 diff --git a/docs/ja_jp/developer/architecture.md b/docs/ja_jp/developer/architecture.md new file mode 100644 index 0000000..19c26b1 --- /dev/null +++ b/docs/ja_jp/developer/architecture.md @@ -0,0 +1,139 @@ +# Pumpkin Architecture & Crates + +Pumpkin is organized as a modular Cargo workspace composed of specialized crates. Each crate has distinct responsibilities, strictly defined boundaries, and minimal cross-dependencies. + +This architecture enables high parallel compilation speeds, isolated testing, zero-cost data sharing, and clean separation between network protocols, game logic, and world management. + +--- + +## Workspace Crate Overview + +### Workspace Architecture at a Glance + +| Layer | Crates | Responsibilities | +| :--- | :--- | :--- | +| **Core Runtime** | `pumpkin` | Game loop, world ticking, player sessions, entity management, and event loops | +| **Networking & Protocols** | `pumpkin-protocol`, `pumpkin-auth`, `pumpkin-nbt` | Java/Bedrock packet serialization, Yggdrasil & Xbox Live authentication, SNBT | +| **World & Gameplay** | `pumpkin-world`, `pumpkin-inventory`, `pumpkin-command` | Region I/O (Anvil/Linear), terrain generation, block ticking, containers, Brigadier tree | +| **Extensibility** | `pumpkin-plugin-runtime`, `pumpkin-host-bindings`, `pumpkin-plugin-api`, `pumpkin-plugin-wit` | WebAssembly plugin execution via Wasmtime, WIT host APIs, native and WASM loaders | +| **Foundations** | `pumpkin-data`, `pumpkin-config`, `pumpkin-util` | Code-generated registries, typed configuration files, mathematical vectors, permissions | + +--- + +## Detailed Crate Breakdown + +### 1. `pumpkin` (Core Server) +- **Path**: `crates/pumpkin` +- **Purpose**: The main server binary and centralized orchestration engine. +- **Responsibilities**: + - Initializes configuration, logging (`tracing`), and telemetry. + - Spawns Tokio network listeners for Minecraft: Java Edition, Bedrock Edition, RCON, Query, and LAN broadcast. + - Manages the server tick loop (`Server::tick`), coordinating world updates, player actions, entity movements, and chunk broadcasting. + - Dispatches incoming client packets through `crates/pumpkin/src/net/java/` and `net/bedrock/`. + - Integrates the plugin manager, firing event handlers before and after packet processing. + +### 2. `pumpkin-protocol` (Network Protocol) +- **Path**: `crates/pumpkin-protocol` +- **Purpose**: High-speed network wire format serialization and deserialization for Minecraft Java and Bedrock editions. +- **Responsibilities**: + - Implements the `ClientPacket` (serialization) and `ServerPacket` (deserialization) traits. + - Handles protocol primitives: `VarInt`, `VarLong`, UUIDs, NBT compounds, and string codecs. + - Implements packets across all states: `Handshake`, `Status`, `Login`, `Config`, and `Play`. + - Provides packet compression (ZLib/Fast compression) and packet encryption (AES-128/CFB8). + - Encapsulates RCON and GameSpy4 Query protocols. + +### 3. `pumpkin-world` (World & Chunks) +- **Path**: `crates/pumpkin-world` +- **Purpose**: World loading, saving, terrain generation, and chunk management. +- **Responsibilities**: + - Supports multiple chunk storage formats: + - **Vanilla Anvil (`.mca`)**: Reading and writing standard region files. + - **Linear (`.linear`)**: High-efficiency zstd-compressed region format saving 50–95% disk space. + - **Slime (`.slime`)**: Single-file world format. + - **Pump**: Native Pumpkin chunk storage format. + - Implements world generation pipelines: Perlin noise heightmaps, 3D noise routers, biome climate parameters, placed features, carvers, and superflat terrain. + - Manages block lighting and sky lighting propagation. + - Coordinates chunk loading, unloading, and streaming via cylindrical chunk iterators. + +### 4. `pumpkin-data` (Minecraft Datasets & Registries) +- **Path**: `crates/pumpkin-data` +- **Purpose**: Compile-time static registries and data tables generated directly from vanilla Minecraft assets and reports. +- **Responsibilities**: + - Contains complete ID mappings, properties, and definitions for blocks, items, biomes, entities, sounds, particles, enchantments, and recipes. + - Packet ID lookups across multiple Minecraft protocol versions. + - Zero-cost lookup tables generated by `tools/pumpkin-codegen`. + +### 5. `pumpkin-config` (Configuration Management) +- **Path**: `crates/pumpkin-config` +- **Purpose**: TOML configuration parsing, serialization, and validation. +- **Responsibilities**: + - Loads and writes `pumpkin.toml`. + - Configures server ports, compression thresholds, proxy modes, resource packs, logging levels, world settings, and Bedrock integration. + - Provides safe defaults and schema validations. + +### 6. `pumpkin-inventory` (Inventory & Containers) +- **Path**: `crates/pumpkin-inventory` +- **Purpose**: Server-side container window handlers and inventory mechanics. +- **Responsibilities**: + - Manages player inventory, chest containers, crafting tables, furnaces, and villager trading screens. + - Validates client slot clicks, drag operations, item stacking, and item splitting. + - Coordinates window packet synchronization to ensure client inventory fidelity. + +### 7. `pumpkin-command` (Command Dispatcher) +- **Path**: `crates/pumpkin-command` +- **Purpose**: Brigadier-inspired hierarchical command tree dispatcher and parser. +- **Responsibilities**: + - Constructs modular command trees with typed arguments (`Integer`, `Float`, `Entity`, `BlockPos`, etc.). + - Executes permission-checked commands. + - Provides real-time autocompletion suggestions sent to connected clients. + +### 8. `pumpkin-auth` (Authentication & Session Verification) +- **Path**: `crates/pumpkin-auth` +- **Purpose**: Cryptographic authentication and player identity verification. +- **Responsibilities**: + - Validates client sessions with Mojang's Yggdrasil authentication servers. + - Handles RSA keypair generation and shared secret exchange during the Login state. + - Supports offline-mode UUID generation and custom authentication endpoints. + - Implements proxy forwarding protocols: [BungeeCord](https://github.com/SpigotMC/BungeeCord), [BungeeGuard](https://github.com/lucko/BungeeGuard), and [Velocity](https://github.com/PaperMC/Velocity) modern forwarding with HMAC-SHA256 signatures. + +### 9. `pumpkin-nbt` (Named Binary Tag Codec) +- **Path**: `crates/pumpkin-nbt` +- **Purpose**: High-throughput binary NBT parser and serializer. +- **Responsibilities**: + - Parses and encodes all standard NBT tag types (`Byte`, `Short`, `Int`, `Long`, `Float`, `Double`, `ByteArray`, `String`, `List`, `Compound`, `IntArray`, `LongArray`). + - Supports compression layers: Gzip, Zlib, and uncompressed Network NBT. + +### 10. `pumpkin-plugin-*` (Extensibility Infrastructure) +- **Paths**: + - `crates/pumpkin-plugin-api`: The Rust developer SDK for compiling native plugins. + - `crates/pumpkin-plugin-runtime`: Wasmtime-powered WebAssembly execution host. + - `crates/pumpkin-plugin-wit`: WebAssembly Interface Type (WIT) specifications. + - `crates/pumpkin-host-bindings`: Auto-generated host bindings connecting Wasmtime and the server core. +- **Purpose**: Enables safe, polyglot plugin execution (Rust, Python, C#, C, Go, Kotlin) with sandboxed memory and near-native performance. + +### 11. `pumpkin-util` & `pumpkin-macros` (Foundations) +- **Paths**: `crates/pumpkin-util`, `crates/pumpkin-macros`, `crates/pumpkin-api-macros` +- **Purpose**: Foundational types, shared algorithms, and procedural macros. +- **Responsibilities**: + - Mathematical utilities: `Vector2`, `Vector3`, `BoundingBox`, and angle conversions. + - Text and chat formatting: `TextComponent` and JSON component serialization. + - Procedural macros such as `#[java_packet(...)]` that bind packet structs to protocol IDs and packet traits. + +### 12. `tools/` (Developer Tooling) +- **`tools/pumpkin-codegen`**: Automated code generator that parses Minecraft version jars, data reports, and asset archives to emit Rust source files into `pumpkin-data`. +- **`tools/pumpkin-fuzzer`**: Fuzz testing suites for network packet parsers, NBT readers, and region file decoders to identify potential crash vectors or memory anomalies. + +--- + +## Concurrency & Threading Model + +| Component | Runtime / Pool | Primary Workload | Communication Mechanism | +| :--- | :--- | :--- | :--- | +| **Network & Socket I/O** | Tokio Async Runtime | TCP packet reads, Bedrock UDP packets, framing, session handshakes | Async tasks (`tokio::spawn`), non-blocking reactors | +| **Heavy Compute** | Rayon Thread Pool | Terrain generation, noise routers, light propagation, chunk compression | Worker threads via crossbeam / Rayon iterators | +| **Inter-Thread Bridge** | Tokio Channels | Dispatching completed chunks and encoded packets back to network tasks | Non-blocking `tokio::sync::mpsc` channels | + +Pumpkin strictly adheres to this separation of concerns: +- **I/O Bound**: Managed on Tokio asynchronous tasks. +- **CPU Bound**: Managed on Rayon thread pools. +- **Communication**: Coordinated via cross-thread channels (`tokio::sync::mpsc`) without ever blocking the Tokio reactor loop. diff --git a/docs/ja_jp/developer/blocks/adding-blocks.md b/docs/ja_jp/developer/blocks/adding-blocks.md new file mode 100644 index 0000000..0770ed8 --- /dev/null +++ b/docs/ja_jp/developer/blocks/adding-blocks.md @@ -0,0 +1,182 @@ +# Adding a Block Behavior + +This guide walks through implementing interactive behavior for a block in Pumpkin. + +--- + +## When Is a Behavior Needed? + +- **Non-Interactive Blocks** (e.g., stone, dirt, terracotta): Do **not** require any custom Rust code. Their breaking speeds, hardness, and placement properties are automatically derived from `pumpkin-data`. +- **Interactive or Functional Blocks** (e.g., doors, chests, levers, crops, campfires, TNT): Require an implementation of the `BlockBehaviour` trait to respond to player clicks, redstone signals, physics steps, or timer ticks. + +--- + +## Step 1: Create the Behavior Struct + +Create a new file under `crates/pumpkin/src/block/blocks//.rs` (or directly in `crates/pumpkin/src/block/blocks/`). + +Use the `#[pumpkin_block(...)]` macro to associate your behavior struct with one or more vanilla block identifiers: + +```rust +use std::sync::Arc; +use pumpkin_data::Block; +use pumpkin_data::block_properties::{DoorLikeProperties, Facing}; +use pumpkin_macros::pumpkin_block; +use pumpkin_util::math::position::BlockPos; +use crate::block::registry::BlockActionResult; +use crate::block::{ + BlockBehaviour, NormalUseArgs, OnNeighborUpdateArgs, PlacedArgs, UseWithItemArgs, +}; +use crate::world::World; + +/// Associates this behavior struct with one or multiple block IDs. +#[pumpkin_block([ + "minecraft:oak_door", + "minecraft:iron_door", + "minecraft:spruce_door", + "minecraft:birch_door" +])] +pub struct DoorBlock; + +impl BlockBehaviour for DoorBlock { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state_id = args.world.get_block_state(args.location).await; + + // Iron doors require redstone power and cannot be opened manually by hand + if args.block.id == pumpkin_data::Block::IRON_DOOR.id { + return BlockActionResult::Pass; + } + + // Toggle open state + let is_open = DoorLikeProperties::open(state_id); + let new_state = DoorLikeProperties::with_open(state_id, !is_open); + args.world.set_block_state(args.location, new_state).await; + + BlockActionResult::Consume + } + + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + // Handle neighbor redstone signals or support block destruction + } +} +``` + +--- + +## Step 2: `BlockBehaviour` Hook Reference + +The `BlockBehaviour` trait exposes lifecycle hooks modeled on vanilla mechanics: + +### Player & Item Interaction Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `normal_use` | `(&self, args: NormalUseArgs<'_>) -> BlockActionResult` | Triggered when right-clicking the block with an empty hand. | +| `use_with_item` | `(&self, args: UseWithItemArgs<'_>) -> BlockActionResult` | Triggered when right-clicking the block while holding an item. | +| `get_screen_handler_factory` | `(&self, args: GetScreenHandlerFactoryArgs<'_>) -> Option>` | Returns an inventory container factory to open a UI screen (e.g., Chests, Crafting Tables). | + +### World Lifecycle & Physics Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `placed` | `(&self, args: PlacedArgs<'_>)` | Triggered immediately after the block is placed in the world. | +| `broken` | `(&self, args: BrokenArgs<'_>)` | Triggered when broken by a player or game mechanics. | +| `on_entity_collision` | `(&self, args: OnEntityCollisionArgs<'_>)` | Triggered when an entity walks inside, steps on, or falls into the block. | +| `on_neighbor_update` | `(&self, args: OnNeighborUpdateArgs<'_>)` | Triggered when an adjacent block state updates or is destroyed. | +| `on_projectile_hit` | `(&self, args: OnProjectileHitArgs<'_>)` | Triggered when an arrow, snowball, or projectile strikes the block. | + +### Redstone & Power Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `emits_redstone_power` | `(&self, state_id: BlockStateId) -> bool` | Declares whether the block emits redstone power to neighbors. | +| `get_weak_redstone_power` | `(&self, ...) -> u8` | Returns emitted weak power level (0–15). | +| `get_strong_redstone_power` | `(&self, ...) -> u8` | Returns emitted strong direct power level (0–15). | + +### Foliage & Bonemeal Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `is_valid_bonemeal_target` | `(&self, args: BonemealArgs<'_>) -> bool` | Checks if bone meal can be applied (e.g., crop not fully grown). | +| `is_bonemeal_success` | `(&self, args: BonemealArgs<'_>) -> bool` | Evaluates random chance roll for bone meal success. | +| `perform_bonemeal` | `(&self, args: BonemealArgs<'_>)` | Advances growth stages or spawns adjacent flora. | + +--- + +## Step 3: Modifying Block States + +All block properties are strongly typed in `pumpkin_data::block_properties`: + +```rust +use pumpkin_data::block_properties::{BedLikeProperties, Part}; + +// 1. Read typed properties from the numeric BlockStateId +let part = BedLikeProperties::part(state_id); // Part::Head or Part::Foot +let is_occupied = BedLikeProperties::occupied(state_id); + +// 2. Generate a new BlockStateId with updated properties +let updated_state = BedLikeProperties::with_occupied(state_id, true); + +// 3. Commit the updated state to the world +world.set_block_state(pos, updated_state).await; +``` + +--- + +## Step 4: Adding a Block Entity (Tile Entity) + +If your block needs to store persistent custom state (inventories, custom text, timers): + +1. Create a block entity struct in `crates/pumpkin/src/block/entities/`: + ```rust + use pumpkin_util::math::position::BlockPos; + use crate::block::entities::BlockEntity; + + pub struct CustomBlockEntity { + pub pos: BlockPos, + pub custom_counter: u32, + } + + impl BlockEntity for CustomBlockEntity { + fn to_nbt(&self) -> pumpkin_nbt::compound::NbtCompound { + let mut compound = pumpkin_nbt::compound::NbtCompound::new(); + compound.put_int("Counter", self.custom_counter as i32); + compound + } + + fn from_nbt(&mut self, tag: &pumpkin_nbt::compound::NbtCompound) { + if let Some(val) = tag.get_int("Counter") { + self.custom_counter = val as u32; + } + } + + fn tick(&mut self, world: &std::sync::Arc, pos: &BlockPos) { + // Periodic logic per game tick + } + } + ``` + +2. Link the block entity in your `BlockBehaviour`: + ```rust + impl BlockBehaviour for CustomBlock { + fn create_block_entity(&self, pos: BlockPos) -> Option>> { + Some(Arc::new(Mutex::new(CustomBlockEntity::new(pos)))) + } + } + ``` + +--- + +## Step 5: Registering the Block + +1. Export your module in `crates/pumpkin/src/block/blocks/mod.rs`: + ```rust + pub mod doors; + ``` + +2. Register the behavior struct in `crates/pumpkin/src/block/registry.rs`: + ```rust + manager.register(DoorBlock); + ``` + +At startup, `manager.register` automatically reads the block IDs declared in `#[pumpkin_block(...)]` and routes interactions to your implementation. diff --git a/docs/ja_jp/developer/blocks/index.md b/docs/ja_jp/developer/blocks/index.md new file mode 100644 index 0000000..525a200 --- /dev/null +++ b/docs/ja_jp/developer/blocks/index.md @@ -0,0 +1,104 @@ +# Block Architecture & State System + +Blocks form the static and dynamic foundation of Minecraft worlds. In Pumpkin, block handling is cleanly bifurcated into two distinct layers: + +1. **Static Data Layer ([`pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data))**: Compile-time generated metadata including numeric block IDs, block state IDs, hardness, blast resistance, light emission, opacity, collision shapes, and block state properties generated by [`pumpkin-codegen`](/developer/codegen). +2. **Dynamic Behavior Layer ([`pumpkin::block`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/block))**: Server-side behavioral hooks—such as opening containers, toggling redstone signals, growing crops, breaking triggers, and collision effects—handled via the `BlockBehaviour` trait. + +--- + +## Static Blocks vs. Interactive Behaviors + +Because Minecraft contains thousands of block state combinations, Pumpkin avoids allocating runtime behavior structs for simple, non-interactive blocks: + +| Category | Description | Examples | Runtime Behavior Required? | +| :--- | :--- | :--- | :--- | +| **Static Blocks** | Rigid solid voxels without interactive logic or custom physics | `stone`, `dirt`, `cobblestone`, `bedrock`, `obsidian` | No — Placement, breaking times, and tool requirements are handled entirely by `pumpkin-data`. | +| **Interactive Blocks** | Blocks triggered by right-clicks or tool actions | `door`, `trapdoor`, `lever`, `button`, `crafting_table` | Yes — Implements `BlockBehaviour::normal_use` or `use_with_item`. | +| **Collision Blocks** | Blocks that modify entity movement or apply forces | `honey_block`, `slime_block`, `soul_sand`, `powder_snow` | Yes — Implements `on_entity_collision` or `on_landed_upon`. | +| **Redstone Components** | Power sources, conductors, and logic gates | `redstone_wire`, `repeater`, `comparator`, `observer` | Yes — Implements `emits_redstone_power` and `on_neighbor_update`. | +| **Block Entities** | Blocks with persistent, serialized custom states | `chest`, `furnace`, `sign`, `decorated_pot`, `spawner` | Yes — Implements `create_block_entity` and links a `BlockEntity`. | + +--- + +## Block IDs, States, and Properties + +Minecraft distinguishes between a **Block** type and a specific **Block State**: + +### 1. `BlockId` and `BlockStateId` + +- **`BlockId`**: An identifier representing a base block type (e.g., `minecraft:oak_door`). +- **`BlockStateId`**: A 16-bit integer representing a unique combination of block properties (e.g., `minecraft:oak_door[facing=north,half=lower,hinge=left,open=false,powered=false]`). + +In Pumpkin, block data is looked up from precomputed lookup tables in `pumpkin-data`: + +```rust +use pumpkin_data::Block; +use pumpkin_data::BlockStateId; + +// Access static properties directly from generated data +let door_block = Block::OAK_DOOR; +let default_state_id: BlockStateId = door_block.default_state_id; + +let hardness = door_block.hardness; +let blast_resistance = door_block.blast_resistance; +let is_air = door_block.is_air; +``` + +### 2. Generated State Property Helpers + +Rather than parsing raw strings or bitmasks manually, [`pumpkin-codegen`](/developer/codegen) generates strongly typed property helpers in `pumpkin_data::block_properties`: + +```rust +use pumpkin_data::block_properties::{DoorLikeProperties, DoubleBlockHalf, Facing}; + +// Read typed properties from a numeric BlockStateId +let is_open = DoorLikeProperties::open(state_id); +let half = DoorLikeProperties::half(state_id); +let facing = DoorLikeProperties::facing(state_id); + +// Generate a new BlockStateId with mutated properties +let toggled_state_id = DoorLikeProperties::with_open(state_id, !is_open); +``` + +--- + +## Physics, Bounding Boxes & Raycasting + +Pumpkin calculates collision and raycasting bounding boxes directly using pre-baked shapes in `pumpkin-data`: + +- **Solid Collisions**: During physics ticks, an entity's axis-aligned bounding box (AABB) is tested against surrounding block collision voxels. +- **Raycasting**: When a player targets a block to place or break, client raycasts are validated against the server-side block outline shapes. +- **Custom Movement Hooks**: Blocks like slime blocks or honey blocks override vertical rebound or horizontal velocity: + - `bounce_entity_after_fall`: Dampens fall damage and multiplies rebound velocity. + - `stop_vertical_movement_after_fall`: Zeros fall velocity upon contact. + +--- + +## Block Entities (Tile Entities) + +When a block must store arbitrary dynamic data that cannot fit into standard block state bits (such as container inventories, custom sign text, brewing timers, or mob spawner delays), it uses a **Block Entity**: + +| Component | Responsibility | Implementation File | +| :--- | :--- | :--- | +| `BlockEntity` Trait | Defines NBT serialization (`to_nbt`, `from_nbt`) and tick execution | [`crates/pumpkin/src/block/entities/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/block/entities) | +| `create_block_entity` | Hook in `BlockBehaviour` called when placing the block | Attached to relevant block behaviors | +| Chunk Entity Storage | Manages active block entities and coordinates within chunk sections | [`crates/pumpkin-world`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-world) | + +--- + +## The Block Registry & Dispatch System + +Block interaction events are routed through the central `BlockRegistry` in [`crates/pumpkin/src/block/registry.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/block/registry.rs): + +1. **Registration**: At server initialization, all behavior structs are registered into the `BlockManager`. +2. **Mapping**: The manager maps numeric `BlockId` keys to thread-safe behavior instances (`Arc`). +3. **Dispatch**: When the network layer receives block action packets (such as `CUseItemOn` or block dig packets), the server looks up the behavior in $O(1)$ time and invokes the relevant hook. + +--- + +## Next Steps + +To implement a new interactive block behavior, continue to the practical guide: + +- **[Adding a Block](/developer/blocks/adding-blocks)**: Complete walk-through of declaring attributes, implementing interaction hooks, creating block entities, and registering behaviors. diff --git a/docs/ja_jp/developer/codegen.md b/docs/ja_jp/developer/codegen.md new file mode 100644 index 0000000..3908d04 --- /dev/null +++ b/docs/ja_jp/developer/codegen.md @@ -0,0 +1,66 @@ +# Code Generation (`pumpkin-codegen`) + +Minecraft is a massive game with thousands of block states, items, packet IDs, sound events, recipes, and biome parameters. These registries frequently change between Minecraft updates. + +To maintain 100% fidelity without manually maintaining tens of thousands of error-prone constants, Pumpkin utilizes an automated code generation tool: [`tools/pumpkin-codegen`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/tools/pumpkin-codegen). + +--- + +## Purpose & Workflow + +`pumpkin-codegen` parses official Minecraft server data generator reports, asset files, and version specifications, and outputs zero-cost, strongly-typed Rust files directly into [`crates/pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data). + +The pipeline operates in three distinct stages: +1. **Ingestion**: Reads raw JSON assets and vanilla server reports from `assets/` and `assets/datapacks/`. +2. **Transformation**: Normalizes states, calculates bitsets, processes ViaVersion remappings, and optimizes lookup tables. +3. **Emission**: Emits compiled Rust constants and structs into [`crates/pumpkin-data/src/generated`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data/src/generated). + +--- + +## Generated Datasets + +`pumpkin-codegen` generates the following components in `pumpkin-data`: + +| Category | Description | Generated Source | +| :--- | :--- | :--- | +| **Packets** | Serverbound and clientbound packet IDs for all protocol states across supported Minecraft versions. | `packet.rs` | +| **Blocks** | Block definitions, default states, state properties (facing, powered, waterlogged, etc.), and block collision boxes. | `block.rs` | +| **Items** | Item definitions, stack sizes, durability, data components, food properties, and equipment slots. | `item.rs` | +| **Registries** | Dynamic registries for biomes, dimensions, chat types, damage types, trim materials, and banner patterns. | `registry.rs` | +| **Biomes & Noise** | Climate parameters, noise router configurations, 3D noise settings, and surface rules. | `biome.rs`, `noise_router.rs` | +| **Entities** | Entity types, default bounding boxes, attributes, and tracked entity metadata schemas. | `entity_type.rs`, `tracked_data.rs` | +| **Recipes & Crafting** | Shaped, shapeless, smelting, blasting, smoking, and stonecutting recipes. | `recipes.rs` | +| **Tags** | Block tags, item tags, fluid tags, and entity type tags (e.g., `#minecraft:mineable/pickaxe`). | `tag.rs` | +| **Enchantments & Potions** | Enchantment definitions, potion effects, and brewing stand recipes. | `enchantments.rs`, `potion.rs` | +| **Translations** | Official localization keys for chat messages and item names. | `translations.rs` | +| **Plugin WIT** | WebAssembly Component Model WIT files defining plugin interfaces. | `wit/` | + +--- + +## Running Code Generation + +To run the code generator locally: + +```bash +# Navigate to the workspace root +cd Pumpkin + +# Run the codegen tool +cargo run -p pumpkin-codegen +``` + +### When Should You Run Codegen? + +- **Upgrading to a New Minecraft Version**: When porting Pumpkin to a new minor or major Minecraft release. +- **Adding Protocol Mappings**: When adding support for an older or newer client protocol version. +- **Updating WIT Definitions**: When adding new functions or types to the WebAssembly plugin API. + +--- + +## Data Sources + +`pumpkin-codegen` reads data from several primary sources: + +1. **Minecraft Data Generator Reports**: Generated by running `java -DbundlerMainClass=net.minecraft.data.Main -jar server.jar --reports`. +2. **Minecraft Client Assets**: Extracted from client `.jar` files for translation keys, model shapes, and sound mappings. +3. **Minecraft Protocol Wiki**: Cross-referenced with protocol specifications for packet IDs. diff --git a/docs/ja_jp/developer/commands/adding-commands.md b/docs/ja_jp/developer/commands/adding-commands.md new file mode 100644 index 0000000..eda739a --- /dev/null +++ b/docs/ja_jp/developer/commands/adding-commands.md @@ -0,0 +1,246 @@ +# Adding a Command + +This guide explains how to implement and register a new command in the Pumpkin server codebase. + +Commands in Pumpkin are implemented in the `crates/pumpkin/src/command/commands/` directory and registered into the central [`CommandDispatcher`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/dispatcher.rs) during server initialization. + +--- + +## Anatomy of a Pumpkin Command + +A typical command file contains: +1. **Metadata Constants**: Command description, permission node identifier, and argument names. +2. **Permission Registration**: Declaring default OP requirements in the server's [`PermissionRegistry`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-util/src/permission.rs). +3. **Command Executors**: Structs implementing the [`CommandExecutor`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/mod.rs) trait for each terminal node or subcommand. +4. **Registration Function**: Constructing the argument tree using fluent builders (`command`, `literal`, `argument`). + +--- + +## Step-by-Step Implementation: Creating `/heal` + +Let's implement a complete `/heal` command that can heal either the executing player or a set of target players: + +``` +/heal -> Heals the executing sender +/heal -> Heals target player(s) +/heal -> Heals target player(s) by a specified amount +``` + +### 1. Create the File + +Create `crates/pumpkin/src/command/commands/heal.rs`: + +```rust +use std::sync::Arc; + +use crate::command::argument_builder::{ArgumentBuilder, argument, command}; +use crate::command::argument_types::core::float::FloatArgumentType; +use crate::command::argument_types::entity::EntityArgumentType; +use crate::command::context::command_context::CommandContext; +use crate::command::errors::error_types::CommandErrorType; +use crate::command::node::dispatcher::CommandDispatcher; +use crate::command::node::{CommandExecutor, CommandExecutorResult}; +use crate::entity::EntityBase; +use pumpkin_data::translation; +use pumpkin_util::PermissionLvl; +use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry}; +use pumpkin_util::text::TextComponent; + +const DESCRIPTION: &str = "Restores health to target entities."; +const PERMISSION: &str = "pumpkin:command.heal"; + +const ARG_TARGETS: &str = "targets"; +const ARG_AMOUNT: &str = "amount"; + +const ERROR_NOT_PLAYER: CommandErrorType<0> = CommandErrorType::new( + translation::java::PERMISSIONS_REQUIRES_PLAYER, + translation::java::PERMISSIONS_REQUIRES_PLAYER, +); +``` + +### 2. Implement the Executors + +Command execution logic is encapsulated in structs implementing [`CommandExecutor`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/mod.rs): + +```rust +/// Heals the executing player to full health. +struct SelfHealExecutor; + +impl CommandExecutor for SelfHealExecutor { + fn execute(&self, context: &CommandContext) -> CommandExecutorResult { + let player = context + .source + .output + .as_player() + .ok_or_else(|| ERROR_NOT_PLAYER.create_without_context())?; + + // Restore player health + player.living_entity.set_health(20.0); + + context.source.send_feedback( + TextComponent::text("Fully restored your health!"), + true, // Broadcast to operators if configured + ); + + // Return number of affected entities + Ok(1) + } +} + +/// Heals specified target players. +struct TargetsHealExecutor { + has_amount: bool, +} + +impl CommandExecutor for TargetsHealExecutor { + fn execute(&self, context: &CommandContext) -> CommandExecutorResult { + let targets = EntityArgumentType::get_players(context, ARG_TARGETS)?; + + let amount = if self.has_amount { + FloatArgumentType::get(context, ARG_AMOUNT)? + } else { + 20.0 + }; + + let count = targets.len(); + for target in &targets { + let current = target.living_entity.health.load(); + target.living_entity.set_health((current + amount).min(20.0)); + } + + let msg = if count == 1 { + TextComponent::text(format!("Healed {} by {} HP", targets[0].gameprofile.name, amount)) + } else { + TextComponent::text(format!("Healed {} players by {} HP", count, amount)) + }; + + context.source.send_feedback(msg, true); + + Ok(count as i32) + } +} +``` + +### 3. Build the Command Tree + +Use the fluent builder functions `command`, `literal`, and `argument` to assemble the syntax tree and register the permission: + +```rust +pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) { + // 1. Register permission node with default requirement (OP level 2) + registry.register_permission_or_panic(Permission::new( + PERMISSION, + DESCRIPTION, + PermissionDefault::Op(PermissionLvl::Two), + )); + + // 2. Assemble command tree + dispatcher.register( + command("heal", DESCRIPTION) + .requires(PERMISSION) + .executes(SelfHealExecutor) + .then( + argument(ARG_TARGETS, EntityArgumentType::Players) + .executes(TargetsHealExecutor { has_amount: false }) + .then( + argument(ARG_AMOUNT, FloatArgumentType::new(1.0, 100.0)) + .executes(TargetsHealExecutor { has_amount: true }), + ), + ), + ); +} +``` + +--- + +## Command Structure Patterns + +### Adding Subcommand Literals +Use `literal("subcommand_name")` to create keyword branches: + +```rust +use crate::command::argument_builder::{argument, command, literal}; + +dispatcher.register( + command("weather", "Set the world weather") + .requires("minecraft:command.weather") + .then(literal("clear").executes(ClearWeatherExecutor)) + .then(literal("rain").executes(RainWeatherExecutor)) + .then(literal("thunder").executes(ThunderWeatherExecutor)) +); +``` + +### Cross-Platform Translation Feedback +Pumpkin servers serve both Java and Bedrock Edition players simultaneously. Whenever possible, use vanilla translation keys with `TextComponent::translate_cross` so messages render localized on the client: + +```rust +use pumpkin_data::translation; +use pumpkin_util::text::TextComponent; + +let msg = TextComponent::translate_cross( + translation::java::COMMANDS_KILL_SUCCESS_SINGLE, + translation::bedrock::COMMANDS_KILL_SUCCESSFUL, + [target.get_display_name()], +); + +context.source.send_feedback(msg, true); +``` + +### Error Handling in Executors +Executors must return `CommandExecutorResult`, which resolves to `Result`. If validation fails, return an error cleanly without panicking: + +```rust +use crate::command::errors::error_types::CommandErrorType; + +const ERROR_INVALID_VALUE: CommandErrorType<0> = CommandErrorType::new( + "Amount must be greater than zero.", + "Amount must be greater than zero.", +); + +if amount <= 0.0 { + return Err(ERROR_INVALID_VALUE.create_without_context()); +} +``` + +--- + +## Registering in `default_dispatcher` + +Once your command is implemented, register it in `crates/pumpkin/src/command/commands/mod.rs`: + +1. **Declare the Module**: + ```rust + // In crates/pumpkin/src/command/commands/mod.rs + mod heal; + ``` + +2. **Invoke `register` in `default_dispatcher`**: + ```rust + pub fn default_dispatcher( + permission_manager: &PermissionManager, + commands_config: &CommandsConfig, + ) -> CommandDispatcher { + let registry = &permission_manager.registry; + let mut dispatcher = CommandDispatcher::new(); + + // ... existing registrations ... + heal::register(&mut dispatcher, registry); + + dispatcher + } + ``` + +--- + +## Best Practices Checklist + +- [ ] **No Panics or Unwraps**: Use `?` or return descriptive `CommandSyntaxError`s when arguments are invalid. +- [ ] **Permission Nodes**: Prefix permission strings with appropriate namespaces (`minecraft:command.` for vanilla commands, `pumpkin:command.` for server-specific commands). +- [ ] **Return Success Counts**: The returned `i32` represents the command's exit code or number of affected objects for redstone command blocks and `/execute store result`. +- [ ] **Respect GameRules**: When notifying players other than the sender, check if `send_command_feedback` is enabled: + ```rust + let server = context.source.server(); + if server.level_info.load().game_rules.send_command_feedback { + target.send_system_message(&feedback); + } + ``` diff --git a/docs/ja_jp/developer/commands/arguments-and-suggestions.md b/docs/ja_jp/developer/commands/arguments-and-suggestions.md new file mode 100644 index 0000000..6794ae1 --- /dev/null +++ b/docs/ja_jp/developer/commands/arguments-and-suggestions.md @@ -0,0 +1,159 @@ +# Argument Types & Suggestions + +Pumpkin provides a comprehensive suite of type-safe command arguments through `pumpkin-command` and the main `pumpkin` server crate. Arguments deserialize raw token streams into structured Rust types, validate ranges, and furnish tab-completion suggestions to clients. + +--- + +## Standard Argument Types + +Argument parsers implement the [`ArgumentType`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/argument_types/argument_type.rs) trait. The following built-in types are available for command builders: + +### 1. Primitive & Scalar Types + +| Argument Type | Import Path | Deserialized Type | Description | +| :--- | :--- | :--- | :--- | +| **`IntegerArgumentType`** | `...::core::integer` | `i32` | Standard 32-bit integer with optional `[min, max]` bounds. | +| **`FloatArgumentType`** | `...::core::float` | `f32` | 32-bit floating point number with bounds. | +| **`DoubleArgumentType`** | `...::core::double` | `f64` | 64-bit floating point number with bounds. | +| **`LongArgumentType`** | `...::core::long` | `i64` | 64-bit integer with bounds. | +| **`BoolArgumentType`** | `...::core::bool` | `bool` | Boolean value (`true` or `false`). | +| **`StringArgumentType`** | `...::core::string` | `String` | String tokens based on quoting rules. | + +#### String Argument Behaviors +- **`StringArgumentType::Word`**: Parses a single word without whitespace. +- **`StringArgumentType::Quotable`**: Parses either a single word or a quoted phrase (`"hello world"`). +- **`StringArgumentType::Greedy`**: Consumes all remaining characters in the input stream (ideal for chat/broadcast commands like `/say` and `/me`). + +--- + +## Coordinate Argument Types + +Minecraft coordinates support absolute positions, relative offsets (`~`), and local caret directions (`^`): + +```rust +use crate::command::argument_builder::argument; +use crate::command::argument_types::coordinates::block_pos::BlockPosArgumentType; +use crate::command::argument_types::coordinates::vec3::Vec3ArgumentType; + +// Block position (integer BlockPos) +argument("pos", BlockPosArgumentType) + +// Precise entity coordinates (f64 Vector3) +argument("location", Vec3ArgumentType) +``` + +### Retrieving Parsed Coordinates +Inside an executor, coordinate arguments resolve relative offsets against the sender's current position and orientation in the [`CommandSource`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/command/context/command_source.rs): + +```rust +// Extracts centered f64 coordinates (handling ~ and ^ relative to sender) +let pos: Vector3 = Vec3ArgumentType::get_coordinates(context, "location")?; + +// Extracts integer block position +let block_pos: BlockPos = BlockPosArgumentType::get_block_pos(context, "pos")?; +``` + +--- + +## Entity Selectors (`EntityArgumentType`) + +Entity selectors allow players and command blocks to target entities using player names, UUIDs, or vanilla target selectors (`@p`, `@r`, `@a`, `@e`, `@s`). + +In Pumpkin, entity selectors are accessed via [`EntityArgumentType`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/command/argument_types/entity.rs): + +```rust +pub enum EntityArgumentType { + /// Matches exactly one player + Player, + /// Matches zero or more players + Players, + /// Matches exactly one entity + Entity, + /// Matches zero or more entities + Entities, +} +``` + +### Resolution in Executors + +```rust +// 1. Target single player +let player: Arc = EntityArgumentType::get_player(context, "target")?; + +// 2. Target multiple players +let players: Vec> = EntityArgumentType::get_players(context, "targets")?; + +// 3. Target single living entity or mob +let entity: Arc = EntityArgumentType::get_entity(context, "target")?; + +// 4. Target multiple entities +let entities: Vec> = EntityArgumentType::get_entities(context, "targets")?; +``` + +### Supported Selector Syntaxes +Pumpkin's entity selector parser (`crates/pumpkin/src/command/argument_types/entity_selector/`) parses and validates: +- `@p`: Nearest player relative to execution coordinates. +- `@r`: Random player. +- `@a`: All online players. +- `@e`: All entities in loaded world chunks. +- `@s`: Executing entity (`context.source.entity`). +- **Bracket Arguments**: Filter queries like `@e[type=zombie,distance=..10,limit=5,sort=nearest]`. + +--- + +## Registry & Game Mechanic Types + +Pumpkin provides specialized argument parsers for Minecraft data types: + +- **`GameModeArgumentType`**: Resolves `survival`, `creative`, `adventure`, `spectator` or numeric IDs `0`–`3`. +- **`BlockArgumentType`**: Parses block identifiers and block states (e.g., `minecraft:chest[facing=north]`). +- **`ItemArgumentType`**: Parses item identifiers and optional data components (e.g., `minecraft:diamond_sword`). +- **`NbtArgumentType`**: Parses SNBT (Stringified NBT) tags into Pumpkin's NBT representation. +- **`TimeArgumentType`**: Parses durations with suffixes (e.g., `10s`, `20t`, `1d`). + +--- + +## Suggestion Providers & Tab Completion + +Arguments can supply dynamic tab-completion candidates to connected players using the [`SuggestionProvider`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/suggestion/mod.rs) trait: + +```rust +use crate::command::context::command_context::CommandContext; +use crate::command::suggestion::provider::SuggestionProvider; +use pumpkin_command::suggestion::suggestions::{Suggestions, SuggestionsBuilder}; + +struct CustomSuggestionProvider; + +impl SuggestionProvider for CustomSuggestionProvider { + fn suggest( + &self, + context: &CommandContext, + mut builder: SuggestionsBuilder, + ) -> Suggestions { + // Retrieve already typed token prefix + let remaining = builder.remaining(); + + let candidates = ["alpha", "bravo", "charlie"]; + for candidate in candidates { + if candidate.starts_with(remaining) { + builder.suggest(candidate); + } + } + + builder.build() + } +} +``` + +### Attaching Suggestions to Arguments +Use the `.suggests(...)` method on `RequiredArgumentBuilder`: + +```rust +argument("code", StringArgumentType::Word) + .suggests(CustomSuggestionProvider) + .executes(MyExecutor) +``` + +### Client Completion Protocol +- **Java Edition**: When a player presses `Tab`, the client transmits a `ServerboundCommandSuggestion` packet. Pumpkin matches the cursor position within the `CommandDispatcher`, executes the target node's `SuggestionProvider`, and responds with `ClientboundCommandSuggestions` containing completion ranges and tooltip tooltips. +- **Bedrock Edition**: Suggestion options for literals and enums are serialized ahead of time in the `CAvailableCommands` packet as dynamic enum value tables. diff --git a/docs/ja_jp/developer/commands/execution-and-context.md b/docs/ja_jp/developer/commands/execution-and-context.md new file mode 100644 index 0000000..6f5f044 --- /dev/null +++ b/docs/ja_jp/developer/commands/execution-and-context.md @@ -0,0 +1,118 @@ +# Execution & Context + +In Pumpkin, command execution separates the **physical origin** of a command from its **runtime context**. This design enables complex command routing, dimension shifts, conditional execution, and vanilla-compliant `/execute` chaining. + +--- + +## `CommandSender` vs. `CommandSource` + +Understanding the distinction between `CommandSender` and `CommandSource` is fundamental to developing commands in Pumpkin: + +| Attribute | `CommandSender` | `CommandSource` | +| :--- | :--- | :--- | +| **Concept** | The physical origin and network destination | The mutable runtime execution environment | +| **Mutability** | Immutable throughout command execution | Cloned and modified across `/execute` branches | +| **Key Fields** | Terminal socket, connection handle, RCON buffer | World, position coordinates, rotation, entity, silent flag | +| **Creation** | Instantiated on network connection or terminal input | Constructed via `sender.into_source(&server)` | + +### 1. `CommandSender` (Physical Caller) +Represents the entity or socket that initiated the command and receives its output: + +```rust +pub enum CommandSender { + Console, + Rcon(Arc>>), + Player(Arc), + CommandBlock(Arc, Arc), + Dummy, +} +``` + +- **Output Target**: Determines where messages formatted with `send_message` are routed (e.g., standard out, RCON buffer, player packet, or block entity NBT). +- **Hard Permissions**: Holds fixed base permissions (e.g., Console has level 4 bypass; Command Blocks have level 2). + +### 2. `CommandSource` (Execution Environment) +Represents the mutable runtime state during command evaluation: + +```rust +pub struct CommandSource { + pub output: CommandSender, + pub world: Option>, + pub entity: Option>, + pub position: Vector3, + pub rotation: Vector2, + pub name: String, + pub display_name: TextComponent, + pub server: Option>, + pub silent: bool, + pub command_result_taker: ResultValueTaker, + pub entity_anchor: EntityAnchor, +} +``` + +A sender converts into a `CommandSource` via `sender.into_source(&server)`. + +--- + +## The `/execute` Command & Context Redirection + +The `/execute` command manipulates `CommandSource` properties before passing control to a downstream command. + +### Branching Modifiers +- **`as `**: Changes `context.source.entity` to target entities without altering position or rotation. +- **`at `**: Updates `context.source.world`, `position`, and `rotation` to match the target entity. +- **`positioned `**: Modifies `context.source.position` to new coordinates. +- **`in `**: Changes `context.source.world` to the specified dimension (Overworld, Nether, The End). +- **`anchored (feet|eyes)`**: Adjusts coordinate offsets for line-of-sight and facing checks. + +### Context Forking & Chaining +When `/execute` matches multiple targets (e.g., `/execute as @e run ...`), `pumpkin-command` uses **forking**: +1. The dispatcher forks the execution context once per matched target. +2. The child command executes independently for each cloned `CommandSource`. +3. The cumulative success count is aggregated across all executions. + +--- + +## Result Values & Success Counts + +Vanilla commands communicate results back through integer exit codes and return values. + +### The Return Value Callback +Executors return `CommandExecutorResult` (`Result`). The returned integer signifies: +- **0**: Failure or zero entities affected. +- **> 0**: Number of entities affected, blocks changed, or items cleared. + +For Command Blocks, this value is persisted into the block entity's `SuccessCount`: + +```rust +pub fn set_success_count(&self, count: u32) { + if let Self::CommandBlock(c, _) = self { + c.success_count.store(count, Ordering::SeqCst); + } +} +``` + +### `/execute store` +When chained with `/execute store (result|success) ...`, Pumpkin intercepts the return value via `ReturnValueCallback` and writes it to scoreboards, bossbars, or entity NBT tags. + +--- + +## Feedback & GameRules Integration + +Sending output to the command sender is governed by server configuration and world game rules: + +```rust +context.source.send_feedback( + TextComponent::text("Command executed successfully!"), + true, // Broadcast to server operators +); +``` + +### Output Rules Matrix + +| Condition | Feedback Behavior | +| :--- | :--- | +| `silent == true` | Output is suppressed completely. | +| `send_command_feedback == false` | Player senders do not receive success feedback (errors are still displayed). | +| `command_block_output == false` | Command block execution output is not broadcast to online operators. | +| `broadcast_console_to_ops == true` | Server console and RCON commands emit a copy of their feedback to all online OP level 2+ players. | diff --git a/docs/ja_jp/developer/commands/index.md b/docs/ja_jp/developer/commands/index.md new file mode 100644 index 0000000..3b31eb9 --- /dev/null +++ b/docs/ja_jp/developer/commands/index.md @@ -0,0 +1,142 @@ +# Commands Architecture & Dispatcher + +Pumpkin features a high-performance, pure-Rust implementation of the **Brigadier** command tree specification through the dedicated `pumpkin-command` crate. It coordinates command parsing, requirement resolution, type-safe argument deserialization, execution branching, and dual-edition client tree synchronization for both **Minecraft: Java Edition** and **Minecraft: Bedrock Edition**. + +### Command Processing Pipeline + +Every command executed in Pumpkin flows through the following discrete stages: + +1. **Input Normalization**: Strips single leading slashes while preserving plugin multi-slash conventions (such as `//set`). +2. **Availability Check**: Rejects commands disabled via server configuration with standard `unknown command` syntax errors. +3. **Permission Evaluation**: Validates requirements (`PermissionLvl` or custom registry nodes) against the executing sender. +4. **Tokenization & Parsing**: `StringReader` recursively walks the `Tree`, matching literal keywords and parsing typed argument parameters. +5. **Context Dispatch**: Invokes the matched `CommandExecutor` callback with a typed `CommandContext`. +6. **Feedback & Result Consumption**: Formats cross-platform translation components, notifies observers, and writes exit codes back to the caller. + +--- + +## The `pumpkin-command` Crate + +Pumpkin decouples command parsing and tree representation from the main server runtime into the modular `pumpkin-command` crate (`crates/pumpkin-command`). + +Key components of `pumpkin-command` include: +- **`Tree`**: Contiguous vector-backed storage of attached command nodes (`AttachedNode`). +- **`CommandDispatcher`**: The central registry and dispatch engine. It processes raw command strings, parses tokens with `StringReader`, traverses the command tree, and executes target callbacks. +- **`ArgumentBuilder`**: Fluent builder API (`command`, `literal`, `argument`) modeled after Mojang's Brigadier library. +- **`CommandSource`**: Generic execution state representing the current position, rotation, dimension, entity, and feedback output stream. + +--- + +## The Command Node Hierarchy + +Commands in Pumpkin are modeled as directed graphs of nodes. To optimize memory allocations and avoid circular reference issues during building, the system separates nodes into **detached** and **attached** states: + +### 1. Detached Nodes (`DetachedNode`) +Constructed using fluent builders (`CommandArgumentBuilder`, `LiteralArgumentBuilder`, `RequiredArgumentBuilder`) before registration: +- **`CommandDetachedNode`**: Represents a root command entrypoint with an identifier and a description. +- **`LiteralDetachedNode`**: Represents a literal keyword in a subcommand branch (e.g., `set` in `/gamemode creative`). +- **`ArgumentDetachedNode`**: Represents a typed parameter parsed by an `ArgumentType` (e.g., coordinates, entity selectors, integer ranges). + +### 2. Attached Nodes (`AttachedNode`) +Once registered into the `CommandDispatcher`, detached trees are flattened into an indexed `Tree` structure: +```rust +pub enum AttachedNode { + Root(RootAttachedNode), + Literal(LiteralAttachedNode), + Argument(ArgumentAttachedNode), + Command(CommandAttachedNode), +} +``` +Each attached node has: +- **`NodeId`**: A zero-cost typed index (`NonZero`) into the contiguous node arena. +- **`children`**: Fast lookup map (`FxHashMap`) for literals and child arguments. +- **`requirements`**: Predicates determining whether a sender has permission to view or execute the node. +- **`command`**: Optional `CommandExecutor` callback invoked when parsing terminates on this node. +- **`redirect`**: Optional `Redirection` pointer to loop back or redirect to another sub-tree (used heavily by `/execute`). + +--- + +## Dispatch & Execution Pipeline + +When a command arrives at the server, `CommandDispatcher::handle_command` processes it through several stages: + +```rust +pub fn handle_command<'a>(&'a self, source: &S, mut input: &'a str) { + // 1. Strip single leading slash (preserving double-slash commands like //set) + if let Some(sliced) = input.strip_prefix('/') { + let first_token = input.split_whitespace().next().unwrap_or(""); + let sliced_token = sliced.split_whitespace().next().unwrap_or(""); + if !self.has_command(first_token) + && (self.has_command(sliced_token) || !first_token.starts_with("//")) + { + input = sliced; + } + } + + // 2. Check disabled commands from configuration + if self.is_disabled(Self::command_name(input)) { + let reader = StringReader::new(input); + Self::send_error_to_source(source, DISPATCHER_UNKNOWN_COMMAND.create(&reader), input); + return; + } + + // 3. Parse and execute + let output = self.execute_input(input, source); + if let Err(error) = output { + Self::send_error_to_source(source, error, input); + } +} +``` + +### Syntax Error Formatting +When parsing encounters invalid syntax, unexpected tokens, or out-of-range arguments, Pumpkin constructs a `CommandSyntaxError`. If context is available, the dispatcher formats a visual pointer highlighting exactly where the parser failed: + +``` +Unknown or incomplete command, see below for error +/gamemode creatve<--[HERE] +``` + +In the Java Edition client, this error text is annotated with a `ClickEvent::SuggestCommand`, allowing the player to click the error in chat to copy the failed command into their input box for quick editing. + +--- + +## Dual-Edition Network Synchronization + +Unlike vanilla servers which only serialize command graphs for Java Edition, Pumpkin dynamically synchronizes the registered command tree across both Java and Bedrock clients: + +### 1. Java Edition: `CCommands` Packet +When a player joins or their permission level updates (e.g., via `/op` or `/deop`), Pumpkin builds and sends a `CCommands` packet (`pumpkin_protocol::java::client::play::CCommands`): +- Converts `AttachedNode`s into a flat array of `ProtoNode` entries. +- Filters out disabled commands and nodes where `requirements.evaluate(source)` returns `false`. +- Serializes argument parsers (`brigadier:string`, `brigadier:integer`, `minecraft:entity`, etc.) and client-side suggestion identifiers. + +### 2. Bedrock Edition: `CAvailableCommands` Packet +Bedrock Edition uses an overload-and-enum format rather than Brigadier's recursive graph. In `client_suggestions.rs`, Pumpkin traverses the `AttachedNode` tree to synthesize: +- **`CommandData`**: Root command name, description, permission flags, and aliases. +- **`OverloadData`**: Flattened permutations of argument chains leading to executable endpoints. +- **`ParamData`**: Bedrock parameter types, bitflags (`ARG_FLAG_VALID`, `ARG_FLAG_ENUM`), and dynamically collected enum value tables. + +--- + +## Universal Execution Targets + +Pumpkin commands can be executed across diverse senders: + +| Sender | Source Representation | Output Destination | Default Permission | +| :--- | :--- | :--- | :--- | +| **Player** | `CommandSender::Player(Arc)` | Client system chat packet | Loaded from permission level / permissions file | +| **Server Console** | `CommandSender::Console` | Standard stdout (`tracing::info!`) | Level 4 (Full administrative bypass) | +| **RCON Console** | `CommandSender::Rcon(Arc>>)` | Network response packet payload | Level 4 | +| **Command Block** | `CommandSender::CommandBlock(Arc, Arc)` | NBT `LastOutput` field | Level 2 | +| **Datapack Function** | `CommandSender` inherited from caller | Inherited | Inherited | +| **WASM Plugin** | Invoked through WIT `execute_command` host call | Captured via `ResultDeferrer` | Configurable | + +--- + +## Command Configuration & Disabling + +Pumpkin allows server administrators to disable individual commands via `config/commands.toml` (`CommandsConfig`). When a command is disabled: +1. It is omitted from the `CCommands` packet for Java clients. +2. It is omitted from the `CAvailableCommands` packet for Bedrock clients. +3. Tab completion ignores the command entirely. +4. Any attempt to invoke the command returns `DISPATCHER_UNKNOWN_COMMAND`. diff --git a/docs/ja_jp/developer/contributing.md b/docs/ja_jp/developer/contributing.md new file mode 100644 index 0000000..f2ccc92 --- /dev/null +++ b/docs/ja_jp/developer/contributing.md @@ -0,0 +1,271 @@ +# Contributing to Pumpkin + +Thank you for your interest in contributing to Pumpkin! As an open-source Minecraft server built from the ground up in Rust, we welcome contributions of all sizes—from fixing typos and adding documentation to implementing vanilla game mechanics and optimizing network protocols. + +This guide outlines our development workflow, coding standards, verification checklist, and pull request procedures. + +--- + +## Getting Started + +The easiest way to get help, discuss proposed features, or coordinate with the team is by joining [our Discord server](https://discord.gg/wT8XjrjKkf). + +Before working on a large change, we strongly recommend opening an issue or starting a discussion in Discord to align on architecture and design decisions before writing code. + +> [!NOTE] +> **For Automated AI Agents**: We have a streamlined process for merging agent PRs. Add `🤖🤖🤖` to the end of your PR title to opt into fast-track review. Ensure your implementation is unique, passes all CI checks, and does not duplicate existing pull requests. + +--- + +## Development Setup + +### 1. Prerequisites + +- **Rust Toolchain**: Pumpkin requires the latest stable Rust toolchain (Rust 2024 edition, 1.96+). Install or update Rust via [rustup](https://rustup.rs/): + ```bash + rustup update stable + rustup default stable + rustup component add rustfmt clippy rust-src rust-analyzer + ``` +- **Git**: Ensure Git is installed and configured on your machine. +- **Recommended Cargo Tools**: + ```bash + # Fast unit test runner used in CI + cargo install cargo-nextest --locked + + # Detects unused dependencies + cargo install cargo-machete --locked + + # Source code spell checker + cargo install typos-cli --locked + ``` + +### 2. Cloning the Repository (Submodules Required!) + +Pumpkin uses Git submodules for interfaces such as `pumpkin-plugin-wit`. Make sure to clone recursively: + +```bash +# Clone with submodules +git clone --recursive https://github.com/Pumpkin-MC/Pumpkin.git +cd Pumpkin +``` + +If you have already cloned without `--recursive`, initialize the submodules manually: + +```bash +git submodule update --init --recursive +``` + +### 3. Building and Running + +You can compile and launch the server in debug mode: + +```bash +cargo run +``` + +To build an optimized release binary (with LTO and single codegen units): + +```bash +cargo build --release +``` + +--- + +## Coding Guidelines & Standards + +Pumpkin maintains high code quality and strict performance criteria. Our workspace enforces stringent Clippy lints, and CI will reject code that fails these checks. + +### 1. The Zero-Panic Policy + +Pumpkin is designed to run 24/7 without crashing under unexpected inputs. Therefore, panic-inducing constructs are denied at the compiler level: + +- **No `unwrap()` or `expect()`**: Never call `.unwrap()` or `.expect()` in production code. Use the question mark operator `?`, pattern matching (`match` / `if let`), `.unwrap_or()`, or `.unwrap_or_default()`. + ```rust + // ❌ FORBIDDEN: Will fail CI + let chunk = level.get_chunk(pos).unwrap(); + + // ✅ CORRECT: Handle Option or propagate error + let Some(chunk) = level.get_chunk(pos) else { + return Ok(()); + }; + ``` +- **No `panic!()`, `todo!()`, or `unimplemented!()`**: Do not leave unhandled stubs or panic statements in committed code. +- **No `println!()` or `eprintln!()`**: Always use the structured logging framework [`tracing`](https://docs.rs/tracing/): + ```rust + use tracing::{debug, error, info, trace, warn}; + + // ❌ FORBIDDEN: Will fail CI + println!("Player joined: {}", player.gameprofile.name); + + // ✅ CORRECT + info!("Player {} joined the server", player.gameprofile.name); + ``` + +### 2. Error Handling + +We use [`thiserror`](https://docs.rs/thiserror/) across our crates and unified error types like `PumpkinError`. Functions should return descriptive `Result` types rather than suppressing errors silently. + +### 3. Concurrency Architecture: Tokio vs. Rayon + +Pumpkin uses a hybrid concurrency model: + +1. **Tokio Runtime (Asynchronous I/O)**: Handles TCP/UDP network connections, incoming/outgoing packet streams, authentication HTTP requests, and timers. +2. **Rayon Thread Pool (CPU-Intensive Tasks)**: Handles heavy, CPU-bound computations such as: + - Chunk generation and terrain carving + - Lighting propagation and recalculation + - Packet compression and world chunk serialization + - Complex pathfinding and physics + +> [!WARNING] +> **Never block Tokio worker threads with CPU-heavy work!** +> Do not call long-running synchronous code inside async tasks. Instead, offload heavy work to Rayon via `rayon::spawn` and communicate results back using asynchronous channels like `tokio::sync::mpsc`. +> +> Refer to `pumpkin_world::level::Level::fetch_chunks` for an architectural example. + +### 4. Unsafe Code Policy + +All `unsafe` blocks must be strictly justified and documented: + +```rust +// SAFETY: The buffer is guaranteed to contain at least 4 valid bytes +// as verified by the bounds check on line 42. +unsafe { + // ... +} +``` +Any undocumented `unsafe` block triggers a denied lint in CI (`undocumented_unsafe_blocks = "deny"`). + +--- + +## Pre-Commit Verification Checklist + +Before submitting a Pull Request, run the following commands locally to ensure your changes will pass our GitHub Actions CI pipeline: + +```bash +# 1. Format code according to rustfmt rules +cargo fmt --check + +# 2. Run debug lints +cargo clippy --all-targets --all-features + +# 3. Run release lints +cargo clippy --release --all-targets --all-features + +# 4. Run tests with nextest (or cargo test) +cargo nextest run --verbose + +# 5. Run documentation tests +cargo test --doc --verbose + +# 6. Check for unused dependencies +cargo machete + +# 7. Check for spelling typos +typos +``` + +### Benchmarking Performance + +If you are modifying performance-sensitive hot paths (such as packet serialization, chunk loading, or entity ticking), run benchmarks with [Criterion](https://github.com/bheisler/criterion.rs): + +```bash +cargo bench +``` + +Consider adding a new benchmark in `benches/` when introducing complex algorithms. + +--- + +## Decompiling Minecraft for Reference + +When implementing vanilla Minecraft mechanics, packet layouts, or game logic, referencing official behavior is essential. Pumpkin is a clean-room Rust implementation, and understanding vanilla Minecraft logic helps ensure 1:1 compatibility. + +### Decompiling with Fabric Yarn + +The easiest and cleanest way to decompile the Minecraft server/client for reference is using [Fabric Yarn](https://github.com/FabricMC/yarn) with the Vineflower decompiler: + +```bash +git clone https://github.com/FabricMC/yarn.git +cd yarn +./gradlew decompileVineflower +``` + +After decompilation completes, the mapped source code will be available in: +``` +build/namedSrc +``` + +### Useful Protocol & Data Resources + +- **[Minecraft Wiki Protocol Reference](https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol)**: Complete reference of packet structures, fields, and IDs across game states. +- **[Mojang Data Reports](https://github.com/Arcensoth/mc-data)**: Raw JSON dumps of registries, blocks, items, commands, and packet IDs. +- **`tools/pumpkin-codegen`**: Our internal tool for parsing Minecraft game data and generating `pumpkin-data` code. + +> [!IMPORTANT] +> **Clean-Room Etiquette**: Do not copy-paste decompiled Java code directly into Pumpkin. Study the algorithm and behavior, then write clean, idiomatic, safe Rust that fits Pumpkin's architecture and licensing (GPLv3). + +--- + +## Submitting Pull Requests + +When your code is tested, formatted, and verified locally: + +1. **Fork the Repository**: Fork [Pumpkin-MC/Pumpkin](https://github.com/Pumpkin-MC/Pumpkin) on GitHub. +2. **Create a Feature Branch**: + ```bash + git checkout -b feature/my-new-feature + ``` +3. **Commit Your Changes**: Follow the **Conventional Commits** specification (detailed below). +4. **Push and Open a Pull Request**: + - **PR Title**: Ensure your pull request title strictly conforms to the Conventional Commits specification. + - **Description**: Use our PR template to explain: + - **What** was changed? + - **Why** was it changed? + - **How** was it tested? + - Are there any known limitations or edge cases? + - **AI Agents**: Suffix your PR title with `🤖🤖🤖` if submitting via an automated AI agent. +5. **CI & Code Review**: GitHub Actions will automatically verify formatting, clippy lints, tests, and dependencies. Maintainers will review your implementation and provide feedback. + +--- + +## Conventional Commits Specification + +All commit messages and pull request titles in Pumpkin must strictly follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification. This convention ensures consistent commit history and automated release changelog generation. + +### Commit & PR Title Structure + +```text +(): + +[optional detailed body] + +[optional footer(s)] +``` + +### Commit Types + +| Type | Purpose | Example | +| :--- | :--- | :--- | +| `feat` | Introduces a new feature or vanilla game mechanic | `feat(entity): implement drowned swimming goal` | +| `fix` | Resolves a bug, glitch, or protocol issue | `fix(protocol): resolve creative slot index offset` | +| `docs` | Documentation additions or updates | `docs(developer): add porting versions guide` | +| `perf` | Code modifications that improve execution speed or memory usage | `perf(world): optimize chunk section block iteration` | +| `refactor` | Code changes that neither fix bugs nor add features | `refactor(command): unify argument parsing helpers` | +| `test` | Adding missing tests or correcting existing tests | `test(inventory): add crafting table recipe tests` | +| `chore` | Routine maintenance, dependency bumps, or toolchain updates | `chore(deps): bump tokio to 1.43` | +| `ci` | Modifications to CI workflows, scripts, or Docker containers | `ci(github): add machete dependency verification` | + +### Common Scopes + +Scopes indicate the crate or subsystem being modified: + +- **Crates**: `protocol`, `world`, `entity`, `command`, `inventory`, `data`, `util`, `macros`, `config` +- **Subsystems**: `bedrock`, `java`, `network`, `proxy`, `query`, `auth`, `plugin`, `linear`, `anvil` + +### Format Guidelines + +- **Imperative Mood**: Use imperative present tense in summaries (`add`, not `added` or `adds`; `fix`, not `fixed` or `fixes`). +- **Lowercase**: Start summaries with lowercase characters and omit trailing punctuation. +- **Breaking Changes**: Mark breaking API or protocol changes with an exclamation mark before the colon (`feat(protocol)!: overhaul packet codec`) or include `BREAKING CHANGE:` in the commit footer. +- **PR Title Uniformity**: Because PR titles are squashed into the main branch history on merge, PR titles must also follow this specification. diff --git a/docs/ja_jp/developer/entities/ai-and-mobs.md b/docs/ja_jp/developer/entities/ai-and-mobs.md new file mode 100644 index 0000000..bb1652f --- /dev/null +++ b/docs/ja_jp/developer/entities/ai-and-mobs.md @@ -0,0 +1,111 @@ +# Mob AI & Goal System + +Pumpkin implements a modular, priority-driven Artificial Intelligence (AI) goal system for computer-controlled mobs, located in [`crates/pumpkin/src/entity/ai/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity/ai) and [`crates/pumpkin/src/entity/mob/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity/mob). + +--- + +## The Goal System Architecture + +Mob behaviors are decomposed into independent modular tasks called **Goals**. Each mob possesses a **GoalSelector** that maintains an ordered collection of goals evaluated on every server tick. + +### Goal Priority Hierarchy + +Goals are assigned an integer priority (where lower numbers represent higher precedence). During each tick, higher-priority actions preempt lower-priority background tasks: + +| Priority | Goal Type | Evaluation Condition | Behavior | +| :--- | :--- | :--- | :--- | +| **1 (Highest)** | `SwimGoal` | Entity is submerged in water | Jump repeatedly to swim to the water surface and avoid drowning. | +| **2** | `MeleeAttackGoal` | Valid hostile target in sight/range | Navigate towards target entity and execute melee attacks. | +| **3** | `WanderAroundGoal` | No active combat target | Pick a random nearby navigable voxel and walk to it. | +| **4 (Lowest)** | `LookAtEntityGoal` | Player or mob within observation radius | Rotate head and body pitch/yaw towards the target entity. | + +### Execution Flow + +1. **Evaluation**: On every game tick, `GoalSelector::tick()` iterates through registered goals in order of priority. +2. **Activation**: If an inactive goal satisfies `can_start()`, it is activated via `start()`. +3. **Execution**: Active goals run their per-tick logic in `tick()`. +4. **Preemption & Termination**: If `should_continue()` returns `false`, or a mutually exclusive goal with higher priority activates, `stop()` is called and the goal resets. + +### The `Goal` Lifecycle + +Every AI behavior implements the `Goal` trait: + +```rust +pub trait Goal: Send + Sync { + /// Evaluates preconditions to determine if this goal can activate. + fn can_start(&self) -> bool; + + /// Called once when the goal transitions from inactive to active. + fn start(&mut self) {} + + /// Evaluates whether the goal should continue executing on subsequent ticks. + fn should_continue(&self) -> bool { + self.can_start() + } + + /// Executes the continuous behavior on each server tick. + fn tick(&mut self) {} + + /// Called when the goal completes, fails, or is preempted by a higher priority goal. + fn stop(&mut self) {} +} +``` + +--- + +## Standard Built-in Goals + +Pumpkin provides standard vanilla AI behaviors in `crates/pumpkin/src/entity/ai/`: + +| Goal | Description | Target Mobs | +| :--- | :--- | :--- | +| **`SwimGoal`** | Jumps repeatedly while submerged in water to prevent drowning. | Almost all land mobs | +| **`MeleeAttackGoal`** | Navigates towards an attack target and executes a strike when within reach. | Zombies, Spiders, Iron Golems | +| **`RangedAttackGoal`** | Stays at range and shoots projectiles (arrows, snowballs). | Skeletons, Pillagers, Blazes | +| **`FleeSunGoal`** | Seeks shade or water during daytime to prevent combustion. | Zombies, Skeletons | +| **`PanicGoal`** | Sprints in random directions when taking damage. | Cows, Pigs, Sheep, Chickens | +| **`WanderAroundGoal`** | Selects random accessible coordinates nearby when idle. | Passive & neutral mobs | +| **`LookAtEntityGoal`** | Rotates head to face nearby players within sensory range. | Villagers, Mobs | + +--- + +## Navigation & Pathfinding + +Mobs navigate the block world using Pumpkin's 3D grid pathfinder: + +1. **Path Calculation**: Resolves a path from the mob's origin to target coordinates using an A* algorithm optimized for Minecraft block geometries. +2. **Obstacle Avoidance**: + - Avoids hazardous blocks (lava, fire, sweet berry bushes). + - Prevents walking off ledges higher than the mob's fall threshold. + - Computes jumping steps over 1-block obstacles. +3. **Waypoint Steering**: The navigation engine updates the mob's velocity vectors per tick to smoothly guide the entity along waypoints. + +--- + +## Entity Attributes System + +Mob capabilities and stats are governed by the **Attributes** system in [`crates/pumpkin/src/entity/attributes.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/entity/attributes.rs): + +```rust +use pumpkin_data::entity::attribute::EntityAttribute; + +// Common vanilla attributes +EntityAttribute::GENERIC_MAX_HEALTH; // Base hit points (e.g. 20.0 for Zombies) +EntityAttribute::GENERIC_MOVEMENT_SPEED; // Base walking velocity (e.g. 0.23) +EntityAttribute::GENERIC_ATTACK_DAMAGE; // Melee attack damage (e.g. 3.0) +EntityAttribute::GENERIC_FOLLOW_RANGE; // Sensory detection range in blocks (e.g. 35.0) +EntityAttribute::GENERIC_KNOCKBACK_RESISTANCE; // Resistance against knockback (0.0 to 1.0) +``` + +### Modifiers +Attributes support dynamic modifiers with additive or multiplicative calculations (e.g., speed bonuses from Swiftness potions or armor attribute boosts). + +--- + +## Ageable & Passive Entities + +Passive animals (cows, sheep, pigs) implement the `Ageable` trait: + +- **Baby State**: Renders with smaller collision boxes and oversized heads. +- **Growing Age**: A negative tick timer counting upward to `0`. When it reaches `0`, the entity matures into an adult. +- **Breeding Cooldown**: Positive tick timer tracking cooldowns between successful matings. diff --git a/docs/ja_jp/developer/entities/index.md b/docs/ja_jp/developer/entities/index.md new file mode 100644 index 0000000..53623ee --- /dev/null +++ b/docs/ja_jp/developer/entities/index.md @@ -0,0 +1,77 @@ +# Entity Architecture & Hierarchy + +Entities in Minecraft represent all dynamic, living and non-living objects that exist within a world—ranging from dropped item stacks and arrows to zombies, villagers, and players. + +In Pumpkin, entity systems are implemented in [`crates/pumpkin/src/entity/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity). The architecture leverages object-oriented trait composition on top of concurrency-safe, cache-friendly data structures. + +--- + +## Entity Trait Hierarchy + +Pumpkin structures entities into an inheritance hierarchy using Rust traits: + +| Trait / Type | Parent Trait | Core Capabilities & State | +| :--- | :--- | :--- | +| `Entity` | *(None / Base Struct)* | Entity ID, UUID, atomic position and velocity vectors, bounding box, pose | +| `EntityBase` | *(Root Trait)* | Dynamic runtime downcasting (`get_living_entity`, etc.), world tick, damage, teleportation | +| `LivingEntity` | `EntityBase` | Health, absorption, potion effects, equipment slots, attribute containers | +| `Mob` | `LivingEntity` | AI goal selector, navigation, pathfinding, target tracking, death loot | +| `Player` | `LivingEntity` | Network client session, inventory, crafting screens, hunger, advancements | + +### 1. `Entity` (Base Struct) +Contains fundamental physics, geometry, and spatial state common to all entities: +- **`entity_id`**: Fast 32-bit unique integer identifier used across network packets. +- **`uuid`**: 128-bit persistent UUID. +- **`position` & `velocity`**: Thread-safe vectors (`AtomicCell>`) allowing concurrent reading without lock contention. +- **`bounding_box`**: 3D axis-aligned bounding box (AABB) used for collision resolution and raycast targeting. +- **`pose`**: Current pose (`Standing`, `Crouching`, `Sleeping`, `FallFlying`, etc.). + +### 2. `EntityBase` (Foundation Trait) +The root dynamic trait implemented by every entity in the server: +- Supports safe runtime downcasting via helper methods (`get_living_entity()`, `get_mob()`, `get_player()`). +- Provides hooks for world interaction: `tick()`, `damage()`, `teleport()`, `on_lightning_strike()`, `is_immune_to_explosion()`. + +### 3. `LivingEntity` (Biological Trait) +Extends `EntityBase` for entities with health, combat, and animation states: +- Manages health bars, absorption, invulnerability ticks, and fall damage. +- Tracks active potion effects (`crates/pumpkin/src/entity/effect/`). +- Handles equipment slots (`MainHand`, `OffHand`, `Helmet`, `Chestplate`, `Leggings`, `Boots`). +- Encapsulates attribute containers (max health, movement speed, knockback resistance). + +### 4. `Mob` (AI Trait) +Extends `LivingEntity` for computer-controlled mobs: +- Manages AI goal selectors (attacking, wandering, fleeing, looking around). +- Controls navigation and pathfinding over blocks. +- Spawns configured loot tables upon death. + +### 5. `Player` (Human Client) +Represents a human player connected through a game client: +- Links to network socket handlers (`JavaClient` or `BedrockClient`). +- Manages container screen handlers, ender chests, player inventory, and crafting. +- Tracks hunger, saturation, exhaustion, experience levels, and advancement criteria. + +--- + +## Physics & Movement Simulation + +On every server tick, Pumpkin simulates entity physics through a deterministic sequence: + +1. **Apply Velocity & Gravity**: Evaluates current velocity and adds downward gravitational acceleration unless `has_gravity()` is false or the entity is flying. +2. **Resolve Block Collisions**: Checks the entity's 3D axis-aligned bounding box (`bounding_box`) against solid block voxels in the surrounding chunk; velocity along colliding axes is dampened or zeroed. +3. **Apply Fluid Drag & Buoyancy**: Evaluates whether the entity intersects water or lava fluids, applying drag coefficients and buoyant upward force. +4. **Synchronize & Broadcast**: Persists the updated coordinates in atomic position cells and broadcasts relative delta movement packets to observing players. +5. **Void Death**: Non-player entities falling below $Y = -128$ are automatically despawned. + +--- + +## Combat & Damage System + +Damage calculations reside in [`crates/pumpkin/src/entity/combat.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/entity/combat.rs): + +1. **Damage Types**: Damage is categorized by `DamageType` (e.g., `GENERIC`, `PLAYER_ATTACK`, `FALL`, `FIRE`, `MAGIC`, `EXPLOSION`, `LIGHTNING_BOLT`). +2. **Damage Scaling**: + - Armor defense points mitigate incoming physical damage according to the vanilla formula: + $$\text{damage} = \text{damage} \times \left(1 - \frac{\min(20, \max(\text{armor} / 5, \text{armor} - \frac{\text{damage}}{2 + \text{toughness} / 4}))}{25}\right)$$ + - Resistance status effects and Protection enchantments apply secondary reductions. +3. **Knockback**: When damage is dealt, a directional impulse vector is added to the victim's velocity, modulated by their knockback resistance attribute. +4. **Hurt Animations**: Triggers hurt sounds and broadcasts red-tint flash metadata to all observers. diff --git a/docs/ja_jp/developer/entities/spawning-and-tracking.md b/docs/ja_jp/developer/entities/spawning-and-tracking.md new file mode 100644 index 0000000..82a3d7b --- /dev/null +++ b/docs/ja_jp/developer/entities/spawning-and-tracking.md @@ -0,0 +1,95 @@ +# Spawning & Network Tracking + +To maintain high tick rates with hundreds of active entities, Pumpkin utilizes a chunk-based entity tracking and streaming system. + +Entities are held in chunk-level collections inside [`crates/pumpkin-world`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-world) and synchronized across clients via edition-specific network packets. + +--- + +## The Spawning Pipeline + +Spawning an entity involves creating an instance, allocating its network IDs, registering it with the world, and broadcasting spawn packets to chunk observers: + +1. **Construct Entity Instance**: Instantiate the specific entity struct (such as `ZombieEntity`, `ArrowEntity`, or `ItemEntity`) initialized with its starting world and spatial coordinates. +2. **Allocate Unique Network Identifiers**: Assign an atomic 32-bit `entity_id` for network protocol packets and a 128-bit persistent UUID. +3. **Register in World & Chunk Map**: Insert the entity into the world's entity storage and index it within its corresponding chunk coordinate entry. +4. **Query Chunk Observers**: Look up all connected players within tracking range whose view distance includes the containing chunk. +5. **Broadcast Edition-Specific Spawn Packets**: Transmit the relevant spawn and metadata packets tailored to each observing client's edition (Java or Bedrock). + +### Spawning via the World API + +```rust +use std::sync::Arc; +use pumpkin_data::entity::EntityType; +use pumpkin_util::math::vector3::Vector3; +use crate::entity::Entity; +use crate::entity::mob::zombie::ZombieEntity; + +// 1. Create base entity with target world and position +let position = Vector3::new(100.5, 64.0, -200.5); +let base_entity = Entity::new(world.clone(), position, &EntityType::ZOMBIE); + +// 2. Wrap in specific entity implementation +let zombie = Arc::new(ZombieEntity::new(base_entity)); + +// 3. Register into the world +world.spawn_entity(zombie); +``` + +--- + +## Network Packet Synchronization + +When a player enters a chunk containing entities—or when an entity moves into an observed chunk—Pumpkin dispatches spawn packets tailored to the client's edition: + +### Java Edition Spawning +1. **`CSpawnEntity`**: Declares the entity ID, UUID, entity type, position, and initial pitch/yaw. +2. **`CSetEntityMetadata`**: Sends initial visual states (custom name tags, burning flags, baby/adult state). +3. **`CEntityVelocity`**: Synchronizes initial momentum. +4. **`CUpdateEntityRot` & `CHeadRot`**: Synchronizes head orientation independently from body yaw. + +### Bedrock Edition Spawning +1. **`CAddActor`**: Declares the runtime ID, actor type identifier, position, rotation, and embedded `SyncedActorDataList`. +2. **`CSetActorMotion`**: Sets initial velocity vector. + +--- + +## Movement & Position Synchronization + +Transmitting absolute 64-bit coordinates every tick would congest network bandwidth. Instead, Pumpkin dynamically switches between compact relative delta updates and authoritative full synchronization based on movement distance: + +| Metric / Scenario | Threshold | Java Protocol Packet | Bedrock Protocol Packet | Bandwidth Impact | +| :--- | :--- | :--- | :--- | :--- | +| **Incremental Movement** | $\Delta < 8$ blocks per tick | `CUpdateEntityPos` / `CUpdateEntityPosRot` | `CMoveActorDelta` | Minimal (fixed-point byte deltas) | +| **Teleport / Long Distance** | $\Delta \ge 8$ blocks or chunk warp | `CTeleportEntity` / `CEntityPositionSync` | `CMoveActorAbsolute` | Full 64-bit float coordinate payload | +| **Rotation Only** | Position unchanged | `CUpdateEntityRot` & `CHeadRot` | `CMoveActorDelta` (rot flags only) | Minimal (byte angles) | + +### 1. Relative Delta Movement +If the entity moved less than 8 blocks in a single tick, Pumpkin sends delta packets using fixed-point encoded increments: +- **Java**: `CUpdateEntityPos` (XYZ delta), `CUpdateEntityRot` (yaw/pitch delta), or `CUpdateEntityPosRot` (both). +- **Bedrock**: `CMoveActorDelta` with bitflags indicating which coordinates changed (`MOVE_ACTOR_DELTA_FLAG_HAS_X`, `HAS_Y`, `HAS_Z`). + +### 2. Full Position Synchronization +When an entity teleports, crosses chunk boundaries, or accumulates floating-point drift: +- **Java**: Sends `CEntityPositionSync` containing absolute coordinates, velocities, and on-ground flags. +- **Bedrock**: Sends `CMoveActorAbsolute`. + +--- + +## Passenger & Vehicle Mounting + +When an entity mounts another entity (e.g., a player mounting a horse, boat, or minecart): + +- **Java Edition**: Sends `CSetPassengers` containing the vehicle's `VarInt` entity ID and an array of passenger entity IDs. +- **Bedrock Edition**: Sends `CSetActorLink` defining the rider and vehicle runtime IDs and link type (`Rider`, `Passenger`). + +--- + +## Despawning & Entity Removal + +When an entity dies, is removed from the world, or steps out of a player's tracking radius: + +- **Java Edition**: Sends `CRemoveEntities` with an array of entity IDs. +- **Bedrock Edition**: Sends `CRemoveActor` with the entity's runtime ID. + +The client immediately frees its local rendering and audio memory for the removed entity. diff --git a/docs/ja_jp/developer/extractor.md b/docs/ja_jp/developer/extractor.md new file mode 100644 index 0000000..2b09f8f --- /dev/null +++ b/docs/ja_jp/developer/extractor.md @@ -0,0 +1,144 @@ +# Vanilla Data Extractor + +The [Pumpkin Extractor](https://github.com/Pumpkin-MC/Extractor) is a dedicated Fabric mod written in Kotlin that introspects a running vanilla Minecraft server or client to dump authoritative game registries and runtime metadata into structured JSON files. + +These JSON dumps are consumed by [`pumpkin-codegen`](/developer/codegen) to generate strongly-typed Rust code in `pumpkin-data` and `pumpkin-world`. + +--- + +## Datapacks vs. Extractor: Our Data Philosophy + +Before creating a new extractor, it is crucial to understand Pumpkin's data hierarchy: + +> [!IMPORTANT] +> **We preferably use vanilla datapacks whenever possible.** +> If vanilla Minecraft already exposes a registry or system through official datapack JSON files, we consume the datapack directly rather than writing custom extractor code. + +### When to Use Datapacks vs. When to Use the Extractor + +| Source | When to Use | Examples | +| :--- | :--- | :--- | +| **Vanilla Datapacks** *(Preferred)* | Any data officially serialized as JSON by Mojang in `data/minecraft/`. | Recipes, tags (`blocks`, `items`, `fluids`), biomes, dimension types, damage types, trim materials, worldgen noise parameters. | +| **Fabric Extractor** *(Only when necessary)* | Game data **hardcoded in Java bytecode** that cannot be exported from raw datapack files. | Block collision shapes, AABB bounding boxes, block hardness, packet ID-to-state mappings, entity tracked data IDs, fuel burn durations, composter chances, and flower pot conversion tables. | + +--- + +## Extractor Mod Architecture + +The Extractor runs on top of the Fabric loader using official Fabric Yarn mappings: + +- **Mod Entry Point (`src/main/kotlin/de/snowii/extractor/Extractor.kt`)**: Registers with Fabric's `ServerLifecycleEvents.SERVER_STARTED` event. When the headless server finishes booting, it invokes all registered extractors and dumps files to `run/pumpkin_extractor_output/`. +- **Extractor Interface**: Every data extractor implements the `Extractor.Extractor` interface: + ```kotlin + interface Extractor { + fun fileName(): String + + @Throws(Exception::class) + fun extract(server: MinecraftServer): JsonElement + + // Returns true if output belongs in tests/ rather than the root output folder + fun isTest(): Boolean = false + } + ``` +- **Output Directory**: JSON files are saved to `pumpkin_extractor_output/` encoded in UTF-8 without HTML escaping. + +--- + +## How to Add a New Extractor + +Follow this step-by-step workflow when you need to extract new hardcoded vanilla mechanics: + +### Step 1: Verify the Data Cannot Be Sourced from Datapacks +Inspect the official Minecraft client/server JAR or Mojang's data reports: +- Check `data/minecraft/` for existing registries, tags, or JSON definitions. +- If the data is present in the datapack, add it directly to `Pumpkin/assets/datapacks/` instead of writing an extractor. + +### Step 2: Create the Extractor Class +If the data is hardcoded in Java methods, create a new Kotlin class in: +```text +src/main/kotlin/de/snowii/extractor/extractors/.kt +``` + +Implement the `Extractor.Extractor` interface: + +```kotlin +package de.snowii.extractor.extractors + +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import de.snowii.extractor.Extractor +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.server.MinecraftServer +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack + +class Fuels : Extractor.Extractor { + override fun fileName(): String { + return "fuels.json" + } + + override fun extract(server: MinecraftServer): JsonElement { + val fuelsJson = JsonObject() + + // Query vanilla runtime methods + server.fuelValues().fuelItems().forEach { fuel -> + val itemId = BuiltInRegistries.ITEM.getKey(fuel).toString() + val burnDuration = server.fuelValues().burnDuration(ItemStack(fuel)) + fuelsJson.add(itemId, JsonPrimitive(burnDuration)) + } + + return fuelsJson + } +} +``` + +### Step 3: Register the Extractor +Open `src/main/kotlin/de/snowii/extractor/Extractor.kt` and add an instance of your class to the `extractors` array: + +```kotlin +val extractors = arrayOf( + // ... existing extractors ... + Blocks(), + Items(), + Entities(), + Fuels(), // [!code ++] +) +``` + +### Step 4: Run the Extractor +Execute the headless server task in the Extractor repository: + +```bash +./gradlew runServer +``` + +Once the extraction finishes, locate your new JSON file in: +```text +run/pumpkin_extractor_output/.json +``` + +### Step 5: Integrate with `pumpkin-codegen` +1. Move the generated JSON file into Pumpkin: + ```bash + cp run/pumpkin_extractor_output/fuels.json ../Pumpkin/assets/ + ``` +2. In the `Pumpkin` repository, open `tools/pumpkin-codegen`: + - Create a parser for `.json`. + - Write a code generation template to produce the corresponding Rust structs or static lookup tables in `crates/pumpkin-data`. +3. Run code generation: + ```bash + cargo run -p pumpkin-codegen + ``` +4. Verify that `pumpkin-data` compiles and passes all unit tests: + ```bash + cargo test -p pumpkin-data + ``` + +--- + +## Best Practices for Extractor Contributions + +- **Sort Keys Deterministically**: When generating JSON objects or lists, sort entries by numeric registry ID or alphabetically by identifier (`minecraft:stone`, `minecraft:dirt`). This prevents noisy git diffs when re-running extraction across updates. +- **Prefer Standard Types**: Use vanilla identifiers (`net.minecraft.resources.Identifier`) as string keys instead of arbitrary integers where possible. +- **Access Wideners**: If private vanilla fields or methods need to be accessed, declare an access widener in `src/main/resources/extractor.accesswidener` rather than resorting to brittle runtime reflection. diff --git a/docs/ja_jp/developer/introduction.md b/docs/ja_jp/developer/introduction.md new file mode 100644 index 0000000..a96b0d0 --- /dev/null +++ b/docs/ja_jp/developer/introduction.md @@ -0,0 +1,57 @@ +# Contributor Documentation + +Welcome to the internal contributor and developer documentation for Pumpkin! + +Pumpkin is an ultra-high-performance, extensible Minecraft server software written entirely in modern **Rust**. Designed from the ground up for concurrency, memory safety, and vanilla fidelity, Pumpkin supports both **Minecraft: Java Edition** and **Minecraft: Bedrock Edition**. + +This section provides comprehensive documentation for contributors working on the core Pumpkin server, its internal crates, and supporting tooling. + +--- + +## Core Philosophy & Design Principles + +Pumpkin is built on several foundational engineering principles: + +- **Zero-Cost Abstractions & Raw Performance**: We leverage Rust's zero-cost abstractions, efficient memory layouts, and cache-friendly data structures to minimize CPU and RAM overhead. +- **Strict Concurrency Model**: + - **Asynchronous I/O via Tokio**: Network socket handling, client handshakes, packets streaming, and file I/O run efficiently on an asynchronous runtime. + - **Parallel Compute via Rayon**: Heavy, CPU-bound computations—such as terrain generation, lighting calculations, chunk serialization, and physics—are dispatched to Rayon thread pools to prevent blocking the async network loop. + - **Thread-safe Communication**: Non-blocking channels (`tokio::sync::mpsc`, crossbeam) coordinate work seamlessly between async tasks and thread pools. +- **Robust Quality & Panic Freedom**: Pumpkin enforces strict Clippy rules. In production server code, panics, `unwrap()`, `expect()`, and unhandled errors are denied at compile time. +- **Modular Workspace Architecture**: Functionality is divided cleanly into focused crates (`pumpkin-protocol`, `pumpkin-world`, `pumpkin-inventory`, `pumpkin-command`, `pumpkin-data`, etc.). Each crate has specific responsibilities and clear dependency boundaries. +- **Protocol Fidelity**: We strive for strict vanilla compatibility while providing modern features like Linear region format support, Bedrock cross-play, and a polyglot WebAssembly plugin runtime. + +--- + +## Documentation Roadmap + +Explore the contributor documentation to understand how Pumpkin works and how to contribute effectively: + +| Section | Description | +| :--- | :--- | +| **[Contributing Guide](/developer/contributing)** | Step-by-step instructions for environment setup, building, testing, clippy rules, and PR standards. | +| **[Architecture & Crates](/developer/architecture)** | Deep dive into Pumpkin's multi-crate workspace structure, crate responsibilities, and data flow. | +| **[Data Extractor](/developer/extractor)** | Extracting hardcoded vanilla Minecraft registries, bounding boxes, and packet structures via our Fabric mod. | +| **[Networking Overview](/developer/networking/)** | Core networking architecture, transports, and multi-edition support. | +| **[Java Edition Networking](/developer/networking/java/overview)** | Protocol states, [adding packets](/developer/networking/java/adding-packets), and [authentication](/developer/networking/java/authentication). | +| **[Bedrock Edition Networking](/developer/networking/bedrock/overview)** | RakNet/NetherNet transport, [adding packets](/developer/networking/bedrock/adding-packets), [authentication](/developer/networking/bedrock/authentication), and [NetherNet](/developer/networking/bedrock/nethernet). | +| **[Proxy Forwarding](/developer/networking/proxy/)** | Secure player info forwarding for [BungeeCord](/developer/networking/proxy/bungeecord), [Velocity](/developer/networking/proxy/velocity), and [Vine](/developer/networking/proxy/vine). | +| **[Query Protocol](/developer/networking/query)** | GameSpy4 UDP query implementation for external monitoring bots and server lists. | +| **[Blocks](/developer/blocks/)** | Block architecture, states, collisions, properties, and [adding block behaviors](/developer/blocks/adding-blocks). | +| **[Items](/developer/items/)** | Item architecture, data components, food, cooldowns, and [adding item behaviors](/developer/items/adding-items). | +| **[Entities & Mobs](/developer/entities/)** | Entity trait hierarchy, [spawning & tracking](/developer/entities/spawning-and-tracking), and [mob AI & pathfinding](/developer/entities/ai-and-mobs). | +| **[Commands](/developer/commands/)** | Pure-Rust Brigadier command tree, [adding commands](/developer/commands/adding-commands), [arguments & suggestions](/developer/commands/arguments-and-suggestions), and [execution context](/developer/commands/execution-and-context). | +| **[Plugin Engine](/developer/plugins/)** | Internal plugin engine architecture, [adding plugin loaders](/developer/plugins/loaders), and [WASM signing & licensing](/developer/plugins/wasm-signing). | +| **[World Engine & Formats](/developer/world)** | Chunk loading, saving, Anvil/Linear/Slime/Pump formats, terrain generation, and lighting. | +| **[Code Generation](/developer/codegen)** | How `tools/pumpkin-codegen` processes vanilla Minecraft data to generate `pumpkin-data`. | +| **[Porting Minecraft Versions](/developer/porting-versions)** | End-to-end guide to upgrading Pumpkin: data extraction, assets & datapacks, codegen, compiler fixes, new block/item/entity logic, and ViaVersion remapping. | +| **[Mobile Development](/developer/mobile)** | Developing and compiling Pumpkin on mobile environments like Android (Termux). | + +--- + +## Plugin Development vs. Core Development + +> [!NOTE] +> If you are looking to build third-party plugins that extend Pumpkin rather than contributing to the server itself, please check out the dedicated **[Plugin Development](/plugin-dev/introduction)** section. +> +> Pumpkin supports writing plugins in **Rust**, **Python**, **C#**, **C**, **Go**, and **Kotlin** powered by our WebAssembly (Wasmtime) runtime and WIT interfaces. diff --git a/docs/ja_jp/developer/items/adding-items.md b/docs/ja_jp/developer/items/adding-items.md new file mode 100644 index 0000000..6335d14 --- /dev/null +++ b/docs/ja_jp/developer/items/adding-items.md @@ -0,0 +1,121 @@ +# Adding an Item Behavior + +This guide details how to implement custom interactive mechanics for an item in Pumpkin. + +--- + +## When Is an Item Behavior Needed? + +- **Passive Items** (e.g., diamonds, gold ingots, sticks): Do **not** require custom Rust code. Their stack sizes, tool categories, and crafting recipes are handled automatically by `pumpkin-data` and `pumpkin-inventory`. +- **Interactive Items** (e.g., snowballs, ender pearls, hoes, bows, potions): Require an implementation of `ItemBehaviour` to handle right-click events, cooldowns, animations, and entity spawns. + +--- + +## Step 1: Define the Item Struct & Metadata + +Create a new file under `crates/pumpkin/src/item/items/.rs` (e.g., `items/snowball.rs`): + +```rust +use std::sync::Arc; +use pumpkin_data::entity::EntityType; +use pumpkin_data::item::Item; +use pumpkin_data::sound::{Sound, SoundCategory}; +use crate::entity::Entity; +use crate::entity::player::Player; +use crate::entity::projectile::snowball::SnowballEntity; +use crate::item::{ItemBehaviour, ItemMetadata}; + +pub struct SnowBallItem; + +// 1. Declare the associated numeric Item ID(s) +impl ItemMetadata for SnowBallItem { + fn ids() -> Box<[u16]> { + [Item::SNOWBALL.id].into() + } +} + +// 2. Implement the interaction behavior +impl ItemBehaviour for SnowBallItem { + fn normal_use(&self, _item: &Item, player: &Player) { + let position = player.position(); + let world = player.world(); + + // Play throw sound effect + world.play_sound( + Sound::EntitySnowballThrow, + SoundCategory::Neutral, + &position, + ); + + // Construct and launch the projectile entity + let entity = Entity::new(world.clone(), position, &EntityType::SNOWBALL); + let snowball = SnowballEntity::new_shot(entity, player.get_entity()); + let (yaw, pitch) = player.rotation(); + snowball.thrown.set_velocity_from(pitch, yaw, 0.0, 1.5, 1.0); + world.spawn_entity(Arc::new(snowball)); + + // Safely decrement held item unless in Creative mode + let mut held = player.inventory.held_item(); + held.decrement_unless_creative(player.gamemode.load(), 1); + player.inventory.set_held_item(held); + } +} +``` + +--- + +## Step 2: `ItemBehaviour` Hook Reference + +The `ItemBehaviour` trait provides comprehensive hooks for different interaction models: + +### Instant Single-Use Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `normal_use` | `(&self, item: &Item, player: &Player)` | Triggered on right-clicking into open air. | +| `normal_use_with_rotation` | `(&self, item: &Item, player: &Player, yaw: f32, pitch: f32)` | Provides client-reported view angles for precise raycasting and aiming. | +| `use_on_block` | `(&self, item: &mut ItemStack, player: &Player, location: BlockPos, face: BlockDirection, cursor_pos: Vector3, block: &Block, server: &Server) -> BlockActionResult` | Triggered when right-clicking a block face (e.g., stripping logs with an axe, tilling farmland with a hoe). | +| `use_on_entity` | `(&self, item: &mut ItemStack, player: &Player, entity: Arc)` | Triggered when right-clicking another living entity (e.g., shearing sheep, nametags). | + +### Channeled & Timed Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `get_use_duration` | `(&self) -> i32` | Duration in game ticks required to complete the action (e.g., `32` ticks for food, `72000` ticks for bows). | +| `on_use_tick` | `(&self, stack: &ItemStack, player: &Player, remaining_use_ticks: i32)` | Executed on each tick while right-click is held. | +| `on_stopped_using` | `(&self, stack: &ItemStack, player: &Player)` | Triggered when the player releases right-click (e.g., firing a charged bow). | + +--- + +## Step 3: Durability & Cooldowns + +### Decrementing Stacks Safely +Always use `decrement_unless_creative` to ensure survival players consume items while creative players retain them: + +```rust +item_stack.decrement_unless_creative(player.gamemode.load(), 1); +``` + +### Applying Cooldowns +To prevent action spamming (e.g., Ender Pearls, Chorus Fruit): + +```rust +// Start a 20-tick (1-second) cooldown on Ender Pearls +player.start_cooldown(Item::ENDER_PEARL.registry_key.to_string(), 20); +``` + +--- + +## Step 4: Registering the Item + +1. Export your module in `crates/pumpkin/src/item/items/mod.rs`: + ```rust + pub mod snowball; + ``` + +2. Register the struct in `crates/pumpkin/src/item/registry.rs`: + ```rust + items.register(SnowBallItem); + ``` + +When the server initializes, `items.register` maps the IDs declared in `ItemMetadata::ids()` to your behavior instance. diff --git a/docs/ja_jp/developer/items/index.md b/docs/ja_jp/developer/items/index.md new file mode 100644 index 0000000..70f9f9b --- /dev/null +++ b/docs/ja_jp/developer/items/index.md @@ -0,0 +1,71 @@ +# Item Architecture & Data Components + +Items represent portable game objects held in player inventories, hotbars, armor slots, and world containers. In Pumpkin, item handling is divided into two primary subsystems: + +1. **Static Data Layer ([`pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data))**: Compile-time generated metadata including item IDs, max stack sizes, max durability, tool tier properties, food nutrition, and data components generated by [`pumpkin-codegen`](/developer/codegen). +2. **Dynamic Behavior Layer ([`pumpkin::item`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/item))**: Interactive right-click actions, projectile launching, eating durations, bow charging, and block interactions handled via the `ItemBehaviour` trait. + +--- + +## Static Items vs. Actionable Items + +| Item Type | Description | Examples | Runtime Behavior Struct Needed? | +| :--- | :--- | :--- | :--- | +| **Passive Items** | Items without active right-click effects | `diamond`, `stick`, `iron_ingot`, `feather` | No — Stacking, moving, crafting, and dropping are handled automatically by `pumpkin-inventory`. | +| **Standard Block Items** | Items corresponding directly to placeable blocks | `stone`, `oak_planks`, `glass` | No — Placement logic queries the target block's data. | +| **Projectiles & Throwables** | Items spawned into physical entities when thrown | `snowball`, `egg`, `ender_pearl`, `splash_potion` | Yes — Implements `ItemBehaviour::normal_use`. | +| **Channeled / Charged Items** | Items with use durations or charging animations | `bow`, `crossbow`, `spyglass`, `trident` | Yes — Implements `get_use_duration`, `on_use_tick`, `on_stopped_using`. | +| **Tools with Block Interactions**| Items modifying existing blocks in the world | `hoe` (tilling), `axe` (stripping), `shovel` (pathmaking) | Yes — Implements `ItemBehaviour::use_on_block`. | + +--- + +## The `ItemStack` Structure + +An `ItemStack` represents an instance of an item in an inventory container. It encapsulates: + +- **`item`**: Reference to the static [`Item`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-data/src/item.rs) definition. +- **`item_count`**: Current stack size ($1 \le \text{count} \le \text{max\_stack\_size}$). +- **Data Components**: Dynamic properties attached to the item stack (custom names, lore, damage values, enchantments, potion effects). + +```rust +use pumpkin_data::item::Item; +use pumpkin_data::item_stack::ItemStack; + +// Create a new item stack +let mut stack = ItemStack::new(1, Item::DIAMOND_SWORD); + +// Safely decrement stack count, respecting creative mode +stack.decrement_unless_creative(player.gamemode.load(), 1); +``` + +--- + +## Data Components System + +Modern Minecraft represents item properties via modular **Data Components**: + +| Component | Responsibility | Description | +| :--- | :--- | :--- | +| `FoodImpl` | Nutrition & Saturation | Controls hunger points restored and saturation ratio. | +| `ConsumableImpl` | Consumption Mechanics | Controls eating duration, sound effects, and animation types. | +| `Damageable` | Durability | Tracks current damage points and breaks when max durability is reached. | +| `UseCooldown` | Cooldown Group | Prevents item re-use for a specified duration in game ticks. | +| `Enchantments` | Enchantment Levels | Key-value mapping of applied enchantments and levels. | + +--- + +## The Item Registry & Dispatch Architecture + +Item interactions are managed by the `ItemRegistry` in [`crates/pumpkin/src/item/registry.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/item/registry.rs): + +1. **Registration**: At server startup, all `ItemBehaviour` implementations are registered along with their target numeric item IDs. +2. **Interaction Routing**: When a client sends use packets (`CUseItem`, `CUseItemOn`), Pumpkin evaluates cooldowns and routes execution to the registered `ItemBehaviour`. +3. **Cooldown System**: Players track active cooldowns keyed by cooldown group strings. If an item is on cooldown, interactions are canceled before calling the behavior. + +--- + +## Next Steps + +To implement interactive mechanics for a new item, proceed to the practical guide: + +- **[Adding an Item](/developer/items/adding-items)**: Step-by-step instructions for implementing `ItemMetadata`, `ItemBehaviour`, managing cooldowns, and registering items. diff --git a/docs/ja_jp/developer/mobile.md b/docs/ja_jp/developer/mobile.md new file mode 100644 index 0000000..2ee78e2 --- /dev/null +++ b/docs/ja_jp/developer/mobile.md @@ -0,0 +1,81 @@ +# Pumpkin Development on Mobile + +If you are a mobile user and want to edit the source code, you can do this! +(This page was written on Android using Helix.) + +First of all, we need a terminal app. +We recommend [Termux](https://github.com/termux/termux-app/releases) because it's stable and open source. +Download the needed apk file for your device's architecture and install Termux. + +After this, you need to run some commands. We use Helix for its simplicity. + +```bash + pkg update && pkg upgrade + pkg install build-essential git rust rust-analyzer taplo helix helix-grammar nodejs +``` + +If you want to contribute, you need to install the GitHub software. + +```bash + pkg install gh +``` + +We also recommend installing the fish shell because it's more friendly than bash. + +```bash + pkg install fish + chsh -s fish +``` + +Now that you've installed basic tools, we need to do some setup. +If you want to contribute, you need log into GitHub. + +```bash + gh auth login +``` + +Also setup git: change the editor to vim, edit your credentials, etc. + +After this, you need to clone the Pumpkin repo. (Before this, you can create a project directory with `mkdir proj`; it's useful) + +```bash + git clone https://github.com/Pumpkin-MC/Pumpkin.git +``` + +If you want to contribute, you need to fork our repo and change `Pumpkin-MC` to your username on GitHub. + +Setup's all done now! Enjoy :) + +## FAQ + +### How to use the text editor? + +Type `hx `. + +### How to navigate through the project? + +You can use `ls`, `cd`, and other programs. +You can also use `hx ` to browse your directory on startup. + +### How can I type in the editor? + +Press `i` if you are in normal mode. + +### HOW EXIT FROM EDITOR???? + +Press esc, then type `:q!` if you don't want to save, or `:wq` if you do want to save. + +### Where can I learn how to use this editor? + +Run `hx --tutor` or go their official website. + +### Why not use VS Code? + +1) VS Code is hard to set up, and it works with limited functionality on web. +2) rust-analyzer doesn't work on it. Maybe an emulator can help with this, but that slows down code compilation. +3) With VS Code, it's highly desirable to have a mouse, while in Helix you only need a keyboard. +4) VS Code is laggy on some devices. + +### Why is it so hard to type? + +Buy a cheap bluetooth keyboard and see how much easier it becomes. diff --git a/docs/ja_jp/developer/networking/authentication.md b/docs/ja_jp/developer/networking/authentication.md new file mode 100644 index 0000000..29c5e0d --- /dev/null +++ b/docs/ja_jp/developer/networking/authentication.md @@ -0,0 +1,57 @@ +# Authentication + +## Why authentication + +Offline accounts, that is, accounts generated from a player's username without contacting an authorization or authentication server, can have any nickname chosen. This, without additional plugins, means that players can impersonate other players, including those with operator permissions. + +## Offline server + +By default, `online_mode` is enabled in the configuration (`[networking.java.online_mode]` / `[networking.bedrock.online_mode]`). This enables authentication, disabling offline accounts. If you want to allow offline accounts, you can disable `online_mode` in `pumpkin.toml`. + +## How Yggdrasil Auth works + +1. The client gets an authentication token and UUID from the launcher. +2. The client, during loading, fetches data from the authorization/authentication server using the authentication token, such as various signing keys and the list of blocked servers. +3. The client, when joining the server, sends a join request to the authorization/authentication servers. Mojang servers can deny this request if the account is banned. +4. The client sends its identification to the server in a packet. +5. The server, based on this identification, sends a `hasJoined` request to the authorization/authentication servers. If it succeeds, it obtains the player information, such as the skin. + +## Custom Authentication Server + +Pumpkin supports custom authentication servers and fallbacks configured in `pumpkin.toml` under `[networking.java.authentication]`. + +### How Pumpkin Authentication Works + +1. **GET Request:** Pumpkin sends a GET request to the specified authentication URL. +2. **Status Code 200:** If the authentication is successful, the server responds with a status code of 200. +3. **Parse JSON Game Profile:** Pumpkin parses the JSON game profile returned in the response. + +### Game Profile + +```rust +struct GameProfile { + id: UUID, + name: String, + properties: Vec, + profile_actions: Option>, // Optional, only present when actions are applied +} +``` + +#### Property + +```rust +struct Property { + name: String, + value: String, // Base64 encoded + signature: Option, // Optional, Base64 encoded +} +``` + +#### Profile Action + +```rust +enum ProfileAction { + FORCED_NAME_CHANGE, + USING_BANNED_SKIN, +} +``` diff --git a/docs/ja_jp/developer/networking/bedrock/adding-packets.md b/docs/ja_jp/developer/networking/bedrock/adding-packets.md new file mode 100644 index 0000000..f68b888 --- /dev/null +++ b/docs/ja_jp/developer/networking/bedrock/adding-packets.md @@ -0,0 +1,182 @@ +# Adding a Bedrock Edition Packet + +This guide explains how to implement, encode, decode, and handle a new Minecraft: Bedrock Edition packet in Pumpkin. + +--- + +## Naming Conventions + +- **Clientbound packets** (Server → Client) must start with **`C`** (e.g., `CChunkRadiusUpdated`, `CContainerOpen`). +- **Serverbound packets** (Client → Server) must start with **`S`** (e.g., `SRequestChunkRadius`, `SPlayerAction`). + +--- + +## Step 1: Identifying the Packet ID + +In the Bedrock protocol, packet IDs are fixed integer constants assigned by Mojang. Verify the numeric ID from the official protocol documentation or network packet traces. + +For example: +- `Disconnect` packet ID: `5` +- `RequestChunkRadius` packet ID: `69` + +--- + +## Step 2: Implementing a Clientbound Packet + +Clientbound packets implement the `PacketWrite` trait from `pumpkin-protocol::serial`. + +### 1. Create the Packet File +Place the file in `crates/pumpkin-protocol/src/bedrock/client/.rs`: + +```rust +use std::io::{Error, Write}; +use pumpkin_macros::packet; +use crate::{codec::var_int::VarInt, serial::PacketWrite}; + +#[packet(5)] +pub struct CDisconnect { + pub reason: VarInt, + pub skip_message: bool, + pub message: String, + pub filtered_message: String, +} + +impl CDisconnect { + #[must_use] + pub const fn new(reason: i32, message: String) -> Self { + Self { + reason: VarInt(reason), + skip_message: message.is_empty(), + message, + filtered_message: String::new(), + } + } +} + +impl PacketWrite for CDisconnect { + fn write(&self, writer: &mut W) -> Result<(), Error> { + self.reason.write(writer)?; + self.skip_message.write(writer)?; + if !self.skip_message { + self.message.write(writer)?; + self.filtered_message.write(writer)?; + } + Ok(()) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/bedrock/client/mod.rs`: + +```rust +pub mod disconnect; +pub use disconnect::*; +``` + +--- + +## Step 3: Implementing a Serverbound Packet + +Serverbound packets implement the `PacketRead` trait. Pumpkin provides a procedural derive macro `#[derive(PacketRead)]` for automatic deserialization when fields implement `PacketRead`. + +### 1. Create the Packet File +Place the file in `crates/pumpkin-protocol/src/bedrock/server/.rs`: + +```rust +use pumpkin_macros::packet; +use crate::{codec::var_int::VarInt, serial::PacketRead}; + +#[derive(PacketRead, Debug)] +#[packet(69)] +pub struct SRequestChunkRadius { + pub chunk_radius: VarInt, + pub max_chunk_radius: u8, +} +``` + +If a packet requires custom parsing logic, implement `PacketRead` manually: + +```rust +use std::io::{Error, Read}; +use crate::serial::PacketRead; + +impl PacketRead for SMyCustomPacket { + fn read(reader: &mut R) -> Result { + let chunk_radius = VarInt::read(reader)?; + let max_chunk_radius = u8::read(reader)?; + Ok(Self { chunk_radius, max_chunk_radius }) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/bedrock/server/mod.rs`: + +```rust +pub mod request_chunk_radius; +pub use request_chunk_radius::*; +``` + +--- + +## Step 4: Dispatching Incoming Packets in `pumpkin` + +Incoming packets are dispatched in `crates/pumpkin/src/net/bedrock/mod.rs`: + +```rust +// Inside BedrockClient::handle_game_packet +match packet.id { + SRequestChunkRadius::PACKET_ID => { + let packet = SRequestChunkRadius::read(reader)?; + self.handle_request_chunk_radius(player, &packet); + } + _ => {} +} +``` + +--- + +## Step 5: Implementing the Handler Function + +Implement the handler logic in `crates/pumpkin/src/net/bedrock/play/.rs`: + +```rust +use super::*; +use pumpkin_protocol::bedrock::server::request_chunk_radius::SRequestChunkRadius; + +impl BedrockClient { + pub fn handle_request_chunk_radius(&self, player: &Arc, packet: &SRequestChunkRadius) { + let requested_radius = packet.chunk_radius.0; + + // Clamp view distance within server configuration limits + let clamped = requested_radius.clamp(2, 16); + + // Notify client that view radius was accepted + self.try_enqueue_client_packet(&CChunkRadiusUpdated { + chunk_radius: VarInt(clamped), + }); + } +} +``` + +Remember to register the new module in `crates/pumpkin/src/net/bedrock/play/mod.rs`: + +```rust +pub mod request_chunk_radius; +``` + +--- + +## Step 6: Sending Outgoing Packets + +To send a Bedrock packet to a connected client: + +```rust +// Enqueues the packet into the client's outgoing batch queue +self.try_enqueue_client_packet(&CChunkRadiusUpdated { + chunk_radius: VarInt(view_distance), +}); +``` + +Batched packets are automatically compressed, framed with `0xfe`, and transmitted over the RakNet/NetherNet socket. diff --git a/docs/ja_jp/developer/networking/bedrock/authentication.md b/docs/ja_jp/developer/networking/bedrock/authentication.md new file mode 100644 index 0000000..1571a73 --- /dev/null +++ b/docs/ja_jp/developer/networking/bedrock/authentication.md @@ -0,0 +1,61 @@ +# Bedrock Edition Authentication + +Minecraft: Bedrock Edition implements a certificate chain authentication protocol based on JSON Web Tokens (JWT) cryptographically anchored to Microsoft / Xbox Live root public keys. + +Authentication handling is powered by the [`pumpkin-auth`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-auth) crate and processed during the Bedrock login handshake in `crates/pumpkin/src/net/bedrock/login/`. + +--- + +## The Handshake Flow + +When a Bedrock client initiates connection, it sends an `SLogin` packet containing the client's protocol version, an **Identity Chain**, and **Client Data**: + +1. **Token Acquisition**: The Bedrock client signs into Xbox Live, obtaining a chained JSON Web Token (JWT) certificate signed by Microsoft's CA. +2. **Login Transmission**: The client packages this identity chain alongside a separate `ClientData` JWT into `SLogin`. +3. **Chain Validation**: Pumpkin walks the certificate chain, verifying each token's signature against Mojang's public root keys and extracting the player's Xbox User ID (XUID), Gamertag, and ephemeral session public key. +4. **Hardware Verification**: Pumpkin verifies the `ClientData` payload using the client's ephemeral public key, extracting geometry, skins, and device OS telemetry. +5. **Session Authorization**: Once validated, Pumpkin emits `CPlayStatus` (Login Success) and progresses to resource pack negotiation. + +--- + +## Token Payloads + +### 1. Identity Chain (`Chain`) +An array of JWT tokens forming an unbroken chain of trust: +- **Root Token**: Cryptographically signed by Mojang's official public root certificate authority. +- **Intermediate Tokens**: Issued by Xbox Live services, verifying the player's Xbox Live User ID (**XUID**), gamertag, and generating an ephemeral ECDSA public key for the session. +- **Claims**: Includes `extraData` with `displayName`, `identity` (UUID), `XUID`, and `titleId`. + +### 2. Client Data (`ClientData`) +A separate JWT signed with the client's ephemeral private key corresponding to the public key in the identity chain: +- **Hardware & Environment**: `DeviceOS`, `DeviceModel`, `CurrentInputMode`, `DefaultInputMode`, `ClientRandomId`. +- **Game Version**: `GameVersion` string. +- **Player Cosmetics**: Full skin texture bitmaps, cape data, geometry names, and persona definitions. + +--- + +## Verification in `pumpkin-auth` + +Pumpkin verifies token signatures using **NIST P-384 ECDSA** cryptography: + +```rust +// In crates/pumpkin/src/net/bedrock/login/mod.rs +match AuthPayload::verify(&jwt_payload) { + Ok(profile) => { + // Successfully verified against Mojang public keys + info!("Authenticated Bedrock player: {} (XUID: {})", profile.name, profile.xuid); + } + Err(LoginError::ChainValidationFailed(err)) => { + warn!("Bedrock JWT chain validation failed: {err}"); + self.try_kick(DisconnectReason::Kicked, "Authentication failed".into()); + } + Err(LoginError::SelfSignedNotAllowed) => { + warn!("Rejected unauthenticated self-signed Bedrock client"); + self.try_kick(DisconnectReason::Kicked, "Server enforces Xbox Live authentication".into()); + } +} +``` + +### Online vs. Offline Mode +- **Online Mode** (`online_mode = true` in `pumpkin.toml`): The chain must successfully validate against Mojang's public keys. Self-signed or unsigned tokens are immediately rejected. +- **Offline Mode** (`online_mode = false`): Pumpkin accepts self-signed client chains, generating offline UUIDs while extracting cosmetic skins and device metadata. diff --git a/docs/ja_jp/developer/networking/bedrock/nethernet.md b/docs/ja_jp/developer/networking/bedrock/nethernet.md new file mode 100644 index 0000000..88c92f0 --- /dev/null +++ b/docs/ja_jp/developer/networking/bedrock/nethernet.md @@ -0,0 +1,61 @@ +# NetherNet WebRTC Transport + +**NetherNet** is Mojang's modern, WebRTC-based transport protocol for Minecraft: Bedrock Edition. Designed for high-resilience peer-to-peer and relayed communication, NetherNet enables seamless cross-platform multiplayer (mobile, console, desktop) without requiring server operators or players to configure router port-forwarding. + +Pumpkin provides a native, zero-dependency NetherNet implementation in [`crates/pumpkin/src/net/bedrock/nethernet.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/bedrock/nethernet.rs). + +--- + +## Architectural Layers + +| Layer | Implementation | Responsibility | +| :--- | :--- | :--- | +| **HTTP Signaling** | Axum HTTP Router (`ice_router.rs`) | Handles SDP Offer (`POST /offer`), SDP Answer (`GET /answer`), and ICE Candidates (`POST /candidate`) | +| **NAT Traversal** | ICE Agent | Coordinates STUN/TURN binding discovery and hole punching | +| **Transport Security** | DTLS Handshake | Generates ephemeral session keys and secures the WebRTC transport using ECDSA P-384 keys | +| **Data Delivery** | WebRTC DataChannels | Unordered/ordered datagram channels streaming standard Bedrock game batches to the dispatcher | + +--- + +## Core Components in Pumpkin + +Pumpkin's NetherNet subsystem is divided into four distinct modules: + +### 1. Cryptographic Identity (`nethernet-key.der`) +- Pumpkin generates and loads a **NIST P-384 ECDSA private key** (`nethernet-key.der`). +- Used to cryptographically sign session descriptions (SDP offers and answers), authenticating the server's identity to connecting clients. +- Encoded using standard PKCS#8 DER formatting. + +### 2. HTTP Signaling Router (`ice_router.rs`) +Signaling is handled over HTTP via an [Axum](https://github.com/tokio-rs/axum) router: + +- **`POST /offer`**: The client submits an SDP offer describing its WebRTC codecs, transport profiles, and network interfaces. +- **`GET /answer`**: Pumpkin generates a corresponding local SDP answer containing its negotiated capabilities. +- **`POST /candidate`**: Both endpoints exchange trickle ICE candidates to discover the fastest direct network path (host, server-reflexive, or relay). + +### 3. Local Network Discovery (`discovery.rs`) +- Listens and broadcasts availability on the local subnet via UDP multicast. +- Allows Bedrock clients on the same Wi-Fi or LAN to discover and connect to the Pumpkin server automatically without entering an IP address. + +### 4. DataChannel Packet Streaming +- Once the WebRTC peer connection transitions to the `Connected` state, an ordered, reliable **WebRTC DataChannel** is opened. +- Bedrock batch packets (`0xfe`) flow bidirectionally through the DataChannel directly into Pumpkin's `incoming_game_packet_send` channel. + +--- + +## Configuration in `pumpkin.toml` + +NetherNet is configured under `[networking.bedrock.nethernet]` in `pumpkin.toml`: + +```toml +[networking.bedrock] +enabled = true +address = "0.0.0.0:19132" + +[networking.bedrock.nethernet] +enabled = true +signaling_address = "0.0.0.0:19133" +ice_servers = [ + "stun:stun.l.google.com:19302" +] +``` diff --git a/docs/ja_jp/developer/networking/bedrock/overview.md b/docs/ja_jp/developer/networking/bedrock/overview.md new file mode 100644 index 0000000..ae0d615 --- /dev/null +++ b/docs/ja_jp/developer/networking/bedrock/overview.md @@ -0,0 +1,80 @@ +# Bedrock Edition Networking Overview + +Pumpkin features native support for **Minecraft: Bedrock Edition** clients (iOS, Android, Windows, Xbox, PlayStation, Switch). + +Bedrock protocol structures and wire codecs reside in `pumpkin-protocol::bedrock`, while connection management and gameplay handlers reside in `pumpkin::net::bedrock`. + +--- + +## Transport Layers: RakNet & NetherNet + +Unlike Java Edition, which communicates exclusively over TCP streams, Bedrock Edition operates over UDP using two primary transport layers: + +1. **RakNet (Port 19132)**: + - A reliable, sequenced UDP networking library originally developed by Jenkins Software. + - Provides connection handshakes, packet acknowledgment (ACK/NACK), MTU negotiation, and packet splitting for payloads larger than the network MTU. +2. **NetherNet**: + - Modern peer-to-peer and relayed transport protocol used by modern Bedrock versions. + - Integrated in Pumpkin via `crates/pumpkin/src/net/bedrock/nethernet.rs`. + +--- + +## Packet Framing & Batching (`0xfe`) + +Bedrock does not send individual game packets over the network directly. Instead, multiple game packets are bundled into a single **batch packet**: + +1. **Header Byte**: Every Bedrock game packet batch starts with `0xfe`. +2. **Compression**: The payload following `0xfe` is compressed using **Deflate** (or ZLib) above a negotiated threshold. +3. **Sub-packet Framing**: When decompressed, the batch contains one or more sequential game packets, each prefixed by its length encoded as a `VarUInt`. + +In Pumpkin, batch decoding and assembly are managed by `BedrockBatchDecoder` and `BedrockBatchEncoder` in `crates/pumpkin-protocol/src/bedrock/`. + +--- + +## Connection Lifecycle & State Progression + +Bedrock does not have explicit protocol states like Java Edition. Instead, it follows a strict sequence of packets: + +| Step | Packet Exchanged | Purpose | +| :--- | :--- | :--- | +| **1. Network Settings** | `SRequestNetworkSettings` -> `CNetworkSettings` | Negotiates compression algorithm (Deflate/Snappy) and payload threshold | +| **2. Authentication** | `SLogin` -> `CPlayStatus` | Transmits JWT identity chain and client hardware data; verifies Xbox Live tokens | +| **3. Resource Negotiation** | `CResourcePacksInfo` -> `SResourcePackClientResponse` | Negotiates server resource packs and download behavior | +| **4. World Initialization** | `CStartGame` + `CItemRegistry` + `CCreativeContent` | Streams world spawn position, dimension settings, runtime player entity ID, and item palettes | +| **5. Ready to Spawn** | `SSetLocalPlayerAsInitialized` -> `CPlayStatus` | Signals client loading completion and transitions to active in-game play | + +--- + +## Packet Dispatching in `pumpkin` + +Inside `crates/pumpkin/src/net/bedrock/mod.rs`, decompressed sub-packets are dispatched based on their numeric packet ID: + +```rust +match packet.id { + SPlayerAuthInput::PACKET_ID => { + let packet = SPlayerAuthInput::read(reader)?; + self.handle_player_auth_input(player, packet, server); + } + SRequestChunkRadius::PACKET_ID => { + let packet = SRequestChunkRadius::read(reader)?; + self.handle_request_chunk_radius(player, &packet); + } + SInventoryTransaction::PACKET_ID => { + let packet = SInventoryTransaction::read(reader)?; + self.handle_inventory_action(player, packet); + } + _ => { + trace!("Unhandled Bedrock packet ID: {}", packet.id); + } +} +``` + +--- + +## Authentication & NetherNet + +- **JWT Authentication**: Bedrock verifies player identities via certificate chains signed by Microsoft / Xbox Live root authorities. +- **NetherNet WebRTC**: High-performance peer-to-peer transport using WebRTC DataChannels, ICE NAT traversal, and P-384 ECDSA keys. + +For complete implementation details, see **[Bedrock Authentication & NetherNet](/developer/networking/bedrock/authentication)**. + diff --git a/docs/ja_jp/developer/networking/index.md b/docs/ja_jp/developer/networking/index.md new file mode 100644 index 0000000..0685c45 --- /dev/null +++ b/docs/ja_jp/developer/networking/index.md @@ -0,0 +1,49 @@ +# Networking Overview + +Pumpkin is built with a high-performance, asynchronous networking stack capable of servicing both **Minecraft: Java Edition** and **Minecraft: Bedrock Edition** clients simultaneously. + +All protocol definitions, serialization traits, and wire-level codecs are isolated in the [`pumpkin-protocol`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-protocol) crate, while client connection management, session state machines, and gameplay packet dispatching are managed in [`pumpkin/src/net`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/net). + +--- + +## Supported Editions + +Pumpkin separates network implementations by edition due to their fundamentally distinct transport layers, packet encodings, and state models: + +| Feature | Minecraft: Java Edition | Minecraft: Bedrock Edition | +| :--- | :--- | :--- | +| **Transport Layer** | TCP socket streams | UDP via RakNet & NetherNet | +| **Framing** | VarInt length-prefixed frames | Batched packets encapsulated in `0xfe` headers | +| **Encryption** | AES-128 / CFB8 stream cipher | Cryptographic session handshake | +| **Compression** | Per-packet ZLib (above configurable threshold) | Per-batch Deflate / ZLib compression | +| **State Model** | Explicit states: Handshake, Status, Login, Config, Play | Packet-driven state progression | +| **Protocol Crate** | `pumpkin-protocol::java` | `pumpkin-protocol::bedrock` | +| **Server Net Module** | `pumpkin::net::java` | `pumpkin::net::bedrock` | + +--- + +## Networking Architecture + +The networking pipeline routes incoming connections through specialized transport and codec layers before delegating to the unified server game loop: + +1. **Transport Listeners**: Independent listeners bind to their respective ports—Tokio TCP for Java Edition (`:25565`) and UDP/RakNet/NetherNet for Bedrock Edition (`:19132`). +2. **Session Abstraction**: Each accepted connection spawns an isolated session task (`JavaClient` or `BedrockClient`) managing wire framing and encryption. +3. **Codec Deserialization**: Wire bytes are deserialized into typed packet structs via `pumpkin-protocol::java` (`ClientPacket`/`ServerPacket`) and `pumpkin-protocol::bedrock` (`PacketWrite`/`PacketRead`). +4. **Unified Gameplay Dispatch**: Decoded packets are dispatched into the unified game loop, updating player entity states and streaming world chunks. + +--- + +## Navigating the Networking Docs + +Explore the documentation for each edition: + +- **[Entity Metadata & Synced Data](/developer/networking/metadata)**: Synchronization of visual entity states (DataWatchers in Java, Actor Data in Bedrock). +- **[Java Edition](/developer/networking/java/overview)**: + - **[Architecture & Lifecycle](/developer/networking/java/overview)**: State machine, `JavaClient` vs. `Player`, and multi-version negotiation. + - **[How to Add a Packet](/developer/networking/java/adding-packets)**: Guide to defining, serializing, and dispatching Java Edition packets. + - **[Authentication](/developer/networking/java/authentication)**: Mojang Yggdrasil validation, GameProfiles, and proxy forwarding. +- **[Bedrock Edition](/developer/networking/bedrock/overview)**: + - **[Architecture & Transports](/developer/networking/bedrock/overview)**: RakNet and NetherNet integration, batching, and session handling. + - **[How to Add a Packet](/developer/networking/bedrock/adding-packets)**: Guide to defining and handling Bedrock Edition packets. + - **[Authentication & NetherNet](/developer/networking/bedrock/authentication)**: JWT certificate chain verification, WebRTC DataChannels, P-384 keys, and signaling. +- **[RCON](/developer/networking/rcon)**: Implementation of Valve's Remote Console protocol for administrative management. diff --git a/docs/ja_jp/developer/networking/java/adding-packets.md b/docs/ja_jp/developer/networking/java/adding-packets.md new file mode 100644 index 0000000..1800d06 --- /dev/null +++ b/docs/ja_jp/developer/networking/java/adding-packets.md @@ -0,0 +1,205 @@ +# Adding a Java Edition Packet + +This guide walks you through implementing, serializing, dispatching, and sending a new Java Edition packet in Pumpkin. + +--- + +## Naming Conventions + +To keep packet definitions uniform across the codebase, follow these naming conventions: + +- **Clientbound packets** (Server → Client) must start with **`C`** (e.g., `CBlockUpdate`, `CPlayDisconnect`). +- **Serverbound packets** (Client → Server) must start with **`S`** (e.g., `SChatCommand`, `SPlayerPosition`). +- If a packet exists in multiple states, prefix the state name before the packet action (e.g., `CLoginDisconnect`, `CConfigDisconnect`, and `CPlayDisconnect`). + +--- + +## Step 1: Find the Packet ID in `pumpkin-data` + +Packet IDs across all supported Minecraft versions are maintained in `pumpkin-data`. Import the corresponding constant: + +```rust +// Clientbound Play state disconnect +use pumpkin_data::packet::clientbound::play::DISCONNECT; + +// Serverbound Play state player position +use pumpkin_data::packet::serverbound::play::MOVE_PLAYER_POS; +``` + +--- + +## Step 2: Implementing a Clientbound Packet + +Clientbound packets are sent from Pumpkin to the Minecraft client. They implement the `ClientPacket` trait from `pumpkin-protocol`. + +### 1. Create the Struct File +Place the file in `crates/pumpkin-protocol/src/java/client//.rs` (e.g., `play/disconnect.rs`): + +```rust +use std::io::Write; +use pumpkin_data::packet::clientbound::play::DISCONNECT; +use pumpkin_macros::java_packet; +use pumpkin_protocol::ClientPacket; +use pumpkin_protocol::ser::{NetworkWriteExt, WritingError}; +use pumpkin_util::text::TextComponent; +use pumpkin_util::version::JavaMinecraftVersion; + +/// Displays a disconnect screen with a message to the player in Play state. +#[java_packet(DISCONNECT)] +pub struct CPlayDisconnect<'a> { + pub reason: &'a TextComponent, +} + +impl<'a> CPlayDisconnect<'a> { + #[must_use] + pub const fn new(reason: &'a TextComponent) -> Self { + Self { reason } + } +} + +impl ClientPacket for CPlayDisconnect<'_> { + fn write_packet_data( + &self, + mut write: impl Write, + version: &JavaMinecraftVersion, + ) -> Result<(), WritingError> { + // Use NetworkWriteExt methods to serialize fields + write.write_component(self.reason, version) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/java/client//mod.rs`: + +```rust +mod disconnect; +pub use disconnect::*; +``` + +--- + +## Step 3: Implementing a Serverbound Packet + +Serverbound packets are received by Pumpkin from the game client. They implement the `ServerPacket` trait from `pumpkin-protocol`. + +### 1. Create the Struct File +Place the file in `crates/pumpkin-protocol/src/java/server//.rs` (e.g., `play/player_position.rs`): + +```rust +use pumpkin_data::packet::serverbound::play::MOVE_PLAYER_POS; +use pumpkin_macros::java_packet; +use pumpkin_protocol::ServerPacket; +use pumpkin_protocol::ser::{NetworkReadExt, ReadingError}; +use pumpkin_util::math::vector3::Vector3; +use pumpkin_util::version::JavaMinecraftVersion; + +#[java_packet(MOVE_PLAYER_POS)] +pub struct SPlayerPosition { + pub position: Vector3, + pub collision: u8, +} + +impl<'a> ServerPacket<'a> for SPlayerPosition { + fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result { + let x = bytebuf.get_f64_be()?; + let y = bytebuf.get_f64_be()?; + + // Handle version-specific fields if necessary + if *version <= JavaMinecraftVersion::V_1_7_6 { + let _stance = bytebuf.get_f64_be()?; + } + + let z = bytebuf.get_f64_be()?; + let collision = bytebuf.get_u8()?; + + Ok(Self { + position: Vector3::new(x, y, z), + collision, + }) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/java/server//mod.rs`: + +```rust +mod player_position; +pub use player_position::*; +``` + +--- + +## Step 4: Network Codec Extension Helpers + +`NetworkWriteExt` and `NetworkReadExt` provide helper methods for standard Minecraft types: + +| Data Type | Reading Method | Writing Method | +| :--- | :--- | :--- | +| **`VarInt`** | `bytebuf.get_var_int()?` | `write.write_var_int(val)?` | +| **`VarLong`** | `bytebuf.get_var_long()?` | `write.write_var_long(val)?` | +| **`String`** | `bytebuf.get_string()?` | `write.write_string(val)?` | +| **`UUID`** | `bytebuf.get_uuid()?` | `write.write_uuid(&val)?` | +| **`TextComponent`** | `bytebuf.get_component()?` | `write.write_component(val, version)?` | +| **`Position`** | `bytebuf.get_block_pos()?` | `write.write_block_pos(val)?` | +| **`NBT`** | `bytebuf.get_nbt()?` | `write.write_nbt(&val)?` | + +--- + +## Step 5: Handling Incoming Packets in `pumpkin` + +Incoming serverbound packets must be handled in the appropriate state handler in `crates/pumpkin/src/net/java/`. + +For packets in the **Play state**, open `crates/pumpkin/src/net/java/mod.rs` and update `handle_play_packet`: + +```rust +// In crates/pumpkin/src/net/java/mod.rs +pub fn handle_play_packet( + &self, + player: &Arc, + server: &Arc, + packet: &RawPacket, +) -> Result<(), Box> { + let version = self.version.load(); + let mut payload = &packet.payload[..]; + + match packet.id { + // Match against version-specific packet ID + id if id == SPlayerPosition::to_id(version) => { + let packet = SPlayerPosition::read(&mut payload, &version)?; + self.handle_player_position(player, &packet); + } + _ => {} + } + Ok(()) +} +``` + +Then implement the handler function: + +```rust +impl JavaClient { + pub fn handle_player_position(&self, player: &Arc, packet: &SPlayerPosition) { + // Validate coordinates, update player position, check collisions + player.set_position(packet.position); + } +} +``` + +--- + +## Step 6: Sending Outgoing Packets + +To send a clientbound packet to a player: + +```rust +// 1. Send directly to a player's connection +player.client.send_packet(&CPlayDisconnect::new(&reason)); + +// 2. Broadcast to all players watching a chunk +world.broadcast_packet_except( + &[player.gameprofile.id], + &CBlockUpdate::new(block_pos, block_id), +).await; +``` diff --git a/docs/ja_jp/developer/networking/java/authentication.md b/docs/ja_jp/developer/networking/java/authentication.md new file mode 100644 index 0000000..861da51 --- /dev/null +++ b/docs/ja_jp/developer/networking/java/authentication.md @@ -0,0 +1,83 @@ +# Java Edition Authentication + +Authentication verifies the identity of players connecting to Pumpkin to prevent username spoofing and account impersonation. + +Pumpkin handles authentication in the [`pumpkin-auth`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-auth) crate and orchestrates it during the `Login` state in `crates/pumpkin/src/net/java/login/`. + +--- + +## Why Authentication Matters + +Offline (non-authenticated) accounts generate player UUIDs strictly based on their offline username string (`UUID::new_v3(...)`). Without verification, any user can join using the username of an operator or administrator and gain privileged access. + +In online mode (`online_mode = true` in `pumpkin.toml`), Pumpkin cryptographically verifies with Mojang's session servers that the connecting client owns the requested account. + +--- + +## How Yggdrasil Authentication Works + +The authentication exchange between the client, Pumpkin, and Mojang's Yggdrasil servers follows these steps: + +1. **Initiation**: The client sends `SLoginStart` declaring its desired username and optional offline UUID. +2. **Key Exchange**: Pumpkin replies with `CEncryptionBegin`, providing a random 4-byte verify token, the server ID string, and Pumpkin's 1024-bit RSA public key. +3. **Session Join**: The client generates a random 16-byte shared secret (`AES-128`), computes a SHA-1 server hash, and calls Mojang's session server (`POST /session/minecraft/join`) using its authenticated Microsoft access token. +4. **Verification**: The client sends `SEncryptionResponse` containing the RSA-encrypted shared secret and encrypted verify token. Pumpkin decrypts the token with its RSA private key, recalculates the SHA-1 digest, and queries Mojang (`GET /session/minecraft/hasJoined`). +5. **Profile Retrieval**: Mojang validates that the client and server hashes match, returning the authenticated player's UUID, username, and signed skin/cape texture properties (`GameProfile`). +6. **Encryption Activation**: Pumpkin activates full-stream symmetric encryption (`AES-128/CFB8`) and completes the login sequence by sending `CLoginSuccess`. + +--- + +## Game Profile Structure + +When Mojang's `hasJoined` endpoint validates a session, it returns a `GameProfile` containing the player's authenticated UUID, username, and signed texture properties: + +```rust +pub struct GameProfile { + pub id: Uuid, + pub name: String, + pub properties: Vec, + pub profile_actions: Option>, +} + +pub struct Property { + pub name: String, + pub value: String, // Base64 encoded texture data + pub signature: Option, // Cryptographic signature verifying authenticity +} +``` + +Pumpkin verifies that skin and cape signatures are cryptographically signed with Mojang's public key to prevent clients from injecting malicious payloads or unauthorized textures. + +--- + +## Custom Authentication Endpoints + +For private networks or enterprise environments running custom Yggdrasil-compatible authentication servers, endpoints can be customized in `pumpkin.toml`: + +```toml +[networking.java.authentication] +enabled = true +fallbacks = [] +connect_timeout = 5000 +read_timeout = 5000 +prevent_proxy_connections = false + +[networking.java.authentication.player_profile] +allow_banned_players = false +allowed_actions = ["FORCED_NAME_CHANGE", "USING_BANNED_SKIN"] + +[networking.java.authentication.textures] +enabled = true +allowed_url_schemes = ["http", "https"] +allowed_url_domains = [".minecraft.net", ".mojang.com"] +``` + +--- + +## Proxy Authentication Forwarding + +When running Pumpkin behind proxies, client connections terminate at the proxy. Pumpkin supports the three primary proxy forwarding standards: + +- **Velocity Modern Forwarding**: Validates player data forwarded with HMAC-SHA256 secret tokens (`[networking.proxy.velocity]`). +- **BungeeGuard**: Token-based authentication header validation for BungeeCord setups. +- **Legacy BungeeCord**: Handshake forwarding appending IP, UUID, and properties to the server host string. diff --git a/docs/ja_jp/developer/networking/java/overview.md b/docs/ja_jp/developer/networking/java/overview.md new file mode 100644 index 0000000..e79753c --- /dev/null +++ b/docs/ja_jp/developer/networking/java/overview.md @@ -0,0 +1,74 @@ +# Java Edition Networking Overview + +The Java Edition network stack handles standard Minecraft PC clients connecting over TCP. Wire-level structures and serialization logic reside in `pumpkin-protocol::java`, while connection management and gameplay packet handlers reside in `pumpkin::net::java`. + +--- + +## The Five Protocol States + +The Minecraft Java protocol operates as an explicit finite state machine. Every connection begins in the `Handshake` state and transitions according to client requests and server responses: + +| State | Transition Trigger | Purpose & Next State | +| :--- | :--- | :--- | +| **Handshake** | Client initiates connection | Inspects protocol version and transitions to `Status`, `Login`, or `Transfer` | +| **Status** | `NextState = 1` | Streams MOTD, active player counts, favicon, and ping response | +| **Login** | `NextState = 2` | Authenticates account (Yggdrasil), negotiates encryption (AES-128), transitions to `Config` | +| **Transfer** | `NextState = 3` | Modern proxy / server-transfer negotiation | +| **Config** | Authentication success | Exchanges dynamic registries, data tags, feature flags, and resource packs | +| **Play** | Configuration acknowledged | Primary bidirectional gameplay loop (chunks, movement, combat, chat) | + +1. **Handshake (`0x00`)**: Always the first packet sent by the client. It declares the client's protocol version, server address, port, and desired next state (`Status`, `Login`, or `Transfer`). +2. **Status**: Lightweight query state. The client requests a server list ping, and Pumpkin responds with the server version, player counts, favicon, and MOTD. +3. **Login**: Authenticates the player. If `online_mode` is enabled, Pumpkin performs an encryption handshake (AES-128/CFB8) and verifies the player's account with Mojang's Yggdrasil session servers. +4. **Config**: Introduced in modern Minecraft (1.20.2+). The server sends feature flags, known registries, resource packs, and custom server links before the player joins the physical world. +5. **Play**: The primary gameplay state. Used for all in-game actions including block breaking, entity movement, combat, chat, and chunk streaming. + +--- + +## Connection Representation: `JavaClient` vs. `Player` + +To keep memory usage minimal and prevent unnecessary allocations, Pumpkin cleanly separates pre-play connections from active players: + +### `JavaClient` +- **Scope**: Exists across `Handshake`, `Status`, `Login`, and `Config` states. +- **Location**: `crates/pumpkin/src/net/java/mod.rs`. +- **Characteristics**: + - Contains connection metadata: client protocol version, remote address, encryption/compression state. + - Has no world coordinates, no physics, no inventory, and no living entity. + - Very low memory footprint. + +### `Player` +- **Scope**: Exists solely during the `Play` state. +- **Location**: `crates/pumpkin/src/entity/player/`. +- **Characteristics**: + - Implements `EntityBase`, representing a living entity inside a `World` and `Level`. + - Holds inventory screens, health, hunger, velocities, and chunk view subscription queues. + - Contains an `Arc` reference for network transmission. + +--- + +## Multi-Version Support (`JavaMinecraftVersion`) + +Pumpkin supports multiple Minecraft client versions. Protocol differences are handled cleanly via `pumpkin_util::version::JavaMinecraftVersion`: + +- Packet IDs may differ across Minecraft versions. The `#[java_packet(ID)]` macro works with `pumpkin-data` to resolve the correct ID for the target version. +- When reading or writing packets, methods accept a `&JavaMinecraftVersion` reference to conditionally include or omit fields that changed in specific Minecraft updates. + +```rust +if *version <= JavaMinecraftVersion::V_1_7_6 { + // Read legacy stance coordinate present in early Minecraft versions + let _stance = bytebuf.get_f64_be()?; +} +``` + +--- + +## The Packet Pipeline + +Incoming Java packets pass through a structured pipeline: + +1. **Length Framing**: `tokio_util::codec` decodes VarInt frame lengths. +2. **Decompression & Decryption**: If enabled, the raw buffer is decrypted via AES/CFB8 and decompressed via ZLib. +3. **Plugin Event Interception**: A `PacketReceivedEvent` is dispatched to the plugin manager. Plugins can inspect or cancel the packet before server processing. +4. **ID Dispatching**: The packet ID is matched against version-specific constants in `crates/pumpkin/src/net/java/mod.rs` (or the respective state module). +5. **Handling**: The packet struct is deserialized and processed by state-specific handler functions. diff --git a/docs/ja_jp/developer/networking/metadata.md b/docs/ja_jp/developer/networking/metadata.md new file mode 100644 index 0000000..9def4ae --- /dev/null +++ b/docs/ja_jp/developer/networking/metadata.md @@ -0,0 +1,123 @@ +# Entity Metadata & Synced Data + +Minecraft synchronizes persistent entity visual states, animation flags, poses, and properties via **Entity Metadata** (historically called *DataWatchers* in Java Edition and *Actor Data* in Bedrock Edition). + +Instead of transmitting full state packets whenever an entity crouches, catches fire, or takes damage, the server broadcasts compact metadata updates to nearby observers. + +--- + +## Edition Comparison + +| Feature | Minecraft: Java Edition | Minecraft: Bedrock Edition | +| :--- | :--- | :--- | +| **Packet Name** | `CSetEntityMetadata` (`SET_ENTITY_DATA`) | `CSetActorData` (Packet ID `39`) | +| **Storage Structure** | `TrackedData` list terminating with `0xFF` | `SyncedActorDataList` (`HashMap`) | +| **Key Type** | Index (`u8`) mapped to typed `MetaDataType` | Enum keys (`entity_data_key::*`) | +| **Flags** | Bitfield byte (fire, crouching, sprinting, glowing) | Bitfield int64 (`entity_data_flag::*`) | +| **Dynamic Properties** | N/A (baked into tracked indices) | `PropertySyncData` (client/server sync) | +| **Crate Location** | `crates/pumpkin-protocol/src/java/client/play/entity_metadata.rs` | `crates/pumpkin-protocol/src/bedrock/client/set_actor_data.rs` | + +--- + +## 1. Java Edition: `CSetEntityMetadata` + +In Java Edition, entity metadata consists of numbered index entries. Each entry defines an index, a `MetaDataType` ID, and the encoded value. The list terminates with a single `0xFF` byte. + +### Key Index Hierarchy + +Entity metadata follows an object-oriented inheritance model: + +1. **Entity (Base)**: + - `0`: State bitfield (flags: bit `0` = on fire, bit `1` = crouching, bit `3` = sprinting, bit `5` = invisible, bit `6` = glowing). + - `1`: Air supply (`VarInt`). + - `2`: Custom name (optional `TextComponent`). + - `3`: Custom name visible (`bool`). + - `4`: Silent (`bool`). + - `5`: No gravity (`bool`). + - `6`: Pose (`Standing`, `FallFlying`, `Sleeping`, `Swimming`, `SpinAttack`, `Crouching`, `Dying`). +2. **LivingEntity (extends Entity)**: + - `8`: Hand states (using item, active hand). + - `9`: Health (`f32`). + - `10`: Potion effect ambient color. +3. **Player (extends LivingEntity)**: + - Additional hearts, skin customization bitmask, main hand indicator. + +### Sending Metadata Updates in Pumpkin + +Pumpkin encodes entity metadata in `pumpkin-protocol`: + +```rust +use pumpkin_protocol::VarInt; +use pumpkin_protocol::java::client::play::CSetEntityMetadata; + +// Construct metadata update for entity with ID 42 +let packet = CSetEntityMetadata::new( + VarInt(entity.entity_id), + serialized_metadata_bytes, +); + +// Broadcast to chunk observers +world.broadcast_packet_all(&packet).await; +``` + +--- + +## 2. Bedrock Edition: `CSetActorData` + +Bedrock Edition uses `CSetActorData` (`packet(39)`), storing properties in a dictionary mapping `u32` keys to strongly-typed `MetadataValue` variants. + +### Key & Flag Constants + +Bedrock exposes flags as a 64-bit integer bitfield under `entity_data_key::FLAGS`: + +- `entity_data_flag::ON_FIRE` (`0`): Entity is burning. +- `entity_data_flag::SNEAKING` (`1`): Player is sneaking. +- `entity_data_flag::RIDING` (`2`): Entity is mounted on a vehicle. +- `entity_data_flag::SPRINTING` (`3`): Player is sprinting. +- `entity_data_flag::USING_ITEM` (`4`): Player is actively charging a bow, eating food, or using a shield. +- `entity_data_flag::INVISIBLE` (`5`): Entity is invisible. +- `entity_data_flag::GLOWING` (`26`): Entity has the glowing effect outline. + +### Constructing and Updating Actor Data in Pumpkin + +In `crates/pumpkin/src/entity/living.rs`: + +```rust +use pumpkin_protocol::bedrock::client::set_actor_data::{ + CSetActorData, MetadataValue, PropertySyncData, SyncedActorDataList, + entity_data_flag, entity_data_key, +}; + +let mut actor_data = SyncedActorDataList::new(); + +// Set entity flags (e.g. actively using an item) +let mut flags: i64 = 0; +if is_using_item { + flags |= 1i64 << entity_data_flag::USING_ITEM; +} +actor_data.set(entity_data_key::FLAGS, MetadataValue::Int64(flags)); + +// Set air supply +actor_data.set(entity_data_key::AIR_SUPPLY, MetadataValue::Short(air as i16)); + +// Set custom name tag +actor_data.set(entity_data_key::NAME, MetadataValue::String(name)); + +let packet = CSetActorData { + target_runtime_id: VarULong(entity_id), + actor_data, + synced_properties: PropertySyncData::default(), + tick: VarULong(current_tick), +}; + +client.try_enqueue_client_packet(&packet); +``` + +--- + +## Multi-Version Compatibility + +Because Mojang frequently shifts metadata indices between Minecraft releases, `pumpkin-protocol` leverages translation tables from `pumpkin-data`: + +- For Java Edition, `pumpkin_data::meta_data_type::MetaDataType` remaps indices dynamically based on the client's `JavaMinecraftVersion`. +- For Bedrock Edition, `SyncedActorDataList` validates keys against the active Bedrock protocol version to prevent client crashes. diff --git a/docs/ja_jp/developer/networking/networking.md b/docs/ja_jp/developer/networking/networking.md new file mode 100644 index 0000000..dc61b7a --- /dev/null +++ b/docs/ja_jp/developer/networking/networking.md @@ -0,0 +1,210 @@ +# Networking & Protocol + +Pumpkin includes a high-performance network stack supporting both **Minecraft: Java Edition** and **Minecraft: Bedrock Edition**. + +Most protocol definitions, codecs, and serialization logic reside in the [`pumpkin-protocol`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-protocol) crate, while connection handling and gameplay packet dispatching reside in the [`pumpkin`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/net) crate. + +> [!NOTE] +> Detailed guides are available in the dedicated subcategories: +> - **[Networking Introduction](/developer/networking/)** +> - **Java Edition**: **[Architecture](/developer/networking/java/overview)**, **[Adding a Java Packet](/developer/networking/java/adding-packets)**, and **[Authentication](/developer/networking/java/authentication)** +> - **Bedrock Edition**: **[Architecture](/developer/networking/bedrock/overview)** and **[Adding a Bedrock Packet](/developer/networking/bedrock/adding-packets)** + +--- + +## Direction & Protocol Terminology + +- **Serverbound (Client → Server)**: Packets sent by the game client and received by Pumpkin. Prefixed with `S` (e.g., `SChatMessage`, `SPlayerPosition`). +- **Clientbound (Server → Client)**: Packets sent by Pumpkin to the game client. Prefixed with `C` (e.g., `CPlayDisconnect`, `CBlockUpdate`). + +--- + +## Protocol States + +The Minecraft Java protocol operates as a finite state machine with five distinct states: + +1. **Handshake**: The initial packet sent when a client connects. It specifies the protocol version, server address, and the target next state (`Status`, `Login`, or `Transfer`). +2. **Status**: Lightweight query state used for the multiplayer server list (MOTD, online player count, latency ping). +3. **Login**: Authenticates the player (via Mojang Yggdrasil or offline mode), sets up encryption (AES-128/CFB8), and activates packet compression if enabled. +4. **Config**: Introduced in modern Minecraft versions to configure client settings, negotiation of features, resource packs, and server links prior to gameplay. +5. **Play**: The primary gameplay state where entities, world chunks, inventory interactions, chat, and movement are processed. + +--- + +## Defining Packets in `pumpkin-protocol` + +Packets in `pumpkin-protocol` are organized by edition (`java`, `bedrock`), direction (`client`, `server`), and state (`handshake`, `status`, `login`, `config`, `play`). + +### 1. Clientbound Packets (`ClientPacket`) + +To create a clientbound packet: + +1. Retrieve the packet ID constant from `pumpkin-data`. +2. Annotate the struct with `#[java_packet(PACKET_CONSTANT)]`. +3. Implement `ClientPacket`, writing the packet fields into the byte stream using `NetworkWriteExt`. + +```rust +use std::io::Write; +use pumpkin_data::packet::clientbound::play::DISCONNECT; +use pumpkin_macros::java_packet; +use pumpkin_protocol::ClientPacket; +use pumpkin_protocol::ser::{NetworkWriteExt, WritingError}; +use pumpkin_util::text::TextComponent; +use pumpkin_util::version::JavaMinecraftVersion; + +/// Disconnects the player while in the Play state. +#[java_packet(DISCONNECT)] +pub struct CPlayDisconnect<'a> { + pub reason: &'a TextComponent, +} + +impl<'a> CPlayDisconnect<'a> { + pub const fn new(reason: &'a TextComponent) -> Self { + Self { reason } + } +} + +impl ClientPacket for CPlayDisconnect<'_> { + fn write_packet_data( + &self, + mut write: impl Write, + version: &JavaMinecraftVersion, + ) -> Result<(), WritingError> { + // Write the text component formatted for the target client version + write.write_component(self.reason, version) + } +} +``` + +### 2. Serverbound Packets (`ServerPacket`) + +To create a serverbound packet: + +1. Retrieve the packet ID constant from `pumpkin-data`. +2. Annotate the struct with `#[java_packet(PACKET_CONSTANT)]`. +3. Implement `ServerPacket<'a>`, reading fields from the incoming byte slice with `NetworkReadExt`. + +```rust +use pumpkin_data::packet::serverbound::play::MOVE_PLAYER_POS; +use pumpkin_macros::java_packet; +use pumpkin_protocol::ServerPacket; +use pumpkin_protocol::ser::{NetworkReadExt, ReadingError}; +use pumpkin_util::math::vector3::Vector3; +use pumpkin_util::version::JavaMinecraftVersion; + +#[java_packet(MOVE_PLAYER_POS)] +pub struct SPlayerPosition { + pub position: Vector3, + pub collision: u8, +} + +impl<'a> ServerPacket<'a> for SPlayerPosition { + fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result { + let x = bytebuf.get_f64_be()?; + let y = bytebuf.get_f64_be()?; + if *version <= JavaMinecraftVersion::V_1_7_6 { + let _stance = bytebuf.get_f64_be()?; + } + let z = bytebuf.get_f64_be()?; + let collision = bytebuf.get_u8()?; + + Ok(Self { + position: Vector3::new(x, y, z), + collision, + }) + } +} +``` + +### Useful Serialization Helpers (`NetworkWriteExt` & `NetworkReadExt`) + +Pumpkin provides extension traits on byte streams for standard Minecraft primitives: + +- `write_var_int(i32)` / `get_var_int()`: Variable-length 32-bit integers. +- `write_var_long(i64)` / `get_var_long()`: Variable-length 64-bit integers. +- `write_string(&str)` / `get_string()`: UTF-8 string prefixed with a VarInt length. +- `write_uuid(&Uuid)` / `get_uuid()`: 128-bit player or entity UUID. +- `write_component(&TextComponent)`: Serializes formatted chat/UI text. +- `write_nbt(&NBTTag)` / `get_nbt()`: Serializes/deserializes binary NBT tags. + +--- + +## Packet Flow & Handling in `pumpkin` + +Incoming packets pass through several stages before modifying game state: + +1. **Frame Decoding**: The Tokio TCP stream decodes the VarInt length prefix and decrypts the frame if encryption is enabled. +2. **Event Interception**: Emits `PacketReceivedEvent` to registered plugins. If cancelled, the packet is immediately dropped. +3. **Version-Aware Dispatch**: Maps the wire packet ID according to the client's negotiated protocol version. +4. **State Handling**: Routes the parsed packet struct to its corresponding handler (e.g., `handle_play_packet`). +5. **State Mutation**: Mutates player session, world chunks, or entity states. + +### 1. `Client` vs `Player` + +Pumpkin maintains a strict distinction between pre-play connections and active players: + +- **`Client`**: Represents a connection in `Handshake`, `Status`, `Login`, or `Config` state. Lightweight, not linked to a world, and has no physical entity. Handled in `crates/pumpkin/src/net/java/mod.rs`. +- **`Player`**: Represents a living entity in an active `World` during the `Play` state. Holds inventory, health, velocity, and chunk subscription queues. Handled in `crates/pumpkin/src/entity/player/`. + +### 2. Packet Dispatching (`handle_play_packet`) + +Inside `crates/pumpkin/src/net/java/mod.rs`, packets are dispatched by matching against their version-specific packet IDs: + +```rust +pub fn handle_play_packet( + &self, + player: &Arc, + server: &Arc, + packet: &RawPacket, +) -> Result<(), Box> { + let version = self.version.load(); + + // Fire plugin event to allow plugins to observe or cancel packets + let mut event = PacketReceivedEvent::new(player.clone(), packet.id, packet.payload.clone()); + server.plugin_manager.fire_blocking(server, &mut event); + if event.cancelled { + return Ok(()); + } + + let mut payload = &event.payload[..]; + match event.packet_id { + id if id == SConfirmTeleport::to_id(version) => { + self.handle_confirm_teleport(player, &SConfirmTeleport::read(&mut payload, &version)?); + } + id if id == SChatCommand::to_id(version) => { + let packet = SChatCommand::read(&mut payload, &version)?; + self.handle_chat_command(player, server, &packet); + } + id if id == SPlayerPosition::to_id(version) => { + let packet = SPlayerPosition::read(&mut payload, &version)?; + self.handle_player_position(player, &packet); + } + _ => { + trace!("Unhandled play packet id: {:#04x}", event.packet_id); + } + } + Ok(()) +} +``` + +### 3. Sending Packets + +To send a packet to a client: + +```rust +// Directly send to a single player +player.client.send_packet(&CPlayDisconnect::new(&reason)); + +// Broadcast to nearby players in the world +world.broadcast_packet_except( + &[player.gameprofile.id], + &CBlockUpdate::new(block_pos, block_id), +).await; +``` + +--- + +## Compression & Encryption + +- **Compression**: Once a client passes the Login state, if `compression_threshold` is enabled in `pumpkin.toml`, packets exceeding the threshold size are compressed using ZLib/fast compression. +- **Encryption**: In online mode, 128-bit AES/CFB8 encryption is activated immediately after shared secret exchange in the Login state. diff --git a/docs/ja_jp/developer/networking/proxy/bungeecord.md b/docs/ja_jp/developer/networking/proxy/bungeecord.md new file mode 100644 index 0000000..cb06ff5 --- /dev/null +++ b/docs/ja_jp/developer/networking/proxy/bungeecord.md @@ -0,0 +1,73 @@ +# BungeeCord Forwarding + +Pumpkin provides native support for BungeeCord and Waterfall proxy networks, including **BungeeGuard** authentication token verification. + +Implementation is located in [`crates/pumpkin/src/net/proxy/bungeecord.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/bungeecord.rs). + +--- + +## How BungeeCord Forwarding Operates + +Unlike modern protocols that use custom login channels, BungeeCord operates by encoding client metadata directly into the `server_address` field of the initial `SHandshake` packet. + +The string uses null character (`\0`) delimiters: + +```text +[original_host]\0[player_ip:port]\0[player_uuid]\0[properties_json] +``` + +1. **`original_host`**: The domain name the player originally typed into their client. +2. **`player_ip:port`**: The genuine IP address and ephemeral source port of the client. +3. **`player_uuid`**: The player's Mojang UUID (32 hex characters without hyphens or 36 characters with hyphens). +4. **`properties_json`**: A JSON array containing skin textures, cape textures, and Mojang cryptographic signatures. + +--- + +## The Security Risk: Proxy Bypasses + +Because BungeeCord's handshake string is unauthenticated plain text, an attacker could connect directly to an exposed backend port, inject an arbitrary UUID and operator username, and gain full server control. + +Traditionally, this required strict Linux firewall rules (`iptables` / `ufw`) to restrict port access exclusively to the proxy IP. + +--- + +## BungeeGuard Protection + +To eliminate proxy bypasses without relying solely on network firewalls, Pumpkin integrates **BungeeGuard** token verification: + +1. The BungeeCord proxy injects a shared secret into the player's profile properties under the key `bungeeguard-token`. +2. When Pumpkin processes the handshake in `bungeecord_login`: + - It checks whether `secret` is configured in `pumpkin.toml`. + - It inspects the forwarded properties for `bungeeguard-token`. + - If the token matches, Pumpkin **strips the secret property** from the player's profile (so other plugins cannot read it) and completes the login. + - If the token is missing or incorrect, Pumpkin rejects the connection with `BungeeCordError::MissingToken` or `BungeeCordError::InvalidToken`. + +```rust +// In crates/pumpkin/src/net/proxy/bungeecord.rs +if !secret.is_empty() { + let Some(token_index) = properties.iter().position(|p| p.name == BUNGEEGUARD_TOKEN_PROPERTY) else { + return Err(BungeeCordError::MissingToken); + }; + + if properties[token_index].value != secret { + return Err(BungeeCordError::InvalidToken); + } + properties.remove(token_index); +} +``` + +--- + +## Configuration in `pumpkin.toml` + +To enable BungeeCord forwarding with BungeeGuard: + +```toml +[networking.proxy] +enabled = true + +[networking.proxy.bungeecord] +enabled = true +# Optional: Configure the BungeeGuard shared secret to block direct connections +secret = "YOUR_BUNGEEGUARD_SECRET_HERE" +``` diff --git a/docs/ja_jp/developer/networking/proxy/index.md b/docs/ja_jp/developer/networking/proxy/index.md new file mode 100644 index 0000000..300c89e --- /dev/null +++ b/docs/ja_jp/developer/networking/proxy/index.md @@ -0,0 +1,46 @@ +# Proxy Forwarding Overview + +In multi-server Minecraft networks, players connect through a front-facing reverse proxy (such as Velocity, BungeeCord, or Vine) that routes connections between lobbies and sub-servers without requiring players to reconnect. + +Because backend servers receive TCP connections directly from the proxy machine rather than the player, the server must be informed of the player's genuine **IP address**, **UUID**, **username**, and **skin properties**. + +Pumpkin provides native support for all three major proxy forwarding protocols in [`crates/pumpkin/src/net/proxy/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/net/proxy). + +--- + +## The Proxy Forwarding Problem + +Without secure proxy forwarding: +1. **IP Loss**: Every connected player appears with the proxy's loopback or internal IP (breaking IP bans and geo-location). +2. **UUID Spoofing & Bypasses**: Malicious users could connect directly to the backend server's port with any spoofed username or operator identity. + +To prevent proxy bypasses, Pumpkin validates cryptographic secrets, tokens, or digital signatures sent by the proxy. + +--- + +## Comparison of Supported Protocols + +| Protocol | Protocol Transport | Security / Cryptography | Replay Protection | Crate Module | +| :--- | :--- | :--- | :--- | :--- | +| **[BungeeCord](/developer/networking/proxy/bungeecord)** | `SHandshake` server address string | Optional token in profile properties (`bungeeguard-token`) | None | `pumpkin::net::proxy::bungeecord` | +| **[Velocity](/developer/networking/proxy/velocity)** | Login plugin message (`velocity:player_info`) | Symmetric HMAC-SHA256 signature | None | `pumpkin::net::proxy::velocity` | +| **[Vine](/developer/networking/proxy/vine)** | Login plugin message (`vine:player_info`) | Asymmetric **Ed25519** digital signatures | 16-byte nonce challenge + 30s timestamp window | `pumpkin::net::proxy::vine` | + +--- + +## Lifecycle Architecture + +1. **Player Authentication**: The player connects to the proxy (BungeeCord, Velocity, or Vine) and authenticates against Mojang's session servers. +2. **Backend Connection**: The proxy connects to Pumpkin over TCP and sends `SHandshake`. +3. **Data Forwarding**: + - **BungeeCord Mode**: The proxy injects the player's remote IP, UUID, and properties directly into the `SHandshake` hostname string (separated by null bytes `\0`). + - **Modern Mode (Velocity / Vine)**: Pumpkin sends `CLoginPluginRequest` on a dedicated channel (`velocity:player_info` or `vine:player_info`). The proxy signs the player profile using HMAC-SHA256 (Velocity) or Ed25519 (Vine) and replies with `SLoginPluginResponse`. +4. **Validation & Session Initialization**: Pumpkin validates the signature, binds the real IP and GameProfile to the `JavaClient`, and sends `CLoginSuccess`. + +--- + +## Proxy Documentation + +- **[BungeeCord](/developer/networking/proxy/bungeecord)**: Legacy host string injection and optional BungeeGuard token validation. +- **[Velocity Modern Forwarding](/developer/networking/proxy/velocity)**: HMAC-SHA256 signed player info during the Login state. +- **[Vine Modern Forwarding](/developer/networking/proxy/vine)**: Next-generation proxy forwarding with Ed25519 signatures and replay attack prevention. diff --git a/docs/ja_jp/developer/networking/proxy/velocity.md b/docs/ja_jp/developer/networking/proxy/velocity.md new file mode 100644 index 0000000..b0333e7 --- /dev/null +++ b/docs/ja_jp/developer/networking/proxy/velocity.md @@ -0,0 +1,88 @@ +# Velocity Modern Forwarding + +[Velocity](https://papermc.io/software/velocity) is a high-performance proxy created by PaperMC. It uses modern plugin messaging during the `Login` state to securely transmit player data with **HMAC-SHA256** message authentication codes. + +Pumpkin implements Velocity modern forwarding in [`crates/pumpkin/src/net/proxy/velocity.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/velocity.rs). + +--- + +## How Velocity Forwarding Operates + +Unlike BungeeCord, which injects unauthenticated data into the handshake string, Velocity operates during the **Login** state over a dedicated plugin channel: + +1. **Handshake & Login**: The player authenticates with Velocity. Velocity initiates a standard TCP connection to Pumpkin and transmits `SHandshake` and `SLoginStart`. +2. **Channel Query**: Recognizing that Velocity modern forwarding is enabled, Pumpkin pauses the login sequence and dispatches a `CLoginPluginRequest` on channel `velocity:player_info`. +3. **Payload Generation**: Velocity bundles the player's remote address, UUID, username, and skin textures. It calculates a 32-byte HMAC-SHA256 signature across this payload using the shared secret. +4. **Signature Verification**: Velocity sends `SLoginPluginResponse`. Pumpkin validates the HMAC-SHA256 digest against its configured secret. If valid, Pumpkin overwrites the connection's network address and GameProfile and transitions to `Config` / `Play`. + +--- + +## Wire Payload Structure + +The payload in `SLoginPluginResponse` is structured as follows: + +```text +┌─────────────────────────────────────────────────────────────┐ +│ 32-byte HMAC-SHA256 Signature │ +├─────────────────────────────────────────────────────────────┤ +│ Forwarding Protocol Version (VarInt: 1 - 4) │ +├─────────────────────────────────────────────────────────────┤ +│ Client Remote IP Address (String / IPv4 / IPv6) │ +├─────────────────────────────────────────────────────────────┤ +│ Player UUID (128-bit / UUID) │ +├─────────────────────────────────────────────────────────────┤ +│ Player Username (String) │ +├─────────────────────────────────────────────────────────────┤ +│ Profile Properties Count (VarInt) │ +│ ├─ Property Name (String) │ +│ ├─ Property Value (String) │ +│ └─ Optional Signature (String) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Cryptographic Validation in Pumpkin + +Inside `crates/pumpkin/src/net/proxy/velocity.rs`: + +```rust +use hmac::{Hmac, KeyInit, Mac}; +use sha2::Sha256; + +type HmacSha256 = Hmac; + +// Extract signature and payload +let (signature, data_to_verify) = data.split_at(32); + +// Initialize HMAC with secret from pumpkin.toml +let mut mac = HmacSha256::new_from_slice(secret.as_bytes()) + .map_err(|_| VelocityError::FailedVerifyIntegrity)?; + +mac.update(data_to_verify); + +// Constant-time verification prevents timing attacks +mac.verify_slice(signature) + .map_err(|_| VelocityError::FailedVerifyIntegrity)?; +``` + +If the signature does not match (e.g., if an attacker attempts to connect directly to the backend without knowing the secret), Pumpkin drops the connection immediately. + +--- + +## Configuration in `pumpkin.toml` + +1. Open `velocity.toml` on your Velocity proxy and set: + ```toml + player-info-forwarding-mode = "modern" + forwarding-secret-file = "forwarding.secret" + ``` +2. Copy the generated secret string from `forwarding.secret` into `pumpkin.toml`: + ```toml + [networking.proxy] + enabled = true + + [networking.proxy.velocity] + enabled = true + secret = "YOUR_VELOCITY_FORWARDING_SECRET_HERE" + ``` diff --git a/docs/ja_jp/developer/networking/proxy/vine.md b/docs/ja_jp/developer/networking/proxy/vine.md new file mode 100644 index 0000000..fed2a8a --- /dev/null +++ b/docs/ja_jp/developer/networking/proxy/vine.md @@ -0,0 +1,112 @@ +# Vine Modern Forwarding + +**Vine** is a next-generation, high-performance Minecraft proxy written in Rust. Designed for ultra-low latency, multi-threading, and enterprise-grade security, Vine introduces modern cryptographic forwarding based on **Ed25519 asymmetric signatures** and **replay attack protection**. + +Pumpkin provides native support for Vine forwarding in [`crates/pumpkin/src/net/proxy/vine.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/vine.rs). + +--- + +## Why Vine Forwarding? + +While Velocity's HMAC-SHA256 forwarding represents an improvement over legacy BungeeCord, it relies on a **symmetric shared secret**. If one backend server in a network is compromised, an attacker obtains the shared secret and can forge authenticated player logins across the entire server cluster. + +Vine solves this with: +1. **Asymmetric Public-Key Cryptography (Ed25519)**: The proxy signs player data using its private key. Backend servers only store the proxy's **Public Key** (`public_key`). Even if a backend server is compromised, the attacker cannot forge identities to other servers. +2. **Replay Attack Protection (16-byte Challenge Nonce)**: Pumpkin generates a cryptographic random 16-byte nonce for each connection. The proxy must sign and return this nonce. Replaying a captured login packet is impossible. +3. **Timestamp Drift Window**: Transmits a Unix timestamp that must be within ±30 seconds of the backend's system clock (`MAX_TIMESTAMP_DRIFT_SECS = 30`). + +--- + +## The Handshake Sequence + +1. **Connection**: The player connects and authenticates with Vine. Vine opens a backend connection to Pumpkin and sends `SHandshake` and `SLoginStart`. +2. **Challenge Query**: Pumpkin generates a cryptographically random 16-byte nonce and sends `CLoginPluginRequest` on the `vine:player_info` channel. +3. **Asymmetric Signing**: Vine serializes the version, timestamp, the returned 16-byte nonce, the client IP address, and the player GameProfile. It signs this byte stream with its Ed25519 private key. +4. **Tri-fold Validation**: Vine replies with `SLoginPluginResponse`. Pumpkin verifies: + - **Clock Skew**: The embedded timestamp is within ±30 seconds of UTC time. + - **Nonce Check**: The signed nonce exactly matches the 16-byte challenge generated in step 2. + - **Ed25519 Signature**: The signature is cryptographically valid against the configured `public_key`. +5. **Session Success**: If all three checks pass, Pumpkin applies the verified identity and emits `CLoginSuccess`. + +--- + +## Wire Payload Structure + +The `SLoginPluginResponse` data layout: + +```text +┌─────────────────────────────────────────────────────────────┐ +│ 64-byte Ed25519 Digital Signature │ +├─────────────────────────────────────────────────────────────┤ +│ Forwarding Protocol Version (i32: 1) │ +├─────────────────────────────────────────────────────────────┤ +│ Unix Timestamp in Seconds (i64) │ +├─────────────────────────────────────────────────────────────┤ +│ 16-byte Challenge Nonce │ +├─────────────────────────────────────────────────────────────┤ +│ Client Remote IP Address (String / IPv4 / IPv6) │ +├─────────────────────────────────────────────────────────────┤ +│ Player UUID (128-bit / UUID) │ +├─────────────────────────────────────────────────────────────┤ +│ Player Username (String) │ +├─────────────────────────────────────────────────────────────┤ +│ Profile Properties Count (VarInt) │ +│ ├─ Property Name (String) │ +│ ├─ Property Value (String) │ +│ └─ Optional Signature (String) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Verification in Pumpkin + +In `crates/pumpkin/src/net/proxy/vine.rs`: + +```rust +use ed25519_dalek::{Signature, Verifier, VerifyingKey}; + +// 1. Validate payload length (64-byte signature + 25-byte headers minimum) +if data.len() < 89 { + return Err(VineError::DataTooShort); +} + +// 2. Extract signature and signed payload +let (sig_bytes, signed_data) = data.split_at(64); +let signature = Signature::from_slice(sig_bytes) + .map_err(|_| VineError::InvalidSignature)?; + +// 3. Verify timestamp skew (30 second maximum drift) +let skew = (server_now - timestamp).abs(); +if skew > MAX_TIMESTAMP_DRIFT_SECS { + return Err(VineError::TimestampExpired(skew, MAX_TIMESTAMP_DRIFT_SECS)); +} + +// 4. Verify challenge nonce matches connection state +if received_nonce != expected_nonce { + return Err(VineError::ChallengeMismatch); +} + +// 5. Verify Ed25519 signature with proxy's public key +verifying_key.verify(signed_data, &signature) + .map_err(|_| VineError::InvalidSignature)?; +``` + +--- + +## Configuration in `pumpkin.toml` + +Configure Vine forwarding in `pumpkin.toml`: + +```toml +[networking.proxy] +enabled = true + +[networking.proxy.vine] +enabled = true +# 64-character hex-encoded Ed25519 public key of the Vine proxy +public_key = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + +# Alternatively, if secret is supplied, the public key is derived automatically +secret = "" +``` diff --git a/docs/ja_jp/developer/networking/query.md b/docs/ja_jp/developer/networking/query.md new file mode 100644 index 0000000..7d97a9c --- /dev/null +++ b/docs/ja_jp/developer/networking/query.md @@ -0,0 +1,97 @@ +# GameSpy4 Query Protocol + +Pumpkin includes native support for the **GameSpy4 Query** protocol over UDP. External monitoring systems, Discord bots, and server listing platforms use Query to poll real-time player counts, MOTD, server versions, and active player lists with minimal network overhead. + +Query wire encoding is implemented in [`crates/pumpkin-protocol/src/query.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-protocol/src/query.rs), and the asynchronous UDP listener is implemented in [`crates/pumpkin/src/net/query.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/query.rs). + +--- + +## Why Query? (UDP vs. Status Ping) + +While standard Minecraft clients ping servers via the TCP `Status` state (SLP), Query operates over stateless **UDP**: + +- **No TCP Handshake**: Avoids TCP connection overhead and connection state tables. +- **Detailed Server State**: Returns comprehensive key-value metadata and a full list of connected player usernames (not available via basic TCP ping). +- **Amplification Attack Protection**: Enforces cryptographic challenge tokens so malicious actors cannot abuse the UDP port for denial-of-service reflection attacks. + +--- + +## The Handshake & Challenge Token Flow + +To prevent UDP spoofing and reflection DDoS attacks, Query requires a two-step handshake: + +1. **Handshake Challenge**: The client transmits `SHandshake` (`0xFEFD`, type `0x09`, and a 32-bit session ID). Pumpkin generates a cryptographic 32-bit integer challenge token tied to the client's `SocketAddr` and responds with `CHandshake`. +2. **Status Query**: The client sends `SStatusRequest` containing the received challenge token: + - **Basic Status** (no padding): Returns MOTD, game type (`SMP`), map name, online player count, and max player capacity. + - **Full Status** (4-byte padding): Returns detailed server metadata key-value pairs (`hostname`, `gametype`, `game_id`, `version`, `plugins`, `map`, `numplayers`, `maxplayers`, `hostport`, `hostip`) followed by an array of online player usernames. + +### Challenge Token Invalidation +In `crates/pumpkin/src/net/query.rs`, challenge tokens are stored in a thread-safe map (`Arc>>`). A background Tokio timer purges all challenge tokens every 30 seconds to prevent replay abuse: + +```rust +// Tokens are strictly bound to the client's IP and port +let valid_challenge_tokens = Arc::new(RwLock::new(HashMap::new())); + +// Background expiration loop +tokio::spawn(async move { + let mut interval = time::interval(Duration::from_secs(30)); + loop { + interval.tick().await; + valid_challenge_tokens.write().await.clear(); + } +}); +``` + +--- + +## Packet Formats + +Every incoming Query packet starts with the 2-byte magic header `0xFEFD` followed by a 1-byte packet type: + +| Type Byte | Name | Direction | Description | +| :--- | :--- | :--- | :--- | +| `0x09` | Handshake Request | Client → Server | Client asks for a challenge token. | +| `0x09` | Handshake Response | Server → Client | Returns the numeric token encoded as an ASCII string. | +| `0x00` | Stat Request | Client → Server | Requests basic or full status with challenge token. | +| `0x00` | Basic Stat Response | Server → Client | 11-byte short summary. | +| `0x00` | Full Stat Response | Server → Client | Key-value dictionary + null-separated player list. | + +--- + +## Response Payloads + +### 1. Basic Status (`CBasicStatus`) +Contains essential server fields: +- **`motd`**: Server message of the day (stripped of color codes). +- **`gametype`**: Fixed string `"SMP"`. +- **`map`**: Active world/level name (e.g., `"world"`). +- **`numplayers`**: Online player count. +- **`maxplayers`**: Maximum configured player capacity. +- **`hostport`**: Port number (`u16` Little Endian). +- **`hostip`**: Server IP string. + +### 2. Full Status (`CFullStatus`) +Returns two null-terminated sections: + +1. **Key-Value Metadata Map**: + - `hostname`: Server description / MOTD. + - `gametype`: `"SMP"`. + - `game_id`: `"MINECRAFT"`. + - `version`: Server protocol / Minecraft version string. + - `plugins`: Installed server software and plugin names. + - `map`: Level name. + - `numplayers` / `maxplayers`: Capacity stats. + - `hostport` / `hostip`: Network bindings. +2. **Player List**: + - An array of null-terminated UTF-8 strings containing the usernames of all currently connected players. + - Concludes with a terminal null byte (`\0`). + +--- + +## Configuration in `pumpkin.toml` + +```toml +[networking.query] +enabled = true +address = "0.0.0.0:25565" +``` diff --git a/docs/ja_jp/developer/networking/rcon.md b/docs/ja_jp/developer/networking/rcon.md new file mode 100644 index 0000000..1a4339e --- /dev/null +++ b/docs/ja_jp/developer/networking/rcon.md @@ -0,0 +1,64 @@ +# RCON (Remote Console) + +## What is RCON + +RCON (Remote Console) is a protocol designed by Valve to allow administrators to control and manage game servers remotely. It provides a way to execute commands on a server from a different location, such as a phone or a separate computer. + +## Why RCON + +- **Convenience:** Manage your server from anywhere with an internet connection. +- **Flexibility:** Execute commands without needing to be physically present at the server's location. +- **Efficiency:** Automate tasks and streamline server management. + +## SSH vs RCON + +### SSH + +- Offers strong encryption to protect data transmitted between the client and server. +- Primarily designed for secure remote login and execution of commands on a remote machine. +- Commonly used for managing Linux/Unix systems, configuring networks, and running scripts. +- Provides a shell-like environment, allowing you to execute various commands and interact with the remote system. + +### RCON + +- Specifically designed for remote administration of game servers, allowing you to control and manage the server's settings and operations. +- Typically less secure than SSH, as it often relies on plain text passwords. +- Primarily used by game server administrators to manage game servers. +- Has a limited set of game-specific commands. + +### Packets + +RCON is a very simple protocol with a few packets. Here's how an RCON packet looks: + +| Field | Description | +| ----- | ----------------------------------------------- | +| ID | Used to indicate whether authentication failed or succeeded | +| Type | Identifies the packet type | +| Body | A message (String), e.g., a command or a password | + +#### Serverbound Packets (Client→Server) + +| Type | Packet | +| ---- | ----------- | +| 2 | Auth | +| 3 | ExecCommand | + +#### Clientbound Packets (Server→Client) + +| Type | Packet | +| ---- | ------------ | +| 2 | AuthResponse | +| 0 | Output | + +### How RCON Works + +1. **Authentication:** + + - The RCON client sends an authentication packet with the desired password. + - The server verifies the password and responds with an authentication response packet. + - If successful, the response packet includes the same ID as the one sent by the client. If unsuccessful, the ID is -1. + +2. **Command Execution:** + + - The authenticated client can now send command execution packets, with each packet containing the command to be executed. + - The server processes the command and sends back an output packet containing the result or any error messages. diff --git a/docs/ja_jp/developer/plugins/index.md b/docs/ja_jp/developer/plugins/index.md new file mode 100644 index 0000000..8cc993f --- /dev/null +++ b/docs/ja_jp/developer/plugins/index.md @@ -0,0 +1,56 @@ +# Plugin Engine Architecture + +Pumpkin features a modular, multi-runtime plugin engine designed for performance, safety, and cross-language interoperability. + +Unlike traditional Minecraft servers that rely on a single JVM class loader, Pumpkin isolates plugin execution through dedicated loaders supporting both **native high-performance Rust binaries** and **sandboxed WebAssembly components** compiled from Rust, Python, C#, C, Go, or Kotlin. + +--- + +## Architectural Overview + +### Architectural Breakdown + +| Subsystem | Primary Crate | Responsibility | +| :--- | :--- | :--- | +| **Plugin Manager** | `crates/pumpkin/src/plugin` | Lifecycle orchestration (load, enable, disable), event dispatching, and loader registration | +| **Native Loader** | `crates/pumpkin/src/plugin/loader/native` | Direct dynamic library loading (`.so`, `.dll`, `.dylib`) for zero-overhead Rust extensions | +| **WASM Loader** | `crates/pumpkin/src/plugin/loader/wasm` | Wasmtime execution engine for sandboxed components compiled from Rust, Python, Go, C#, C, and Kotlin | +| **Interface Specifications** | `crates/pumpkin-plugin-wit` | WebAssembly Interface Types (WIT) defining typed functions, types, and host imports | +| **Security & Verification** | `crates/pumpkin/src/plugin/loader/wasm/sign` | Cryptographic signature verification via W3C wasmsign2 / Ed25519 public keys | + +--- + +## The Dual Execution Model + +| Feature | Native Plugins | WebAssembly (Wasmtime) Plugins | +| :--- | :--- | :--- | +| **File Extension** | `.so` (Linux), `.dll` (Windows), `.dylib` (macOS) | `.wasm` (Component Model) | +| **Languages** | Rust (compiled dynamically) | Rust, Python, C#, C, Go, Kotlin | +| **Performance** | Native CPU execution (zero overhead) | JIT-compiled with near-native speed | +| **Sandboxing** | None (runs in host address space) | Memory-isolated sandboxed execution | +| **Security & Signing** | OS-level permissions | Cryptographic W3C Wasm-Sign verification | +| **Hot Unloading** | Limited (OS shared library constraints) | Fully supported memory reclamation | + +--- + +## Event Lifecycle & Dispatching + +Plugins interact with Pumpkin primarily through events: + +1. **Event Dispatch**: + - `server.plugin_manager.fire_blocking(&server, &mut event)`: Dispatches events synchronously on the executing thread (e.g., cancelling player block placement or modifying packet payloads). + - Asynchronous events run on non-blocking Tokio tasks. +2. **Cancellable Events**: + - Handlers can set `event.cancelled = true` to abort the default game logic (such as cancelling player movement, interactions, or packet processing). + +--- + +## Plugin Engine Documentation + +Explore the internal mechanics of Pumpkin's plugin system: + +- **[Adding a Plugin Loader](/developer/plugins/loaders)**: Step-by-step guide on implementing the `PluginLoader` trait for new scripting languages and runtimes. +- **[WASM Signing & Verification](/developer/plugins/wasm-signing)**: Detailed specification of Ed25519 cryptographic signing, W3C `wasmsign2` custom sections, and marketplace license verification. + +> [!NOTE] +> Looking to develop third-party plugins rather than working on Pumpkin's internal plugin engine? Check out the **[Plugin Development Guide](/plugin-dev/introduction)**. diff --git a/docs/ja_jp/developer/plugins/loaders.md b/docs/ja_jp/developer/plugins/loaders.md new file mode 100644 index 0000000..7e0f070 --- /dev/null +++ b/docs/ja_jp/developer/plugins/loaders.md @@ -0,0 +1,161 @@ +# Adding a Plugin Loader + +Pumpkin's plugin architecture is runtime-agnostic. The server does not assume all plugins run in WebAssembly or native shared libraries; instead, all execution runtimes implement the unified [`PluginLoader`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/plugin/loader/mod.rs) trait. + +This guide explains how `PluginLoader` operates and demonstrates how to implement a custom loader for new script runtimes or virtual machines (such as Lua, JavaScript/Boa, or GraalVM). + +--- + +## The `PluginLoader` Trait + +All plugin loaders implement the `PluginLoader` trait located in `crates/pumpkin/src/plugin/loader/mod.rs`: + +```rust +use std::{any::Any, path::Path, pin::Pin, sync::Arc}; +use crate::plugin::{PluginMetadata, api::Plugin}; + +pub type PluginLoadFuture<'a> = Pin< + Box< + dyn Future< + Output = Result< + (Arc, PluginMetadata, Box), + LoaderError, + >, + > + Send + + 'a, + >, +>; + +pub type PluginUnloadFuture<'a> = + Pin> + Send + 'a>>; + +pub trait PluginLoader: Send + Sync { + /// Determines whether this loader handles the target file. + fn can_load(&self, path: &Path) -> bool; + + /// Loads, parses, and initializes a plugin instance asynchronously. + fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a>; + + /// Unloads the plugin and reclaims associated resources. + fn unload(&self, data: Box) -> PluginUnloadFuture<'_>; + + /// Indicates whether the runtime supports safe unloading without memory leaks. + fn can_unload(&self) -> bool; +} +``` + +--- + +## Existing Loaders in Pumpkin + +Pumpkin currently provides two official loaders: + +1. **`NativePluginLoader` (`loader/native.rs`)**: + - Matches dynamic library files (`.so` on Linux, `.dll` on Windows, `.dylib` on macOS). + - Dynamically links the library using [`libloading`](https://docs.rs/libloading/). + - Locates and invokes the extern entrypoint function (`pumpkin_plugin_init`). + - `can_unload()` returns `false` due to OS-level shared library unloading hazards. +2. **`WasmPluginLoader` (`loader/wasm/mod.rs`)**: + - Matches `.wasm` files. + - Verifies cryptographic signatures (see [WASM Signing](/developer/plugins/wasm-signing)). + - JIT-compiles WebAssembly components using [Wasmtime](https://wasmtime.dev/). + - Links host WIT exports (`pumpkin-host-bindings`). + - `can_unload()` returns `true` because Wasmtime safely drops guest memory. + +--- + +## Step-by-Step: Adding a Custom Plugin Loader + +Here is how to create a custom loader (e.g., for a scripting runtime): + +### Step 1: Define the Loader Struct +Create a new file in `crates/pumpkin/src/plugin/loader/my_loader.rs`: + +```rust +use std::{any::Any, path::Path, sync::Arc}; +use crate::plugin::loader::{LoaderError, PluginLoadFuture, PluginLoader, PluginUnloadFuture}; +use crate::plugin::{PluginMetadata, api::Plugin}; + +pub struct MyCustomLoader; + +impl MyCustomLoader { + #[must_use] + pub fn new() -> Self { + Self + } +} +``` + +### Step 2: Implement File Recognition (`can_load`) +Check the file extension or inspect header bytes: + +```rust +impl PluginLoader for MyCustomLoader { + fn can_load(&self, path: &Path) -> bool { + path.extension() + .and_then(|ext| ext.to_str()) + .is_some_and(|ext| ext.eq_ignore_ascii_case("script")) + } +``` + +### Step 3: Implement Asynchronous Loading (`load`) +The `load` method reads the script, initializes the VM, registers server host callbacks, and returns a tuple containing: +1. `Arc`: The generic plugin handle. +2. `PluginMetadata`: Name, author, version, description. +3. `Box`: Loader-specific context (e.g., VM state) retained for unloading. + +```rust + fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a> { + Box::pin(async move { + let script_content = tokio::fs::read_to_string(path).await + .map_err(|e| LoaderError::InitializationFailed(e.to_string()))?; + + // 1. Parse plugin metadata from header or accompanying manifest + let metadata = PluginMetadata { + name: "ExampleScript".into(), + version: "1.0.0".into(), + authors: vec!["Author".into()], + description: Some("Custom script plugin".into()), + }; + + // 2. Initialize runtime instance + let plugin_instance = Arc::new(MyScriptPluginWrapper::new(script_content)); + let runtime_state: Box = Box::new(()); + + Ok((plugin_instance, metadata, runtime_state)) + }) + } +``` + +### Step 4: Implement Clean Unloading (`unload` & `can_unload`) +If your runtime can cleanly terminate without leaking resources: + +```rust + fn can_unload(&self) -> bool { + true + } + + fn unload(&self, data: Box) -> PluginUnloadFuture<'_> { + Box::pin(async move { + // Drop runtime state and disconnect event listeners + drop(data); + Ok(()) + }) + } +} +``` + +### Step 5: Register the Loader in `PluginManager` + +Open `crates/pumpkin/src/plugin/mod.rs` and register your loader in `PluginManager::new()`: + +```rust +// In crates/pumpkin/src/plugin/mod.rs +let mut loaders: Vec> = Vec::new(); + +loaders.push(Box::new(NativePluginLoader::new())); +loaders.push(Box::new(WasmPluginLoader::new())); +loaders.push(Box::new(MyCustomLoader::new())); // Add custom loader +``` + +During server startup, Pumpkin iterates through all registered loaders in sequence when scanning the `plugins/` folder, dispatching each file to the first loader whose `can_load` returns `true`. diff --git a/docs/ja_jp/developer/plugins/wasm-signing.md b/docs/ja_jp/developer/plugins/wasm-signing.md new file mode 100644 index 0000000..40a36eb --- /dev/null +++ b/docs/ja_jp/developer/plugins/wasm-signing.md @@ -0,0 +1,86 @@ +# WASM Plugin Signing & Verification + +Pumpkin implements a cryptographic signing and license verification system for WebAssembly plugins. This ensures plugin integrity, authenticates author identity, and protects verified or marketplace plugins against unauthorized tampering. + +The signing subsystem is implemented in [`crates/pumpkin/src/plugin/loader/wasm/wasm_host/signature.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/plugin/loader/wasm/wasm_host/signature.rs) based on the **W3C `wasmsign2`** specification using custom WebAssembly sections. + +--- + +## Why Sign WebAssembly Plugins? + +- **Tamper Protection**: Ensures that compiled bytecode has not been injected with malicious logic after compilation. +- **Author Identity**: Authenticates that a plugin was built and released by a verified developer. +- **Marketplace Licensing**: Validates license keys and entitlements for plugins distributed through the official [Pumpkin Marketplace](https://market.pumpkinmc.org). + +--- + +## Custom Sections in the WASM Binary + +Pumpkin embeds signing and verification metadata directly into two standard WebAssembly custom sections: +- **`pumpkin.metadata`**: Stores developer, publication, and license claims in JSON format. +- **`wasm_signature`**: Stores the cryptographic signature envelope compliant with the W3C `wasmsign2` standard. + +### 1. `pumpkin.metadata` Section +Contains a JSON string holding publication and license claims: + +```json +{ + "marketplace_url": "https://market.pumpkinmc.org", + "plugin_id": 1024, + "plugin_name": "EconomyPlus", + "version": "1.2.0", + "dev_id": 42, + "dev_name": "DevName", + "is_paid": true, + "user_id": 1001, + "license_key": "XXXX-XXXX-XXXX-XXXX", + "issued_at": "2026-09-14T05:00:00Z" +} +``` + +### 2. `wasm_signature` Section +Follows the W3C `wasmsign2` envelope structure: + +```json +{ + "version": 1, + "algorithm": "Ed25519", + "public_key_hex": "4a7b...", + "signature_hex": "8e1c..." +} +``` + +--- + +## The Signing Pipeline + +The signing process guarantees integrity by hashing and signing the clean WebAssembly bytecode combined with the metadata claims: + +### Signing Algorithm Steps +1. **Sanitization (`strip_pumpkin_sections`)**: Uses [`wasmparser`](https://docs.rs/wasmparser/) to strip any preexisting `pumpkin.metadata` or `wasm_signature` custom sections to avoid recursion. +2. **Payload Construction**: + ```rust + let mut sign_payload = Vec::new(); + sign_payload.extend_from_slice(&clean_wasm); + sign_payload.extend_from_slice(&metadata_json); + ``` +3. **Ed25519 Signature**: The payload is signed using [`ed25519-dalek`](https://docs.rs/ed25519-dalek/) with the developer's 32-byte secret key. +4. **Encoding**: Uses [`wasm-encoder`](https://docs.rs/wasm-encoder/) to append the `pumpkin.metadata` and `wasm_signature` custom sections to the clean binary. + +--- + +## The Verification Pipeline + +When Pumpkin boots and loads a WebAssembly plugin, the verification sequence executes before the plugin is instantiated: + +1. **Custom Section Extraction**: The `WasmPluginLoader` scans the binary for `pumpkin.metadata` and `wasm_signature`. +2. **Key Retrieval**: + - If the plugin is marked as marketplace-verified, Pumpkin verifies the public key against the Marketplace Public Key Endpoint (`https://market.pumpkinmc.org/api/v1/rest/public-key`). + - If signed by an independent developer, the key is matched against trusted developer keys configured in `pumpkin.toml`. +3. **Bytecode Verification**: + - Pumpkin strips the custom sections from the loaded binary to recover the original `clean_wasm`. + - Reconstructs the payload `(clean_wasm + metadata_json)`. + - Verifies the signature using `verifying_key.verify(&payload, &signature)`. +4. **License & Integrity Validation**: + - If verification succeeds, Wasmtime instantiates the plugin safely. + - If verification fails or the signature is invalid, loading is aborted with an error, protecting the server from running compromised code. diff --git a/docs/ja_jp/developer/porting-versions.md b/docs/ja_jp/developer/porting-versions.md new file mode 100644 index 0000000..0a5014a --- /dev/null +++ b/docs/ja_jp/developer/porting-versions.md @@ -0,0 +1,287 @@ +# Porting to Newer Minecraft Versions + +Upgrading Pumpkin to support a newer Minecraft release—whether a minor patch (e.g., `1.21.4` to `1.21.5`) or a major update (e.g., `1.21` to `26.1`/`26.2`)—is a structured, multi-step process. + +> [!IMPORTANT] +> **The Extractor mod must be ported first!** +> Pumpkin cannot be ported in isolation. The primary source for Pumpkin's assets (`blocks.json`, `items.json`, `entities.json`, `packets.json`, `tracked_data.json`, `synced_registries.json`, etc.) is our dedicated Fabric mod: [Pumpkin-MC/Extractor](https://github.com/Pumpkin-MC/Extractor). You **must first port and run the Extractor on the target Minecraft version** before running `pumpkin-codegen` or updating server crates. + +### Porting Lifecycle at a Glance + +| Stage | Responsibility | Primary Crates / Tools | +| :--- | :--- | :--- | +| **1. Port Extractor** | Extract vanilla block states, entities, items, packets, and registries | `Pumpkin-MC/Extractor` (Fabric mod) | +| **2. Update Datapacks** | Ingest new data packs, tags, recipes, and Bedrock mappings | `assets/datapacks/`, `assets/bedrock` | +| **3. Version Declarations** | Register protocol version and ViaVersion/ViaBackwards remap nodes | `pumpkin-util`, `tools/pumpkin-codegen` | +| **4. Code Generation** | Regenerate zero-cost Rust data tables and registries | `pumpkin-codegen` -> `pumpkin-data` | +| **5. Compiler Fixes** | Update packet layouts, codecs, and data components | `pumpkin-protocol`, `pumpkin-inventory` | +| **6. Gameplay Logic** | Implement new block behaviors, item mechanics, and mob AI | `crates/pumpkin/src/block`, `item`, `entity` | +| **7. Multi-Version QA** | Validate backward compatibility, test suites, and client joins | `cargo test`, `pumpkin-gametest` | + +--- + +## Phase 1: Port the Extractor Mod First (`Pumpkin-MC/Extractor`) + +The [Extractor](https://github.com/Pumpkin-MC/Extractor) is a Kotlin-based Fabric mod that boots an actual Minecraft client or server instance to introspect live game registries and extract data into the exact JSON formats Pumpkin expects. + +### 1. Clone or Update the Extractor Repository +```bash +git clone https://github.com/Pumpkin-MC/Extractor +cd Extractor +``` + +### 2. Update `gradle.properties` +Bump the dependency versions to match the target Minecraft version, Yarn mappings, and Fabric ecosystem: + +```properties +minecraft_version=26.2 +yarn_mappings=26.2+build.1:v2 +loader_version=0.16.10 +fabric_version=0.115.0+26.2 +``` + +### 3. Resolve Extractor Compilation Errors +Minecraft and Fabric internal APIs often change between releases. Check and fix any broken extractors in `src/main/kotlin/de/snowii/extractor/extractors/`: +- **`Blocks.kt`**: Extracts block states, hardness, resistance, luminance, and collision box offsets. +- **`Items.kt`**: Extracts item IDs, max stack sizes, and default components. +- **`Entities.kt`**: Extracts entity IDs, dimensions (width, height), and eye heights. +- **`Packets.kt`**: Extracts packet IDs for handshake, status, login, configuration, and play states. +- **`SyncedRegistries.kt`**: Extracts dynamic registries synchronized to clients. +- **`TrackedData.kt`**: Extracts entity tracked data indices and serializers. +- **`MultiNoise.kt` & `Biome.kt`**: Extracts world generation biome trees and noise parameters. + +### 4. Run the Extractor +Execute the server runner (or client runner for translation/screen dumps): + +```bash +./gradlew runServer +``` + +Once the extraction finishes, all dumped JSON files will be located in `run/pumpkin_extractor_output/`. + +### 5. Replace the Old Asset Files in Pumpkin +Copy the newly generated JSON files from `run/pumpkin_extractor_output/` and use them to **replace the old asset files** in Pumpkin: +- Replace the core game assets in `Pumpkin/assets/` (`blocks.json`, `items.json`, `entities.json`, `packets.json`, `properties.json`, `synced_registries.json`, `tracked_data.json`, `en_us.json`, etc.). +- Replace the world generation test files in `Pumpkin/crates/pumpkin-world/assets/`. + +--- + +## Phase 2: Update Assets & Datapacks + +Once the raw extractor assets are in place, update the versioned datapacks and supplemental files in Pumpkin: + +### 1. Add the Versioned Datapack +Extract the internal vanilla data pack from the new Minecraft server JAR and place it under `assets/datapacks//`: + +``` +assets/datapacks// +└── data/ + ├── minecraft/ + │ ├── advancement/ + │ ├── chat_type/ + │ ├── damage_type/ + │ ├── dimension_type/ + │ ├── enchantment/ + │ ├── item_modifier/ + │ ├── loot_table/ + │ ├── recipe/ + │ ├── tags/ + │ └── worldgen/ + └── c/ + └── tags/ (Conventional / Common Tags) +``` + +### 2. Update Versioned Assets +Update or generate the corresponding versioned metadata files: +- `assets/registry/_synced_registries.json`: Dynamic registries streamed to the client during the `Configuration` network state. +- `assets/tags/_tags.json`: Tag collections. +- `assets/tracked_data/_tracked_data.json`: Entity metadata indices. + +### 3. Update Bedrock Mappings +If porting Bedrock Edition support simultaneously, update the files in `assets/bedrock/` using the latest mappings from [GeyserMC](https://github.com/GeyserMC/Geyser) and Bedrock Dedicated Server (BDS). + +### 4. Decompile Sources for Reverse Engineering (Optional but Recommended) +For complex changes—such as worldgen noise router formulas and custom block collision shapes—decompile the matching vanilla JAR into `minecraft-sources-/` (using Vineflower or Fabric Loom with official Mojang mappings) for reference. + +--- + +## Phase 3: Register Version Constants & ViaVersion Mappings + +Pumpkin supports multi-version clients through automated protocol remapping powered by **ViaBackwards** / **ViaRewind** mapping tables. + +### 1. Declare Version in `pumpkin-util` +Open `crates/pumpkin-util/src/version.rs` and add the new version to [`JavaMinecraftVersion`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-util/src/version.rs): + +```rust +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum JavaMinecraftVersion { + // ... existing versions ... + V_1_21_4, + V_1_21_11, + V_26_2, // New target version +} + +impl JavaMinecraftVersion { + pub const fn protocol_version(&self) -> i32 { + match self { + // ... + Self::V_26_2 => 770, // Protocol version number + } + } + + pub const fn version_name(&self) -> &'static str { + match self { + // ... + Self::V_26_2 => "26.2", + } + } +} +``` + +### 2. Declare Version in `pumpkin-codegen` +Open `tools/pumpkin-codegen/src/version.rs` and add the identical enum variant to the codegen AST. + +### 3. Add ViaVersion / ViaBackwards Mappings +To enable clients on older versions to connect to the updated server (e.g., a `1.21.4` client connecting to a `26.2` server): +1. Obtain the NBT mapping file from the [ViaBackwards repository](https://github.com/ViaVersion/ViaBackwards): + `mappings-to.nbt` +2. Save it to: + `assets/viabackwards/data/mappings-to.nbt` +3. In `tools/pumpkin-codegen/src/remap/mod.rs`, link the new node into the `remap_nodes!` macro: + +```rust +let node_26_2 = $crate::remap::MappingNode { + version: $crate::version::JavaMinecraftVersion::V_26_2, + value: "../../assets/viabackwards/data/mappings-26.2to26.1.nbt", + child: Some(&node_26_1), +}; +$remapper.process(&node_26_2) +``` + +4. Check `tools/pumpkin-codegen/src/remap/` to ensure specific remap builders (`block_state.rs`, `item_id.rs`, `entity_id.rs`, `sound_id.rs`, `argument_type.rs`) correctly process any new remapping structures introduced by ViaVersion. + +--- + +## Phase 4: Run Code Generation (`pumpkin-codegen`) + +With all extractor assets, datapacks, and mapping nodes in place, execute the codegen tool from the workspace root: + +```bash +cargo run -p pumpkin-codegen +``` + +### What Gets Regenerated? +`pumpkin-codegen` generates zero-cost Rust data tables into `crates/pumpkin-data/src/generated/`: +- **`block.rs`**: All block IDs, properties, state masks, and collision shapes. +- **`item.rs`**: Items, default components, and tool tiers. +- **`entity_type.rs` & `tracked_data.rs`**: Entity type IDs, default bounding boxes, and metadata serializers. +- **`packet.rs`**: Serverbound and clientbound packet ID constants across protocol states. +- **`recipes.rs`**: All crafting, cooking, and stonecutting recipes parsed from the new datapack. +- **`tag.rs`**: Tag lookups (`#minecraft:mineable/axe`, `#c:iron_ingots`). +- **`remap/*.rs`**: State remap lookup tables (`block_state_remap.rs`, `item_id_remap.rs`, `entity_id_remap.rs`, `sound_id_remap.rs`). + +--- + +## Phase 5: Resolve Workspace Compilation Errors + +After regenerating `pumpkin-data`, the Rust compiler will flag breaking changes across the workspace. Run: + +```bash +cargo check --workspace +``` + +### Common Compiler Breakages & Fixes: + +#### 1. Packet ID & Layout Changes (`pumpkin-protocol`) +Mojang frequently adds, moves, or alters packet IDs and payloads: +- Update `pumpkin-protocol/src/java/client/` and `pumpkin-protocol/src/java/server/`. +- Inspect packets like `LoginSuccess`, `PlayLogin`, `Respawn`, and `PlayerSpawnData` for newly added fields (e.g., new game mode flags, dimension attributes, or teleportation IDs). +- For Bedrock Edition, check `StartGame`, `NetworkItem`, and container transaction packets in `pumpkin-protocol/src/bedrock/`. + +#### 2. Data Component Changes +If items switched from older NBT structures to new Data Components (or if components were renamed/restructured): +- Update `crates/pumpkin-data/src/data_component.rs`. +- Update serialization and deserialization in `pumpkin-inventory`. + +#### 3. Registry Synchronization +During the `Configuration` phase, servers send dynamic registry contents to Java clients. If new registries were added to vanilla Minecraft (e.g., trim materials, banner patterns, instrument sounds): +- Verify `crates/pumpkin-data/src/generated/registry.rs`. +- Ensure `pumpkin/src/net/java/config.rs` streams all required registry codecs to the client. + +--- + +## Phase 6: Implement Logic for New Blocks, Items & Entities + +Once the server compiles, any newly introduced gameplay content must be implemented. + +### 1. New Blocks +1. Locate new block identifiers in `pumpkin-data::Block`. +2. Create a behavior module under `crates/pumpkin/src/block/blocks/.rs` implementing [`BlockBehaviour`](/developer/blocks/adding-blocks): + ```rust + pub struct NewBlockBehaviour; + + impl BlockBehaviour for NewBlockBehaviour { + fn on_use(&self, block: &Block, player: &Arc, location: BlockPos, world: &Arc) { + // Interaction logic + } + } + ``` +3. Register the behavior in the block registry in `crates/pumpkin/src/block/mod.rs`. + +### 2. New Items +1. Locate new item identifiers in `pumpkin-data::Item`. +2. If the item has unique right-click or use behavior (e.g., weapons, projectiles, instruments), create an item behavior in `crates/pumpkin/src/item/items/.rs` implementing [`ItemBehaviour`](/developer/items/adding-items). +3. If the item places entities (e.g., spawn eggs, boats, minecarts), configure entity spawning logic in `pumpkin/src/item/items/spawn_egg.rs`. +4. Register the behavior in `crates/pumpkin/src/item/mod.rs`. + +### 3. New Entities +1. Check `pumpkin-data::entity_type::EntityType` for new entity IDs. +2. Create an entity struct in `crates/pumpkin/src/entity/mob/` or `living/` extending [`LivingEntity`](/developer/entities/). +3. Implement `TrackedData` fields to match vanilla client expectations (e.g., poses, animations, glowing states). +4. Assign AI goals and navigation tasks using the [Mob AI & Goals system](/developer/entities/ai-and-mobs). +5. Add spawn packet construction in `crates/pumpkin/src/entity/spawning.rs`. + +--- + +## Phase 7: Multi-Version & ViaVersion Verification + +Ensure that older clients can still connect seamlessly: + +1. **Verify Generated Remap Tables**: + Check `crates/pumpkin-data/src/generated/block_state_remap.rs` and `item_id_remap.rs`. Ensure that novel block states fall back gracefully to compatible equivalents on older client versions (e.g., new wood variants mapping to oak or spruce). +2. **Packet Translation**: + Verify that packet writers in `pumpkin-protocol` apply `remap` functions when serializing data for a connection whose negotiated `protocol_version` is older than the server's native version. + +--- + +## Phase 8: Testing & Verification Checklist + +Before opening a Pull Request for a version port, complete the following QA checklist: + +- [ ] **Automated Tests**: + ```bash + cargo test --workspace + ``` +- [ ] **Game Test Suite**: + ```bash + cargo test -p pumpkin-gametest + ``` +- [ ] **Strict Linter Rules**: + ```bash + cargo clippy --workspace --all-targets -- -D warnings + ``` +- [ ] **Spellcheck**: + ```bash + typos + ``` +- [ ] **In-Game Java Client Test**: + - Connect with the native new Minecraft client version. + - Test login, world loading, chunk generation, and player movement. + - Place and break new blocks; verify collision boxes and redstone reactivity. + - Use and equip new items; test attack speed, durability, and animations. + - Spawn and fight new entities; test animations, hurt sounds, and drops. +- [ ] **In-Game Older Version Client Test**: + - Connect with an older supported client version (e.g., 1.21.x). + - Verify that blocks, items, and entities translate without causing client crashes or desyncs. +- [ ] **In-Game Bedrock Client Test**: + - Verify that Bedrock clients can authenticate, join, load world chunks, and view custom items. diff --git a/docs/ja_jp/developer/world.md b/docs/ja_jp/developer/world.md new file mode 100644 index 0000000..c88e420 --- /dev/null +++ b/docs/ja_jp/developer/world.md @@ -0,0 +1,105 @@ +# World Engine & Formats + +The world engine is managed primarily within the [`pumpkin-world`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-world) crate. It handles world storage formats, chunk reading and writing, terrain generation, lighting calculations, and block ticking. + +--- + +## Supported World Formats + +Pumpkin is designed to support multiple region and world formats, giving server administrators flexibility between vanilla compatibility and modern high-performance compression. + +### 1. Anvil File Format (`.mca`) + +The Anvil format is the standard format used by vanilla Minecraft: Java Edition. + +- **Structure**: Chunks are grouped into 32×32 chunk regions stored in `.mca` files (`r.{x}.{z}.mca`). +- **Data Layers**: Each chunk contains block state palettes, biome palettes, heightmaps, tile entities, and light arrays. +- **Compression**: Individual chunks within the region file are compressed using ZLib or LZ4. +- **Support**: Pumpkin supports **both loading and saving** Anvil region files. + +### 2. Linear Region Format (`.linear`) + +Linear is a modern alternative to Anvil that drastically reduces world save sizes and speeds up disk I/O by utilizing **Zstandard (zstd)** compression across the entire region rather than per-chunk zlib blocks. + +- **Savings**: Reduces disk usage by ~50% in the Overworld and Nether, and up to ~95% in the End. +- **Performance**: High decompression speeds and reduced disk writes make it ideal for high-throughput production servers. +- **Support**: Native read/write support is built into `pumpkin-world`. + +### 3. Slime World Format (`.slime`) + +Originally developed by Hypixel, the Slime format packages entire worlds into a single, compact, easily duplicable binary file. It is optimized for minigames and temporary instance-based worlds. + +### 4. Pump Format + +Pumpkin's native chunk storage format designed specifically for rapid serialization without the legacy overhead of the Anvil format. + +--- + +The world engine organizes chunk management and ticking into hierarchical layers: + +### Hierarchy + +1. **`World`**: Manages dimensions (`overworld`, `the_nether`, `the_end`), coordinates players within that dimension, and broadcasts updates. +2. **`Level`**: The core data structure holding chunk tables, height bounds (-64 to 320 for vanilla 1.18+ worlds), block registries, and tick schedulers. +3. **`Chunker`**: Manages view distances and player subscriptions. Uses `CylindricalChunkIterator` to compute which chunks to stream to players as they move. + +### Asynchronous Chunk Pipeline + +To maintain steady 20 TPS tick rates: + +- Chunks requested by players are checked in the memory cache. +- Uncached chunks are loaded from disk or generated via **Rayon** background tasks without stalling the main game tick loop. +- Completed chunks are dispatched through asynchronous channels to the player's connection stream. + +--- + +## World Generation Pipeline + +When a world does not yet have saved chunks, or when players explore past generated boundaries, Pumpkin generates new chunks through a 6-stage pipeline: + +1. **Noise Routers**: Multi-octave 3D Perlin and Simplex noise algorithms compute continentalness, erosion, and peaks/valleys. +2. **Density Shaping**: Transforms continuous 3D noise values into terrain density and solid vs. air voxel thresholds. +3. **Biome Climate Mapping**: Samples temperature, humidity, continentalness, and erosion to assign biome IDs. +4. **Surface Rules**: Evaluates layered rules to place grass, dirt, sand, sandstone, or deepslate depending on exposure and altitude. +5. **Carvers**: Carves 3D cave tunnels, ravines, and aquifers into solid rock. +6. **Features & Decoration**: Places ore veins, vegetation, trees, structures, and surface foliage. + +### 1. Noise Routers & Density Functions + +Pumpkin implements Minecraft's multi-octave 3D Perlin and Simplex noise algorithms: + +- Calculates continentalness, erosion, peaks & valleys, and 3D density. +- Generates natural terrain shapes including overhangs, floating islands, mountains, and deep caves. + +### 2. Biome Climate Parameters + +Biomes are resolved dynamically based on multi-dimensional climate coordinates: + +- Temperature +- Humidity +- Continentalness +- Erosion +- Depth +- Weirdness + +The parameters match vanilla Minecraft biome palettes and generate smooth biome transitions. + +### 3. Generators + +- **Vanilla-like Terrain Generator**: Full 3D noise generation matching modern Minecraft world limits (Y = -64 to Y = 320). +- **Superflat Generator**: Configurable flat layers (e.g., Bedrock at Y=-64, Dirt, Grass Block top layer). + +--- + +## Lighting & Ticking + +### 1. Lighting Engine + +- **Block Light**: Propagated dynamically from light-emitting blocks (torches, glowstone, lava) up to level 15. +- **Sky Light**: Propagated vertically from the sky down through transparent and semi-transparent blocks (leaves, water). +- Light arrays are computed and serialized into clientbound chunk packets so clients render accurate shadows. + +### 2. Block & Chunk Ticking + +- **Scheduled Ticks**: Blocks requesting delayed execution (water flow, falling sand, redstone repeaters). +- **Random Ticks**: Random blocks selected per sub-chunk each tick to simulate plant growth, ice melting, and crop maturation. diff --git a/docs/ja_jp/plugin-dev/c/first-command.md b/docs/ja_jp/plugin-dev/c/first-command.md new file mode 100644 index 0000000..3ac3ebd --- /dev/null +++ b/docs/ja_jp/plugin-dev/c/first-command.md @@ -0,0 +1,90 @@ +# Creating Your First Command + +Writing commands in C for Pumpkin is low-overhead, fast, and uses C WIT bindings. + +--- + +## 1. Quick Example + +Here is a complete C plugin file (`main.c`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```c [main.c] +#include +#include "pumpkin.h" + +void plugin_on_load(pumpkin_context_t ctx) { + // 1. Register permission node + pumpkin_permission_t perm = { + .node = "my_c_plugin:hello", + .description = "Allows executing the /hello command", + .default_access = PERMISSION_DEFAULT_ALLOW + }; + pumpkin_register_permission(ctx, &perm); + + // 2. Build and register command tree + const char* names[] = {"hello", "hi"}; + pumpkin_command_t cmd = pumpkin_command_new(names, 2, "Greets the player"); + + pumpkin_register_command(ctx, cmd, "my_c_plugin:hello"); +} + +int32_t plugin_handle_command(uint32_t command_id, pumpkin_sender_t sender, pumpkin_server_t server) { + pumpkin_sender_send_message(sender, "Hello from C Plugin!"); + return 1; +} + +pumpkin_metadata_t plugin_get_metadata() { + pumpkin_metadata_t meta = { + .name = "my_c_plugin", + .version = "0.1.0", + .description = "My first Pumpkin C plugin" + }; + return meta; +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Struct +Populate a `pumpkin_permission_t` struct and call `pumpkin_register_permission`: +```c +pumpkin_permission_t perm = { + .node = "my_c_plugin:hello", + .description = "Allows executing the /hello command", + .default_access = PERMISSION_DEFAULT_ALLOW +}; +pumpkin_register_permission(ctx, &perm); +``` + +### Step 2: Build the Command Tree +Create the command tree by passing name array and description: +```c +const char* names[] = {"hello", "hi"}; +pumpkin_command_t cmd = pumpkin_command_new(names, 2, "Greets the player"); +``` + +### Step 3: Register Command & Handle Execution +Pass the command handle to `pumpkin_register_command` and handle responses inside `plugin_handle_command`: +```c +pumpkin_register_command(ctx, cmd, "my_c_plugin:hello"); +``` + +::: + +--- + +## Next Steps + +- Check out the [C Quick Start Guide](./quick-start) to set up your compiler toolchain. diff --git a/docs/ja_jp/plugin-dev/c/quick-start.md b/docs/ja_jp/plugin-dev/c/quick-start.md new file mode 100644 index 0000000..7b8560a --- /dev/null +++ b/docs/ja_jp/plugin-dev/c/quick-start.md @@ -0,0 +1,64 @@ +# Quick Start + +This guide will help you get started with writing Pumpkin server plugins using C. + +## Prerequisites + +Before you start, ensure you have the following installed: +- [wasi-sdk](https://github.com/WebAssembly/wasi-sdk/releases) (for compiling C to WASI) +- `git` (to clone the API) + +## Setting up the project + +First, clone the Pumpkin C API repository with submodules: + +```bash +git clone --recursive https://github.com/Pumpkin-MC/pumpkin-api-c.git +cd pumpkin-api-c +``` + +## Creating your first plugin + +Create a file named `main.c` in the root of the repository and add the following content: + +```c +#include "pumpkin_api.h" +#include + +pumpkin_metadata_t get_meta(void) { + static const char* authors[] = {"you"}; + return (pumpkin_metadata_t) { + .name = "my-c-plugin", + .version = "0.1.0", + .authors = authors, + .authors_count = 1, + .description = "A simple C plugin for Pumpkin", + .dependencies_count = 0 + }; +} + +void on_load(plugin_own_context_t ctx) { + printf("C plugin loaded!\n"); +} + +REGISTER_PUMPKIN_PLUGIN(((pumpkin_plugin_t){ + .get_metadata = get_meta, + .on_load = on_load +})) +``` + +## Building the plugin + +To build your plugin into a WebAssembly component, use the `clang` compiler from your `wasi-sdk` installation. + +Replace `/path/to/wasi-sdk` with the actual path to your WASI SDK. + +```bash +/path/to/wasi-sdk/bin/clang -O3 \ + -Iinclude -Isrc/gen \ + src/gen/plugin.c src/pumpkin_api.c main.c \ + -o my_plugin.wasm \ + -mexec-model=reactor +``` + +This will generate a `my_plugin.wasm` file that you can place in the `plugins` folder of your Pumpkin server. diff --git a/docs/ja_jp/plugin-dev/csharp/first-command.md b/docs/ja_jp/plugin-dev/csharp/first-command.md new file mode 100644 index 0000000..c2a34ca --- /dev/null +++ b/docs/ja_jp/plugin-dev/csharp/first-command.md @@ -0,0 +1,110 @@ +# Creating Your First Command + +Registering custom commands in Pumpkin using C# provides a strongly-typed, clean structure. + +--- + +## 1. Quick Example + +Here is a complete C# plugin file (`MyPlugin.cs`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```csharp [MyPlugin.cs] +using PluginWorld; +using PluginWorld.wit.Exports.pumpkin.plugin.v0_1_0; +using PluginWorld.wit.Imports.pumpkin.plugin.v0_1_0; + +namespace MyPumpkinPlugin; + +public class MyPlugin : IPluginWorldExports, IMetadataExports +{ + public static void InitPlugin() { } + + public static void OnLoad(IContextImports.Context context) + { + // 1. Register permission node + var perm = new IPermissionImports.Permission( + "my_csharp_plugin:hello", + "Allows executing the /hello command", + IPermissionImports.PermissionDefault.Allow, + [] + ); + IContextImports.RegisterPermission(context, perm); + + // 2. Build and register command tree + var command = ICommandImports.Command.New(["hello", "hi"], "Greets the player"); + IContextImports.RegisterCommand(context, command, "my_csharp_plugin:hello"); + } + + public static void OnUnload(IContextImports.Context context) { } + + public static IEventImports.Event HandleEvent(uint eventId, IServerImports.Server server, IEventImports.Event @event) + { + return @event; + } + + public static int HandleCommand(uint commandId, ICommandImports.CommandSender sender, IServerImports.Server server, ICommandImports.ConsumedArgs args) + { + ICommandImports.SendMessage(sender, "Hello from C# Plugin!"); + return 1; + } + + public static void HandleTask(uint handlerId, IServerImports.Server server) { } + + public static IMetadataExports.PluginMetadata Metadata() + { + return new IMetadataExports.PluginMetadata( + "my_csharp_plugin", + "0.1.0", + "My first Pumpkin C# plugin", + ["Developer"], + [] + ); + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Node +Construct an `IPermissionImports.Permission` record and register it via `IContextImports.RegisterPermission`: +```csharp +var perm = new IPermissionImports.Permission( + "my_csharp_plugin:hello", + "Allows executing the /hello command", + IPermissionImports.PermissionDefault.Allow, + [] +); +IContextImports.RegisterPermission(context, perm); +``` + +### Step 2: Build the Command Tree +Instantiate your command with primary names and description: +```csharp +var command = ICommandImports.Command.New(["hello", "hi"], "Greets the player"); +``` + +### Step 3: Register and Handle +Register the command with `IContextImports.RegisterCommand` and process incoming executions inside `HandleCommand`: +```csharp +IContextImports.RegisterCommand(context, command, "my_csharp_plugin:hello"); +``` + +::: + +--- + +## Next Steps + +- Follow the [C# Quick Start Guide](./quick-start) to configure `.csproj` and build WebAssembly outputs. diff --git a/docs/ja_jp/plugin-dev/csharp/quick-start.md b/docs/ja_jp/plugin-dev/csharp/quick-start.md new file mode 100644 index 0000000..37b8e15 --- /dev/null +++ b/docs/ja_jp/plugin-dev/csharp/quick-start.md @@ -0,0 +1,116 @@ +# Quick Start + +This guide will help you get started with writing Pumpkin server plugins using C#. + +## Prerequisites + +Before you start, ensure you have the following installed: +- [.NET 10.0](https://dotnet.microsoft.com/download/dotnet/10.0) or later. +- [WebAssembly Workload](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/webassembly-overview): You may need to install the WASI workload: + ```bash + dotnet workload install wasi-experimental + ``` + +## Setting up the project + +First, create a new class library project: + +```bash +dotnet new classlib -n MyPumpkinPlugin +cd MyPumpkinPlugin +``` + +Create a `NuGet.Config` file in your project root to include the experimental .NET feed: + +```xml + + + + + + +``` + +Add the Pumpkin API and the WebAssembly SDK: + +```bash +dotnet add package PumpkinMC.PumpkinApi +dotnet add package ByteCodeAlliance.Componentize.DotNet.Wasm.SDK --prerelease +``` + +Edit your `.csproj` file to target `wasi-wasm` and use .NET 10.0: + +```xml + + + + net10.0 + enable + enable + wasi-wasm + + + + + + + + +``` + +## Creating your first plugin + +Replace the content of `Class1.cs` (or create a new file `MyPlugin.cs`) with the following code: + +```csharp +using PluginWorld; +using PluginWorld.wit.Exports.pumpkin.plugin.v0_1_0; +using PluginWorld.wit.Imports.pumpkin.plugin.v0_1_0; + +namespace MyPumpkinPlugin; + +public class MyPlugin : IPluginWorldExports, IMetadataExports +{ + public static void InitPlugin() { } + + public static void OnLoad(IContextImports.Context context) + { + ILoggingImports.Log(ILoggingImports.Level.Info, "C# plugin loaded!"); + } + + public static void OnUnload(IContextImports.Context context) { } + + public static IEventImports.Event HandleEvent(uint eventId, IServerImports.Server server, IEventImports.Event @event) + { + return @event; + } + + public static int HandleCommand(uint commandId, ICommandImports.CommandSender sender, IServerImports.Server server, ICommandImports.ConsumedArgs args) + { + return 0; + } + + public static void HandleTask(uint handlerId, IServerImports.Server server) { } + + public static IMetadataExports.PluginMetadata Metadata() + { + return new IMetadataExports.PluginMetadata( + "my-csharp-plugin", + "0.1.0", + "An example C# plugin.", + ["YourName"], + [] + ); + } +} +``` + +## Building the plugin + +To build your plugin into a WebAssembly component: + +```bash +dotnet build -c Release +``` + +The compiled `.wasm` file will be located in `bin/Release/net10.0/wasi-wasm/publish/`. You can place this file in the `plugins` folder of your Pumpkin server. diff --git a/docs/ja_jp/plugin-dev/d/quick-start.md b/docs/ja_jp/plugin-dev/d/quick-start.md new file mode 100644 index 0000000..5448f9e --- /dev/null +++ b/docs/ja_jp/plugin-dev/d/quick-start.md @@ -0,0 +1,130 @@ +# クイックスタート + +このガイドでは、[D言語](https://dlang.org/)を使用してPumpkinサーバープラグインを作成する手順を説明します。 + +Pumpkin用のD言語プラグインは、公式の[`pumpkin-api-d`](https://github.com/Pumpkin-MC/pumpkin-api-d)バインディングを使用してWebAssembly (Wasm) コンポーネントにコンパイルされます。 + +--- + +## 前提条件 + +開始する前に、以下のツールがインストールされていることを確認してください: + +- **[LDC](https://github.com/ldc-developers/ldc)**: バージョン 1.43 以降 (LLVMベースのD言語コンパイラ)。 +- **`addon-wasi`**: LDC用のWASIターゲットパッケージ。 +- **[DUB](https://dub.pm/)**: D言語のパッケージおよびビルド管理ツール (通常LDCに含まれています)。 + +--- + +## 1. プロジェクトのセットアップ + +プラグインプロジェクト用の新しいディレクトリを作成し、`dub.json`設定ファイルを追加します: + +```bash +mkdir my-d-plugin +cd my-d-plugin +``` + +プロジェクトルートに `dub.json` を作成します: + +```json [dub.json] +{ + "name": "my-d-plugin", + "description": "D言語で構築された最小限のPumpkinプラグイン。", + "license": "proprietary", + "authors": ["あなたの名前"], + "copyright": "Copyright © 2026, あなたの名前", + + "dependencies": { + "pumpkin-api-d": "~>0.1.0" + }, + + "targetType": "executable", + "dflags": ["-Xcc=-mexec-model=reactor"], + "buildTypes": { + "debug": { + "buildOptions": ["debugMode", "debugInfo"] + }, + "release": { + "buildOptions": ["optimize", "inline"], + "dflags": ["-L-S"] + } + } +} +``` + +> [!NOTE] +> `-Xcc=-mexec-model=reactor` フラグは、WebAssemblyリンカーによってプラグインがコマンドライン実行プログラムではなくエントリポイントを公開するコンポーネント(リアクター)として扱われるために必要です。 + +--- + +## 2. プラグインの記述 + +`source` ディレクトリを作成し、`source/app.d` にコードを記述します: + +```d [source/app.d] +module plugin; + +import wit.pumpkin.plugin.plugin; +import wit.common; +import pumpkin.register; + +struct MyPlugin { + @GetMetadata PluginMetadata getMetadata() + { + immutable(WitString)[$] authors = [ + "あなたの名前".witList + ]; + + return (immutable PluginMetadata( + name: "my-d-plugin".witList, + version_: "0.1.0".witList, + authors: authors.witList, + description: "D言語によるサンプルプラグイン。".witList, + dependencies: WitList!WitString(), + permissions: WitList!WitString() + )).witClone; + } + + @OnLoad + Result!(void, WitString) onLoad(Context ctx) + { + scope (exit) + ctx.witDrop; // 受信したリソースは使用後に解放する必要があります + + log(Level.info, "D言語プラグインからこんにちは!".witList); + return ok!WitString; + } +} + +mixin RegisterPlugin!MyPlugin; +``` + +--- + +## 3. プラグインのビルド + +`wasm32-wasip2` をターゲットとしてWebAssemblyコンポーネントをビルドします: + +```bash +dub build -b release -a wasm32-wasip2 +``` + +プロジェクトルートに `my-d-plugin.wasm` が生成されます。 + +--- + +## 4. プラグインの実行 + +1. 生成された `.wasm` ファイルをPumpkinサーバーの `plugins/` ディレクトリにコピーします: + ```bash + cp my-d-plugin.wasm /path/to/pumpkin/plugins/ + ``` +2. Pumpkinサーバーを起動または再起動します: + ```bash + ./pumpkin + ``` +3. サーバーログを確認して、プラグインが正常に読み込まれたことを確認します: + ```text + [INFO] D言語プラグインからこんにちは! + ``` diff --git a/docs/ja_jp/plugin-dev/go/basic-logic.md b/docs/ja_jp/plugin-dev/go/basic-logic.md new file mode 100644 index 0000000..7e0e4a6 --- /dev/null +++ b/docs/ja_jp/plugin-dev/go/basic-logic.md @@ -0,0 +1,60 @@ +# Writing the basic logic + +## Plugin Entry Point + +In Go, your plugin must implement the `api.Plugin` interface and register itself in the `init()` function. You also need to import `wit_exports` for WebAssembly compatibility. + +:::code-group + +```go [main.go] +package main + +import ( + "github.com/Pumpkin-MC/pumpkin-api-go/api" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_context" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_logging" + _ "github.com/Pumpkin-MC/pumpkin-api-go/pkg/wit_exports" // Required for WASM exports +) + +type MyPlugin struct { + api.DefaultPlugin +} + +func (p *MyPlugin) Metadata() api.Metadata { + return api.Metadata{ + Name: "my-go-plugin", + Version: "0.1.0", + Authors: []string{"you"}, + } +} + +func (p *MyPlugin) OnLoad(ctx *pumpkin_plugin_context.Context) { + pumpkin_plugin_logging.Log(pumpkin_plugin_logging.LevelInfo(), "Go plugin loaded!") +} + +func init() { + api.RegisterPlugin(&MyPlugin{}) +} + +func main() {} +``` + +::: + +## Compiling the plugin + +To compile your plugin to WebAssembly, you must use **TinyGo**. The standard Go compiler does not yet support the specific WASI targets required by Pumpkin in the same way. + +Run the following command in your project folder: + +```bash +tinygo build -o my_plugin.wasm -target=wasi main.go +``` + +This will generate a `my_plugin.wasm` file that can be loaded by Pumpkin. + +## Testing the plugin + +Installing a plugin is as simple as putting the plugin binary (`.wasm`) into the `plugins/` folder of your Pumpkin server! + +When you start up the server and run the `/plugins` command, you should see your plugin listed. diff --git a/docs/ja_jp/plugin-dev/go/first-command.md b/docs/ja_jp/plugin-dev/go/first-command.md new file mode 100644 index 0000000..f20ffdd --- /dev/null +++ b/docs/ja_jp/plugin-dev/go/first-command.md @@ -0,0 +1,103 @@ +# Creating Your First Command + +Registering commands in Go with Pumpkin is built on standard Go idiom using TinyGo WASM compilation. + +--- + +## 1. Quick Example + +Here is a complete Go plugin file (`main.go`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```go [main.go] +package main + +import ( + "github.com/Pumpkin-MC/pumpkin-api-go/api" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_command" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_context" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_permission" + _ "github.com/Pumpkin-MC/pumpkin-api-go/pkg/wit_exports" +) + +type MyPlugin struct { + api.DefaultPlugin +} + +func (p *MyPlugin) Metadata() api.Metadata { + return api.Metadata{ + Name: "my_go_plugin", + Version: "0.1.0", + Authors: []string{"Developer"}, + Description: "My first Pumpkin Go plugin", + } +} + +func (p *MyPlugin) OnLoad(ctx *pumpkin_plugin_context.Context) { + // 1. Register permission node + ctx.RegisterPermission(pumpkin_plugin_permission.Permission{ + Node: "my_go_plugin:hello", + Description: "Allows executing the /hello command", + Default: pumpkin_plugin_permission.PermissionDefaultAllow(), + }) + + // 2. Build and register command tree + cmd := pumpkin_plugin_command.New([]string{"hello", "hi"}, "Greets the player") + ctx.RegisterCommand(cmd, "my_go_plugin:hello") +} + +func (p *MyPlugin) HandleCommand(cmdID uint32, sender pumpkin_plugin_command.CommandSender) int32 { + sender.SendMessage("Hello from Go Plugin!") + return 1 +} + +func init() { + api.RegisterPlugin(&MyPlugin{}) +} + +func main() {} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Register Permission +Call `ctx.RegisterPermission` inside `OnLoad`: +```go +ctx.RegisterPermission(pumpkin_plugin_permission.Permission{ + Node: "my_go_plugin:hello", + Description: "Allows executing the /hello command", + Default: pumpkin_plugin_permission.PermissionDefaultAllow(), +}) +``` + +### Step 2: Build the Command Tree +Instantiate the command tree with `pumpkin_plugin_command.New`: +```go +cmd := pumpkin_plugin_command.New([]string{"hello", "hi"}, "Greets the player") +``` + +### Step 3: Register & Execute +Register the command with `ctx.RegisterCommand` and handle execution in `HandleCommand`: +```go +ctx.RegisterCommand(cmd, "my_go_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Follow the [Go Quick Start Guide](./quick-start) to set up TinyGo compilation. +- Learn more about [Basic Logic](./basic-logic). diff --git a/docs/ja_jp/plugin-dev/go/quick-start.md b/docs/ja_jp/plugin-dev/go/quick-start.md new file mode 100644 index 0000000..90bacb4 --- /dev/null +++ b/docs/ja_jp/plugin-dev/go/quick-start.md @@ -0,0 +1,35 @@ +# Creating a new project + +Pumpkin plugins use the [Go](https://go.dev/) programming language and are compiled to WebAssembly. + +## Prerequisites + +Before we can start, make sure you have the following installed: +- [Go](https://go.dev/doc/install) (latest version recommended) +- [TinyGo](https://tinygo.org/getting-started/install/) (required for WASM compilation) + +## Initializing a new module + +First, create a new directory for your project and initialize a Go module: + +```bash +mkdir hello-pumpkin +cd hello-pumpkin +go mod init github.com/yourname/hello-pumpkin +``` + +Next, add the Pumpkin Go API as a dependency: + +```bash +go get github.com/Pumpkin-MC/pumpkin-api-go +``` + +## Project Structure + +Your project should have at least a `main.go` file. The structure should look like this: + +```text +├── go.mod +├── go.sum +└── main.go +``` diff --git a/docs/ja_jp/plugin-dev/introduction.md b/docs/ja_jp/plugin-dev/introduction.md new file mode 100644 index 0000000..dfa95d4 --- /dev/null +++ b/docs/ja_jp/plugin-dev/introduction.md @@ -0,0 +1,18 @@ +# Pumpkin Plugin Development + +::: warning +The Pumpkin Plugin API is still in a very early stage of development and may change at any time. +If you run into any issues please reach out on [our Discord server](https://discord.gg/aaNuD6rFEe). +::: + +The Pumpkin Plugin API takes inspiration from the Spigot/Bukkit plugin API in many places, +so if you have previous experience with these and have experience with Rust, Python, C#, Go, C, Kotlin, or D development, +you should have a pretty easy time writing plugins for Pumpkin. :smile: + +* [Rust API](https://github.com/Pumpkin-MC/Pumpkin) +* [Python API](https://github.com/Pumpkin-MC/pumpkin-api-py) +* [C# API](https://github.com/Pumpkin-MC/pumpkin-api-cs) +* [Go API](https://github.com/Pumpkin-MC/pumpkin-api-go) +* [C API](https://github.com/Pumpkin-MC/pumpkin-api-c) +* [Kotlin API](https://github.com/Pumpkin-MC/pumpkin-api-kt) +* [D API](https://github.com/Pumpkin-MC/pumpkin-api-d) diff --git a/docs/ja_jp/plugin-dev/kotlin/first-command.md b/docs/ja_jp/plugin-dev/kotlin/first-command.md new file mode 100644 index 0000000..12831f5 --- /dev/null +++ b/docs/ja_jp/plugin-dev/kotlin/first-command.md @@ -0,0 +1,94 @@ +# Creating Your First Command + +Registering custom commands in Pumpkin using Kotlin is concise, idiomatic, and strongly typed. + +--- + +## 1. Quick Example + +Here is a complete Kotlin plugin (`Plugin.kt`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```kotlin [src/wasmWasiMain/kotlin/plugin/Plugin.kt] +package plugin + +import pumpkin.plugin.context.Context +import pumpkin.plugin.command.Command +import pumpkin.plugin.permission.Permission +import pumpkin.plugin.permission.PermissionDefault + +class MyPlugin { + fun onLoad(ctx: Context) { + // 1. Register permission node + ctx.registerPermission( + Permission( + node = "my_kotlin_plugin:hello", + description = "Allows executing the /hello command", + default = PermissionDefault.ALLOW, + children = emptyList() + ) + ) + + // 2. Build and register command tree + val cmd = Command( + names = listOf("hello", "hi"), + description = "Greets the player" + ) + ctx.registerCommand(cmd, "my_kotlin_plugin:hello") + } + + fun handleCommand(sender: pumpkin.plugin.command.CommandSender): Int { + sender.sendMessage("Hello from Kotlin Plugin!") + return 1 + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Node +Construct a `Permission` object and pass it to `ctx.registerPermission`: +```kotlin +ctx.registerPermission( + Permission( + node = "my_kotlin_plugin:hello", + description = "Allows executing the /hello command", + default = PermissionDefault.ALLOW, + children = emptyList() + ) +) +``` + +### Step 2: Build the Command Tree +Instantiate `Command` with primary names and aliases: +```kotlin +val cmd = Command( + names = listOf("hello", "hi"), + description = "Greets the player" +) +``` + +### Step 3: Register with Context +Register the command with `ctx.registerCommand` and handle calls inside `handleCommand`: +```kotlin +ctx.registerCommand(cmd, "my_kotlin_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Follow the [Kotlin Quick Start Guide](./quick-start) to configure your `Makefile` and Kotlin/Wasm build tools. diff --git a/docs/ja_jp/plugin-dev/kotlin/quick-start.md b/docs/ja_jp/plugin-dev/kotlin/quick-start.md new file mode 100644 index 0000000..acefd9c --- /dev/null +++ b/docs/ja_jp/plugin-dev/kotlin/quick-start.md @@ -0,0 +1,81 @@ +# Quick Start + +This guide will help you get started with writing Pumpkin server plugins using Kotlin. + +:::warning +Until the Kotlin + Wasm component toolchain matures more, there will be some oddity and inconvenience. + +Bugs are also expected. +::: + +## Prerequisites + +Before you start, ensure you have the following installed: +- JDK 17 or later + - To run Gradle 9.4 +- [Rust](https://rust-lang.org/) + - This is required as a key component (wit-bindgen) is written and Rust must be built from a particular Kotlin-enabled fork. + - You only need a default Rust install for your host platform. NOT for any WebAssembly targets +- [wasm-tools](https://github.com/bytecodealliance/wasm-tools) + - To bundle the Wasm Kotlin produces into a component +- Make (e.g. [GNU Make](https://www.gnu.org/software/make/)) + - To run the convenience `Makefile`. You may opt to do without and perform the contained steps manually. + +## Setting up the project + +Unlike most of the other APIs packages available, [pumpkin-api-kt](https://github.com/Pumpkin-MC/pumpkin-api-kt) is a TEMPLATE, not e.g. a Maven package. + +To start off, clone the template (and rename it as you please): +```sh +git clone --recurse-submodules https://github.com/Pumpkin-MC/pumpkin-api-kt +mv pumpkin-api-kt my_kotlin_plugin +cd my_kotlin_plugin +``` + +Next, we'll want to update the `wit` submodule so we are using the latest version of the Pumpkin plugin API. + +```sh +cd wit +git pull origin master +cd .. +``` + +Finally, rename the Gradle project. Change the `rootProject.name` in `settings.gradle.kts`, AND the `PROJECT_NAME` in `Makefile`. They must both match. Whatever you name the project, is going to be the filename of the Wasm produced. + +## Creating your first plugin + +As part of the template, a basic plugin is implemented in `src/wasmWasiMain/kotlin/plugin/Plugin.kt`. + +Feel free to modify the metadata at the bottom. However, before changing anything else, it's recommended that you build the plugin (see next section) first so that the bindings are generated and IDE completion works for `pumpkin`. + +## Building the plugin + +To build your plugin into a WebAssembly component: + +```sh +make +``` + +The compiled `.wasm` file will be located in `build`. You can place this file in the `plugins` folder of your Pumpkin server. + +Note that doing this the first time might take awhile, while it builds `wit-bindgen` from Rust source. + +Running `make` will check for `wit-bindgen` updates each time. You may want to run instead `make componentify` after the initial setup to avoid this. + +## Troubleshooting + +### Linker errors +If you start getting errors like +``` +main ThreadId(01) pumpkin::plugin: Failed to load plugin from +"./plugins/my_plugin.wasm": Wasm plugin initialization error: plugin failed +to load with error: component imports instance 'pumpkin:plugin/gui@0.1.0', but +a matching implementation was not found in the linker +``` +or other "linker" errors when loading your plugin into Pumpkin, update the `wit` submodule. + +```sh +cd wit +git pull origin master +cd .. +``` diff --git a/docs/ja_jp/plugin-dev/migrating-from-bukkit/commands.md b/docs/ja_jp/plugin-dev/migrating-from-bukkit/commands.md new file mode 100644 index 0000000..a6d3d51 --- /dev/null +++ b/docs/ja_jp/plugin-dev/migrating-from-bukkit/commands.md @@ -0,0 +1,171 @@ +# Migrating Commands from Bukkit + +In Bukkit, commands are defined in `plugin.yml` and handled through `CommandExecutor` classes or the Brigadier library. + +In Pumpkin, commands are built using a **Brigadier-inspired command tree** and registered programmatically via `context.register_command`. No YAML configuration files are used. + +--- + +## Key Differences + +| Feature | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Declaration** | Declared in `plugin.yml` under `commands:` | Constructed in code using `Command::new(...)` | +| **Permissions** | Linked in `plugin.yml` or checked via `player.hasPermission()` | Explicit permission node bound during `register_command()` | +| **Tab Completion** | Manual string matching in `TabCompleter` | Automatic tree-based client-side tab completion | +| **Subcommands** | Manual `if (args[0].equalsIgnoreCase("..."))` | Nested `Command` tree nodes | + +--- + +## Code Comparison: Simple Command + +Compare registering a `/feed` command in Bukkit vs. Pumpkin: + +### 1. Bukkit Implementation (Java) + +```java [FeedCommand.java] +// 1. Registered in plugin.yml: +// commands: +// feed: +// description: Feeds the player +// permission: myplugin.feed + +public class FeedCommand implements CommandExecutor { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("Only players can execute this command!"); + return true; + } + Player player = (Player) sender; + if (!player.hasPermission("myplugin.feed")) { + player.sendMessage("No permission!"); + return true; + } + player.setFoodLevel(20); + player.sendMessage("Your hunger has been satisfied!"); + return true; + } +} +``` + +### 2. Pumpkin Implementations + +::: code-group + +```rust [Rust] +use pumpkin_plugin_api::{ + command::{CommandHandler, CommandSender, ConsumedArgs, CommandError, Command}, + permission::{Permission, PermissionDefault}, + Context, Plugin, PluginMetadata, Server, +}; + +struct FeedExecutor; + +impl CommandHandler for FeedExecutor { + fn handle(&self, sender: CommandSender, _server: Server, _args: ConsumedArgs) -> Result { + sender.send_message("Your hunger has been satisfied!"); + Ok(1) + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { MyPlugin } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "feed_plugin".into(), + version: "1.0.0".into(), + authors: vec!["Developer".into()], + description: "Feed command plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Register permission node (Replaces plugin.yml permission) + context.register_permission(&Permission { + node: "feed_plugin:feed".to_string(), + description: "Allows executing /feed".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), + })?; + + // 2. Build command tree + let command = Command::new(&["feed".to_string(), "eat".to_string()], "Feeds the player") + .execute(FeedExecutor); + + // 3. Register with context + context.register_command(command, "feed_plugin:feed")?; + + Ok(()) + } +} +``` + +```python [Python] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + permission, command, context, server +) + +class FeedExecutor: + def handle(self, sender: command.CommandSender, srv: server.Server, args: command.ConsumedArgs) -> int: + sender.send_message("Your hunger has been satisfied!") + return 1 + +class FeedPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata(name="feed_plugin", version="1.0.0", authors=["Dev"], description="Feed command") + + def on_load(self, ctx: context.Context) -> None: + ctx.register_permission(permission.Permission( + node="feed_plugin:feed", + description="Allows executing /feed", + default=permission.PermissionDefault.ALLOW, + children=[] + )) + + cmd = command.Command(["feed", "eat"], "Feeds the player") + cmd.execute(FeedExecutor()) + ctx.register_command(cmd, "feed_plugin:feed") + +register_plugin(FeedPlugin) +``` + +```kotlin [Kotlin] +package plugin + +import pumpkin.plugin.context.Context +import pumpkin.plugin.command.Command +import pumpkin.plugin.command.CommandSender +import pumpkin.plugin.permission.Permission +import pumpkin.plugin.permission.PermissionDefault + +class FeedPlugin { + fun onLoad(ctx: Context) { + ctx.registerPermission( + Permission( + node = "feed_plugin:feed", + description = "Allows executing /feed", + default = PermissionDefault.ALLOW, + children = emptyList() + ) + ) + + val cmd = Command( + names = listOf("feed", "eat"), + description = "Feeds the player" + ) + ctx.registerCommand(cmd, "feed_plugin:feed") + } + + fun handleCommand(sender: CommandSender): Int { + sender.sendMessage("Your hunger has been satisfied!") + return 1 + } +} +``` + +::: diff --git a/docs/ja_jp/plugin-dev/migrating-from-bukkit/configuration.md b/docs/ja_jp/plugin-dev/migrating-from-bukkit/configuration.md new file mode 100644 index 0000000..82838d0 --- /dev/null +++ b/docs/ja_jp/plugin-dev/migrating-from-bukkit/configuration.md @@ -0,0 +1,69 @@ +# Migrating Configuration & Data Storage + +Bukkit plugins heavily rely on `config.yml`, `YamlConfiguration`, and the Bukkit configuration serializing system. + +In Pumpkin, WebAssembly plugins run in a secure sandbox environment where configuration files can be handled natively using standard formats like **TOML**, **JSON**, or **YAML**. + +--- + +## Key Differences + +| Feature | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Config File Format** | `config.yml` (YAML format) | Flexible: TOML, JSON, or custom files | +| **Default Config Generation** | `saveDefaultConfig()` | Reading config from plugin directory or embedded WASM asset | +| **Data Parsing** | `getConfig().getString(...)` | Native serde / JSON / TOML deserialization | +| **File I/O Sandboxing** | Direct filesystem access anywhere | Sandboxed directory access granted to WASM plugin | + +--- + +## Code Comparison: Loading Configuration + +### 1. Bukkit Implementation (Java) + +```java [ConfigExample.java] +public class ConfigExample extends JavaPlugin { + @Override + public void onEnable() { + saveDefaultConfig(); // Saves config.yml from jar resources + FileConfiguration config = getConfig(); + + String welcomeMsg = config.getString("welcome-message", "Default welcome!"); + int maxItems = config.getInt("max-items", 10); + getLogger().info("Loaded message: " + welcomeMsg); + } +} +``` + +### 2. Pumpkin Implementation (Rust) + +In Pumpkin, you can derive `serde::Deserialize` to parse clean TOML configuration files: + +```rust [config.rs] +use serde::Deserialize; +use std::fs; + +#[derive(Deserialize, Debug)] +pub struct PluginConfig { + pub welcome_message: String, + pub max_items: i32, +} + +impl Default for PluginConfig { + fn default() -> Self { + Self { + welcome_message: "Default welcome!".to_string(), + max_items: 10, + } + } +} + +pub fn load_config() -> PluginConfig { + let config_path = "plugins/my_plugin/config.toml"; + if let Ok(content) = fs::read_to_string(config_path) { + toml::from_str(&content).unwrap_or_default() + } else { + PluginConfig::default() + } +} +``` diff --git a/docs/ja_jp/plugin-dev/migrating-from-bukkit/events.md b/docs/ja_jp/plugin-dev/migrating-from-bukkit/events.md new file mode 100644 index 0000000..6e15a7a --- /dev/null +++ b/docs/ja_jp/plugin-dev/migrating-from-bukkit/events.md @@ -0,0 +1,117 @@ +# Migrating Events from Bukkit + +In Bukkit, events are handled by creating classes that implement `Listener` and annotating methods with `@EventHandler`. + +In Pumpkin, event handlers are structs or objects registered explicitly via `context.register_event_handler(...)`. Pumpkin also differentiates between **Blocking** and **Non-blocking** events to maximize server concurrency. + +--- + +## Key Differences + +| Feature | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Listener Declaration** | `@EventHandler` annotation on methods | `EventHandler` trait / interface implementation | +| **Registration** | `pm.registerEvents(listener, plugin)` | `context.register_event_handler(handler, priority, is_blocking)` | +| **Execution Model** | Main thread execution (`EventPriority`) | Choice between **Blocking** (sequential/cancellable) and **Non-blocking** (concurrent) | +| **Cancellation** | `event.setCancelled(true)` | `event.cancel()` on blocking events | + +--- + +## Blocking vs. Non-Blocking Events in Pumpkin + +Unlike Bukkit where all event handlers run sequentially on the main server thread, Pumpkin lets you specify whether an event handler is **blocking**: + +```rust +// Registration signature in Rust: +context.register_event_handler(handler, priority, is_blocking)?; +``` + +- **Blocking (`is_blocking = true`)**: Executes in sequential priority order. Can modify event data (e.g. edit join message) or cancel the event. +- **Non-blocking (`is_blocking = false`)**: Executes concurrently across worker threads. Ideal for logging, metrics, or external notifications where cancellation is not needed. + +--- + +## Code Comparison: Player Join Event + +### 1. Bukkit Implementation (Java) + +```java [JoinListener.java] +public class JoinListener implements Listener { + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + event.setJoinMessage("Welcome " + player.getName() + " to the server!"); + } +} + +// In JavaPlugin: +// getServer().getPluginManager().registerEvents(new JoinListener(), this); +``` + +### 2. Pumpkin Implementations + +::: code-group + +```rust [Rust] +use pumpkin_plugin_api::{ + Context, Plugin, PluginMetadata, Server, + events::{EventData, EventHandler, EventPriority, PlayerJoinEvent}, + text::TextComponent, +}; + +struct JoinHandler; + +impl EventHandler for JoinHandler { + fn handle<'a>( + &'a self, + _server: Server, + mut event: EventData, + ) -> EventData { + event.join_message = TextComponent::text("Welcome to the Pumpkin server!"); + event + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { MyPlugin } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "join_plugin".into(), + version: "1.0.0".into(), + authors: vec!["Developer".into()], + description: "Join event handler".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Register blocking event handler with Normal priority + context.register_event_handler(JoinHandler, EventPriority::Normal, true)?; + Ok(()) + } +} +``` + +```python [Python] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + event, server, context +) + +class MyPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata(name="join_plugin", version="1.0.0", authors=["Dev"], description="Join event") + + def on_load(self, ctx: context.Context) -> None: + self.register_event(ctx, event.EventType.PLAYER_JOIN_EVENT, self.on_player_join) + + def on_player_join(self, srv: server.Server, evt: event.PlayerJoinEventData) -> event.PlayerJoinEventData: + print(f"Player joined!") + return evt + +register_plugin(MyPlugin) +``` + +::: diff --git a/docs/ja_jp/plugin-dev/migrating-from-bukkit/index.md b/docs/ja_jp/plugin-dev/migrating-from-bukkit/index.md new file mode 100644 index 0000000..c70c6cb --- /dev/null +++ b/docs/ja_jp/plugin-dev/migrating-from-bukkit/index.md @@ -0,0 +1,27 @@ +# Migrating Overview & Architecture + +Moving from **Bukkit / Spigot / Paper** plugin development to Pumpkin involves shifting from a Java-centric object-oriented model to a compiled, multi-language **WebAssembly (WASM)** component model. + +--- + +The table below contrasts the legacy Bukkit/Paper paradigm with Pumpkin's modern WebAssembly architecture: + +| Concept | Bukkit / Spigot / Paper | Pumpkin | +| :--- | :--- | :--- | +| **Language Support** | Java / Kotlin / Scala (JVM) | Rust, Python, Kotlin, C#, Go, C | +| **Binary Output** | `.jar` Java Archive | `.wasm` WebAssembly Component | +| **Plugin Descriptor** | `plugin.yml` file | Programmatic `PluginMetadata` struct | +| **Lifecycle Hooks** | `onEnable()` / `onDisable()` | `on_load(context)` / `on_unload(context)` | +| **Security & Isolation** | Unrestricted JVM Reflection | Sandboxed WASM capability model | +| **Concurrency** | Single-threaded tick loop (`BukkitScheduler`) | Multithreaded native execution with Async runtime | + +--- + +## Detailed Migration Topics + +Explore dedicated guides on migrating each major plugin subsystem: + +- [Migrating Commands](./commands) — Transitioning from `getCommand().setExecutor()` and `plugin.yml` to Brigadier command trees. +- [Migrating Events](./events) — Replacing `@EventHandler` and `Listener` interfaces with Pumpkin's blocking vs. non-blocking event system. +- [Migrating Inventories & GUIs](./inventories) — Moving from `Bukkit.createInventory()` to Pumpkin container and window handlers. +- [Migrating Configuration & Data](./configuration) — Replaces `getConfig()` / `config.yml` with native TOML, JSON, or custom storage. diff --git a/docs/ja_jp/plugin-dev/migrating-from-bukkit/inventories.md b/docs/ja_jp/plugin-dev/migrating-from-bukkit/inventories.md new file mode 100644 index 0000000..0ba7352 --- /dev/null +++ b/docs/ja_jp/plugin-dev/migrating-from-bukkit/inventories.md @@ -0,0 +1,72 @@ +# Migrating Inventories & GUIs from Bukkit + +Creating inventory GUIs (like chest menus, shop interfaces, or custom player windows) is a common task in Minecraft plugin development. + +In Bukkit, custom GUIs are created using `Bukkit.createInventory()` and handled via `InventoryClickEvent`. In Pumpkin, inventory windows are managed through **Window APIs** and container click packets. + +--- + +## Key Differences + +| Feature | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Inventory Creation** | `Bukkit.createInventory(owner, size, title)` | Window container definitions (`WindowType`) | +| **Opening Windows** | `player.openInventory(inv)` | `player.open_window(window)` | +| **Click Interception** | `InventoryClickEvent` handling | Window click callback handlers | +| **Item Stacking** | `ItemStack` with `ItemMeta` | `ItemStack` with components / NBT | + +--- + +## Code Comparison: Opening a Custom Menu GUI + +### 1. Bukkit Implementation (Java) + +```java [MenuGUI.java] +public class MenuGUI implements Listener { + + public void openMenu(Player player) { + Inventory inv = Bukkit.createInventory(null, 9, Component.text("Custom Menu")); + + ItemStack item = new ItemStack(Material.DIAMOND); + ItemMeta meta = item.getItemMeta(); + meta.displayName(Component.text("Click Me!")); + item.setItemMeta(meta); + + inv.setItem(4, item); + player.openInventory(inv); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (event.getView().getTitle().equals("Custom Menu")) { + event.setCancelled(true); + if (event.getSlot() == 4) { + event.getWhoClicked().sendMessage("Diamond clicked!"); + } + } + } +} +``` + +### 2. Pumpkin Implementation (Rust) + +```rust [gui.rs] +use pumpkin_plugin_api::{ + player::Player, + item::ItemStack, + window::{Window, WindowType}, + text::TextComponent, +}; + +pub fn open_custom_menu(player: &Player) { + // 1. Create a 9-slot chest window + let mut window = Window::new(WindowType::Generic9x1, TextComponent::text("Custom Menu")); + + // 2. Set item in slot 4 + let item = ItemStack::new("minecraft:diamond", 1); + window.set_item(4, Some(item)); + + // 3. Open window for player + player.open_window(window); +} +``` diff --git a/docs/ja_jp/plugin-dev/python/basic-logic.md b/docs/ja_jp/plugin-dev/python/basic-logic.md new file mode 100644 index 0000000..94d1d6e --- /dev/null +++ b/docs/ja_jp/plugin-dev/python/basic-logic.md @@ -0,0 +1,38 @@ +# Basic Logic + +This section covers the basic structure of a Pumpkin plugin in Python. + +## Plugin Class + +Every Python plugin must inherit from the `Plugin` class. This class provides the base structure and methods needed for the server to interact with your plugin. + +```python +from pumpkin_api import Plugin, PluginMetadata, context + +class MyPlugin(Plugin): + def metadata(self) -> PluginMetadata: + # Define plugin metadata here + pass + + def on_load(self, ctx: context.Context) -> None: + # Code to run when the plugin is loaded + pass + + def on_unload(self, ctx: context.Context) -> None: + # Code to run when the plugin is unloaded + pass +``` + +## Plugin Metadata + +The `metadata` method must return a `PluginMetadata` object, which contains information about your plugin. + +- `name`: The name of your plugin. +- `version`: The version of your plugin. +- `authors`: A list of authors. +- `description`: A short description of what your plugin does. + +## Loading and Unloading + +- `on_load`: This method is called when the server loads your plugin. You should use this to register events, commands, and perform any initialization. +- `on_unload`: This method is called when the server unloads your plugin. Use this for cleanup if necessary. diff --git a/docs/ja_jp/plugin-dev/python/events.md b/docs/ja_jp/plugin-dev/python/events.md new file mode 100644 index 0000000..135b08e --- /dev/null +++ b/docs/ja_jp/plugin-dev/python/events.md @@ -0,0 +1,30 @@ +# Events + +Events allow your plugin to respond to actions occurring on the server, such as players joining or messages being sent. + +## Registering an Event + +You can register an event handler in the `on_load` method of your plugin using `self.register_event`. + +```python +def on_load(self, ctx: context.Context) -> None: + self.register_event(ctx, event.EventType.PLAYER_JOIN_EVENT, self.on_player_join) +``` + +## Event Handlers + +An event handler is a method that receives the server instance and the event data. It should return the (possibly modified) event data. + +```python +def on_player_join(self, srv: server.Server, evt: event.PlayerJoinEventData) -> event.PlayerJoinEventData: + print(f"Player {evt.player.get_name()} joined!") + return evt +``` + +## Event Types + +The `event.EventType` enum contains all available events. Some common events include: + +- `PLAYER_JOIN_EVENT` +- `PLAYER_QUIT_EVENT` +- `PLAYER_CHAT_EVENT` diff --git a/docs/ja_jp/plugin-dev/python/first-command.md b/docs/ja_jp/plugin-dev/python/first-command.md new file mode 100644 index 0000000..00058f4 --- /dev/null +++ b/docs/ja_jp/plugin-dev/python/first-command.md @@ -0,0 +1,95 @@ +# Creating Your First Command + +Registering and handling custom commands in Pumpkin with Python is designed to be straightforward and expressive. + +--- + +## 1. Quick Example + +Here is a complete, minimal Python plugin (`main.py`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```python [main.py] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + permission, command, context, server +) + +class HelloCommand: + def handle(self, sender: command.CommandSender, srv: server.Server, args: command.ConsumedArgs) -> int: + sender.send_message("Hello from Python Plugin!") + return 1 + +class MyPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata( + name="my_python_plugin", + version="0.1.0", + authors=["Developer"], + description="My first Pumpkin Python plugin" + ) + + def on_load(self, ctx: context.Context) -> None: + # 1. Register permission node + ctx.register_permission(permission.Permission( + node="my_python_plugin:hello", + description="Allows executing the /hello command", + default=permission.PermissionDefault.ALLOW, + children=[] + )) + + # 2. Build the command tree with aliases & executor + cmd = command.Command(["hello", "hi"], "Greets the player") + cmd.execute(HelloCommand()) + + # 3. Register the command + ctx.register_command(cmd, "my_python_plugin:hello") + +register_plugin(MyPlugin) +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission +Commands in Pumpkin require a permission node. Register it with `permission.PermissionDefault.ALLOW` to allow execution by default: +```python +ctx.register_permission(permission.Permission( + node="my_python_plugin:hello", + description="Allows executing the /hello command", + default=permission.PermissionDefault.ALLOW, + children=[] +)) +``` + +### Step 2: Build the Command Tree +Create a `command.Command` instance with primary names and aliases, then attach your handler with `.execute(...)`: +```python +cmd = command.Command(["hello", "hi"], "Greets the player") +cmd.execute(HelloCommand()) +``` + +### Step 3: Register with Context +Pass the command tree and permission node string to `ctx.register_command`: +```python +ctx.register_command(cmd, "my_python_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Check out [Events](./events) to handle player actions and server events in Python. +- Learn more about [Basic Logic](./basic-logic). diff --git a/docs/ja_jp/plugin-dev/python/quick-start.md b/docs/ja_jp/plugin-dev/python/quick-start.md new file mode 100644 index 0000000..282df4c --- /dev/null +++ b/docs/ja_jp/plugin-dev/python/quick-start.md @@ -0,0 +1,53 @@ +# Quick Start + +This guide will help you get started with writing Pumpkin server plugins using Python. + +## Installation + +First, you need to install the `pumpkin-api-py` library: + +```bash +pip install pumpkin-api-py +``` + +## Creating your first plugin + +Create a file named `main.py` and add the following content: + +```python +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + server, event, command, text, context +) + +class MyPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata( + name="my-plugin", + version="0.1.0", + authors=["you"], + description="An example plugin." + ) + + def on_load(self, ctx: context.Context) -> None: + print("Python plugin loaded!") + + # Register an event handler + self.register_event(ctx, event.EventType.PLAYER_JOIN_EVENT, self.on_player_join) + + def on_player_join(self, srv: server.Server, evt: event.PlayerJoinEventData) -> event.PlayerJoinEventData: + print(f"Player {evt.player.get_name()} joined!") + return evt + +register_plugin(MyPlugin) +``` + +## Building the plugin + +Build your plugin into a WebAssembly component using the provided build tool: + +```bash +pumpkin-api-build main -o my_plugin.wasm +``` + +This will generate a `my_plugin.wasm` file that you can place in the `plugins` folder of your Pumpkin server. diff --git a/docs/ja_jp/plugin-dev/rust/basic-logic.md b/docs/ja_jp/plugin-dev/rust/basic-logic.md new file mode 100644 index 0000000..1864a6f --- /dev/null +++ b/docs/ja_jp/plugin-dev/rust/basic-logic.md @@ -0,0 +1,107 @@ +# Writing the basic logic + +## Plugin base + +Even in a basic plugin, there is a lot going on under the hood, so to greatly simplify plugin development +we will use the `pumpkin-plugin-api` crate to create a basic empty plugin. + +:::code-group + +```rust:line-numbers [lib.rs] +use pumpkin_plugin_api::{Context, Plugin, PluginMetadata}; +use tracing::*; + +struct HelloPlugin; +impl Plugin for HelloPlugin { + fn new() -> Self { + HelloPlugin + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "Hello Plugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Bjorn".into()], + description: "A simple example plugin".into(), + } + } + + fn on_load(&mut self, _context: Context) -> pumpkin_plugin_api::Result<()> { + info!("Hello from the example plugin!"); + Ok(()) + } + + fn on_unload(&mut self, _context: Context) -> pumpkin_plugin_api::Result<()> { + info!("Example plugin unloaded. Goodbye!"); + Ok(()) + } +} + +pumpkin_plugin_api::register_plugin!(HelloPlugin); +``` + +::: + +This will create an empty plugin and implement all the necessary methods for it to be loaded by Pumpkin. + +We can now try to compile our plugin for the first time. To do so, run this command in your project folder: + +```bash +cargo build --release +``` + +You do not need to build in release mode, but it greatly reduces the size of the wasm plugin and +reduces startup times. + +If all went well, you should be left with a message like this: + +```log +╰─ cargo build --release + Compiling hello-pumpkin-wasm v0.1.0 (/home/bjorn/Documents/GitHub/Hello-Pumpkin-Wasm) + Finished `release` profile [optimized] target(s) in 0.05s +``` + +Now you can go to the `./target/wasm32-wasip2/release` folder (or `./target/wasm32-wasip2/debug` +if you didn't use `--release`) and locate your plugin binary. The filename will then be like the following. + +``` +hello_pumpkin_wasm.wasm +``` + +::: info NOTE +If you used a different project name in the `Cargo.toml` file, look for a file which contains your project name. +::: + +You can rename this file to whatever you like, however you must keep the file extension (`.wasm`) the same. + +## Testing the plugin + +Now that we have our plugin binary, we can go ahead and test it on the Pumpkin server. Installing a plugin is as +simple as putting the plugin binary that we just built into the `plugins/` folder of your Pumpkin server! + +When you start up the server and run the `/plugins` command, you should see an output like this: + +```text +There is 1 plugin loaded: +hello-pumpkin-wasm +``` + +## Methods implemented on the `Context` object + +```rust +fn get_server(&self) -> Server +``` + +Returns an instance of the server. + +```rust +fn register_command(&self, command: Command, permission: &str) +``` + +Registers a new command handler, with the permission that is for the command. + +```rust +fn register_event_handler(&self, handler: H, event_priority: EventPriority, blocking: bool) -> Result +``` + +Registers a new event handler with a set priority and if it is blocking or not. diff --git a/docs/ja_jp/plugin-dev/rust/command/first-command.md b/docs/ja_jp/plugin-dev/rust/command/first-command.md new file mode 100644 index 0000000..9840346 --- /dev/null +++ b/docs/ja_jp/plugin-dev/rust/command/first-command.md @@ -0,0 +1,115 @@ +# Creating Your First Command + +Registering and handling custom commands in Pumpkin is designed to be developer-friendly, fast, and structured like Mojang's Brigadier system. + +--- + +## 1. Quick Example + +Here is a complete, minimal Rust plugin that registers a `/hello` command with permission checks and sends a response back to the player. + +```rust [src/lib.rs] +use pumpkin_plugin_api::{ + command::{CommandHandler, CommandSender, ConsumedArgs, CommandError, Command}, + permission::{Permission, PermissionDefault}, + Context, Plugin, PluginMetadata, Server, +}; + +struct HelloExecutor; + +impl CommandHandler for HelloExecutor { + fn handle( + &self, + sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + sender.send_message("Hello from Pumpkin!"); + Ok(1) + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { + MyPlugin + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "my_plugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Developer".into()], + description: "My first Pumpkin plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Register permission node + context.register_permission(&Permission { + node: "my_plugin:hello".to_string(), + description: "Allows executing the /hello command".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), + })?; + + // 2. Build the command tree with aliases & executor + let names = ["hello".to_string(), "hi".to_string()]; + let command = Command::new(&names, "Greets the player").execute(HelloExecutor); + + // 3. Register the command + context.register_command(command, "my_plugin:hello")?; + + Ok(()) + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission +Commands in Pumpkin require a permission node. Using `PermissionDefault::Allow` allows all players to execute it by default: +```rust +context.register_permission(&Permission { + node: "my_plugin:hello".to_string(), + description: "Allows executing the /hello command".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), +})?; +``` + +### Step 2: Build the Command Tree +Use `Command::new` to define primary names and aliases, then attach your `CommandHandler` struct with `.execute(...)`: +```rust +let names = ["hello".to_string(), "hi".to_string()]; +let command = Command::new(&names, "Greets the player") + .execute(HelloExecutor); +``` + +### Step 3: Register with Context +Pass the command tree and the permission node string to `context.register_command`: +```rust +context.register_command(command, "my_plugin:hello")?; +``` + +::: + +--- + +## Next Steps + +- Explore complex subcommands and argument parsing in the [Rock-Paper-Scissors Tutorial](./rock-paper-scissors). +- Learn how to handle inventory and GUI interactions in plugin logic. diff --git a/docs/ja_jp/plugin-dev/rust/command/rock-paper-scissors.md b/docs/ja_jp/plugin-dev/rust/command/rock-paper-scissors.md new file mode 100644 index 0000000..c1f188b --- /dev/null +++ b/docs/ja_jp/plugin-dev/rust/command/rock-paper-scissors.md @@ -0,0 +1,255 @@ +# Writing a Command Handler + +In Minecraft, commands are the primary way for both regular players and server operators to +interact with the game on a lower level. They can be used to perform a wide range of tasks, +from simple chat messages to complex server management commands. In this tutorial, we will +create a basic Rock-Paper-Scissors command handler that allows players to play the game +against the server. + +Pumpkin has its own system for handling commands, which is based around each command having a +'tree', which defines the exact structure of the command and its arguments. Each node in the +tree represents a command or argument, and the tree is traversed to determine the command to +execute and its parameters. + +We would also like to thank [ploxxxy](https://github.com/ploxxxy) for writing the original +[Rock-Paper-Scissors plugin](https://github.com/ploxxxy/rock-paper-scissors-mc) from which +this tutorial is based. + +## Adding the basics + +Each command in Pumpkin is defined as a structure that implements the `CommandHandler` trait. +This trait requires the implementation of a `handle` method, which takes the sender, server, +and consumed arguments as parameters, and returns a `-> Result<(), CommandError>`. Let's +define this structure now: + +```rust +use pumpkin_plugin_api::{ + Server, + command::{CommandError, CommandSender, ConsumedArgs}, + commands::CommandHandler, +}; + +struct RockPaperScissorsExecutor; +impl CommandHandler for RockPaperScissorsExecutor { + fn handle( + &self, + _sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + Ok(1) + } +} +``` + +This code defines an empty structure `RockPaperScissorsExecutor` that implements the +`CommandExecutor` trait. The `handler` method is defined to return `Ok(1)` when called. + +## Adding helper enums + +First install the `rand` crate via + +```bash +cargo add rand +``` + +To make our lives easier, we will also define a couple of enums to represent the possible +choices and outcomes of the game, as well as well as a couple functions to generate random +choices and check the outcome. Add these to your plugin's code. + +```rust +use rand::{rng, Rng}; + +#[derive(PartialEq, Debug, Clone, Copy)] +enum Choice { + Rock, + Paper, + Scissors, +} + +enum Outcome { + Win, + Lose, + Draw, +} + +impl Choice { + pub fn beats(&self, other: &Choice) -> Outcome { + if self == other { + return Outcome::Draw; + } + + match (self, other) { + (Choice::Rock, Choice::Scissors) => Outcome::Win, + (Choice::Paper, Choice::Rock) => Outcome::Win, + (Choice::Scissors, Choice::Paper) => Outcome::Win, + _ => Outcome::Lose, + } + } +} + +fn get_random_choice() -> Choice { + let choices = [Choice::Rock, Choice::Paper, Choice::Scissors]; + let index = rng().random_range(0..3); + choices[index] +} +``` + +Now we need to modify the `RockPaperScissorsExecutor` struct to accept a `Choice` parameter +and implement the game logic. + +```rust +struct RockPaperScissorsExecutor(Choice); // [!code ++] +struct RockPaperScissorsExecutor; // [!code --] + +impl CommandHandler for RockPaperScissorsExecutor { + fn handle( + &self, + sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + let player_choice = self.0; // [!code ++:3] + let computer_choice = get_random_choice(); + Ok(1) + } +} +``` + +This code will allow us to later pass in the player's choice and use it in the game logic, +as well as compare it with the computer's choice to determine the outcome of the game. + +## Implementing the Game Logic + +Now we can move on to actually implementing the game logic, and showing the outcome to the +players. + +First we will show the player their and the computer's choice. Add this code to your plugin: + +```rust +impl CommandHandler for RockPaperScissorsExecutor { + fn handle( + &self, + sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + let player_choice = self.0; + let computer_choice = get_random_choice(); + + // [!code ++:9] + let you_chose = TextComponent::text("You chose: "); + you_chose.add_child(TextComponent::text(&format!("{:?}", player_choice))); + you_chose.color_named(NamedColor::Aqua); + sender.send_message(you_chose); + + let i_chose = TextComponent::text("I chose: "); + i_chose.add_child(TextComponent::text(&format!("{:?}", computer_choice))); + i_chose.color_named(NamedColor::Gold); + sender.send_message(i_chose); + } +} +``` + +Next we can compute the game outcome and show it to the player. Add this code to your plugin: + +```rust +impl CommandHandler for RockPaperScissorsExecutor { + fn handle( + &self, + sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + // Existing Code + + match player_choice.beats(&computer_choice) { // [!code ++:17] + Outcome::Win => { + let message = TextComponent::text("You win!"); + message.color_named(NamedColor::Green); + sender.send_message(message); + } + Outcome::Lose => { + let message = TextComponent::text("You lose!"); + message.color_named(NamedColor::Red); + sender.send_message(message); + } + Outcome::Draw => { + let message = TextComponent::text("It's a tie!"); + message.color_named(NamedColor::Yellow); + sender.send_message(message); + } + } + + Ok(1) + } +} +``` + +And that's it! The core logic is done. Now we only have one last thing to do. + +## Building and registering a command tree + +As stated earlier, we need to build a command tree and register it with the server. This will +allow players to execute our plugin's commands. + +Building a command tree isn't very hard, but you have to know the exact structure of the command +and its arguments. In this case, we have a command named `rock-paper-scissors`, which will take +one required argument (the player's choice). + +The command tree is initialized using the `Command::new()` function. This function takes two +arguments: a list of names, where the first is the main command name, and the others are aliases +for the command; and a command description which is used to describe the command in the help menu. +We can then add 'branches' to the tree using the `.then()` method. This method accepts a 'leaf' +which can be built with the `CommandNode::literal()`, `CommandNode::argument()`, or +`CommandNode::require()` functions. + +For the rock-paper-scissors command, we'll create 3 separate branches, each with a `CommandNode::literal()` +leaf node for the player's choice. We will also register the command tree with the server and +with a `permission` of `hello-pumpkin:command.rockpaperscisors` which will allow anyone with that +permission to execute the command. Add the following code to your `on_load()` method: + +```rust +struct HelloPlugin; +impl Plugin for HelloPlugin { + fn new() -> Self { + HelloPlugin + } + + fn metadata(&self) -> PluginMetadata { + // Existing Code + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Existing Code + + let command = Command::new( // [!code ++:20] + &["rps".to_string(), "rockpaperscissors".to_string()], + "Play Rock Paper Scissors with the server.", + ); + command.then(CommandNode::literal("rock").execute(RockPaperScissorsExecutor(Choice::Rock))); + command + .then(CommandNode::literal("paper").execute(RockPaperScissorsExecutor(Choice::Paper))); + command.then( + CommandNode::literal("scissors").execute(RockPaperScissorsExecutor(Choice::Scissors)), + ); + + let permission = Permission { + node: "hello-pumpkin:command.rockpaperscisors".to_string(), + description: "Allows the player to play rock paper scisors".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), + }; + + context.register_permission(&permission)?; + context.register_command(command, "hello-pumpkin:command.rockpaperscisors"); + Ok(()) + } +} +``` + +And that's it! If you compile the plugin, you can test it by running the following command: + +```bash +/rps rock +``` diff --git a/docs/ja_jp/plugin-dev/rust/creating-project.md b/docs/ja_jp/plugin-dev/rust/creating-project.md new file mode 100644 index 0000000..40e6be5 --- /dev/null +++ b/docs/ja_jp/plugin-dev/rust/creating-project.md @@ -0,0 +1,95 @@ +# Creating a new project + +Pumpkin plugins use the [Cargo](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) build system. + +The complete code for this plugin can be found as a [template on GitHub](https://github.com/BjornTheProgrammer/Hello-Pumpkin-Wasm). + +## Installing the toolchain + +Before we can compile a plugin, we have to have the `wasm32-wasip2` target installed. You can install the target +by running: + +```bash +rustup target add wasm32-wasip2 +``` + +## Initializing a new crate + +First we need to create a new project folder. You can do this by running this command in the folder you created: + +```bash +cargo new --lib +``` + +After adding this, we want to create a new directory called `.cargo` and add in a `config.toml` file with the following +contents + +```toml [config.toml] +[build] +target = "wasm32-wasip2" +``` + +Altogether your new folder structure should look like this: + +```bash +├── .cargo/ +│ └── config.toml +├── src/ +│ └── lib.rs +├── Cargo.toml +└── Cargo.lock +``` + +## Configuring the crate + +Since Pumpkin plugins are loaded at runtime as dynamic libraries, we need to tell Cargo to build this crate as one. +:::code-group + +```toml [Cargo.toml] +[package] +name = "hello-pumpkin-wasm" +version = "0.1.0" +edition = "2024" + +[lib] // [!code ++:2] +crate-type = ["cdylib"] + +[dependencies] +``` + +::: + +Next we need to add some basic dependencies from [crates.io](https://crates.io): +:::code-group + +```toml [Cargo.toml] +[package] +name = "hello-pumpkin" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +// [!code ++:5] +# This is the api crate that makes creating plugins easier, and has wit definitions +pumpkin-plugin-api = "0.1.0-dev+26.2-26.45" +# Optional: utilities for marketplace licensing and update checking +pumpkin-plugin-utils = "0.1.0-dev+26.2-26.45" +tracing = "0.1" +``` + +::: + +For improved performance and smaller file sizes, we recommend enabling Link-Time Optimization (LTO). +Be aware that this will increase compilation time. +:::code-group + +```toml [Cargo.toml] +[profile.release] // [!code ++:2] +lto = true +``` + +::: +Enables LTO only for release builds. diff --git a/docs/ja_jp/plugin-dev/rust/events.md b/docs/ja_jp/plugin-dev/rust/events.md new file mode 100644 index 0000000..29ca8ff --- /dev/null +++ b/docs/ja_jp/plugin-dev/rust/events.md @@ -0,0 +1,109 @@ +# Writing a Event Handler + +Event handlers are one of the main functions of plugins. They allow a plugin to tap into the +internal workings of the server and alter its behavior to perform some other action. For a +simple example, we will implement a handler for the `PlayerJoinEvent` and the `PlayerLeaveEvent`. + +## Implementing an Event + +Individual event handlers are just structs which implement the `EventHandler` trait (where `E` is a specific event data). + +### What are blocking events? + +The Pumpkin plugin event system differentiates between two types of events: blocking and non-blocking. Each have their benefits: + +#### Blocking events + +```diff +Pros: ++ Can modify the event (like editing the join message) ++ Can cancel the event ++ Have a priority system +Cons: +- Are executed in sequence +- Can slow down the server if not implemented well +``` + +#### Non-blocking events + +```diff +Pros: ++ Are executed concurrently ++ Are executed after all blocking events finish ++ Can still do some modifications (anything that is behind a Mutex or RwLock) +Cons: +- Cannot cancel the event +- Have no priority system +- Allow for less control over the event +``` + +### Writing a handler + +Since our main aim here is to change the welcome message that the player sees when they join a server, we will be choosing the blocking event type with a normal priority. + +:::code-group + +```rust [lib.rs] +// [!code ++:20] +use pumpkin_plugin_api::{ + Context, Plugin, PluginMetadata, Server, + events::{EventData, EventHandler, EventPriority, PlayerJoinEvent}, + text::TextComponent, +}; +use tracing::*; + +struct MyJoinHandler; +impl EventHandler for MyJoinHandler { + fn handle<'a>( + &'a self, + server: Server, + mut event: EventData, + ) -> EventData { + event.join_message = TextComponent::text("Hello, world!"); + event + } +} +``` + +::: + +**Explanation**: + +- `struct MyJoinHandler;`: The struct for our event handler +- If the event is non-blocking, we still use the handle function, and return the event data. The event data will still be ignored. + +### Registering the handler + +Now that we have written the event handler, we need to tell the plugin to use it. We can do that by adding a single line into the `on_load` method: +:::code-group + +```rust [lib.rs] +struct HelloPlugin; +impl Plugin for HelloPlugin { + fn new() -> Self { + HelloPlugin + } + + fn metadata(&self) -> PluginMetadata { + // ... + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + info!("Hello from the example plugin!"); + context.register_event_handler(MyJoinHandler, EventPriority::Normal, true)?; + Ok(()) + } + + fn on_unload(&mut self, _context: Context) -> pumpkin_plugin_api::Result<()> { + info!("Example plugin unloaded. Goodbye!"); + Ok(()) + } +} +``` + +::: +Now if we build the plugin and join the server, we should see a "Hello, World!" message! + +## Adding a leave event + +As an exercise for the reader, try adding a PlayerLeaveEvent diff --git a/docs/ja_jp/plugin-dev/rust/plugin-utils.md b/docs/ja_jp/plugin-dev/rust/plugin-utils.md new file mode 100644 index 0000000..3adfbbe --- /dev/null +++ b/docs/ja_jp/plugin-dev/rust/plugin-utils.md @@ -0,0 +1,348 @@ +# Licensing & Updates (`pumpkin-plugin-utils`) + +`pumpkin-plugin-utils` is an official utility crate for Pumpkin Rust plugins. It provides built-in tools for **licensing checks**, **cryptographic signature verification**, and **update checks** against the [Pumpkin Marketplace](https://market.pumpkinmc.org). + +## Features + +- **Offline Ed25519 Signature Verification**: Verifies plugin integrity and embedded marketplace metadata in `< 1ms` on startup without blocking the server. +- **Global Metadata Caching**: Call `init(&context)` once in your plugin's `on_load` lifecycle method to cache verified metadata globally. +- **Online License Verification**: Query the marketplace `/api/v1/rest/check-license` endpoint with automatic 7-day lease caching (`license_lease.json`). +- **Offline Grace Period**: Resilient offline lease evaluation ensures your plugin keeps working during marketplace maintenance or network outages. +- **Zero-Argument Update Checking**: Easily check if newer plugin releases are available on the marketplace. +- **Dynamic Public Key Resolution**: Resolves the marketplace public key dynamically via host WIT imports or HTTPS fallback without hardcoding keys. + +--- + +## Adding the Dependency + +Add `pumpkin-plugin-utils` alongside `pumpkin-plugin-api` in your plugin's `Cargo.toml`: + +:::code-group + +```toml [Cargo.toml] +[package] +name = "my-plugin" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +pumpkin-plugin-api = "0.1.0-dev+26.2-26.45" +pumpkin-plugin-utils = "0.1.0-dev+26.2-26.45" +tracing = "0.1" +``` + +::: + +--- + +## Initialization + +Before performing license or update checks, initialize the utility crate inside your plugin's `on_load` method using the plugin's `Context`: + +```rust +use pumpkin_plugin_api::{Context, Plugin, PluginMetadata}; +use tracing::{info, warn}; + +struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { + Self + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "MyPlugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Author".into()], + description: "Plugin with license and update checks".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Initialize pumpkin-plugin-utils (verifies signature & caches metadata) + let metadata = pumpkin_plugin_utils::init(&context) + .map_err(|e| format!("Initialization failed: {e}"))?; + + info!( + "Loaded plugin '{}' v{} (Dev: {})", + metadata.plugin_name, metadata.version, metadata.dev_name + ); + + Ok(()) + } +} +``` + +::: info What happens during `init`? +1. Locates the plugin's `.wasm` binary in the server directory. +2. Extracts the `pumpkin.metadata` and `wasm_signature` custom sections. +3. Cryptographically verifies the Ed25519 signature over the WASM binary and metadata. +4. Caches the verified `PumpkinMetadata` globally for subsequent calls. +::: + +--- + +## Checking for Updates + +`pumpkin-plugin-utils` provides both zero-argument automatic checking and custom manual checking. + +### 1. Automatic Update Check (Recommended) + +Once initialized, call `pumpkin_plugin_utils::check_for_updates()` without arguments. It automatically uses the cached plugin name, current version, and marketplace endpoint: + +```rust +use tracing::{info, warn}; + +fn check_updates() { + match pumpkin_plugin_utils::check_for_updates() { + Ok(update) => { + if update.update_available { + info!( + "A new update is available: {}!", + update.latest_version.as_deref().unwrap_or("unknown") + ); + } else { + info!("Plugin is up to date."); + } + } + Err(err) => { + warn!("Failed to check for updates: {err}"); + } + } +} +``` + +### 2. Manual Update Check (`UpdateChecker`) + +If you want to manually specify the plugin name, version, or a custom marketplace URL, use `UpdateChecker`: + +```rust +use pumpkin_plugin_utils::UpdateChecker; + +let checker = UpdateChecker::new(); +let response = checker.check_for_updates( + "my-plugin", + "1.0.0", + "https://market.pumpkinmc.org", +)?; + +if response.update_available { + println!("Update found: {:?}", response.latest_version); +} +``` + +### Update Response Structure + +The update check returns a `CheckUpdateResponse`: + +```rust +pub struct CheckUpdateResponse { + /// Whether a newer stable release exists on the marketplace. + pub update_available: bool, + /// The latest stable version string, if available. + pub latest_version: Option, +} +``` + +--- + +## Checking and Verifying Licenses + +`pumpkin-plugin-utils` supports online verification against the marketplace API as well as offline lease management with configurable grace periods. + +### 1. Online License Check + +To verify a license against the marketplace (`GET /api/v1/rest/check-license`): + +```rust +// Pass `None` to use the embedded license key from metadata +let response = pumpkin_plugin_utils::check_license_online(None) + .map_err(|e| format!("License verification failed: {e}"))?; + +if response.valid { + info!("License is valid (status: {})", response.status); +} else { + warn!("License is invalid: {}", response.status); + return Err(format!("Invalid license status: {}", response.status).into()); +} +``` + +#### Providing a Custom License Key Override +If your plugin allows server owners to configure a license key in a `config.toml` file, pass `Some(&key)`: + +```rust +let config_key = "LIC-CUSTOM-KEY-12345"; +let response = pumpkin_plugin_utils::check_license_online(Some(config_key))?; +``` + +::: tip 7-Day Lease Caching +Successful online checks automatically save a verified lease file (`license_lease.json`) into the plugin's data folder with a 7-day expiration. +::: + +### 2. Evaluating License with Grace Period + +To keep paid plugins functional when the server is offline or the marketplace is unreachable, use `evaluate_license`: + +```rust +use pumpkin_plugin_utils::LicenseStatus; +use tracing::{info, warn}; + +// Specify grace period duration in days (e.g., 7 days) +let grace_period_days = 7; + +match pumpkin_plugin_utils::evaluate_license(grace_period_days) { + LicenseStatus::Valid(meta) => { + info!("License valid for buyer (User ID: {})", meta.user_id); + } + LicenseStatus::GracePeriod { metadata, days_remaining, reason } => { + warn!( + "Running in offline grace period ({} days remaining). Reason: {}", + days_remaining, reason + ); + } + LicenseStatus::Invalid(reason) => { + return Err(format!("Plugin license invalid or tampered: {reason}").into()); + } + LicenseStatus::Unsigned => { + info!("Plugin is running in unsigned / development mode."); + } +} +``` + +### `LicenseStatus` Enum + +| Variant | Description | +| :--- | :--- | +| `LicenseStatus::Valid(PumpkinMetadata)` | License is valid, verified, or the plugin is free / open-source. | +| `LicenseStatus::GracePeriod { metadata, days_remaining, reason }` | Operating offline with a previously validated lease that is within the allowable grace period. | +| `LicenseStatus::Invalid(String)` | Signature is invalid, expired, revoked, or metadata was tampered with. | +| `LicenseStatus::Unsigned` | Binary does not contain marketplace metadata/signature (useful during local development). | + +--- + +## Accessing Plugin Metadata + +You can access the verified plugin metadata anywhere in your codebase using `get_metadata` or `metadata()`: + +```rust +if let Some(meta) = pumpkin_plugin_utils::get_metadata() { + println!("Plugin Name: {}", meta.plugin_name); + println!("Plugin Version: {}", meta.version); + println!("Is Paid: {}", meta.is_paid); + println!("License Key: {:?}", meta.license_key); + println!("Buyer User ID: {}", meta.user_id); + println!("Developer: {} (ID: {})", meta.dev_name, meta.dev_id); + println!("Marketplace URL: {}", meta.marketplace_url); + println!("Issued At: {}", meta.issued_at); +} +``` + +### `PumpkinMetadata` Fields + +| Field | Type | Description | +| :--- | :--- | :--- | +| `plugin_name` | `String` | Canonical plugin name on the marketplace. | +| `version` | `String` | SemVer version string of the plugin binary. | +| `plugin_id` | `i64` | Unique plugin identifier on the marketplace. | +| `dev_id` | `i64` | Developer account ID. | +| `dev_name` | `String` | Developer display name. | +| `is_paid` | `bool` | `true` if this is a premium/commercial plugin. | +| `user_id` | `i64` | Buyer / licensee user ID (`0` for free plugins). | +| `license_key` | `Option` | Unique license key issued to the licensee. | +| `marketplace_url` | `String` | Base marketplace URL (`https://market.pumpkinmc.org`). | +| `issued_at` | `String` | ISO-8601 timestamp when the binary/license was issued. | + +--- + +## Complete Example + +Here is a full plugin implementation combining initialization, online licensing checks, offline grace periods, and update checking: + +:::code-group + +```rust:line-numbers [src/lib.rs] +use pumpkin_plugin_api::{Context, Plugin, PluginMetadata}; +use pumpkin_plugin_utils::LicenseStatus; +use tracing::{error, info, warn}; + +struct CommercialPlugin; + +impl Plugin for CommercialPlugin { + fn new() -> Self { + Self + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "CommercialPlugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Developer".into()], + description: "A commercial Pumpkin plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Initialize plugin-utils (verifies Ed25519 signature & caches metadata) + let meta = pumpkin_plugin_utils::init(&context).map_err(|err| { + error!("Signature verification failed: {err}"); + format!("Security verification error: {err}") + })?; + + info!("Loaded {} v{}", meta.plugin_name, meta.version); + + // 2. Validate license (offline evaluation + online check) + match pumpkin_plugin_utils::evaluate_license(7) { + LicenseStatus::Valid(_) => { + info!("Local license status: Valid"); + } + LicenseStatus::GracePeriod { days_remaining, .. } => { + warn!("Local license in grace period: {days_remaining} days remaining"); + } + LicenseStatus::Unsigned => { + info!("Running in development mode (unsigned binary)"); + } + LicenseStatus::Invalid(reason) => { + error!("Invalid license: {reason}"); + return Err(format!("License verification failed: {reason}").into()); + } + } + + // 3. Online license check if paid + if meta.is_paid { + if let Ok(check) = pumpkin_plugin_utils::check_license_online(None) { + if !check.valid { + return Err(format!("Online license rejected: {}", check.status).into()); + } + info!("Online license check confirmed: {}", check.status); + } else { + warn!("Online license verification unreachable, falling back to local lease"); + } + } + + // 4. Check for updates + if let Ok(update) = pumpkin_plugin_utils::check_for_updates() { + if update.update_available { + info!( + "A new version is available on the marketplace: {}", + update.latest_version.as_deref().unwrap_or("unknown") + ); + } + } + + Ok(()) + } + + fn on_unload(&mut self, _context: Context) -> pumpkin_plugin_api::Result<()> { + info!("CommercialPlugin unloaded."); + Ok(()) + } +} + +pumpkin_plugin_api::register_plugin!(CommercialPlugin); +``` + +::: diff --git a/docs/ja_jp/troubleshooting/common_issues.md b/docs/ja_jp/troubleshooting/common_issues.md index bbe9f0f..b3cf6f7 100644 --- a/docs/ja_jp/troubleshooting/common_issues.md +++ b/docs/ja_jp/troubleshooting/common_issues.md @@ -7,3 +7,14 @@ **原因:** これは認証に関連しており、通常は `prevent_proxy_connections` 設定に関係しています。 **修正:** `pumpkin.toml` で `prevent_proxy_connections` を無効にしてください。 + +2. ## 0.0.0.0 を使用して接続できない + + **問題:** 自身のPCでサーバーを実行しているプレイヤーが、`0.0.0.0` または `0.0.0.0:25565` を入力すると接続できません(例: 「Connection refused」エラー)。 + + **原因:** `pumpkin.toml` において `0.0.0.0` は、Pumpkin がすべてのローカルネットワークインターフェースでリッスンすることを意味します。これはサーバー側のバインドアドレスであり、Minecraft クライアントが接続するための有効な宛先アドレスではありません。 + + **修正:** + - **同じPC**から接続する場合は、Minecraft クライアントで `localhost` または `127.0.0.1` を入力してください。 + - **同じローカルネットワーク(LAN)上の別のデバイス**から接続する場合は、ホストマシンのローカルIPアドレス(例: `192.168.x.x`)を使用してください。 + - **インターネット経由**で接続する場合、他のプレイヤーはあなたのグローバルIPアドレスまたはドメイン名を使用する必要があります(ルーターのポート開放が必要です)。 diff --git a/docs/nl/admin/introduction.md b/docs/nl/admin/introduction.md new file mode 100644 index 0000000..6deacaa --- /dev/null +++ b/docs/nl/admin/introduction.md @@ -0,0 +1,25 @@ +# Beheerdersoverzicht + +Welkom bij het **Beheergedeelte** voor Pumpkin-serverbeheerders. Pumpkin is een krachtige, multithreaded Minecraft-server geschreven in Rust, ontworpen om gigantische spelersaantallen te ondersteunen met een minimaal resourceverbruik. + +--- + +## Belangrijkste functies voor serverbeheerders + +- **Extreme multithreading**: Vanaf de grond af opgebouwd voor moderne multi-core processors. +- **WASM-pluginarchitectuur**: Veilige, gesandboxte plugin-uitvoering in Rust, Python, Kotlin, C#, Go of C zonder JVM-overhead. +- **Standaard cross-proxy-ondersteuning**: Directe ondersteuning voor Velocity, BungeeCord en moderne proxy-forwarding. +- **Op TOML gebaseerde configuratie**: Overzichtelijke, voor mensen leesbare configuratiebestanden in `pumpkin.toml` en functiespecifieke TOML-configuraties. + +--- + +## Beheerdershandleidingen & Onderwerpen + +Bekijk de volgende handleidingen voor serverbeheer: + +- [Migreren van een Bukkit- / Paper- / Spigot-server](./migrating-from-bukkit) — Belangrijke verschillen in serverbeheer, plugins, wereldopslag en prestaties. +- [Serverconfiguratie](../config/introduction) — Gedetailleerd overzicht van de instellingen in `pumpkin.toml`. +- [Proxy-configuratie](../config/proxy) — BungeeCord- en Velocity-spelerforwarding instellen. +- [Commando's & Permissies](../config/commands) — Beheer van in-game operatorcommando's en permissies. +- [Authenticatie](../config/authentication) — Online-modus vs. offline-modus en Yggdrasil-instellingen. +- [Probleemoplossing & Veelvoorkomende problemen](../troubleshooting/common_issues) — Oplossingen voor problemen met poortbinding, geheugen en het laden van plugins. diff --git a/docs/nl/admin/migrating-from-bukkit.md b/docs/nl/admin/migrating-from-bukkit.md new file mode 100644 index 0000000..772d45d --- /dev/null +++ b/docs/nl/admin/migrating-from-bukkit.md @@ -0,0 +1,126 @@ +# Migreren van Bukkit / Spigot / Paper naar Pumpkin + +Deze handleiding beschrijft de architectonische en configuratieverschillen bij het overzetten van een server van Bukkit, Spigot of Paper naar Pumpkin. + +--- + +## 1. Architectonische verschillen + +| Aspect | Bukkit / Spigot / Paper | Pumpkin | +| :--- | :--- | :--- | +| **Runtime-omgeving** | Java Virtual Machine (vereist Java 17/21+) | Native executable (gecompileerd in Rust, geen Java vereist) | +| **Opstartopdracht** | `java [flags] -jar server.jar` | `./pumpkin` | +| **Tick-lus** | Single-threaded tick-lus met asynchrone taken | Multithreaded tick-uitvoering over CPU-kernen | +| **Plugin-formaat** | Java-bytecode (`.jar`) gericht op Bukkit API | WebAssembly (`.wasm`), of `.jar` via PatchBukkit | +| **Plugin-talen** | Java, Kotlin, Scala | Rust, Python, Kotlin, C#, Go, C | +| **Configuratie** | `server.properties`, `paper.yml`, `spigot.yml` | `pumpkin.toml` (TOML-formaat) | +| **Geheugenbeheer** | JVM Garbage Collection | Native OS-geheugenallocatie | + +--- + +## 2. Plugins & Extensies + +### WebAssembly-plugins +Het primaire plugin-formaat van Pumpkin is WebAssembly (`.wasm`): +- **Isolatie**: Plugins draaien in een gesandboxte WebAssembly-runtime. +- **Taalondersteuning**: Plugins kunnen naar WASM worden gecompileerd vanuit Rust, Python, Go, C#, C of Kotlin. +- **Aangepaste plugins**: Zie voor het overzetten van eigen Bukkit-plugins de [Migratiehandleiding voor ontwikkelaars](../plugin-dev/migrating-from-bukkit/index). + +### Bukkit-plugincompatibiliteit: PatchBukkit +Voor servers die afhankelijk zijn van bestaande Bukkit-/Spigot-`.jar`-plugins biedt het **PatchBukkit**-project een compatibiliteitslaag: +- **Architectuur**: PatchBukkit sluit een JVM in binnen Pumpkin en implementeert delen van de Bukkit API opnieuw, waarbij aanroepen via JNI en FFI worden overbrugd naar Pumpkin. +- **Plugin-locatie**: Compatibele `.jar`-plugins worden in `patchbukkit/patchbukkit-plugins/` geplaatst. +- **Status & Beperkingen**: PatchBukkit is actief in ontwikkeling. Standaard Bukkit API-aanroepen worden ondersteund; plugins die sterk afhankelijk zijn van interne NMS-klassen (`net.minecraft.server`), CraftBukkit-reflectie of bytecode-manipulatie werken mogelijk nog niet volledig. +- **Java-vereiste**: Het gebruik van PatchBukkit vereist een geïnstalleerde Java-runtime (JRE/JDK) op het hostsysteem om de ingesloten JVM uit te voeren. + +--- + +## 3. Configuratie & Eigenschappen + +Pumpkin gebruikt TOML-configuratiebestanden in plaats van `server.properties` en YAML: + +| Paper / Spigot (`server.properties`) | Pumpkin (`pumpkin.toml`) | +| :--- | :--- | +| `server-port=25565` | `server_address = "0.0.0.0:25565"` | +| `motd=...` | `motd = "A Pumpkin Server"` | +| `max-players=20` | `max_players = 20` | +| `online-mode=true` | `online_mode = true` | +| `view-distance=10` | `view_distance = 10` | +| `simulation-distance=8` | `simulation_distance = 8` | + +--- + +## 4. Wereld- & Gegevensopslag + +- **Formaatcompatibiliteit**: Pumpkin leest het standaard Anvil-chunkformaat (`.mca`-bestanden). +- **Mapindeling**: Zorg ervoor dat dimensiemappen (`world`, `world_nether`, `world_the_end`) overeenkomen met de verwachte structuur van Pumpkin bij het kopiëren van werelden vanuit bestaande Paper-/Spigot-installaties. +- **Nieuwste wereld- & datapack-formaat vereist**: Vooralsnog ondersteunt Pumpkin alleen het nieuwste wereldformaat en datapack-formaat voor de ondersteunde Minecraft-versie. Oudere wereldformaten of verouderde datapack-versies worden tijdens runtime niet automatisch geconverteerd. + - **Werelden bijwerken in de client**: Als je een oudere wereld migreert, werk deze dan eerst bij voordat je deze in Pumpkin laadt. Je kunt een bestaande wereld direct in de officiële Minecraft-client upgraden: open de client in de overeenkomende nieuwste versie, ga naar **Singleplayer**, selecteer de wereld, klik op **Edit** en kies **Optimize World**. Als alternatief kun je de wereld eenmalig laden en opslaan op een vanilla-server van de nieuwste versie. + - **Datapacks**: Zorg ervoor dat alle datapacks in `world/datapacks` zijn bijgewerkt naar het formaat dat door de doelversie wordt verwacht. + +--- + +## 5. Opstarten & JVM-flags + +### Geen Java-installatie nodig +Pure Pumpkin is een zelfstandige native executable. Je hebt geen Java-runtime (JRE/JDK) nodig op je systeem of in je container om de server uit te voeren. + +### Opstartopdrachten +Paper- en Spigot-servers gebruiken vaak JVM-opstartscripts met heap-allocatie en garbage collection-flags (zoals Aikar's Flags): + +:::code-group + +```bash [Paper (JVM)] +# Typische Paper-opstartopdracht met Aikar's Flags: +java -Xms10G -Xmx10G \ + -XX:+UseG1GC \ + -XX:+ParallelRefProcEnabled \ + -XX:MaxGCPauseMillis=200 \ + -XX:+UnlockExperimentalVMOptions \ + -XX:+DisableExplicitGC \ + -XX:+AlwaysPreTouch \ + -XX:G1NewSizePercent=30 \ + -XX:G1MaxNewSizePercent=40 \ + -XX:G1HeapRegionSize=8M \ + -XX:G1ReservePercent=20 \ + -XX:G1HeapWastePercent=5 \ + -XX:G1MixedGCCountTarget=4 \ + -XX:InitiatingHeapOccupancyPercent=15 \ + -XX:G1MixedGCLiveThresholdPercent=90 \ + -XX:G1RSetUpdatingPauseTimePercent=5 \ + -XX:SurvivorRatio=32 \ + -XX:+PerfDisableSharedMem \ + -XX:MaxTenuringThreshold=1 \ + -Dusing.aikars.flags=https://mcflags.emc.gs \ + -Daikars.new.flags=true \ + -jar paper.jar --nogui +``` + +```bash [Pumpkin (Native)] +# Linux / macOS: +./pumpkin + +# Windows: +./pumpkin.exe + +# Docker: +docker run -p 25565:25565 -v ./data:/data pumpkinmc/pumpkin:latest +``` + +::: + +### Geheugenverschillen +- **Geen Garbage Collector**: Pumpkin is geschreven in Rust en gebruikt deterministisch geheugenbeheer tijdens het compileren (RAII). Er is geen runtime garbage collector of GC-pauze-afstemming. +- **Geen vaste heap-reservering**: Vlaggen zoals `-Xms` en `-Xmx` bestaan niet bij native binaries. Geheugen wordt dynamisch aangevraagd bij het besturingssysteem wanneer chunks en entiteiten laden, en weer vrijgegeven bij het ontladen. +- **JVM-flags zijn niet van toepassing**: Vlaggen voor het afstemmen van garbage collection, generatiegroottes of JVM-interne instellingen zijn niet nodig en kunnen niet worden doorgegeven. + +--- + +## Beheerderschecklist + +1. [ ] Maak een back-up van bestaande servergegevens en wereldbestanden en zorg dat de wereld en datapacks zijn bijgewerkt naar het nieuwste formaat (bijv. via "Optimize World" in de client). +2. [ ] Vertaal `server.properties`-instellingen naar `pumpkin.toml`. +3. [ ] Identificeer vereiste plugins en zoek naar WebAssembly-equivalenten (`.wasm`) of test met [PatchBukkit](#bukkit-plugincompatibiliteit-patchbukkit). +4. [ ] Configureer proxy-instellingen (`Velocity` / `BungeeCord`) in `pumpkin.toml` bij gebruik van een proxynetwerk. +5. [ ] Werk opstartscripts bij om `./pumpkin` direct uit te voeren zonder JVM-argumenten. +6. [ ] Start de server en controleer de connectiviteit en logs. diff --git a/docs/nl/config/bedrock.md b/docs/nl/config/bedrock.md new file mode 100644 index 0000000..06ca467 --- /dev/null +++ b/docs/nl/config/bedrock.md @@ -0,0 +1,50 @@ +# Bedrock & NetherNet + +Pumpkin bevat ingebouwde ondersteuning voor Minecraft Bedrock Edition-clients, inclusief NetherNet WebRTC/ICE-transport en aanpasbare gebruikersnamen. + +## Configuratie + +:::code-group + +```toml [pumpkin.toml] +[networking.bedrock] +enabled = true +online_mode = true +max_players = 1000 +view_distance = 16 +simulation_distance = 10 +motd = "A blazingly fast Pumpkin server!" +username_prefix = "" +replace_username_spaces = true +chunk_caching = true + +[networking.bedrock.nethernet] +enabled = true +address = "0.0.0.0:19132" +identity_key = "nethernet-key.der" +stun_servers = [] +``` + +::: + +### Bedrock-opties + +- **`enabled`**: Of verbindingen van Bedrock Edition-clients worden geaccepteerd (standaard: `true`). +- **`online_mode`**: Of Xbox Live-authenticatie wordt afgedwongen voor Bedrock-spelers (standaard: `true`). +- **`max_players`**: Maximaal aantal gelijktijdige Bedrock-spelers (`0` schakelt de limiet uit, standaard: `1000`). +- **`view_distance`**: Maximale chunk-zichtafstand die naar Bedrock-clients wordt verzonden (standaard: `16`). +- **`simulation_distance`**: Maximale tick-simulatieafstand voor Bedrock-spelers (standaard: `10`). +- **`motd`**: Message of the Day weergegeven in de Bedrock-serverlijst. +- **`username_prefix`**: Optioneel voorvoegsel dat vóór Bedrock-gamertags wordt geplaatst (bijv. `"."` of `"*"`), om naamconflicten met Java Edition-accounts op cross-play-servers te voorkomen (standaard: `""`). +- **`replace_username_spaces`**: Of spaties in Bedrock-gamertags automatisch worden vervangen door underscores `_`, zodat spelersnamen kunnen worden gebruikt in Minecraft-slashcommando's (standaard: `true`). +- **`chunk_caching`**: Of chunk-blob-caching aan de clientzijde is ingeschakeld om netwerkbandbreedte te besparen (standaard: `true`). + +### NetherNet-transportinstellingen + +NetherNet is het moderne WebRTC/ICE-netwerktransport van Minecraft Bedrock: + +- **`enabled`**: Of clients verbinding mogen maken via NetherNet (standaard: `true`). +- **`address`**: Netwerksocketadres voor TCP-signalering en UDP-ICE-multiplexing (standaard: `"0.0.0.0:19132"`). +- **`external_ip`**: Optioneel openbaar IP-adres dat wordt geadverteerd wanneer de server achter NAT wordt gehost. +- **`identity_key`**: Bestandspad naar het PKCS#8 P-384 privésleutelbestand voor identiteit, behouden tussen herstarts voor Trust On First Use (TOFU) van de client (standaard: `"nethernet-key.der"`). +- **`stun_servers`**: Optionele lijst van STUN-server-URL's voor ICE-NAT-traversal. diff --git a/docs/nl/config/chat.md b/docs/nl/config/chat.md new file mode 100644 index 0000000..a78971b --- /dev/null +++ b/docs/nl/config/chat.md @@ -0,0 +1,39 @@ +# Chat & Anti-Spam + +Pumpkin biedt aanpasbare in-game chatformattering en ingebouwde anti-spambescherming onder `[chat]` in `pumpkin.toml`. + +## Configuratie + +:::code-group + +```toml [pumpkin.toml] +[chat] +format = "<{DISPLAYNAME}> {MESSAGE}" + +[chat.anti_spam] +enabled = true +spam_threshold = 200 +message_cost = 20 +decay_per_tick = 1 +ops_bypass = true +``` + +::: + +### Chatinstellingen + +- **`format`**: De formaatstring voor chatberichten (standaard: `"<{DISPLAYNAME}> {MESSAGE}"`). + - `{DISPLAYNAME}`: Weergavenaam van de speler. + - `{MESSAGE}`: Tekst van het chatbericht. + > [!NOTE] + > Aangepaste chatformattering is niet van toepassing wanneer beveiligde chatrapportage (`allow_chat_reports = true`) is ingeschakeld. + +### Anti-spambescherming + +Pumpkin implementeert een leaky-bucket-spamteller om de server te beschermen tegen spammende spelers en bot-aanvallen: + +- **`enabled`**: Of anti-spambescherming actief is (standaard: `true`). +- **`spam_threshold`**: De drempelwaarde van de spamteller in ticks waarbij een speler wordt gekickt (standaard: `200` ticks). +- **`message_cost`**: De hoeveelheid die wordt toegevoegd aan de spamteller van de speler voor elk verzonden chatbericht of commando (standaard: `20` ticks). +- **`decay_per_tick`**: De hoeveelheid die bij elke servertick van de spamteller van de speler wordt afgetrokken (standaard: `1` tick). +- **`ops_bypass`**: Of serveroperators de anti-spamcontroles omzeilen (standaard: `true`). diff --git a/docs/nl/config/packet-limiter.md b/docs/nl/config/packet-limiter.md new file mode 100644 index 0000000..477e3f8 --- /dev/null +++ b/docs/nl/config/packet-limiter.md @@ -0,0 +1,30 @@ +# Packet-limiter + +Pumpkin bevat een ingebouwde packet-rate-limiter om de server te beschermen tegen packet flooding, spam-aanvallen en client-side exploits. Packet-snelheidslimieten kunnen onafhankelijk worden geconfigureerd voor Java Edition- en Bedrock Edition-clients. + +## Configuratie + +:::code-group + +```toml [pumpkin.toml] +[networking.java.packet_limiter] +enabled = true +max_packet_rate = 500.0 +burst_capacity = 500.0 +kick_message = "Kicked for spamming packets" + +[networking.bedrock.packet_limiter] +enabled = true +max_packet_rate = 500.0 +burst_capacity = 500.0 +kick_message = "Kicked for spamming packets" +``` + +::: + +### Configuratie-opties + +- **`enabled`**: Of de packet-rate-limiter is ingeschakeld voor deze clientversie (standaard: `true`). +- **`max_packet_rate`**: Maximaal toegestane inkomende pakketten per seconde per clientverbinding (standaard: `500.0`). Stel in op `<= 0.0` om de snelheidslimiet uit te schakelen zonder de limiter-module uit te schakelen. +- **`burst_capacity`**: Token-bucket burst-capaciteit voor korte pieken in clientpakketten (standaard: `500.0`). +- **`kick_message`**: Het bericht bij verbreking van de verbinding dat aan de speler wordt getoond wanneer diens verbinding de packetlimiet overschrijdt (standaard: `"Kicked for spamming packets"`). diff --git a/docs/nl/config/player-data.md b/docs/nl/config/player-data.md new file mode 100644 index 0000000..5b9708a --- /dev/null +++ b/docs/nl/config/player-data.md @@ -0,0 +1,41 @@ +# Spelergegevens & Gameplay + +Instellingen voor het opslaan van persistente spelergegevens, advancements, receptsynchronisatie en leuke seizoensgebonden functies worden geconfigureerd in `pumpkin.toml`. + +## Configuratie + +:::code-group + +```toml [pumpkin.toml] +[player_data] +save_player_data = true +save_player_cron_interval = 300 + +[advancement] +save_advancements = true + +[recipe] +send_recipes = true + +[fun] +april_fools = true +``` + +::: + +### Spelergegevens + +- **`save_player_data`**: Of persistente spelergegevens (inventaris, locatie, levenspunten) worden opgeslagen op schijf (standaard: `true`). +- **`save_player_cron_interval`**: Tijdsinterval in seconden tussen periodieke automatische opslag van gegevens van online spelers (standaard: `300` seconden / 5 minuten). + +### Advancements + +- **`save_advancements`**: Of de voortgang van advancements van spelers wordt bijgehouden en opgeslagen op schijf (standaard: `true`). + +### Recepten + +- **`send_recipes`**: Of crafting- en smelting-recepten worden gesynchroniseerd met verbonden clients, waardoor het receptenboek aan de clientzijde werkt (standaard: `true`). + +### Leuke functies + +- **`april_fools`**: Of leuke en seizoensgebonden 1-april-easter-eggs en -functies zijn ingeschakeld (standaard: `true`). diff --git a/docs/nl/config/plugins.md b/docs/nl/config/plugins.md new file mode 100644 index 0000000..1532555 --- /dev/null +++ b/docs/nl/config/plugins.md @@ -0,0 +1,59 @@ +# Plugins + +Pumpkin biedt een WebAssembly (WASM) plugin-runtime met beveiliging op basis van capabilities. Plugingedrag, handtekeningverificatie en permissies worden geconfigureerd onder `[plugins]` in `pumpkin.toml`. + +## Configuratie + +:::code-group + +```toml [pumpkin.toml] +[plugins] +enabled = true +hot_reload = false +ask_permission_confirmation = true +allow_unsigned = true +allowed_permissions = [] +blocked_permissions = [] +inherit_env = false +loopback_only = false +verify_signatures = true + +# Optionele overschrijvingen per plugin +[plugins.overrides.my_plugin] +enabled = true +allow_unsigned = true +max_memory_mb = 128 +allowed_permissions = ["fs:read:data"] +blocked_permissions = ["network:outbound"] +loopback_only = true + +[plugins.overrides.my_plugin.environment] +API_KEY = "example_secret" +``` + +::: + +### Globale plugininstellingen + +- **`enabled`**: Hoofdschakelaar om de plugin-runtime in of uit te schakelen (standaard: `true`). +- **`hot_reload`**: De map `plugins/` in de gaten houden en gewijzigde plugins automatisch herladen tijdens runtime (standaard: `false`). +- **`ask_permission_confirmation`**: Een bevestiging vragen in de serverconsole wanneer een plugin niet-goedgekeurde capabilities/permissies aanvraagt (standaard: `true`). +- **`allow_unsigned`**: Het laden van niet-ondertekende WASM-plugins toestaan (standaard: `true`). +- **`allowed_permissions`**: Lijst met permissies die globaal vooraf zijn goedgekeurd voor alle plugins, waardoor interactieve consolevragen worden overgeslagen (standaard: `[]`). +- **`blocked_permissions`**: Lijst met permissies die globaal worden geweigerd voor alle plugins (standaard: `[]`). +- **`inherit_env`**: Of omgevingsvariabelen van de host standaard worden overgenomen in WASI-sandboxes van plugins (standaard: `false`). +- **`loopback_only`**: Of uitgaande netwerkverbindingen van plugins worden beperkt tot `127.0.0.1` / localhost (standaard: `false`). +- **`max_memory_mb`**: (Optioneel) Globale maximale geheugenlimiet in megabytes (MB) per plugin-instantie. +- **`verify_signatures`**: Of Pumpkin cryptografische handtekeningen op WASM-plugins verifieert vóór het laden (standaard: `true`). + +### Overschrijvingen per plugin + +Stem permissies en omgevingsvariabelen voor specifieke plugins nauwkeurig af onder `[plugins.overrides.]`: + +- **`enabled`**: Schakel deze specifieke plugin in of uit (standaard: `true`). +- **`allow_unsigned`**: Overschrijf of deze specifieke plugin niet-ondertekend mag draaien (`true`/`false`). +- **`max_memory_mb`**: Maximale geheugenlimiet in MB die specifiek voor deze plugin is toegewezen. +- **`allowed_permissions`**: Vooraf goedgekeurde permissies voor deze plugin. +- **`blocked_permissions`**: Expliciet geblokkeerde permissies voor deze plugin. +- **`loopback_only`**: Overschrijf de loopback-netwerkbeperking voor deze plugin. +- **`environment`**: Tabel met aangepaste omgevingsvariabelen die rechtstreeks worden doorgegeven aan de WASI-omgeving van de plugin. diff --git a/docs/nl/config/server-links.md b/docs/nl/config/server-links.md new file mode 100644 index 0000000..fa68f2f --- /dev/null +++ b/docs/nl/config/server-links.md @@ -0,0 +1,50 @@ +# Serverlinks + +Minecraft-clients (sinds 1.21) kunnen aanklikbare serverlinks weergeven in het pauzemenu en de spelmenu's. Configureer standaard en aangepaste serverlinks onder `[server_links]` in `pumpkin.toml`. + +## Configuratie + +:::code-group + +```toml [pumpkin.toml] +[server_links] +enabled = true +bug_report = "https://github.com/Pumpkin-MC/Pumpkin/issues" +support = "" +status = "" +feedback = "" +community = "" +website = "" +forums = "" +news = "" +announcements = "" + +[server_links.custom] +# "Store" = "https://store.example.com" +# "Discord" = "https://discord.gg/example" +``` + +::: + +### Configuratie-opties + +- **`enabled`**: Hoofdschakelaar om het uitzenden van serverlinks naar verbindende clients in te schakelen (standaard: `true`). +- **`bug_report`**: Link naar bugtracker van de server of het project (standaard: `"https://github.com/Pumpkin-MC/Pumpkin/issues"`). +- **`support`**: Link naar technische ondersteuning of helpdesk. +- **`status`**: Link naar uptime-/statuspagina van de server. +- **`feedback`**: Link naar feedback- of suggestiepagina voor spelers. +- **`community`**: Link naar communityforums of -groep. +- **`website`**: Link naar officiële website. +- **`forums`**: Link naar discussieforums. +- **`news`**: Link naar serverblog of nieuwspagina. +- **`announcements`**: Link naar aankondigingenpagina. + +### Aangepaste links + +Definieer willekeurige sleutel-waardelinks die in clientmenu's worden getoond onder `[server_links.custom]`: + +```toml +[server_links.custom] +"Store" = "https://store.example.com" +"Discord" = "https://discord.gg/example" +``` diff --git a/docs/nl/config/world.md b/docs/nl/config/world.md new file mode 100644 index 0000000..97f4432 --- /dev/null +++ b/docs/nl/config/world.md @@ -0,0 +1,41 @@ +# Wereld + +Pumpkin maakt het mogelijk om formaten voor wereldopslag, intervallen voor automatisch opslaan en verlichtingsmodi fijn af te stemmen onder `[world]` in `pumpkin.toml`. + +## Configuratie + +:::code-group + +```toml [pumpkin.toml] +[world] +lighting = "default" +autosave_ticks = 6000 + +[world.chunk] +type = "anvil" +write_in_place = false + +[world.chunk.compression] +algorithm = "LZ4" +level = 6 +``` + +::: + +### Wereldinstellingen + +- **`lighting`**: Berekeningsmodus voor de propagatie van de verlichtingsengine. + - `"default"`: Standaard vanille Minecraft-lichtpropagatie. + - `"full"`: Overal volledig hemellicht zonder schaduwen. + - `"dark"`: Overal volledige duisternis (nul licht). +- **`autosave_ticks`**: Aantal serverticks tussen automatische wereldopslagen (standaard: `6000`, wat gelijkstaat aan 5 minuten bij 20 TPS). Dit instellen op `0` schakelt automatisch opslaan van de wereld uit. + +### Chunk-opslaginstellingen + +- **`type`**: Het te gebruiken formaat voor chunk-opslag: + - `"anvil"`: Standaard Minecraft Anvil-regiobestandsformaat (`.mca`). + - `"linear"`: Linear-regio-opslagformaat dat snelle compressie gebruikt voor minder schijfgebruik. + - `"pump"`: Pumpkin's eigen geoptimaliseerde wereldformaat. +- **`write_in_place`**: Of chunks ter plekke moeten worden herschreven binnen bestaande regiobestanden in plaats van vrije chunks opnieuw toe te wijzen (standaard: `false`). +- **`compression.algorithm`**: Compressie-algoritme dat wordt gebruikt voor chunkgegevens (`"LZ4"`, `"ZLib"`, `"GZip"`, `"Custom"`). +- **`compression.level`**: Compressieniveau voor chunk-opslag (standaard: `6`). diff --git a/docs/nl/developer/architecture.md b/docs/nl/developer/architecture.md new file mode 100644 index 0000000..19c26b1 --- /dev/null +++ b/docs/nl/developer/architecture.md @@ -0,0 +1,139 @@ +# Pumpkin Architecture & Crates + +Pumpkin is organized as a modular Cargo workspace composed of specialized crates. Each crate has distinct responsibilities, strictly defined boundaries, and minimal cross-dependencies. + +This architecture enables high parallel compilation speeds, isolated testing, zero-cost data sharing, and clean separation between network protocols, game logic, and world management. + +--- + +## Workspace Crate Overview + +### Workspace Architecture at a Glance + +| Layer | Crates | Responsibilities | +| :--- | :--- | :--- | +| **Core Runtime** | `pumpkin` | Game loop, world ticking, player sessions, entity management, and event loops | +| **Networking & Protocols** | `pumpkin-protocol`, `pumpkin-auth`, `pumpkin-nbt` | Java/Bedrock packet serialization, Yggdrasil & Xbox Live authentication, SNBT | +| **World & Gameplay** | `pumpkin-world`, `pumpkin-inventory`, `pumpkin-command` | Region I/O (Anvil/Linear), terrain generation, block ticking, containers, Brigadier tree | +| **Extensibility** | `pumpkin-plugin-runtime`, `pumpkin-host-bindings`, `pumpkin-plugin-api`, `pumpkin-plugin-wit` | WebAssembly plugin execution via Wasmtime, WIT host APIs, native and WASM loaders | +| **Foundations** | `pumpkin-data`, `pumpkin-config`, `pumpkin-util` | Code-generated registries, typed configuration files, mathematical vectors, permissions | + +--- + +## Detailed Crate Breakdown + +### 1. `pumpkin` (Core Server) +- **Path**: `crates/pumpkin` +- **Purpose**: The main server binary and centralized orchestration engine. +- **Responsibilities**: + - Initializes configuration, logging (`tracing`), and telemetry. + - Spawns Tokio network listeners for Minecraft: Java Edition, Bedrock Edition, RCON, Query, and LAN broadcast. + - Manages the server tick loop (`Server::tick`), coordinating world updates, player actions, entity movements, and chunk broadcasting. + - Dispatches incoming client packets through `crates/pumpkin/src/net/java/` and `net/bedrock/`. + - Integrates the plugin manager, firing event handlers before and after packet processing. + +### 2. `pumpkin-protocol` (Network Protocol) +- **Path**: `crates/pumpkin-protocol` +- **Purpose**: High-speed network wire format serialization and deserialization for Minecraft Java and Bedrock editions. +- **Responsibilities**: + - Implements the `ClientPacket` (serialization) and `ServerPacket` (deserialization) traits. + - Handles protocol primitives: `VarInt`, `VarLong`, UUIDs, NBT compounds, and string codecs. + - Implements packets across all states: `Handshake`, `Status`, `Login`, `Config`, and `Play`. + - Provides packet compression (ZLib/Fast compression) and packet encryption (AES-128/CFB8). + - Encapsulates RCON and GameSpy4 Query protocols. + +### 3. `pumpkin-world` (World & Chunks) +- **Path**: `crates/pumpkin-world` +- **Purpose**: World loading, saving, terrain generation, and chunk management. +- **Responsibilities**: + - Supports multiple chunk storage formats: + - **Vanilla Anvil (`.mca`)**: Reading and writing standard region files. + - **Linear (`.linear`)**: High-efficiency zstd-compressed region format saving 50–95% disk space. + - **Slime (`.slime`)**: Single-file world format. + - **Pump**: Native Pumpkin chunk storage format. + - Implements world generation pipelines: Perlin noise heightmaps, 3D noise routers, biome climate parameters, placed features, carvers, and superflat terrain. + - Manages block lighting and sky lighting propagation. + - Coordinates chunk loading, unloading, and streaming via cylindrical chunk iterators. + +### 4. `pumpkin-data` (Minecraft Datasets & Registries) +- **Path**: `crates/pumpkin-data` +- **Purpose**: Compile-time static registries and data tables generated directly from vanilla Minecraft assets and reports. +- **Responsibilities**: + - Contains complete ID mappings, properties, and definitions for blocks, items, biomes, entities, sounds, particles, enchantments, and recipes. + - Packet ID lookups across multiple Minecraft protocol versions. + - Zero-cost lookup tables generated by `tools/pumpkin-codegen`. + +### 5. `pumpkin-config` (Configuration Management) +- **Path**: `crates/pumpkin-config` +- **Purpose**: TOML configuration parsing, serialization, and validation. +- **Responsibilities**: + - Loads and writes `pumpkin.toml`. + - Configures server ports, compression thresholds, proxy modes, resource packs, logging levels, world settings, and Bedrock integration. + - Provides safe defaults and schema validations. + +### 6. `pumpkin-inventory` (Inventory & Containers) +- **Path**: `crates/pumpkin-inventory` +- **Purpose**: Server-side container window handlers and inventory mechanics. +- **Responsibilities**: + - Manages player inventory, chest containers, crafting tables, furnaces, and villager trading screens. + - Validates client slot clicks, drag operations, item stacking, and item splitting. + - Coordinates window packet synchronization to ensure client inventory fidelity. + +### 7. `pumpkin-command` (Command Dispatcher) +- **Path**: `crates/pumpkin-command` +- **Purpose**: Brigadier-inspired hierarchical command tree dispatcher and parser. +- **Responsibilities**: + - Constructs modular command trees with typed arguments (`Integer`, `Float`, `Entity`, `BlockPos`, etc.). + - Executes permission-checked commands. + - Provides real-time autocompletion suggestions sent to connected clients. + +### 8. `pumpkin-auth` (Authentication & Session Verification) +- **Path**: `crates/pumpkin-auth` +- **Purpose**: Cryptographic authentication and player identity verification. +- **Responsibilities**: + - Validates client sessions with Mojang's Yggdrasil authentication servers. + - Handles RSA keypair generation and shared secret exchange during the Login state. + - Supports offline-mode UUID generation and custom authentication endpoints. + - Implements proxy forwarding protocols: [BungeeCord](https://github.com/SpigotMC/BungeeCord), [BungeeGuard](https://github.com/lucko/BungeeGuard), and [Velocity](https://github.com/PaperMC/Velocity) modern forwarding with HMAC-SHA256 signatures. + +### 9. `pumpkin-nbt` (Named Binary Tag Codec) +- **Path**: `crates/pumpkin-nbt` +- **Purpose**: High-throughput binary NBT parser and serializer. +- **Responsibilities**: + - Parses and encodes all standard NBT tag types (`Byte`, `Short`, `Int`, `Long`, `Float`, `Double`, `ByteArray`, `String`, `List`, `Compound`, `IntArray`, `LongArray`). + - Supports compression layers: Gzip, Zlib, and uncompressed Network NBT. + +### 10. `pumpkin-plugin-*` (Extensibility Infrastructure) +- **Paths**: + - `crates/pumpkin-plugin-api`: The Rust developer SDK for compiling native plugins. + - `crates/pumpkin-plugin-runtime`: Wasmtime-powered WebAssembly execution host. + - `crates/pumpkin-plugin-wit`: WebAssembly Interface Type (WIT) specifications. + - `crates/pumpkin-host-bindings`: Auto-generated host bindings connecting Wasmtime and the server core. +- **Purpose**: Enables safe, polyglot plugin execution (Rust, Python, C#, C, Go, Kotlin) with sandboxed memory and near-native performance. + +### 11. `pumpkin-util` & `pumpkin-macros` (Foundations) +- **Paths**: `crates/pumpkin-util`, `crates/pumpkin-macros`, `crates/pumpkin-api-macros` +- **Purpose**: Foundational types, shared algorithms, and procedural macros. +- **Responsibilities**: + - Mathematical utilities: `Vector2`, `Vector3`, `BoundingBox`, and angle conversions. + - Text and chat formatting: `TextComponent` and JSON component serialization. + - Procedural macros such as `#[java_packet(...)]` that bind packet structs to protocol IDs and packet traits. + +### 12. `tools/` (Developer Tooling) +- **`tools/pumpkin-codegen`**: Automated code generator that parses Minecraft version jars, data reports, and asset archives to emit Rust source files into `pumpkin-data`. +- **`tools/pumpkin-fuzzer`**: Fuzz testing suites for network packet parsers, NBT readers, and region file decoders to identify potential crash vectors or memory anomalies. + +--- + +## Concurrency & Threading Model + +| Component | Runtime / Pool | Primary Workload | Communication Mechanism | +| :--- | :--- | :--- | :--- | +| **Network & Socket I/O** | Tokio Async Runtime | TCP packet reads, Bedrock UDP packets, framing, session handshakes | Async tasks (`tokio::spawn`), non-blocking reactors | +| **Heavy Compute** | Rayon Thread Pool | Terrain generation, noise routers, light propagation, chunk compression | Worker threads via crossbeam / Rayon iterators | +| **Inter-Thread Bridge** | Tokio Channels | Dispatching completed chunks and encoded packets back to network tasks | Non-blocking `tokio::sync::mpsc` channels | + +Pumpkin strictly adheres to this separation of concerns: +- **I/O Bound**: Managed on Tokio asynchronous tasks. +- **CPU Bound**: Managed on Rayon thread pools. +- **Communication**: Coordinated via cross-thread channels (`tokio::sync::mpsc`) without ever blocking the Tokio reactor loop. diff --git a/docs/nl/developer/blocks/adding-blocks.md b/docs/nl/developer/blocks/adding-blocks.md new file mode 100644 index 0000000..0770ed8 --- /dev/null +++ b/docs/nl/developer/blocks/adding-blocks.md @@ -0,0 +1,182 @@ +# Adding a Block Behavior + +This guide walks through implementing interactive behavior for a block in Pumpkin. + +--- + +## When Is a Behavior Needed? + +- **Non-Interactive Blocks** (e.g., stone, dirt, terracotta): Do **not** require any custom Rust code. Their breaking speeds, hardness, and placement properties are automatically derived from `pumpkin-data`. +- **Interactive or Functional Blocks** (e.g., doors, chests, levers, crops, campfires, TNT): Require an implementation of the `BlockBehaviour` trait to respond to player clicks, redstone signals, physics steps, or timer ticks. + +--- + +## Step 1: Create the Behavior Struct + +Create a new file under `crates/pumpkin/src/block/blocks//.rs` (or directly in `crates/pumpkin/src/block/blocks/`). + +Use the `#[pumpkin_block(...)]` macro to associate your behavior struct with one or more vanilla block identifiers: + +```rust +use std::sync::Arc; +use pumpkin_data::Block; +use pumpkin_data::block_properties::{DoorLikeProperties, Facing}; +use pumpkin_macros::pumpkin_block; +use pumpkin_util::math::position::BlockPos; +use crate::block::registry::BlockActionResult; +use crate::block::{ + BlockBehaviour, NormalUseArgs, OnNeighborUpdateArgs, PlacedArgs, UseWithItemArgs, +}; +use crate::world::World; + +/// Associates this behavior struct with one or multiple block IDs. +#[pumpkin_block([ + "minecraft:oak_door", + "minecraft:iron_door", + "minecraft:spruce_door", + "minecraft:birch_door" +])] +pub struct DoorBlock; + +impl BlockBehaviour for DoorBlock { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state_id = args.world.get_block_state(args.location).await; + + // Iron doors require redstone power and cannot be opened manually by hand + if args.block.id == pumpkin_data::Block::IRON_DOOR.id { + return BlockActionResult::Pass; + } + + // Toggle open state + let is_open = DoorLikeProperties::open(state_id); + let new_state = DoorLikeProperties::with_open(state_id, !is_open); + args.world.set_block_state(args.location, new_state).await; + + BlockActionResult::Consume + } + + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + // Handle neighbor redstone signals or support block destruction + } +} +``` + +--- + +## Step 2: `BlockBehaviour` Hook Reference + +The `BlockBehaviour` trait exposes lifecycle hooks modeled on vanilla mechanics: + +### Player & Item Interaction Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `normal_use` | `(&self, args: NormalUseArgs<'_>) -> BlockActionResult` | Triggered when right-clicking the block with an empty hand. | +| `use_with_item` | `(&self, args: UseWithItemArgs<'_>) -> BlockActionResult` | Triggered when right-clicking the block while holding an item. | +| `get_screen_handler_factory` | `(&self, args: GetScreenHandlerFactoryArgs<'_>) -> Option>` | Returns an inventory container factory to open a UI screen (e.g., Chests, Crafting Tables). | + +### World Lifecycle & Physics Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `placed` | `(&self, args: PlacedArgs<'_>)` | Triggered immediately after the block is placed in the world. | +| `broken` | `(&self, args: BrokenArgs<'_>)` | Triggered when broken by a player or game mechanics. | +| `on_entity_collision` | `(&self, args: OnEntityCollisionArgs<'_>)` | Triggered when an entity walks inside, steps on, or falls into the block. | +| `on_neighbor_update` | `(&self, args: OnNeighborUpdateArgs<'_>)` | Triggered when an adjacent block state updates or is destroyed. | +| `on_projectile_hit` | `(&self, args: OnProjectileHitArgs<'_>)` | Triggered when an arrow, snowball, or projectile strikes the block. | + +### Redstone & Power Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `emits_redstone_power` | `(&self, state_id: BlockStateId) -> bool` | Declares whether the block emits redstone power to neighbors. | +| `get_weak_redstone_power` | `(&self, ...) -> u8` | Returns emitted weak power level (0–15). | +| `get_strong_redstone_power` | `(&self, ...) -> u8` | Returns emitted strong direct power level (0–15). | + +### Foliage & Bonemeal Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `is_valid_bonemeal_target` | `(&self, args: BonemealArgs<'_>) -> bool` | Checks if bone meal can be applied (e.g., crop not fully grown). | +| `is_bonemeal_success` | `(&self, args: BonemealArgs<'_>) -> bool` | Evaluates random chance roll for bone meal success. | +| `perform_bonemeal` | `(&self, args: BonemealArgs<'_>)` | Advances growth stages or spawns adjacent flora. | + +--- + +## Step 3: Modifying Block States + +All block properties are strongly typed in `pumpkin_data::block_properties`: + +```rust +use pumpkin_data::block_properties::{BedLikeProperties, Part}; + +// 1. Read typed properties from the numeric BlockStateId +let part = BedLikeProperties::part(state_id); // Part::Head or Part::Foot +let is_occupied = BedLikeProperties::occupied(state_id); + +// 2. Generate a new BlockStateId with updated properties +let updated_state = BedLikeProperties::with_occupied(state_id, true); + +// 3. Commit the updated state to the world +world.set_block_state(pos, updated_state).await; +``` + +--- + +## Step 4: Adding a Block Entity (Tile Entity) + +If your block needs to store persistent custom state (inventories, custom text, timers): + +1. Create a block entity struct in `crates/pumpkin/src/block/entities/`: + ```rust + use pumpkin_util::math::position::BlockPos; + use crate::block::entities::BlockEntity; + + pub struct CustomBlockEntity { + pub pos: BlockPos, + pub custom_counter: u32, + } + + impl BlockEntity for CustomBlockEntity { + fn to_nbt(&self) -> pumpkin_nbt::compound::NbtCompound { + let mut compound = pumpkin_nbt::compound::NbtCompound::new(); + compound.put_int("Counter", self.custom_counter as i32); + compound + } + + fn from_nbt(&mut self, tag: &pumpkin_nbt::compound::NbtCompound) { + if let Some(val) = tag.get_int("Counter") { + self.custom_counter = val as u32; + } + } + + fn tick(&mut self, world: &std::sync::Arc, pos: &BlockPos) { + // Periodic logic per game tick + } + } + ``` + +2. Link the block entity in your `BlockBehaviour`: + ```rust + impl BlockBehaviour for CustomBlock { + fn create_block_entity(&self, pos: BlockPos) -> Option>> { + Some(Arc::new(Mutex::new(CustomBlockEntity::new(pos)))) + } + } + ``` + +--- + +## Step 5: Registering the Block + +1. Export your module in `crates/pumpkin/src/block/blocks/mod.rs`: + ```rust + pub mod doors; + ``` + +2. Register the behavior struct in `crates/pumpkin/src/block/registry.rs`: + ```rust + manager.register(DoorBlock); + ``` + +At startup, `manager.register` automatically reads the block IDs declared in `#[pumpkin_block(...)]` and routes interactions to your implementation. diff --git a/docs/nl/developer/blocks/index.md b/docs/nl/developer/blocks/index.md new file mode 100644 index 0000000..525a200 --- /dev/null +++ b/docs/nl/developer/blocks/index.md @@ -0,0 +1,104 @@ +# Block Architecture & State System + +Blocks form the static and dynamic foundation of Minecraft worlds. In Pumpkin, block handling is cleanly bifurcated into two distinct layers: + +1. **Static Data Layer ([`pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data))**: Compile-time generated metadata including numeric block IDs, block state IDs, hardness, blast resistance, light emission, opacity, collision shapes, and block state properties generated by [`pumpkin-codegen`](/developer/codegen). +2. **Dynamic Behavior Layer ([`pumpkin::block`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/block))**: Server-side behavioral hooks—such as opening containers, toggling redstone signals, growing crops, breaking triggers, and collision effects—handled via the `BlockBehaviour` trait. + +--- + +## Static Blocks vs. Interactive Behaviors + +Because Minecraft contains thousands of block state combinations, Pumpkin avoids allocating runtime behavior structs for simple, non-interactive blocks: + +| Category | Description | Examples | Runtime Behavior Required? | +| :--- | :--- | :--- | :--- | +| **Static Blocks** | Rigid solid voxels without interactive logic or custom physics | `stone`, `dirt`, `cobblestone`, `bedrock`, `obsidian` | No — Placement, breaking times, and tool requirements are handled entirely by `pumpkin-data`. | +| **Interactive Blocks** | Blocks triggered by right-clicks or tool actions | `door`, `trapdoor`, `lever`, `button`, `crafting_table` | Yes — Implements `BlockBehaviour::normal_use` or `use_with_item`. | +| **Collision Blocks** | Blocks that modify entity movement or apply forces | `honey_block`, `slime_block`, `soul_sand`, `powder_snow` | Yes — Implements `on_entity_collision` or `on_landed_upon`. | +| **Redstone Components** | Power sources, conductors, and logic gates | `redstone_wire`, `repeater`, `comparator`, `observer` | Yes — Implements `emits_redstone_power` and `on_neighbor_update`. | +| **Block Entities** | Blocks with persistent, serialized custom states | `chest`, `furnace`, `sign`, `decorated_pot`, `spawner` | Yes — Implements `create_block_entity` and links a `BlockEntity`. | + +--- + +## Block IDs, States, and Properties + +Minecraft distinguishes between a **Block** type and a specific **Block State**: + +### 1. `BlockId` and `BlockStateId` + +- **`BlockId`**: An identifier representing a base block type (e.g., `minecraft:oak_door`). +- **`BlockStateId`**: A 16-bit integer representing a unique combination of block properties (e.g., `minecraft:oak_door[facing=north,half=lower,hinge=left,open=false,powered=false]`). + +In Pumpkin, block data is looked up from precomputed lookup tables in `pumpkin-data`: + +```rust +use pumpkin_data::Block; +use pumpkin_data::BlockStateId; + +// Access static properties directly from generated data +let door_block = Block::OAK_DOOR; +let default_state_id: BlockStateId = door_block.default_state_id; + +let hardness = door_block.hardness; +let blast_resistance = door_block.blast_resistance; +let is_air = door_block.is_air; +``` + +### 2. Generated State Property Helpers + +Rather than parsing raw strings or bitmasks manually, [`pumpkin-codegen`](/developer/codegen) generates strongly typed property helpers in `pumpkin_data::block_properties`: + +```rust +use pumpkin_data::block_properties::{DoorLikeProperties, DoubleBlockHalf, Facing}; + +// Read typed properties from a numeric BlockStateId +let is_open = DoorLikeProperties::open(state_id); +let half = DoorLikeProperties::half(state_id); +let facing = DoorLikeProperties::facing(state_id); + +// Generate a new BlockStateId with mutated properties +let toggled_state_id = DoorLikeProperties::with_open(state_id, !is_open); +``` + +--- + +## Physics, Bounding Boxes & Raycasting + +Pumpkin calculates collision and raycasting bounding boxes directly using pre-baked shapes in `pumpkin-data`: + +- **Solid Collisions**: During physics ticks, an entity's axis-aligned bounding box (AABB) is tested against surrounding block collision voxels. +- **Raycasting**: When a player targets a block to place or break, client raycasts are validated against the server-side block outline shapes. +- **Custom Movement Hooks**: Blocks like slime blocks or honey blocks override vertical rebound or horizontal velocity: + - `bounce_entity_after_fall`: Dampens fall damage and multiplies rebound velocity. + - `stop_vertical_movement_after_fall`: Zeros fall velocity upon contact. + +--- + +## Block Entities (Tile Entities) + +When a block must store arbitrary dynamic data that cannot fit into standard block state bits (such as container inventories, custom sign text, brewing timers, or mob spawner delays), it uses a **Block Entity**: + +| Component | Responsibility | Implementation File | +| :--- | :--- | :--- | +| `BlockEntity` Trait | Defines NBT serialization (`to_nbt`, `from_nbt`) and tick execution | [`crates/pumpkin/src/block/entities/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/block/entities) | +| `create_block_entity` | Hook in `BlockBehaviour` called when placing the block | Attached to relevant block behaviors | +| Chunk Entity Storage | Manages active block entities and coordinates within chunk sections | [`crates/pumpkin-world`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-world) | + +--- + +## The Block Registry & Dispatch System + +Block interaction events are routed through the central `BlockRegistry` in [`crates/pumpkin/src/block/registry.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/block/registry.rs): + +1. **Registration**: At server initialization, all behavior structs are registered into the `BlockManager`. +2. **Mapping**: The manager maps numeric `BlockId` keys to thread-safe behavior instances (`Arc`). +3. **Dispatch**: When the network layer receives block action packets (such as `CUseItemOn` or block dig packets), the server looks up the behavior in $O(1)$ time and invokes the relevant hook. + +--- + +## Next Steps + +To implement a new interactive block behavior, continue to the practical guide: + +- **[Adding a Block](/developer/blocks/adding-blocks)**: Complete walk-through of declaring attributes, implementing interaction hooks, creating block entities, and registering behaviors. diff --git a/docs/nl/developer/codegen.md b/docs/nl/developer/codegen.md new file mode 100644 index 0000000..3908d04 --- /dev/null +++ b/docs/nl/developer/codegen.md @@ -0,0 +1,66 @@ +# Code Generation (`pumpkin-codegen`) + +Minecraft is a massive game with thousands of block states, items, packet IDs, sound events, recipes, and biome parameters. These registries frequently change between Minecraft updates. + +To maintain 100% fidelity without manually maintaining tens of thousands of error-prone constants, Pumpkin utilizes an automated code generation tool: [`tools/pumpkin-codegen`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/tools/pumpkin-codegen). + +--- + +## Purpose & Workflow + +`pumpkin-codegen` parses official Minecraft server data generator reports, asset files, and version specifications, and outputs zero-cost, strongly-typed Rust files directly into [`crates/pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data). + +The pipeline operates in three distinct stages: +1. **Ingestion**: Reads raw JSON assets and vanilla server reports from `assets/` and `assets/datapacks/`. +2. **Transformation**: Normalizes states, calculates bitsets, processes ViaVersion remappings, and optimizes lookup tables. +3. **Emission**: Emits compiled Rust constants and structs into [`crates/pumpkin-data/src/generated`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data/src/generated). + +--- + +## Generated Datasets + +`pumpkin-codegen` generates the following components in `pumpkin-data`: + +| Category | Description | Generated Source | +| :--- | :--- | :--- | +| **Packets** | Serverbound and clientbound packet IDs for all protocol states across supported Minecraft versions. | `packet.rs` | +| **Blocks** | Block definitions, default states, state properties (facing, powered, waterlogged, etc.), and block collision boxes. | `block.rs` | +| **Items** | Item definitions, stack sizes, durability, data components, food properties, and equipment slots. | `item.rs` | +| **Registries** | Dynamic registries for biomes, dimensions, chat types, damage types, trim materials, and banner patterns. | `registry.rs` | +| **Biomes & Noise** | Climate parameters, noise router configurations, 3D noise settings, and surface rules. | `biome.rs`, `noise_router.rs` | +| **Entities** | Entity types, default bounding boxes, attributes, and tracked entity metadata schemas. | `entity_type.rs`, `tracked_data.rs` | +| **Recipes & Crafting** | Shaped, shapeless, smelting, blasting, smoking, and stonecutting recipes. | `recipes.rs` | +| **Tags** | Block tags, item tags, fluid tags, and entity type tags (e.g., `#minecraft:mineable/pickaxe`). | `tag.rs` | +| **Enchantments & Potions** | Enchantment definitions, potion effects, and brewing stand recipes. | `enchantments.rs`, `potion.rs` | +| **Translations** | Official localization keys for chat messages and item names. | `translations.rs` | +| **Plugin WIT** | WebAssembly Component Model WIT files defining plugin interfaces. | `wit/` | + +--- + +## Running Code Generation + +To run the code generator locally: + +```bash +# Navigate to the workspace root +cd Pumpkin + +# Run the codegen tool +cargo run -p pumpkin-codegen +``` + +### When Should You Run Codegen? + +- **Upgrading to a New Minecraft Version**: When porting Pumpkin to a new minor or major Minecraft release. +- **Adding Protocol Mappings**: When adding support for an older or newer client protocol version. +- **Updating WIT Definitions**: When adding new functions or types to the WebAssembly plugin API. + +--- + +## Data Sources + +`pumpkin-codegen` reads data from several primary sources: + +1. **Minecraft Data Generator Reports**: Generated by running `java -DbundlerMainClass=net.minecraft.data.Main -jar server.jar --reports`. +2. **Minecraft Client Assets**: Extracted from client `.jar` files for translation keys, model shapes, and sound mappings. +3. **Minecraft Protocol Wiki**: Cross-referenced with protocol specifications for packet IDs. diff --git a/docs/nl/developer/commands/adding-commands.md b/docs/nl/developer/commands/adding-commands.md new file mode 100644 index 0000000..eda739a --- /dev/null +++ b/docs/nl/developer/commands/adding-commands.md @@ -0,0 +1,246 @@ +# Adding a Command + +This guide explains how to implement and register a new command in the Pumpkin server codebase. + +Commands in Pumpkin are implemented in the `crates/pumpkin/src/command/commands/` directory and registered into the central [`CommandDispatcher`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/dispatcher.rs) during server initialization. + +--- + +## Anatomy of a Pumpkin Command + +A typical command file contains: +1. **Metadata Constants**: Command description, permission node identifier, and argument names. +2. **Permission Registration**: Declaring default OP requirements in the server's [`PermissionRegistry`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-util/src/permission.rs). +3. **Command Executors**: Structs implementing the [`CommandExecutor`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/mod.rs) trait for each terminal node or subcommand. +4. **Registration Function**: Constructing the argument tree using fluent builders (`command`, `literal`, `argument`). + +--- + +## Step-by-Step Implementation: Creating `/heal` + +Let's implement a complete `/heal` command that can heal either the executing player or a set of target players: + +``` +/heal -> Heals the executing sender +/heal -> Heals target player(s) +/heal -> Heals target player(s) by a specified amount +``` + +### 1. Create the File + +Create `crates/pumpkin/src/command/commands/heal.rs`: + +```rust +use std::sync::Arc; + +use crate::command::argument_builder::{ArgumentBuilder, argument, command}; +use crate::command::argument_types::core::float::FloatArgumentType; +use crate::command::argument_types::entity::EntityArgumentType; +use crate::command::context::command_context::CommandContext; +use crate::command::errors::error_types::CommandErrorType; +use crate::command::node::dispatcher::CommandDispatcher; +use crate::command::node::{CommandExecutor, CommandExecutorResult}; +use crate::entity::EntityBase; +use pumpkin_data::translation; +use pumpkin_util::PermissionLvl; +use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry}; +use pumpkin_util::text::TextComponent; + +const DESCRIPTION: &str = "Restores health to target entities."; +const PERMISSION: &str = "pumpkin:command.heal"; + +const ARG_TARGETS: &str = "targets"; +const ARG_AMOUNT: &str = "amount"; + +const ERROR_NOT_PLAYER: CommandErrorType<0> = CommandErrorType::new( + translation::java::PERMISSIONS_REQUIRES_PLAYER, + translation::java::PERMISSIONS_REQUIRES_PLAYER, +); +``` + +### 2. Implement the Executors + +Command execution logic is encapsulated in structs implementing [`CommandExecutor`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/mod.rs): + +```rust +/// Heals the executing player to full health. +struct SelfHealExecutor; + +impl CommandExecutor for SelfHealExecutor { + fn execute(&self, context: &CommandContext) -> CommandExecutorResult { + let player = context + .source + .output + .as_player() + .ok_or_else(|| ERROR_NOT_PLAYER.create_without_context())?; + + // Restore player health + player.living_entity.set_health(20.0); + + context.source.send_feedback( + TextComponent::text("Fully restored your health!"), + true, // Broadcast to operators if configured + ); + + // Return number of affected entities + Ok(1) + } +} + +/// Heals specified target players. +struct TargetsHealExecutor { + has_amount: bool, +} + +impl CommandExecutor for TargetsHealExecutor { + fn execute(&self, context: &CommandContext) -> CommandExecutorResult { + let targets = EntityArgumentType::get_players(context, ARG_TARGETS)?; + + let amount = if self.has_amount { + FloatArgumentType::get(context, ARG_AMOUNT)? + } else { + 20.0 + }; + + let count = targets.len(); + for target in &targets { + let current = target.living_entity.health.load(); + target.living_entity.set_health((current + amount).min(20.0)); + } + + let msg = if count == 1 { + TextComponent::text(format!("Healed {} by {} HP", targets[0].gameprofile.name, amount)) + } else { + TextComponent::text(format!("Healed {} players by {} HP", count, amount)) + }; + + context.source.send_feedback(msg, true); + + Ok(count as i32) + } +} +``` + +### 3. Build the Command Tree + +Use the fluent builder functions `command`, `literal`, and `argument` to assemble the syntax tree and register the permission: + +```rust +pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) { + // 1. Register permission node with default requirement (OP level 2) + registry.register_permission_or_panic(Permission::new( + PERMISSION, + DESCRIPTION, + PermissionDefault::Op(PermissionLvl::Two), + )); + + // 2. Assemble command tree + dispatcher.register( + command("heal", DESCRIPTION) + .requires(PERMISSION) + .executes(SelfHealExecutor) + .then( + argument(ARG_TARGETS, EntityArgumentType::Players) + .executes(TargetsHealExecutor { has_amount: false }) + .then( + argument(ARG_AMOUNT, FloatArgumentType::new(1.0, 100.0)) + .executes(TargetsHealExecutor { has_amount: true }), + ), + ), + ); +} +``` + +--- + +## Command Structure Patterns + +### Adding Subcommand Literals +Use `literal("subcommand_name")` to create keyword branches: + +```rust +use crate::command::argument_builder::{argument, command, literal}; + +dispatcher.register( + command("weather", "Set the world weather") + .requires("minecraft:command.weather") + .then(literal("clear").executes(ClearWeatherExecutor)) + .then(literal("rain").executes(RainWeatherExecutor)) + .then(literal("thunder").executes(ThunderWeatherExecutor)) +); +``` + +### Cross-Platform Translation Feedback +Pumpkin servers serve both Java and Bedrock Edition players simultaneously. Whenever possible, use vanilla translation keys with `TextComponent::translate_cross` so messages render localized on the client: + +```rust +use pumpkin_data::translation; +use pumpkin_util::text::TextComponent; + +let msg = TextComponent::translate_cross( + translation::java::COMMANDS_KILL_SUCCESS_SINGLE, + translation::bedrock::COMMANDS_KILL_SUCCESSFUL, + [target.get_display_name()], +); + +context.source.send_feedback(msg, true); +``` + +### Error Handling in Executors +Executors must return `CommandExecutorResult`, which resolves to `Result`. If validation fails, return an error cleanly without panicking: + +```rust +use crate::command::errors::error_types::CommandErrorType; + +const ERROR_INVALID_VALUE: CommandErrorType<0> = CommandErrorType::new( + "Amount must be greater than zero.", + "Amount must be greater than zero.", +); + +if amount <= 0.0 { + return Err(ERROR_INVALID_VALUE.create_without_context()); +} +``` + +--- + +## Registering in `default_dispatcher` + +Once your command is implemented, register it in `crates/pumpkin/src/command/commands/mod.rs`: + +1. **Declare the Module**: + ```rust + // In crates/pumpkin/src/command/commands/mod.rs + mod heal; + ``` + +2. **Invoke `register` in `default_dispatcher`**: + ```rust + pub fn default_dispatcher( + permission_manager: &PermissionManager, + commands_config: &CommandsConfig, + ) -> CommandDispatcher { + let registry = &permission_manager.registry; + let mut dispatcher = CommandDispatcher::new(); + + // ... existing registrations ... + heal::register(&mut dispatcher, registry); + + dispatcher + } + ``` + +--- + +## Best Practices Checklist + +- [ ] **No Panics or Unwraps**: Use `?` or return descriptive `CommandSyntaxError`s when arguments are invalid. +- [ ] **Permission Nodes**: Prefix permission strings with appropriate namespaces (`minecraft:command.` for vanilla commands, `pumpkin:command.` for server-specific commands). +- [ ] **Return Success Counts**: The returned `i32` represents the command's exit code or number of affected objects for redstone command blocks and `/execute store result`. +- [ ] **Respect GameRules**: When notifying players other than the sender, check if `send_command_feedback` is enabled: + ```rust + let server = context.source.server(); + if server.level_info.load().game_rules.send_command_feedback { + target.send_system_message(&feedback); + } + ``` diff --git a/docs/nl/developer/commands/arguments-and-suggestions.md b/docs/nl/developer/commands/arguments-and-suggestions.md new file mode 100644 index 0000000..6794ae1 --- /dev/null +++ b/docs/nl/developer/commands/arguments-and-suggestions.md @@ -0,0 +1,159 @@ +# Argument Types & Suggestions + +Pumpkin provides a comprehensive suite of type-safe command arguments through `pumpkin-command` and the main `pumpkin` server crate. Arguments deserialize raw token streams into structured Rust types, validate ranges, and furnish tab-completion suggestions to clients. + +--- + +## Standard Argument Types + +Argument parsers implement the [`ArgumentType`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/argument_types/argument_type.rs) trait. The following built-in types are available for command builders: + +### 1. Primitive & Scalar Types + +| Argument Type | Import Path | Deserialized Type | Description | +| :--- | :--- | :--- | :--- | +| **`IntegerArgumentType`** | `...::core::integer` | `i32` | Standard 32-bit integer with optional `[min, max]` bounds. | +| **`FloatArgumentType`** | `...::core::float` | `f32` | 32-bit floating point number with bounds. | +| **`DoubleArgumentType`** | `...::core::double` | `f64` | 64-bit floating point number with bounds. | +| **`LongArgumentType`** | `...::core::long` | `i64` | 64-bit integer with bounds. | +| **`BoolArgumentType`** | `...::core::bool` | `bool` | Boolean value (`true` or `false`). | +| **`StringArgumentType`** | `...::core::string` | `String` | String tokens based on quoting rules. | + +#### String Argument Behaviors +- **`StringArgumentType::Word`**: Parses a single word without whitespace. +- **`StringArgumentType::Quotable`**: Parses either a single word or a quoted phrase (`"hello world"`). +- **`StringArgumentType::Greedy`**: Consumes all remaining characters in the input stream (ideal for chat/broadcast commands like `/say` and `/me`). + +--- + +## Coordinate Argument Types + +Minecraft coordinates support absolute positions, relative offsets (`~`), and local caret directions (`^`): + +```rust +use crate::command::argument_builder::argument; +use crate::command::argument_types::coordinates::block_pos::BlockPosArgumentType; +use crate::command::argument_types::coordinates::vec3::Vec3ArgumentType; + +// Block position (integer BlockPos) +argument("pos", BlockPosArgumentType) + +// Precise entity coordinates (f64 Vector3) +argument("location", Vec3ArgumentType) +``` + +### Retrieving Parsed Coordinates +Inside an executor, coordinate arguments resolve relative offsets against the sender's current position and orientation in the [`CommandSource`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/command/context/command_source.rs): + +```rust +// Extracts centered f64 coordinates (handling ~ and ^ relative to sender) +let pos: Vector3 = Vec3ArgumentType::get_coordinates(context, "location")?; + +// Extracts integer block position +let block_pos: BlockPos = BlockPosArgumentType::get_block_pos(context, "pos")?; +``` + +--- + +## Entity Selectors (`EntityArgumentType`) + +Entity selectors allow players and command blocks to target entities using player names, UUIDs, or vanilla target selectors (`@p`, `@r`, `@a`, `@e`, `@s`). + +In Pumpkin, entity selectors are accessed via [`EntityArgumentType`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/command/argument_types/entity.rs): + +```rust +pub enum EntityArgumentType { + /// Matches exactly one player + Player, + /// Matches zero or more players + Players, + /// Matches exactly one entity + Entity, + /// Matches zero or more entities + Entities, +} +``` + +### Resolution in Executors + +```rust +// 1. Target single player +let player: Arc = EntityArgumentType::get_player(context, "target")?; + +// 2. Target multiple players +let players: Vec> = EntityArgumentType::get_players(context, "targets")?; + +// 3. Target single living entity or mob +let entity: Arc = EntityArgumentType::get_entity(context, "target")?; + +// 4. Target multiple entities +let entities: Vec> = EntityArgumentType::get_entities(context, "targets")?; +``` + +### Supported Selector Syntaxes +Pumpkin's entity selector parser (`crates/pumpkin/src/command/argument_types/entity_selector/`) parses and validates: +- `@p`: Nearest player relative to execution coordinates. +- `@r`: Random player. +- `@a`: All online players. +- `@e`: All entities in loaded world chunks. +- `@s`: Executing entity (`context.source.entity`). +- **Bracket Arguments**: Filter queries like `@e[type=zombie,distance=..10,limit=5,sort=nearest]`. + +--- + +## Registry & Game Mechanic Types + +Pumpkin provides specialized argument parsers for Minecraft data types: + +- **`GameModeArgumentType`**: Resolves `survival`, `creative`, `adventure`, `spectator` or numeric IDs `0`–`3`. +- **`BlockArgumentType`**: Parses block identifiers and block states (e.g., `minecraft:chest[facing=north]`). +- **`ItemArgumentType`**: Parses item identifiers and optional data components (e.g., `minecraft:diamond_sword`). +- **`NbtArgumentType`**: Parses SNBT (Stringified NBT) tags into Pumpkin's NBT representation. +- **`TimeArgumentType`**: Parses durations with suffixes (e.g., `10s`, `20t`, `1d`). + +--- + +## Suggestion Providers & Tab Completion + +Arguments can supply dynamic tab-completion candidates to connected players using the [`SuggestionProvider`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/suggestion/mod.rs) trait: + +```rust +use crate::command::context::command_context::CommandContext; +use crate::command::suggestion::provider::SuggestionProvider; +use pumpkin_command::suggestion::suggestions::{Suggestions, SuggestionsBuilder}; + +struct CustomSuggestionProvider; + +impl SuggestionProvider for CustomSuggestionProvider { + fn suggest( + &self, + context: &CommandContext, + mut builder: SuggestionsBuilder, + ) -> Suggestions { + // Retrieve already typed token prefix + let remaining = builder.remaining(); + + let candidates = ["alpha", "bravo", "charlie"]; + for candidate in candidates { + if candidate.starts_with(remaining) { + builder.suggest(candidate); + } + } + + builder.build() + } +} +``` + +### Attaching Suggestions to Arguments +Use the `.suggests(...)` method on `RequiredArgumentBuilder`: + +```rust +argument("code", StringArgumentType::Word) + .suggests(CustomSuggestionProvider) + .executes(MyExecutor) +``` + +### Client Completion Protocol +- **Java Edition**: When a player presses `Tab`, the client transmits a `ServerboundCommandSuggestion` packet. Pumpkin matches the cursor position within the `CommandDispatcher`, executes the target node's `SuggestionProvider`, and responds with `ClientboundCommandSuggestions` containing completion ranges and tooltip tooltips. +- **Bedrock Edition**: Suggestion options for literals and enums are serialized ahead of time in the `CAvailableCommands` packet as dynamic enum value tables. diff --git a/docs/nl/developer/commands/execution-and-context.md b/docs/nl/developer/commands/execution-and-context.md new file mode 100644 index 0000000..6f5f044 --- /dev/null +++ b/docs/nl/developer/commands/execution-and-context.md @@ -0,0 +1,118 @@ +# Execution & Context + +In Pumpkin, command execution separates the **physical origin** of a command from its **runtime context**. This design enables complex command routing, dimension shifts, conditional execution, and vanilla-compliant `/execute` chaining. + +--- + +## `CommandSender` vs. `CommandSource` + +Understanding the distinction between `CommandSender` and `CommandSource` is fundamental to developing commands in Pumpkin: + +| Attribute | `CommandSender` | `CommandSource` | +| :--- | :--- | :--- | +| **Concept** | The physical origin and network destination | The mutable runtime execution environment | +| **Mutability** | Immutable throughout command execution | Cloned and modified across `/execute` branches | +| **Key Fields** | Terminal socket, connection handle, RCON buffer | World, position coordinates, rotation, entity, silent flag | +| **Creation** | Instantiated on network connection or terminal input | Constructed via `sender.into_source(&server)` | + +### 1. `CommandSender` (Physical Caller) +Represents the entity or socket that initiated the command and receives its output: + +```rust +pub enum CommandSender { + Console, + Rcon(Arc>>), + Player(Arc), + CommandBlock(Arc, Arc), + Dummy, +} +``` + +- **Output Target**: Determines where messages formatted with `send_message` are routed (e.g., standard out, RCON buffer, player packet, or block entity NBT). +- **Hard Permissions**: Holds fixed base permissions (e.g., Console has level 4 bypass; Command Blocks have level 2). + +### 2. `CommandSource` (Execution Environment) +Represents the mutable runtime state during command evaluation: + +```rust +pub struct CommandSource { + pub output: CommandSender, + pub world: Option>, + pub entity: Option>, + pub position: Vector3, + pub rotation: Vector2, + pub name: String, + pub display_name: TextComponent, + pub server: Option>, + pub silent: bool, + pub command_result_taker: ResultValueTaker, + pub entity_anchor: EntityAnchor, +} +``` + +A sender converts into a `CommandSource` via `sender.into_source(&server)`. + +--- + +## The `/execute` Command & Context Redirection + +The `/execute` command manipulates `CommandSource` properties before passing control to a downstream command. + +### Branching Modifiers +- **`as `**: Changes `context.source.entity` to target entities without altering position or rotation. +- **`at `**: Updates `context.source.world`, `position`, and `rotation` to match the target entity. +- **`positioned `**: Modifies `context.source.position` to new coordinates. +- **`in `**: Changes `context.source.world` to the specified dimension (Overworld, Nether, The End). +- **`anchored (feet|eyes)`**: Adjusts coordinate offsets for line-of-sight and facing checks. + +### Context Forking & Chaining +When `/execute` matches multiple targets (e.g., `/execute as @e run ...`), `pumpkin-command` uses **forking**: +1. The dispatcher forks the execution context once per matched target. +2. The child command executes independently for each cloned `CommandSource`. +3. The cumulative success count is aggregated across all executions. + +--- + +## Result Values & Success Counts + +Vanilla commands communicate results back through integer exit codes and return values. + +### The Return Value Callback +Executors return `CommandExecutorResult` (`Result`). The returned integer signifies: +- **0**: Failure or zero entities affected. +- **> 0**: Number of entities affected, blocks changed, or items cleared. + +For Command Blocks, this value is persisted into the block entity's `SuccessCount`: + +```rust +pub fn set_success_count(&self, count: u32) { + if let Self::CommandBlock(c, _) = self { + c.success_count.store(count, Ordering::SeqCst); + } +} +``` + +### `/execute store` +When chained with `/execute store (result|success) ...`, Pumpkin intercepts the return value via `ReturnValueCallback` and writes it to scoreboards, bossbars, or entity NBT tags. + +--- + +## Feedback & GameRules Integration + +Sending output to the command sender is governed by server configuration and world game rules: + +```rust +context.source.send_feedback( + TextComponent::text("Command executed successfully!"), + true, // Broadcast to server operators +); +``` + +### Output Rules Matrix + +| Condition | Feedback Behavior | +| :--- | :--- | +| `silent == true` | Output is suppressed completely. | +| `send_command_feedback == false` | Player senders do not receive success feedback (errors are still displayed). | +| `command_block_output == false` | Command block execution output is not broadcast to online operators. | +| `broadcast_console_to_ops == true` | Server console and RCON commands emit a copy of their feedback to all online OP level 2+ players. | diff --git a/docs/nl/developer/commands/index.md b/docs/nl/developer/commands/index.md new file mode 100644 index 0000000..3b31eb9 --- /dev/null +++ b/docs/nl/developer/commands/index.md @@ -0,0 +1,142 @@ +# Commands Architecture & Dispatcher + +Pumpkin features a high-performance, pure-Rust implementation of the **Brigadier** command tree specification through the dedicated `pumpkin-command` crate. It coordinates command parsing, requirement resolution, type-safe argument deserialization, execution branching, and dual-edition client tree synchronization for both **Minecraft: Java Edition** and **Minecraft: Bedrock Edition**. + +### Command Processing Pipeline + +Every command executed in Pumpkin flows through the following discrete stages: + +1. **Input Normalization**: Strips single leading slashes while preserving plugin multi-slash conventions (such as `//set`). +2. **Availability Check**: Rejects commands disabled via server configuration with standard `unknown command` syntax errors. +3. **Permission Evaluation**: Validates requirements (`PermissionLvl` or custom registry nodes) against the executing sender. +4. **Tokenization & Parsing**: `StringReader` recursively walks the `Tree`, matching literal keywords and parsing typed argument parameters. +5. **Context Dispatch**: Invokes the matched `CommandExecutor` callback with a typed `CommandContext`. +6. **Feedback & Result Consumption**: Formats cross-platform translation components, notifies observers, and writes exit codes back to the caller. + +--- + +## The `pumpkin-command` Crate + +Pumpkin decouples command parsing and tree representation from the main server runtime into the modular `pumpkin-command` crate (`crates/pumpkin-command`). + +Key components of `pumpkin-command` include: +- **`Tree`**: Contiguous vector-backed storage of attached command nodes (`AttachedNode`). +- **`CommandDispatcher`**: The central registry and dispatch engine. It processes raw command strings, parses tokens with `StringReader`, traverses the command tree, and executes target callbacks. +- **`ArgumentBuilder`**: Fluent builder API (`command`, `literal`, `argument`) modeled after Mojang's Brigadier library. +- **`CommandSource`**: Generic execution state representing the current position, rotation, dimension, entity, and feedback output stream. + +--- + +## The Command Node Hierarchy + +Commands in Pumpkin are modeled as directed graphs of nodes. To optimize memory allocations and avoid circular reference issues during building, the system separates nodes into **detached** and **attached** states: + +### 1. Detached Nodes (`DetachedNode`) +Constructed using fluent builders (`CommandArgumentBuilder`, `LiteralArgumentBuilder`, `RequiredArgumentBuilder`) before registration: +- **`CommandDetachedNode`**: Represents a root command entrypoint with an identifier and a description. +- **`LiteralDetachedNode`**: Represents a literal keyword in a subcommand branch (e.g., `set` in `/gamemode creative`). +- **`ArgumentDetachedNode`**: Represents a typed parameter parsed by an `ArgumentType` (e.g., coordinates, entity selectors, integer ranges). + +### 2. Attached Nodes (`AttachedNode`) +Once registered into the `CommandDispatcher`, detached trees are flattened into an indexed `Tree` structure: +```rust +pub enum AttachedNode { + Root(RootAttachedNode), + Literal(LiteralAttachedNode), + Argument(ArgumentAttachedNode), + Command(CommandAttachedNode), +} +``` +Each attached node has: +- **`NodeId`**: A zero-cost typed index (`NonZero`) into the contiguous node arena. +- **`children`**: Fast lookup map (`FxHashMap`) for literals and child arguments. +- **`requirements`**: Predicates determining whether a sender has permission to view or execute the node. +- **`command`**: Optional `CommandExecutor` callback invoked when parsing terminates on this node. +- **`redirect`**: Optional `Redirection` pointer to loop back or redirect to another sub-tree (used heavily by `/execute`). + +--- + +## Dispatch & Execution Pipeline + +When a command arrives at the server, `CommandDispatcher::handle_command` processes it through several stages: + +```rust +pub fn handle_command<'a>(&'a self, source: &S, mut input: &'a str) { + // 1. Strip single leading slash (preserving double-slash commands like //set) + if let Some(sliced) = input.strip_prefix('/') { + let first_token = input.split_whitespace().next().unwrap_or(""); + let sliced_token = sliced.split_whitespace().next().unwrap_or(""); + if !self.has_command(first_token) + && (self.has_command(sliced_token) || !first_token.starts_with("//")) + { + input = sliced; + } + } + + // 2. Check disabled commands from configuration + if self.is_disabled(Self::command_name(input)) { + let reader = StringReader::new(input); + Self::send_error_to_source(source, DISPATCHER_UNKNOWN_COMMAND.create(&reader), input); + return; + } + + // 3. Parse and execute + let output = self.execute_input(input, source); + if let Err(error) = output { + Self::send_error_to_source(source, error, input); + } +} +``` + +### Syntax Error Formatting +When parsing encounters invalid syntax, unexpected tokens, or out-of-range arguments, Pumpkin constructs a `CommandSyntaxError`. If context is available, the dispatcher formats a visual pointer highlighting exactly where the parser failed: + +``` +Unknown or incomplete command, see below for error +/gamemode creatve<--[HERE] +``` + +In the Java Edition client, this error text is annotated with a `ClickEvent::SuggestCommand`, allowing the player to click the error in chat to copy the failed command into their input box for quick editing. + +--- + +## Dual-Edition Network Synchronization + +Unlike vanilla servers which only serialize command graphs for Java Edition, Pumpkin dynamically synchronizes the registered command tree across both Java and Bedrock clients: + +### 1. Java Edition: `CCommands` Packet +When a player joins or their permission level updates (e.g., via `/op` or `/deop`), Pumpkin builds and sends a `CCommands` packet (`pumpkin_protocol::java::client::play::CCommands`): +- Converts `AttachedNode`s into a flat array of `ProtoNode` entries. +- Filters out disabled commands and nodes where `requirements.evaluate(source)` returns `false`. +- Serializes argument parsers (`brigadier:string`, `brigadier:integer`, `minecraft:entity`, etc.) and client-side suggestion identifiers. + +### 2. Bedrock Edition: `CAvailableCommands` Packet +Bedrock Edition uses an overload-and-enum format rather than Brigadier's recursive graph. In `client_suggestions.rs`, Pumpkin traverses the `AttachedNode` tree to synthesize: +- **`CommandData`**: Root command name, description, permission flags, and aliases. +- **`OverloadData`**: Flattened permutations of argument chains leading to executable endpoints. +- **`ParamData`**: Bedrock parameter types, bitflags (`ARG_FLAG_VALID`, `ARG_FLAG_ENUM`), and dynamically collected enum value tables. + +--- + +## Universal Execution Targets + +Pumpkin commands can be executed across diverse senders: + +| Sender | Source Representation | Output Destination | Default Permission | +| :--- | :--- | :--- | :--- | +| **Player** | `CommandSender::Player(Arc)` | Client system chat packet | Loaded from permission level / permissions file | +| **Server Console** | `CommandSender::Console` | Standard stdout (`tracing::info!`) | Level 4 (Full administrative bypass) | +| **RCON Console** | `CommandSender::Rcon(Arc>>)` | Network response packet payload | Level 4 | +| **Command Block** | `CommandSender::CommandBlock(Arc, Arc)` | NBT `LastOutput` field | Level 2 | +| **Datapack Function** | `CommandSender` inherited from caller | Inherited | Inherited | +| **WASM Plugin** | Invoked through WIT `execute_command` host call | Captured via `ResultDeferrer` | Configurable | + +--- + +## Command Configuration & Disabling + +Pumpkin allows server administrators to disable individual commands via `config/commands.toml` (`CommandsConfig`). When a command is disabled: +1. It is omitted from the `CCommands` packet for Java clients. +2. It is omitted from the `CAvailableCommands` packet for Bedrock clients. +3. Tab completion ignores the command entirely. +4. Any attempt to invoke the command returns `DISPATCHER_UNKNOWN_COMMAND`. diff --git a/docs/nl/developer/entities/ai-and-mobs.md b/docs/nl/developer/entities/ai-and-mobs.md new file mode 100644 index 0000000..bb1652f --- /dev/null +++ b/docs/nl/developer/entities/ai-and-mobs.md @@ -0,0 +1,111 @@ +# Mob AI & Goal System + +Pumpkin implements a modular, priority-driven Artificial Intelligence (AI) goal system for computer-controlled mobs, located in [`crates/pumpkin/src/entity/ai/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity/ai) and [`crates/pumpkin/src/entity/mob/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity/mob). + +--- + +## The Goal System Architecture + +Mob behaviors are decomposed into independent modular tasks called **Goals**. Each mob possesses a **GoalSelector** that maintains an ordered collection of goals evaluated on every server tick. + +### Goal Priority Hierarchy + +Goals are assigned an integer priority (where lower numbers represent higher precedence). During each tick, higher-priority actions preempt lower-priority background tasks: + +| Priority | Goal Type | Evaluation Condition | Behavior | +| :--- | :--- | :--- | :--- | +| **1 (Highest)** | `SwimGoal` | Entity is submerged in water | Jump repeatedly to swim to the water surface and avoid drowning. | +| **2** | `MeleeAttackGoal` | Valid hostile target in sight/range | Navigate towards target entity and execute melee attacks. | +| **3** | `WanderAroundGoal` | No active combat target | Pick a random nearby navigable voxel and walk to it. | +| **4 (Lowest)** | `LookAtEntityGoal` | Player or mob within observation radius | Rotate head and body pitch/yaw towards the target entity. | + +### Execution Flow + +1. **Evaluation**: On every game tick, `GoalSelector::tick()` iterates through registered goals in order of priority. +2. **Activation**: If an inactive goal satisfies `can_start()`, it is activated via `start()`. +3. **Execution**: Active goals run their per-tick logic in `tick()`. +4. **Preemption & Termination**: If `should_continue()` returns `false`, or a mutually exclusive goal with higher priority activates, `stop()` is called and the goal resets. + +### The `Goal` Lifecycle + +Every AI behavior implements the `Goal` trait: + +```rust +pub trait Goal: Send + Sync { + /// Evaluates preconditions to determine if this goal can activate. + fn can_start(&self) -> bool; + + /// Called once when the goal transitions from inactive to active. + fn start(&mut self) {} + + /// Evaluates whether the goal should continue executing on subsequent ticks. + fn should_continue(&self) -> bool { + self.can_start() + } + + /// Executes the continuous behavior on each server tick. + fn tick(&mut self) {} + + /// Called when the goal completes, fails, or is preempted by a higher priority goal. + fn stop(&mut self) {} +} +``` + +--- + +## Standard Built-in Goals + +Pumpkin provides standard vanilla AI behaviors in `crates/pumpkin/src/entity/ai/`: + +| Goal | Description | Target Mobs | +| :--- | :--- | :--- | +| **`SwimGoal`** | Jumps repeatedly while submerged in water to prevent drowning. | Almost all land mobs | +| **`MeleeAttackGoal`** | Navigates towards an attack target and executes a strike when within reach. | Zombies, Spiders, Iron Golems | +| **`RangedAttackGoal`** | Stays at range and shoots projectiles (arrows, snowballs). | Skeletons, Pillagers, Blazes | +| **`FleeSunGoal`** | Seeks shade or water during daytime to prevent combustion. | Zombies, Skeletons | +| **`PanicGoal`** | Sprints in random directions when taking damage. | Cows, Pigs, Sheep, Chickens | +| **`WanderAroundGoal`** | Selects random accessible coordinates nearby when idle. | Passive & neutral mobs | +| **`LookAtEntityGoal`** | Rotates head to face nearby players within sensory range. | Villagers, Mobs | + +--- + +## Navigation & Pathfinding + +Mobs navigate the block world using Pumpkin's 3D grid pathfinder: + +1. **Path Calculation**: Resolves a path from the mob's origin to target coordinates using an A* algorithm optimized for Minecraft block geometries. +2. **Obstacle Avoidance**: + - Avoids hazardous blocks (lava, fire, sweet berry bushes). + - Prevents walking off ledges higher than the mob's fall threshold. + - Computes jumping steps over 1-block obstacles. +3. **Waypoint Steering**: The navigation engine updates the mob's velocity vectors per tick to smoothly guide the entity along waypoints. + +--- + +## Entity Attributes System + +Mob capabilities and stats are governed by the **Attributes** system in [`crates/pumpkin/src/entity/attributes.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/entity/attributes.rs): + +```rust +use pumpkin_data::entity::attribute::EntityAttribute; + +// Common vanilla attributes +EntityAttribute::GENERIC_MAX_HEALTH; // Base hit points (e.g. 20.0 for Zombies) +EntityAttribute::GENERIC_MOVEMENT_SPEED; // Base walking velocity (e.g. 0.23) +EntityAttribute::GENERIC_ATTACK_DAMAGE; // Melee attack damage (e.g. 3.0) +EntityAttribute::GENERIC_FOLLOW_RANGE; // Sensory detection range in blocks (e.g. 35.0) +EntityAttribute::GENERIC_KNOCKBACK_RESISTANCE; // Resistance against knockback (0.0 to 1.0) +``` + +### Modifiers +Attributes support dynamic modifiers with additive or multiplicative calculations (e.g., speed bonuses from Swiftness potions or armor attribute boosts). + +--- + +## Ageable & Passive Entities + +Passive animals (cows, sheep, pigs) implement the `Ageable` trait: + +- **Baby State**: Renders with smaller collision boxes and oversized heads. +- **Growing Age**: A negative tick timer counting upward to `0`. When it reaches `0`, the entity matures into an adult. +- **Breeding Cooldown**: Positive tick timer tracking cooldowns between successful matings. diff --git a/docs/nl/developer/entities/index.md b/docs/nl/developer/entities/index.md new file mode 100644 index 0000000..53623ee --- /dev/null +++ b/docs/nl/developer/entities/index.md @@ -0,0 +1,77 @@ +# Entity Architecture & Hierarchy + +Entities in Minecraft represent all dynamic, living and non-living objects that exist within a world—ranging from dropped item stacks and arrows to zombies, villagers, and players. + +In Pumpkin, entity systems are implemented in [`crates/pumpkin/src/entity/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity). The architecture leverages object-oriented trait composition on top of concurrency-safe, cache-friendly data structures. + +--- + +## Entity Trait Hierarchy + +Pumpkin structures entities into an inheritance hierarchy using Rust traits: + +| Trait / Type | Parent Trait | Core Capabilities & State | +| :--- | :--- | :--- | +| `Entity` | *(None / Base Struct)* | Entity ID, UUID, atomic position and velocity vectors, bounding box, pose | +| `EntityBase` | *(Root Trait)* | Dynamic runtime downcasting (`get_living_entity`, etc.), world tick, damage, teleportation | +| `LivingEntity` | `EntityBase` | Health, absorption, potion effects, equipment slots, attribute containers | +| `Mob` | `LivingEntity` | AI goal selector, navigation, pathfinding, target tracking, death loot | +| `Player` | `LivingEntity` | Network client session, inventory, crafting screens, hunger, advancements | + +### 1. `Entity` (Base Struct) +Contains fundamental physics, geometry, and spatial state common to all entities: +- **`entity_id`**: Fast 32-bit unique integer identifier used across network packets. +- **`uuid`**: 128-bit persistent UUID. +- **`position` & `velocity`**: Thread-safe vectors (`AtomicCell>`) allowing concurrent reading without lock contention. +- **`bounding_box`**: 3D axis-aligned bounding box (AABB) used for collision resolution and raycast targeting. +- **`pose`**: Current pose (`Standing`, `Crouching`, `Sleeping`, `FallFlying`, etc.). + +### 2. `EntityBase` (Foundation Trait) +The root dynamic trait implemented by every entity in the server: +- Supports safe runtime downcasting via helper methods (`get_living_entity()`, `get_mob()`, `get_player()`). +- Provides hooks for world interaction: `tick()`, `damage()`, `teleport()`, `on_lightning_strike()`, `is_immune_to_explosion()`. + +### 3. `LivingEntity` (Biological Trait) +Extends `EntityBase` for entities with health, combat, and animation states: +- Manages health bars, absorption, invulnerability ticks, and fall damage. +- Tracks active potion effects (`crates/pumpkin/src/entity/effect/`). +- Handles equipment slots (`MainHand`, `OffHand`, `Helmet`, `Chestplate`, `Leggings`, `Boots`). +- Encapsulates attribute containers (max health, movement speed, knockback resistance). + +### 4. `Mob` (AI Trait) +Extends `LivingEntity` for computer-controlled mobs: +- Manages AI goal selectors (attacking, wandering, fleeing, looking around). +- Controls navigation and pathfinding over blocks. +- Spawns configured loot tables upon death. + +### 5. `Player` (Human Client) +Represents a human player connected through a game client: +- Links to network socket handlers (`JavaClient` or `BedrockClient`). +- Manages container screen handlers, ender chests, player inventory, and crafting. +- Tracks hunger, saturation, exhaustion, experience levels, and advancement criteria. + +--- + +## Physics & Movement Simulation + +On every server tick, Pumpkin simulates entity physics through a deterministic sequence: + +1. **Apply Velocity & Gravity**: Evaluates current velocity and adds downward gravitational acceleration unless `has_gravity()` is false or the entity is flying. +2. **Resolve Block Collisions**: Checks the entity's 3D axis-aligned bounding box (`bounding_box`) against solid block voxels in the surrounding chunk; velocity along colliding axes is dampened or zeroed. +3. **Apply Fluid Drag & Buoyancy**: Evaluates whether the entity intersects water or lava fluids, applying drag coefficients and buoyant upward force. +4. **Synchronize & Broadcast**: Persists the updated coordinates in atomic position cells and broadcasts relative delta movement packets to observing players. +5. **Void Death**: Non-player entities falling below $Y = -128$ are automatically despawned. + +--- + +## Combat & Damage System + +Damage calculations reside in [`crates/pumpkin/src/entity/combat.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/entity/combat.rs): + +1. **Damage Types**: Damage is categorized by `DamageType` (e.g., `GENERIC`, `PLAYER_ATTACK`, `FALL`, `FIRE`, `MAGIC`, `EXPLOSION`, `LIGHTNING_BOLT`). +2. **Damage Scaling**: + - Armor defense points mitigate incoming physical damage according to the vanilla formula: + $$\text{damage} = \text{damage} \times \left(1 - \frac{\min(20, \max(\text{armor} / 5, \text{armor} - \frac{\text{damage}}{2 + \text{toughness} / 4}))}{25}\right)$$ + - Resistance status effects and Protection enchantments apply secondary reductions. +3. **Knockback**: When damage is dealt, a directional impulse vector is added to the victim's velocity, modulated by their knockback resistance attribute. +4. **Hurt Animations**: Triggers hurt sounds and broadcasts red-tint flash metadata to all observers. diff --git a/docs/nl/developer/entities/spawning-and-tracking.md b/docs/nl/developer/entities/spawning-and-tracking.md new file mode 100644 index 0000000..82a3d7b --- /dev/null +++ b/docs/nl/developer/entities/spawning-and-tracking.md @@ -0,0 +1,95 @@ +# Spawning & Network Tracking + +To maintain high tick rates with hundreds of active entities, Pumpkin utilizes a chunk-based entity tracking and streaming system. + +Entities are held in chunk-level collections inside [`crates/pumpkin-world`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-world) and synchronized across clients via edition-specific network packets. + +--- + +## The Spawning Pipeline + +Spawning an entity involves creating an instance, allocating its network IDs, registering it with the world, and broadcasting spawn packets to chunk observers: + +1. **Construct Entity Instance**: Instantiate the specific entity struct (such as `ZombieEntity`, `ArrowEntity`, or `ItemEntity`) initialized with its starting world and spatial coordinates. +2. **Allocate Unique Network Identifiers**: Assign an atomic 32-bit `entity_id` for network protocol packets and a 128-bit persistent UUID. +3. **Register in World & Chunk Map**: Insert the entity into the world's entity storage and index it within its corresponding chunk coordinate entry. +4. **Query Chunk Observers**: Look up all connected players within tracking range whose view distance includes the containing chunk. +5. **Broadcast Edition-Specific Spawn Packets**: Transmit the relevant spawn and metadata packets tailored to each observing client's edition (Java or Bedrock). + +### Spawning via the World API + +```rust +use std::sync::Arc; +use pumpkin_data::entity::EntityType; +use pumpkin_util::math::vector3::Vector3; +use crate::entity::Entity; +use crate::entity::mob::zombie::ZombieEntity; + +// 1. Create base entity with target world and position +let position = Vector3::new(100.5, 64.0, -200.5); +let base_entity = Entity::new(world.clone(), position, &EntityType::ZOMBIE); + +// 2. Wrap in specific entity implementation +let zombie = Arc::new(ZombieEntity::new(base_entity)); + +// 3. Register into the world +world.spawn_entity(zombie); +``` + +--- + +## Network Packet Synchronization + +When a player enters a chunk containing entities—or when an entity moves into an observed chunk—Pumpkin dispatches spawn packets tailored to the client's edition: + +### Java Edition Spawning +1. **`CSpawnEntity`**: Declares the entity ID, UUID, entity type, position, and initial pitch/yaw. +2. **`CSetEntityMetadata`**: Sends initial visual states (custom name tags, burning flags, baby/adult state). +3. **`CEntityVelocity`**: Synchronizes initial momentum. +4. **`CUpdateEntityRot` & `CHeadRot`**: Synchronizes head orientation independently from body yaw. + +### Bedrock Edition Spawning +1. **`CAddActor`**: Declares the runtime ID, actor type identifier, position, rotation, and embedded `SyncedActorDataList`. +2. **`CSetActorMotion`**: Sets initial velocity vector. + +--- + +## Movement & Position Synchronization + +Transmitting absolute 64-bit coordinates every tick would congest network bandwidth. Instead, Pumpkin dynamically switches between compact relative delta updates and authoritative full synchronization based on movement distance: + +| Metric / Scenario | Threshold | Java Protocol Packet | Bedrock Protocol Packet | Bandwidth Impact | +| :--- | :--- | :--- | :--- | :--- | +| **Incremental Movement** | $\Delta < 8$ blocks per tick | `CUpdateEntityPos` / `CUpdateEntityPosRot` | `CMoveActorDelta` | Minimal (fixed-point byte deltas) | +| **Teleport / Long Distance** | $\Delta \ge 8$ blocks or chunk warp | `CTeleportEntity` / `CEntityPositionSync` | `CMoveActorAbsolute` | Full 64-bit float coordinate payload | +| **Rotation Only** | Position unchanged | `CUpdateEntityRot` & `CHeadRot` | `CMoveActorDelta` (rot flags only) | Minimal (byte angles) | + +### 1. Relative Delta Movement +If the entity moved less than 8 blocks in a single tick, Pumpkin sends delta packets using fixed-point encoded increments: +- **Java**: `CUpdateEntityPos` (XYZ delta), `CUpdateEntityRot` (yaw/pitch delta), or `CUpdateEntityPosRot` (both). +- **Bedrock**: `CMoveActorDelta` with bitflags indicating which coordinates changed (`MOVE_ACTOR_DELTA_FLAG_HAS_X`, `HAS_Y`, `HAS_Z`). + +### 2. Full Position Synchronization +When an entity teleports, crosses chunk boundaries, or accumulates floating-point drift: +- **Java**: Sends `CEntityPositionSync` containing absolute coordinates, velocities, and on-ground flags. +- **Bedrock**: Sends `CMoveActorAbsolute`. + +--- + +## Passenger & Vehicle Mounting + +When an entity mounts another entity (e.g., a player mounting a horse, boat, or minecart): + +- **Java Edition**: Sends `CSetPassengers` containing the vehicle's `VarInt` entity ID and an array of passenger entity IDs. +- **Bedrock Edition**: Sends `CSetActorLink` defining the rider and vehicle runtime IDs and link type (`Rider`, `Passenger`). + +--- + +## Despawning & Entity Removal + +When an entity dies, is removed from the world, or steps out of a player's tracking radius: + +- **Java Edition**: Sends `CRemoveEntities` with an array of entity IDs. +- **Bedrock Edition**: Sends `CRemoveActor` with the entity's runtime ID. + +The client immediately frees its local rendering and audio memory for the removed entity. diff --git a/docs/nl/developer/extractor.md b/docs/nl/developer/extractor.md new file mode 100644 index 0000000..2b09f8f --- /dev/null +++ b/docs/nl/developer/extractor.md @@ -0,0 +1,144 @@ +# Vanilla Data Extractor + +The [Pumpkin Extractor](https://github.com/Pumpkin-MC/Extractor) is a dedicated Fabric mod written in Kotlin that introspects a running vanilla Minecraft server or client to dump authoritative game registries and runtime metadata into structured JSON files. + +These JSON dumps are consumed by [`pumpkin-codegen`](/developer/codegen) to generate strongly-typed Rust code in `pumpkin-data` and `pumpkin-world`. + +--- + +## Datapacks vs. Extractor: Our Data Philosophy + +Before creating a new extractor, it is crucial to understand Pumpkin's data hierarchy: + +> [!IMPORTANT] +> **We preferably use vanilla datapacks whenever possible.** +> If vanilla Minecraft already exposes a registry or system through official datapack JSON files, we consume the datapack directly rather than writing custom extractor code. + +### When to Use Datapacks vs. When to Use the Extractor + +| Source | When to Use | Examples | +| :--- | :--- | :--- | +| **Vanilla Datapacks** *(Preferred)* | Any data officially serialized as JSON by Mojang in `data/minecraft/`. | Recipes, tags (`blocks`, `items`, `fluids`), biomes, dimension types, damage types, trim materials, worldgen noise parameters. | +| **Fabric Extractor** *(Only when necessary)* | Game data **hardcoded in Java bytecode** that cannot be exported from raw datapack files. | Block collision shapes, AABB bounding boxes, block hardness, packet ID-to-state mappings, entity tracked data IDs, fuel burn durations, composter chances, and flower pot conversion tables. | + +--- + +## Extractor Mod Architecture + +The Extractor runs on top of the Fabric loader using official Fabric Yarn mappings: + +- **Mod Entry Point (`src/main/kotlin/de/snowii/extractor/Extractor.kt`)**: Registers with Fabric's `ServerLifecycleEvents.SERVER_STARTED` event. When the headless server finishes booting, it invokes all registered extractors and dumps files to `run/pumpkin_extractor_output/`. +- **Extractor Interface**: Every data extractor implements the `Extractor.Extractor` interface: + ```kotlin + interface Extractor { + fun fileName(): String + + @Throws(Exception::class) + fun extract(server: MinecraftServer): JsonElement + + // Returns true if output belongs in tests/ rather than the root output folder + fun isTest(): Boolean = false + } + ``` +- **Output Directory**: JSON files are saved to `pumpkin_extractor_output/` encoded in UTF-8 without HTML escaping. + +--- + +## How to Add a New Extractor + +Follow this step-by-step workflow when you need to extract new hardcoded vanilla mechanics: + +### Step 1: Verify the Data Cannot Be Sourced from Datapacks +Inspect the official Minecraft client/server JAR or Mojang's data reports: +- Check `data/minecraft/` for existing registries, tags, or JSON definitions. +- If the data is present in the datapack, add it directly to `Pumpkin/assets/datapacks/` instead of writing an extractor. + +### Step 2: Create the Extractor Class +If the data is hardcoded in Java methods, create a new Kotlin class in: +```text +src/main/kotlin/de/snowii/extractor/extractors/.kt +``` + +Implement the `Extractor.Extractor` interface: + +```kotlin +package de.snowii.extractor.extractors + +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import de.snowii.extractor.Extractor +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.server.MinecraftServer +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack + +class Fuels : Extractor.Extractor { + override fun fileName(): String { + return "fuels.json" + } + + override fun extract(server: MinecraftServer): JsonElement { + val fuelsJson = JsonObject() + + // Query vanilla runtime methods + server.fuelValues().fuelItems().forEach { fuel -> + val itemId = BuiltInRegistries.ITEM.getKey(fuel).toString() + val burnDuration = server.fuelValues().burnDuration(ItemStack(fuel)) + fuelsJson.add(itemId, JsonPrimitive(burnDuration)) + } + + return fuelsJson + } +} +``` + +### Step 3: Register the Extractor +Open `src/main/kotlin/de/snowii/extractor/Extractor.kt` and add an instance of your class to the `extractors` array: + +```kotlin +val extractors = arrayOf( + // ... existing extractors ... + Blocks(), + Items(), + Entities(), + Fuels(), // [!code ++] +) +``` + +### Step 4: Run the Extractor +Execute the headless server task in the Extractor repository: + +```bash +./gradlew runServer +``` + +Once the extraction finishes, locate your new JSON file in: +```text +run/pumpkin_extractor_output/.json +``` + +### Step 5: Integrate with `pumpkin-codegen` +1. Move the generated JSON file into Pumpkin: + ```bash + cp run/pumpkin_extractor_output/fuels.json ../Pumpkin/assets/ + ``` +2. In the `Pumpkin` repository, open `tools/pumpkin-codegen`: + - Create a parser for `.json`. + - Write a code generation template to produce the corresponding Rust structs or static lookup tables in `crates/pumpkin-data`. +3. Run code generation: + ```bash + cargo run -p pumpkin-codegen + ``` +4. Verify that `pumpkin-data` compiles and passes all unit tests: + ```bash + cargo test -p pumpkin-data + ``` + +--- + +## Best Practices for Extractor Contributions + +- **Sort Keys Deterministically**: When generating JSON objects or lists, sort entries by numeric registry ID or alphabetically by identifier (`minecraft:stone`, `minecraft:dirt`). This prevents noisy git diffs when re-running extraction across updates. +- **Prefer Standard Types**: Use vanilla identifiers (`net.minecraft.resources.Identifier`) as string keys instead of arbitrary integers where possible. +- **Access Wideners**: If private vanilla fields or methods need to be accessed, declare an access widener in `src/main/resources/extractor.accesswidener` rather than resorting to brittle runtime reflection. diff --git a/docs/nl/developer/items/adding-items.md b/docs/nl/developer/items/adding-items.md new file mode 100644 index 0000000..6335d14 --- /dev/null +++ b/docs/nl/developer/items/adding-items.md @@ -0,0 +1,121 @@ +# Adding an Item Behavior + +This guide details how to implement custom interactive mechanics for an item in Pumpkin. + +--- + +## When Is an Item Behavior Needed? + +- **Passive Items** (e.g., diamonds, gold ingots, sticks): Do **not** require custom Rust code. Their stack sizes, tool categories, and crafting recipes are handled automatically by `pumpkin-data` and `pumpkin-inventory`. +- **Interactive Items** (e.g., snowballs, ender pearls, hoes, bows, potions): Require an implementation of `ItemBehaviour` to handle right-click events, cooldowns, animations, and entity spawns. + +--- + +## Step 1: Define the Item Struct & Metadata + +Create a new file under `crates/pumpkin/src/item/items/.rs` (e.g., `items/snowball.rs`): + +```rust +use std::sync::Arc; +use pumpkin_data::entity::EntityType; +use pumpkin_data::item::Item; +use pumpkin_data::sound::{Sound, SoundCategory}; +use crate::entity::Entity; +use crate::entity::player::Player; +use crate::entity::projectile::snowball::SnowballEntity; +use crate::item::{ItemBehaviour, ItemMetadata}; + +pub struct SnowBallItem; + +// 1. Declare the associated numeric Item ID(s) +impl ItemMetadata for SnowBallItem { + fn ids() -> Box<[u16]> { + [Item::SNOWBALL.id].into() + } +} + +// 2. Implement the interaction behavior +impl ItemBehaviour for SnowBallItem { + fn normal_use(&self, _item: &Item, player: &Player) { + let position = player.position(); + let world = player.world(); + + // Play throw sound effect + world.play_sound( + Sound::EntitySnowballThrow, + SoundCategory::Neutral, + &position, + ); + + // Construct and launch the projectile entity + let entity = Entity::new(world.clone(), position, &EntityType::SNOWBALL); + let snowball = SnowballEntity::new_shot(entity, player.get_entity()); + let (yaw, pitch) = player.rotation(); + snowball.thrown.set_velocity_from(pitch, yaw, 0.0, 1.5, 1.0); + world.spawn_entity(Arc::new(snowball)); + + // Safely decrement held item unless in Creative mode + let mut held = player.inventory.held_item(); + held.decrement_unless_creative(player.gamemode.load(), 1); + player.inventory.set_held_item(held); + } +} +``` + +--- + +## Step 2: `ItemBehaviour` Hook Reference + +The `ItemBehaviour` trait provides comprehensive hooks for different interaction models: + +### Instant Single-Use Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `normal_use` | `(&self, item: &Item, player: &Player)` | Triggered on right-clicking into open air. | +| `normal_use_with_rotation` | `(&self, item: &Item, player: &Player, yaw: f32, pitch: f32)` | Provides client-reported view angles for precise raycasting and aiming. | +| `use_on_block` | `(&self, item: &mut ItemStack, player: &Player, location: BlockPos, face: BlockDirection, cursor_pos: Vector3, block: &Block, server: &Server) -> BlockActionResult` | Triggered when right-clicking a block face (e.g., stripping logs with an axe, tilling farmland with a hoe). | +| `use_on_entity` | `(&self, item: &mut ItemStack, player: &Player, entity: Arc)` | Triggered when right-clicking another living entity (e.g., shearing sheep, nametags). | + +### Channeled & Timed Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `get_use_duration` | `(&self) -> i32` | Duration in game ticks required to complete the action (e.g., `32` ticks for food, `72000` ticks for bows). | +| `on_use_tick` | `(&self, stack: &ItemStack, player: &Player, remaining_use_ticks: i32)` | Executed on each tick while right-click is held. | +| `on_stopped_using` | `(&self, stack: &ItemStack, player: &Player)` | Triggered when the player releases right-click (e.g., firing a charged bow). | + +--- + +## Step 3: Durability & Cooldowns + +### Decrementing Stacks Safely +Always use `decrement_unless_creative` to ensure survival players consume items while creative players retain them: + +```rust +item_stack.decrement_unless_creative(player.gamemode.load(), 1); +``` + +### Applying Cooldowns +To prevent action spamming (e.g., Ender Pearls, Chorus Fruit): + +```rust +// Start a 20-tick (1-second) cooldown on Ender Pearls +player.start_cooldown(Item::ENDER_PEARL.registry_key.to_string(), 20); +``` + +--- + +## Step 4: Registering the Item + +1. Export your module in `crates/pumpkin/src/item/items/mod.rs`: + ```rust + pub mod snowball; + ``` + +2. Register the struct in `crates/pumpkin/src/item/registry.rs`: + ```rust + items.register(SnowBallItem); + ``` + +When the server initializes, `items.register` maps the IDs declared in `ItemMetadata::ids()` to your behavior instance. diff --git a/docs/nl/developer/items/index.md b/docs/nl/developer/items/index.md new file mode 100644 index 0000000..70f9f9b --- /dev/null +++ b/docs/nl/developer/items/index.md @@ -0,0 +1,71 @@ +# Item Architecture & Data Components + +Items represent portable game objects held in player inventories, hotbars, armor slots, and world containers. In Pumpkin, item handling is divided into two primary subsystems: + +1. **Static Data Layer ([`pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data))**: Compile-time generated metadata including item IDs, max stack sizes, max durability, tool tier properties, food nutrition, and data components generated by [`pumpkin-codegen`](/developer/codegen). +2. **Dynamic Behavior Layer ([`pumpkin::item`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/item))**: Interactive right-click actions, projectile launching, eating durations, bow charging, and block interactions handled via the `ItemBehaviour` trait. + +--- + +## Static Items vs. Actionable Items + +| Item Type | Description | Examples | Runtime Behavior Struct Needed? | +| :--- | :--- | :--- | :--- | +| **Passive Items** | Items without active right-click effects | `diamond`, `stick`, `iron_ingot`, `feather` | No — Stacking, moving, crafting, and dropping are handled automatically by `pumpkin-inventory`. | +| **Standard Block Items** | Items corresponding directly to placeable blocks | `stone`, `oak_planks`, `glass` | No — Placement logic queries the target block's data. | +| **Projectiles & Throwables** | Items spawned into physical entities when thrown | `snowball`, `egg`, `ender_pearl`, `splash_potion` | Yes — Implements `ItemBehaviour::normal_use`. | +| **Channeled / Charged Items** | Items with use durations or charging animations | `bow`, `crossbow`, `spyglass`, `trident` | Yes — Implements `get_use_duration`, `on_use_tick`, `on_stopped_using`. | +| **Tools with Block Interactions**| Items modifying existing blocks in the world | `hoe` (tilling), `axe` (stripping), `shovel` (pathmaking) | Yes — Implements `ItemBehaviour::use_on_block`. | + +--- + +## The `ItemStack` Structure + +An `ItemStack` represents an instance of an item in an inventory container. It encapsulates: + +- **`item`**: Reference to the static [`Item`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-data/src/item.rs) definition. +- **`item_count`**: Current stack size ($1 \le \text{count} \le \text{max\_stack\_size}$). +- **Data Components**: Dynamic properties attached to the item stack (custom names, lore, damage values, enchantments, potion effects). + +```rust +use pumpkin_data::item::Item; +use pumpkin_data::item_stack::ItemStack; + +// Create a new item stack +let mut stack = ItemStack::new(1, Item::DIAMOND_SWORD); + +// Safely decrement stack count, respecting creative mode +stack.decrement_unless_creative(player.gamemode.load(), 1); +``` + +--- + +## Data Components System + +Modern Minecraft represents item properties via modular **Data Components**: + +| Component | Responsibility | Description | +| :--- | :--- | :--- | +| `FoodImpl` | Nutrition & Saturation | Controls hunger points restored and saturation ratio. | +| `ConsumableImpl` | Consumption Mechanics | Controls eating duration, sound effects, and animation types. | +| `Damageable` | Durability | Tracks current damage points and breaks when max durability is reached. | +| `UseCooldown` | Cooldown Group | Prevents item re-use for a specified duration in game ticks. | +| `Enchantments` | Enchantment Levels | Key-value mapping of applied enchantments and levels. | + +--- + +## The Item Registry & Dispatch Architecture + +Item interactions are managed by the `ItemRegistry` in [`crates/pumpkin/src/item/registry.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/item/registry.rs): + +1. **Registration**: At server startup, all `ItemBehaviour` implementations are registered along with their target numeric item IDs. +2. **Interaction Routing**: When a client sends use packets (`CUseItem`, `CUseItemOn`), Pumpkin evaluates cooldowns and routes execution to the registered `ItemBehaviour`. +3. **Cooldown System**: Players track active cooldowns keyed by cooldown group strings. If an item is on cooldown, interactions are canceled before calling the behavior. + +--- + +## Next Steps + +To implement interactive mechanics for a new item, proceed to the practical guide: + +- **[Adding an Item](/developer/items/adding-items)**: Step-by-step instructions for implementing `ItemMetadata`, `ItemBehaviour`, managing cooldowns, and registering items. diff --git a/docs/nl/developer/networking/bedrock/adding-packets.md b/docs/nl/developer/networking/bedrock/adding-packets.md new file mode 100644 index 0000000..f68b888 --- /dev/null +++ b/docs/nl/developer/networking/bedrock/adding-packets.md @@ -0,0 +1,182 @@ +# Adding a Bedrock Edition Packet + +This guide explains how to implement, encode, decode, and handle a new Minecraft: Bedrock Edition packet in Pumpkin. + +--- + +## Naming Conventions + +- **Clientbound packets** (Server → Client) must start with **`C`** (e.g., `CChunkRadiusUpdated`, `CContainerOpen`). +- **Serverbound packets** (Client → Server) must start with **`S`** (e.g., `SRequestChunkRadius`, `SPlayerAction`). + +--- + +## Step 1: Identifying the Packet ID + +In the Bedrock protocol, packet IDs are fixed integer constants assigned by Mojang. Verify the numeric ID from the official protocol documentation or network packet traces. + +For example: +- `Disconnect` packet ID: `5` +- `RequestChunkRadius` packet ID: `69` + +--- + +## Step 2: Implementing a Clientbound Packet + +Clientbound packets implement the `PacketWrite` trait from `pumpkin-protocol::serial`. + +### 1. Create the Packet File +Place the file in `crates/pumpkin-protocol/src/bedrock/client/.rs`: + +```rust +use std::io::{Error, Write}; +use pumpkin_macros::packet; +use crate::{codec::var_int::VarInt, serial::PacketWrite}; + +#[packet(5)] +pub struct CDisconnect { + pub reason: VarInt, + pub skip_message: bool, + pub message: String, + pub filtered_message: String, +} + +impl CDisconnect { + #[must_use] + pub const fn new(reason: i32, message: String) -> Self { + Self { + reason: VarInt(reason), + skip_message: message.is_empty(), + message, + filtered_message: String::new(), + } + } +} + +impl PacketWrite for CDisconnect { + fn write(&self, writer: &mut W) -> Result<(), Error> { + self.reason.write(writer)?; + self.skip_message.write(writer)?; + if !self.skip_message { + self.message.write(writer)?; + self.filtered_message.write(writer)?; + } + Ok(()) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/bedrock/client/mod.rs`: + +```rust +pub mod disconnect; +pub use disconnect::*; +``` + +--- + +## Step 3: Implementing a Serverbound Packet + +Serverbound packets implement the `PacketRead` trait. Pumpkin provides a procedural derive macro `#[derive(PacketRead)]` for automatic deserialization when fields implement `PacketRead`. + +### 1. Create the Packet File +Place the file in `crates/pumpkin-protocol/src/bedrock/server/.rs`: + +```rust +use pumpkin_macros::packet; +use crate::{codec::var_int::VarInt, serial::PacketRead}; + +#[derive(PacketRead, Debug)] +#[packet(69)] +pub struct SRequestChunkRadius { + pub chunk_radius: VarInt, + pub max_chunk_radius: u8, +} +``` + +If a packet requires custom parsing logic, implement `PacketRead` manually: + +```rust +use std::io::{Error, Read}; +use crate::serial::PacketRead; + +impl PacketRead for SMyCustomPacket { + fn read(reader: &mut R) -> Result { + let chunk_radius = VarInt::read(reader)?; + let max_chunk_radius = u8::read(reader)?; + Ok(Self { chunk_radius, max_chunk_radius }) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/bedrock/server/mod.rs`: + +```rust +pub mod request_chunk_radius; +pub use request_chunk_radius::*; +``` + +--- + +## Step 4: Dispatching Incoming Packets in `pumpkin` + +Incoming packets are dispatched in `crates/pumpkin/src/net/bedrock/mod.rs`: + +```rust +// Inside BedrockClient::handle_game_packet +match packet.id { + SRequestChunkRadius::PACKET_ID => { + let packet = SRequestChunkRadius::read(reader)?; + self.handle_request_chunk_radius(player, &packet); + } + _ => {} +} +``` + +--- + +## Step 5: Implementing the Handler Function + +Implement the handler logic in `crates/pumpkin/src/net/bedrock/play/.rs`: + +```rust +use super::*; +use pumpkin_protocol::bedrock::server::request_chunk_radius::SRequestChunkRadius; + +impl BedrockClient { + pub fn handle_request_chunk_radius(&self, player: &Arc, packet: &SRequestChunkRadius) { + let requested_radius = packet.chunk_radius.0; + + // Clamp view distance within server configuration limits + let clamped = requested_radius.clamp(2, 16); + + // Notify client that view radius was accepted + self.try_enqueue_client_packet(&CChunkRadiusUpdated { + chunk_radius: VarInt(clamped), + }); + } +} +``` + +Remember to register the new module in `crates/pumpkin/src/net/bedrock/play/mod.rs`: + +```rust +pub mod request_chunk_radius; +``` + +--- + +## Step 6: Sending Outgoing Packets + +To send a Bedrock packet to a connected client: + +```rust +// Enqueues the packet into the client's outgoing batch queue +self.try_enqueue_client_packet(&CChunkRadiusUpdated { + chunk_radius: VarInt(view_distance), +}); +``` + +Batched packets are automatically compressed, framed with `0xfe`, and transmitted over the RakNet/NetherNet socket. diff --git a/docs/nl/developer/networking/bedrock/authentication.md b/docs/nl/developer/networking/bedrock/authentication.md new file mode 100644 index 0000000..1571a73 --- /dev/null +++ b/docs/nl/developer/networking/bedrock/authentication.md @@ -0,0 +1,61 @@ +# Bedrock Edition Authentication + +Minecraft: Bedrock Edition implements a certificate chain authentication protocol based on JSON Web Tokens (JWT) cryptographically anchored to Microsoft / Xbox Live root public keys. + +Authentication handling is powered by the [`pumpkin-auth`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-auth) crate and processed during the Bedrock login handshake in `crates/pumpkin/src/net/bedrock/login/`. + +--- + +## The Handshake Flow + +When a Bedrock client initiates connection, it sends an `SLogin` packet containing the client's protocol version, an **Identity Chain**, and **Client Data**: + +1. **Token Acquisition**: The Bedrock client signs into Xbox Live, obtaining a chained JSON Web Token (JWT) certificate signed by Microsoft's CA. +2. **Login Transmission**: The client packages this identity chain alongside a separate `ClientData` JWT into `SLogin`. +3. **Chain Validation**: Pumpkin walks the certificate chain, verifying each token's signature against Mojang's public root keys and extracting the player's Xbox User ID (XUID), Gamertag, and ephemeral session public key. +4. **Hardware Verification**: Pumpkin verifies the `ClientData` payload using the client's ephemeral public key, extracting geometry, skins, and device OS telemetry. +5. **Session Authorization**: Once validated, Pumpkin emits `CPlayStatus` (Login Success) and progresses to resource pack negotiation. + +--- + +## Token Payloads + +### 1. Identity Chain (`Chain`) +An array of JWT tokens forming an unbroken chain of trust: +- **Root Token**: Cryptographically signed by Mojang's official public root certificate authority. +- **Intermediate Tokens**: Issued by Xbox Live services, verifying the player's Xbox Live User ID (**XUID**), gamertag, and generating an ephemeral ECDSA public key for the session. +- **Claims**: Includes `extraData` with `displayName`, `identity` (UUID), `XUID`, and `titleId`. + +### 2. Client Data (`ClientData`) +A separate JWT signed with the client's ephemeral private key corresponding to the public key in the identity chain: +- **Hardware & Environment**: `DeviceOS`, `DeviceModel`, `CurrentInputMode`, `DefaultInputMode`, `ClientRandomId`. +- **Game Version**: `GameVersion` string. +- **Player Cosmetics**: Full skin texture bitmaps, cape data, geometry names, and persona definitions. + +--- + +## Verification in `pumpkin-auth` + +Pumpkin verifies token signatures using **NIST P-384 ECDSA** cryptography: + +```rust +// In crates/pumpkin/src/net/bedrock/login/mod.rs +match AuthPayload::verify(&jwt_payload) { + Ok(profile) => { + // Successfully verified against Mojang public keys + info!("Authenticated Bedrock player: {} (XUID: {})", profile.name, profile.xuid); + } + Err(LoginError::ChainValidationFailed(err)) => { + warn!("Bedrock JWT chain validation failed: {err}"); + self.try_kick(DisconnectReason::Kicked, "Authentication failed".into()); + } + Err(LoginError::SelfSignedNotAllowed) => { + warn!("Rejected unauthenticated self-signed Bedrock client"); + self.try_kick(DisconnectReason::Kicked, "Server enforces Xbox Live authentication".into()); + } +} +``` + +### Online vs. Offline Mode +- **Online Mode** (`online_mode = true` in `pumpkin.toml`): The chain must successfully validate against Mojang's public keys. Self-signed or unsigned tokens are immediately rejected. +- **Offline Mode** (`online_mode = false`): Pumpkin accepts self-signed client chains, generating offline UUIDs while extracting cosmetic skins and device metadata. diff --git a/docs/nl/developer/networking/bedrock/nethernet.md b/docs/nl/developer/networking/bedrock/nethernet.md new file mode 100644 index 0000000..88c92f0 --- /dev/null +++ b/docs/nl/developer/networking/bedrock/nethernet.md @@ -0,0 +1,61 @@ +# NetherNet WebRTC Transport + +**NetherNet** is Mojang's modern, WebRTC-based transport protocol for Minecraft: Bedrock Edition. Designed for high-resilience peer-to-peer and relayed communication, NetherNet enables seamless cross-platform multiplayer (mobile, console, desktop) without requiring server operators or players to configure router port-forwarding. + +Pumpkin provides a native, zero-dependency NetherNet implementation in [`crates/pumpkin/src/net/bedrock/nethernet.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/bedrock/nethernet.rs). + +--- + +## Architectural Layers + +| Layer | Implementation | Responsibility | +| :--- | :--- | :--- | +| **HTTP Signaling** | Axum HTTP Router (`ice_router.rs`) | Handles SDP Offer (`POST /offer`), SDP Answer (`GET /answer`), and ICE Candidates (`POST /candidate`) | +| **NAT Traversal** | ICE Agent | Coordinates STUN/TURN binding discovery and hole punching | +| **Transport Security** | DTLS Handshake | Generates ephemeral session keys and secures the WebRTC transport using ECDSA P-384 keys | +| **Data Delivery** | WebRTC DataChannels | Unordered/ordered datagram channels streaming standard Bedrock game batches to the dispatcher | + +--- + +## Core Components in Pumpkin + +Pumpkin's NetherNet subsystem is divided into four distinct modules: + +### 1. Cryptographic Identity (`nethernet-key.der`) +- Pumpkin generates and loads a **NIST P-384 ECDSA private key** (`nethernet-key.der`). +- Used to cryptographically sign session descriptions (SDP offers and answers), authenticating the server's identity to connecting clients. +- Encoded using standard PKCS#8 DER formatting. + +### 2. HTTP Signaling Router (`ice_router.rs`) +Signaling is handled over HTTP via an [Axum](https://github.com/tokio-rs/axum) router: + +- **`POST /offer`**: The client submits an SDP offer describing its WebRTC codecs, transport profiles, and network interfaces. +- **`GET /answer`**: Pumpkin generates a corresponding local SDP answer containing its negotiated capabilities. +- **`POST /candidate`**: Both endpoints exchange trickle ICE candidates to discover the fastest direct network path (host, server-reflexive, or relay). + +### 3. Local Network Discovery (`discovery.rs`) +- Listens and broadcasts availability on the local subnet via UDP multicast. +- Allows Bedrock clients on the same Wi-Fi or LAN to discover and connect to the Pumpkin server automatically without entering an IP address. + +### 4. DataChannel Packet Streaming +- Once the WebRTC peer connection transitions to the `Connected` state, an ordered, reliable **WebRTC DataChannel** is opened. +- Bedrock batch packets (`0xfe`) flow bidirectionally through the DataChannel directly into Pumpkin's `incoming_game_packet_send` channel. + +--- + +## Configuration in `pumpkin.toml` + +NetherNet is configured under `[networking.bedrock.nethernet]` in `pumpkin.toml`: + +```toml +[networking.bedrock] +enabled = true +address = "0.0.0.0:19132" + +[networking.bedrock.nethernet] +enabled = true +signaling_address = "0.0.0.0:19133" +ice_servers = [ + "stun:stun.l.google.com:19302" +] +``` diff --git a/docs/nl/developer/networking/bedrock/overview.md b/docs/nl/developer/networking/bedrock/overview.md new file mode 100644 index 0000000..ae0d615 --- /dev/null +++ b/docs/nl/developer/networking/bedrock/overview.md @@ -0,0 +1,80 @@ +# Bedrock Edition Networking Overview + +Pumpkin features native support for **Minecraft: Bedrock Edition** clients (iOS, Android, Windows, Xbox, PlayStation, Switch). + +Bedrock protocol structures and wire codecs reside in `pumpkin-protocol::bedrock`, while connection management and gameplay handlers reside in `pumpkin::net::bedrock`. + +--- + +## Transport Layers: RakNet & NetherNet + +Unlike Java Edition, which communicates exclusively over TCP streams, Bedrock Edition operates over UDP using two primary transport layers: + +1. **RakNet (Port 19132)**: + - A reliable, sequenced UDP networking library originally developed by Jenkins Software. + - Provides connection handshakes, packet acknowledgment (ACK/NACK), MTU negotiation, and packet splitting for payloads larger than the network MTU. +2. **NetherNet**: + - Modern peer-to-peer and relayed transport protocol used by modern Bedrock versions. + - Integrated in Pumpkin via `crates/pumpkin/src/net/bedrock/nethernet.rs`. + +--- + +## Packet Framing & Batching (`0xfe`) + +Bedrock does not send individual game packets over the network directly. Instead, multiple game packets are bundled into a single **batch packet**: + +1. **Header Byte**: Every Bedrock game packet batch starts with `0xfe`. +2. **Compression**: The payload following `0xfe` is compressed using **Deflate** (or ZLib) above a negotiated threshold. +3. **Sub-packet Framing**: When decompressed, the batch contains one or more sequential game packets, each prefixed by its length encoded as a `VarUInt`. + +In Pumpkin, batch decoding and assembly are managed by `BedrockBatchDecoder` and `BedrockBatchEncoder` in `crates/pumpkin-protocol/src/bedrock/`. + +--- + +## Connection Lifecycle & State Progression + +Bedrock does not have explicit protocol states like Java Edition. Instead, it follows a strict sequence of packets: + +| Step | Packet Exchanged | Purpose | +| :--- | :--- | :--- | +| **1. Network Settings** | `SRequestNetworkSettings` -> `CNetworkSettings` | Negotiates compression algorithm (Deflate/Snappy) and payload threshold | +| **2. Authentication** | `SLogin` -> `CPlayStatus` | Transmits JWT identity chain and client hardware data; verifies Xbox Live tokens | +| **3. Resource Negotiation** | `CResourcePacksInfo` -> `SResourcePackClientResponse` | Negotiates server resource packs and download behavior | +| **4. World Initialization** | `CStartGame` + `CItemRegistry` + `CCreativeContent` | Streams world spawn position, dimension settings, runtime player entity ID, and item palettes | +| **5. Ready to Spawn** | `SSetLocalPlayerAsInitialized` -> `CPlayStatus` | Signals client loading completion and transitions to active in-game play | + +--- + +## Packet Dispatching in `pumpkin` + +Inside `crates/pumpkin/src/net/bedrock/mod.rs`, decompressed sub-packets are dispatched based on their numeric packet ID: + +```rust +match packet.id { + SPlayerAuthInput::PACKET_ID => { + let packet = SPlayerAuthInput::read(reader)?; + self.handle_player_auth_input(player, packet, server); + } + SRequestChunkRadius::PACKET_ID => { + let packet = SRequestChunkRadius::read(reader)?; + self.handle_request_chunk_radius(player, &packet); + } + SInventoryTransaction::PACKET_ID => { + let packet = SInventoryTransaction::read(reader)?; + self.handle_inventory_action(player, packet); + } + _ => { + trace!("Unhandled Bedrock packet ID: {}", packet.id); + } +} +``` + +--- + +## Authentication & NetherNet + +- **JWT Authentication**: Bedrock verifies player identities via certificate chains signed by Microsoft / Xbox Live root authorities. +- **NetherNet WebRTC**: High-performance peer-to-peer transport using WebRTC DataChannels, ICE NAT traversal, and P-384 ECDSA keys. + +For complete implementation details, see **[Bedrock Authentication & NetherNet](/developer/networking/bedrock/authentication)**. + diff --git a/docs/nl/developer/networking/index.md b/docs/nl/developer/networking/index.md new file mode 100644 index 0000000..0685c45 --- /dev/null +++ b/docs/nl/developer/networking/index.md @@ -0,0 +1,49 @@ +# Networking Overview + +Pumpkin is built with a high-performance, asynchronous networking stack capable of servicing both **Minecraft: Java Edition** and **Minecraft: Bedrock Edition** clients simultaneously. + +All protocol definitions, serialization traits, and wire-level codecs are isolated in the [`pumpkin-protocol`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-protocol) crate, while client connection management, session state machines, and gameplay packet dispatching are managed in [`pumpkin/src/net`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/net). + +--- + +## Supported Editions + +Pumpkin separates network implementations by edition due to their fundamentally distinct transport layers, packet encodings, and state models: + +| Feature | Minecraft: Java Edition | Minecraft: Bedrock Edition | +| :--- | :--- | :--- | +| **Transport Layer** | TCP socket streams | UDP via RakNet & NetherNet | +| **Framing** | VarInt length-prefixed frames | Batched packets encapsulated in `0xfe` headers | +| **Encryption** | AES-128 / CFB8 stream cipher | Cryptographic session handshake | +| **Compression** | Per-packet ZLib (above configurable threshold) | Per-batch Deflate / ZLib compression | +| **State Model** | Explicit states: Handshake, Status, Login, Config, Play | Packet-driven state progression | +| **Protocol Crate** | `pumpkin-protocol::java` | `pumpkin-protocol::bedrock` | +| **Server Net Module** | `pumpkin::net::java` | `pumpkin::net::bedrock` | + +--- + +## Networking Architecture + +The networking pipeline routes incoming connections through specialized transport and codec layers before delegating to the unified server game loop: + +1. **Transport Listeners**: Independent listeners bind to their respective ports—Tokio TCP for Java Edition (`:25565`) and UDP/RakNet/NetherNet for Bedrock Edition (`:19132`). +2. **Session Abstraction**: Each accepted connection spawns an isolated session task (`JavaClient` or `BedrockClient`) managing wire framing and encryption. +3. **Codec Deserialization**: Wire bytes are deserialized into typed packet structs via `pumpkin-protocol::java` (`ClientPacket`/`ServerPacket`) and `pumpkin-protocol::bedrock` (`PacketWrite`/`PacketRead`). +4. **Unified Gameplay Dispatch**: Decoded packets are dispatched into the unified game loop, updating player entity states and streaming world chunks. + +--- + +## Navigating the Networking Docs + +Explore the documentation for each edition: + +- **[Entity Metadata & Synced Data](/developer/networking/metadata)**: Synchronization of visual entity states (DataWatchers in Java, Actor Data in Bedrock). +- **[Java Edition](/developer/networking/java/overview)**: + - **[Architecture & Lifecycle](/developer/networking/java/overview)**: State machine, `JavaClient` vs. `Player`, and multi-version negotiation. + - **[How to Add a Packet](/developer/networking/java/adding-packets)**: Guide to defining, serializing, and dispatching Java Edition packets. + - **[Authentication](/developer/networking/java/authentication)**: Mojang Yggdrasil validation, GameProfiles, and proxy forwarding. +- **[Bedrock Edition](/developer/networking/bedrock/overview)**: + - **[Architecture & Transports](/developer/networking/bedrock/overview)**: RakNet and NetherNet integration, batching, and session handling. + - **[How to Add a Packet](/developer/networking/bedrock/adding-packets)**: Guide to defining and handling Bedrock Edition packets. + - **[Authentication & NetherNet](/developer/networking/bedrock/authentication)**: JWT certificate chain verification, WebRTC DataChannels, P-384 keys, and signaling. +- **[RCON](/developer/networking/rcon)**: Implementation of Valve's Remote Console protocol for administrative management. diff --git a/docs/nl/developer/networking/java/adding-packets.md b/docs/nl/developer/networking/java/adding-packets.md new file mode 100644 index 0000000..1800d06 --- /dev/null +++ b/docs/nl/developer/networking/java/adding-packets.md @@ -0,0 +1,205 @@ +# Adding a Java Edition Packet + +This guide walks you through implementing, serializing, dispatching, and sending a new Java Edition packet in Pumpkin. + +--- + +## Naming Conventions + +To keep packet definitions uniform across the codebase, follow these naming conventions: + +- **Clientbound packets** (Server → Client) must start with **`C`** (e.g., `CBlockUpdate`, `CPlayDisconnect`). +- **Serverbound packets** (Client → Server) must start with **`S`** (e.g., `SChatCommand`, `SPlayerPosition`). +- If a packet exists in multiple states, prefix the state name before the packet action (e.g., `CLoginDisconnect`, `CConfigDisconnect`, and `CPlayDisconnect`). + +--- + +## Step 1: Find the Packet ID in `pumpkin-data` + +Packet IDs across all supported Minecraft versions are maintained in `pumpkin-data`. Import the corresponding constant: + +```rust +// Clientbound Play state disconnect +use pumpkin_data::packet::clientbound::play::DISCONNECT; + +// Serverbound Play state player position +use pumpkin_data::packet::serverbound::play::MOVE_PLAYER_POS; +``` + +--- + +## Step 2: Implementing a Clientbound Packet + +Clientbound packets are sent from Pumpkin to the Minecraft client. They implement the `ClientPacket` trait from `pumpkin-protocol`. + +### 1. Create the Struct File +Place the file in `crates/pumpkin-protocol/src/java/client//.rs` (e.g., `play/disconnect.rs`): + +```rust +use std::io::Write; +use pumpkin_data::packet::clientbound::play::DISCONNECT; +use pumpkin_macros::java_packet; +use pumpkin_protocol::ClientPacket; +use pumpkin_protocol::ser::{NetworkWriteExt, WritingError}; +use pumpkin_util::text::TextComponent; +use pumpkin_util::version::JavaMinecraftVersion; + +/// Displays a disconnect screen with a message to the player in Play state. +#[java_packet(DISCONNECT)] +pub struct CPlayDisconnect<'a> { + pub reason: &'a TextComponent, +} + +impl<'a> CPlayDisconnect<'a> { + #[must_use] + pub const fn new(reason: &'a TextComponent) -> Self { + Self { reason } + } +} + +impl ClientPacket for CPlayDisconnect<'_> { + fn write_packet_data( + &self, + mut write: impl Write, + version: &JavaMinecraftVersion, + ) -> Result<(), WritingError> { + // Use NetworkWriteExt methods to serialize fields + write.write_component(self.reason, version) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/java/client//mod.rs`: + +```rust +mod disconnect; +pub use disconnect::*; +``` + +--- + +## Step 3: Implementing a Serverbound Packet + +Serverbound packets are received by Pumpkin from the game client. They implement the `ServerPacket` trait from `pumpkin-protocol`. + +### 1. Create the Struct File +Place the file in `crates/pumpkin-protocol/src/java/server//.rs` (e.g., `play/player_position.rs`): + +```rust +use pumpkin_data::packet::serverbound::play::MOVE_PLAYER_POS; +use pumpkin_macros::java_packet; +use pumpkin_protocol::ServerPacket; +use pumpkin_protocol::ser::{NetworkReadExt, ReadingError}; +use pumpkin_util::math::vector3::Vector3; +use pumpkin_util::version::JavaMinecraftVersion; + +#[java_packet(MOVE_PLAYER_POS)] +pub struct SPlayerPosition { + pub position: Vector3, + pub collision: u8, +} + +impl<'a> ServerPacket<'a> for SPlayerPosition { + fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result { + let x = bytebuf.get_f64_be()?; + let y = bytebuf.get_f64_be()?; + + // Handle version-specific fields if necessary + if *version <= JavaMinecraftVersion::V_1_7_6 { + let _stance = bytebuf.get_f64_be()?; + } + + let z = bytebuf.get_f64_be()?; + let collision = bytebuf.get_u8()?; + + Ok(Self { + position: Vector3::new(x, y, z), + collision, + }) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/java/server//mod.rs`: + +```rust +mod player_position; +pub use player_position::*; +``` + +--- + +## Step 4: Network Codec Extension Helpers + +`NetworkWriteExt` and `NetworkReadExt` provide helper methods for standard Minecraft types: + +| Data Type | Reading Method | Writing Method | +| :--- | :--- | :--- | +| **`VarInt`** | `bytebuf.get_var_int()?` | `write.write_var_int(val)?` | +| **`VarLong`** | `bytebuf.get_var_long()?` | `write.write_var_long(val)?` | +| **`String`** | `bytebuf.get_string()?` | `write.write_string(val)?` | +| **`UUID`** | `bytebuf.get_uuid()?` | `write.write_uuid(&val)?` | +| **`TextComponent`** | `bytebuf.get_component()?` | `write.write_component(val, version)?` | +| **`Position`** | `bytebuf.get_block_pos()?` | `write.write_block_pos(val)?` | +| **`NBT`** | `bytebuf.get_nbt()?` | `write.write_nbt(&val)?` | + +--- + +## Step 5: Handling Incoming Packets in `pumpkin` + +Incoming serverbound packets must be handled in the appropriate state handler in `crates/pumpkin/src/net/java/`. + +For packets in the **Play state**, open `crates/pumpkin/src/net/java/mod.rs` and update `handle_play_packet`: + +```rust +// In crates/pumpkin/src/net/java/mod.rs +pub fn handle_play_packet( + &self, + player: &Arc, + server: &Arc, + packet: &RawPacket, +) -> Result<(), Box> { + let version = self.version.load(); + let mut payload = &packet.payload[..]; + + match packet.id { + // Match against version-specific packet ID + id if id == SPlayerPosition::to_id(version) => { + let packet = SPlayerPosition::read(&mut payload, &version)?; + self.handle_player_position(player, &packet); + } + _ => {} + } + Ok(()) +} +``` + +Then implement the handler function: + +```rust +impl JavaClient { + pub fn handle_player_position(&self, player: &Arc, packet: &SPlayerPosition) { + // Validate coordinates, update player position, check collisions + player.set_position(packet.position); + } +} +``` + +--- + +## Step 6: Sending Outgoing Packets + +To send a clientbound packet to a player: + +```rust +// 1. Send directly to a player's connection +player.client.send_packet(&CPlayDisconnect::new(&reason)); + +// 2. Broadcast to all players watching a chunk +world.broadcast_packet_except( + &[player.gameprofile.id], + &CBlockUpdate::new(block_pos, block_id), +).await; +``` diff --git a/docs/nl/developer/networking/java/authentication.md b/docs/nl/developer/networking/java/authentication.md new file mode 100644 index 0000000..861da51 --- /dev/null +++ b/docs/nl/developer/networking/java/authentication.md @@ -0,0 +1,83 @@ +# Java Edition Authentication + +Authentication verifies the identity of players connecting to Pumpkin to prevent username spoofing and account impersonation. + +Pumpkin handles authentication in the [`pumpkin-auth`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-auth) crate and orchestrates it during the `Login` state in `crates/pumpkin/src/net/java/login/`. + +--- + +## Why Authentication Matters + +Offline (non-authenticated) accounts generate player UUIDs strictly based on their offline username string (`UUID::new_v3(...)`). Without verification, any user can join using the username of an operator or administrator and gain privileged access. + +In online mode (`online_mode = true` in `pumpkin.toml`), Pumpkin cryptographically verifies with Mojang's session servers that the connecting client owns the requested account. + +--- + +## How Yggdrasil Authentication Works + +The authentication exchange between the client, Pumpkin, and Mojang's Yggdrasil servers follows these steps: + +1. **Initiation**: The client sends `SLoginStart` declaring its desired username and optional offline UUID. +2. **Key Exchange**: Pumpkin replies with `CEncryptionBegin`, providing a random 4-byte verify token, the server ID string, and Pumpkin's 1024-bit RSA public key. +3. **Session Join**: The client generates a random 16-byte shared secret (`AES-128`), computes a SHA-1 server hash, and calls Mojang's session server (`POST /session/minecraft/join`) using its authenticated Microsoft access token. +4. **Verification**: The client sends `SEncryptionResponse` containing the RSA-encrypted shared secret and encrypted verify token. Pumpkin decrypts the token with its RSA private key, recalculates the SHA-1 digest, and queries Mojang (`GET /session/minecraft/hasJoined`). +5. **Profile Retrieval**: Mojang validates that the client and server hashes match, returning the authenticated player's UUID, username, and signed skin/cape texture properties (`GameProfile`). +6. **Encryption Activation**: Pumpkin activates full-stream symmetric encryption (`AES-128/CFB8`) and completes the login sequence by sending `CLoginSuccess`. + +--- + +## Game Profile Structure + +When Mojang's `hasJoined` endpoint validates a session, it returns a `GameProfile` containing the player's authenticated UUID, username, and signed texture properties: + +```rust +pub struct GameProfile { + pub id: Uuid, + pub name: String, + pub properties: Vec, + pub profile_actions: Option>, +} + +pub struct Property { + pub name: String, + pub value: String, // Base64 encoded texture data + pub signature: Option, // Cryptographic signature verifying authenticity +} +``` + +Pumpkin verifies that skin and cape signatures are cryptographically signed with Mojang's public key to prevent clients from injecting malicious payloads or unauthorized textures. + +--- + +## Custom Authentication Endpoints + +For private networks or enterprise environments running custom Yggdrasil-compatible authentication servers, endpoints can be customized in `pumpkin.toml`: + +```toml +[networking.java.authentication] +enabled = true +fallbacks = [] +connect_timeout = 5000 +read_timeout = 5000 +prevent_proxy_connections = false + +[networking.java.authentication.player_profile] +allow_banned_players = false +allowed_actions = ["FORCED_NAME_CHANGE", "USING_BANNED_SKIN"] + +[networking.java.authentication.textures] +enabled = true +allowed_url_schemes = ["http", "https"] +allowed_url_domains = [".minecraft.net", ".mojang.com"] +``` + +--- + +## Proxy Authentication Forwarding + +When running Pumpkin behind proxies, client connections terminate at the proxy. Pumpkin supports the three primary proxy forwarding standards: + +- **Velocity Modern Forwarding**: Validates player data forwarded with HMAC-SHA256 secret tokens (`[networking.proxy.velocity]`). +- **BungeeGuard**: Token-based authentication header validation for BungeeCord setups. +- **Legacy BungeeCord**: Handshake forwarding appending IP, UUID, and properties to the server host string. diff --git a/docs/nl/developer/networking/java/overview.md b/docs/nl/developer/networking/java/overview.md new file mode 100644 index 0000000..e79753c --- /dev/null +++ b/docs/nl/developer/networking/java/overview.md @@ -0,0 +1,74 @@ +# Java Edition Networking Overview + +The Java Edition network stack handles standard Minecraft PC clients connecting over TCP. Wire-level structures and serialization logic reside in `pumpkin-protocol::java`, while connection management and gameplay packet handlers reside in `pumpkin::net::java`. + +--- + +## The Five Protocol States + +The Minecraft Java protocol operates as an explicit finite state machine. Every connection begins in the `Handshake` state and transitions according to client requests and server responses: + +| State | Transition Trigger | Purpose & Next State | +| :--- | :--- | :--- | +| **Handshake** | Client initiates connection | Inspects protocol version and transitions to `Status`, `Login`, or `Transfer` | +| **Status** | `NextState = 1` | Streams MOTD, active player counts, favicon, and ping response | +| **Login** | `NextState = 2` | Authenticates account (Yggdrasil), negotiates encryption (AES-128), transitions to `Config` | +| **Transfer** | `NextState = 3` | Modern proxy / server-transfer negotiation | +| **Config** | Authentication success | Exchanges dynamic registries, data tags, feature flags, and resource packs | +| **Play** | Configuration acknowledged | Primary bidirectional gameplay loop (chunks, movement, combat, chat) | + +1. **Handshake (`0x00`)**: Always the first packet sent by the client. It declares the client's protocol version, server address, port, and desired next state (`Status`, `Login`, or `Transfer`). +2. **Status**: Lightweight query state. The client requests a server list ping, and Pumpkin responds with the server version, player counts, favicon, and MOTD. +3. **Login**: Authenticates the player. If `online_mode` is enabled, Pumpkin performs an encryption handshake (AES-128/CFB8) and verifies the player's account with Mojang's Yggdrasil session servers. +4. **Config**: Introduced in modern Minecraft (1.20.2+). The server sends feature flags, known registries, resource packs, and custom server links before the player joins the physical world. +5. **Play**: The primary gameplay state. Used for all in-game actions including block breaking, entity movement, combat, chat, and chunk streaming. + +--- + +## Connection Representation: `JavaClient` vs. `Player` + +To keep memory usage minimal and prevent unnecessary allocations, Pumpkin cleanly separates pre-play connections from active players: + +### `JavaClient` +- **Scope**: Exists across `Handshake`, `Status`, `Login`, and `Config` states. +- **Location**: `crates/pumpkin/src/net/java/mod.rs`. +- **Characteristics**: + - Contains connection metadata: client protocol version, remote address, encryption/compression state. + - Has no world coordinates, no physics, no inventory, and no living entity. + - Very low memory footprint. + +### `Player` +- **Scope**: Exists solely during the `Play` state. +- **Location**: `crates/pumpkin/src/entity/player/`. +- **Characteristics**: + - Implements `EntityBase`, representing a living entity inside a `World` and `Level`. + - Holds inventory screens, health, hunger, velocities, and chunk view subscription queues. + - Contains an `Arc` reference for network transmission. + +--- + +## Multi-Version Support (`JavaMinecraftVersion`) + +Pumpkin supports multiple Minecraft client versions. Protocol differences are handled cleanly via `pumpkin_util::version::JavaMinecraftVersion`: + +- Packet IDs may differ across Minecraft versions. The `#[java_packet(ID)]` macro works with `pumpkin-data` to resolve the correct ID for the target version. +- When reading or writing packets, methods accept a `&JavaMinecraftVersion` reference to conditionally include or omit fields that changed in specific Minecraft updates. + +```rust +if *version <= JavaMinecraftVersion::V_1_7_6 { + // Read legacy stance coordinate present in early Minecraft versions + let _stance = bytebuf.get_f64_be()?; +} +``` + +--- + +## The Packet Pipeline + +Incoming Java packets pass through a structured pipeline: + +1. **Length Framing**: `tokio_util::codec` decodes VarInt frame lengths. +2. **Decompression & Decryption**: If enabled, the raw buffer is decrypted via AES/CFB8 and decompressed via ZLib. +3. **Plugin Event Interception**: A `PacketReceivedEvent` is dispatched to the plugin manager. Plugins can inspect or cancel the packet before server processing. +4. **ID Dispatching**: The packet ID is matched against version-specific constants in `crates/pumpkin/src/net/java/mod.rs` (or the respective state module). +5. **Handling**: The packet struct is deserialized and processed by state-specific handler functions. diff --git a/docs/nl/developer/networking/metadata.md b/docs/nl/developer/networking/metadata.md new file mode 100644 index 0000000..9def4ae --- /dev/null +++ b/docs/nl/developer/networking/metadata.md @@ -0,0 +1,123 @@ +# Entity Metadata & Synced Data + +Minecraft synchronizes persistent entity visual states, animation flags, poses, and properties via **Entity Metadata** (historically called *DataWatchers* in Java Edition and *Actor Data* in Bedrock Edition). + +Instead of transmitting full state packets whenever an entity crouches, catches fire, or takes damage, the server broadcasts compact metadata updates to nearby observers. + +--- + +## Edition Comparison + +| Feature | Minecraft: Java Edition | Minecraft: Bedrock Edition | +| :--- | :--- | :--- | +| **Packet Name** | `CSetEntityMetadata` (`SET_ENTITY_DATA`) | `CSetActorData` (Packet ID `39`) | +| **Storage Structure** | `TrackedData` list terminating with `0xFF` | `SyncedActorDataList` (`HashMap`) | +| **Key Type** | Index (`u8`) mapped to typed `MetaDataType` | Enum keys (`entity_data_key::*`) | +| **Flags** | Bitfield byte (fire, crouching, sprinting, glowing) | Bitfield int64 (`entity_data_flag::*`) | +| **Dynamic Properties** | N/A (baked into tracked indices) | `PropertySyncData` (client/server sync) | +| **Crate Location** | `crates/pumpkin-protocol/src/java/client/play/entity_metadata.rs` | `crates/pumpkin-protocol/src/bedrock/client/set_actor_data.rs` | + +--- + +## 1. Java Edition: `CSetEntityMetadata` + +In Java Edition, entity metadata consists of numbered index entries. Each entry defines an index, a `MetaDataType` ID, and the encoded value. The list terminates with a single `0xFF` byte. + +### Key Index Hierarchy + +Entity metadata follows an object-oriented inheritance model: + +1. **Entity (Base)**: + - `0`: State bitfield (flags: bit `0` = on fire, bit `1` = crouching, bit `3` = sprinting, bit `5` = invisible, bit `6` = glowing). + - `1`: Air supply (`VarInt`). + - `2`: Custom name (optional `TextComponent`). + - `3`: Custom name visible (`bool`). + - `4`: Silent (`bool`). + - `5`: No gravity (`bool`). + - `6`: Pose (`Standing`, `FallFlying`, `Sleeping`, `Swimming`, `SpinAttack`, `Crouching`, `Dying`). +2. **LivingEntity (extends Entity)**: + - `8`: Hand states (using item, active hand). + - `9`: Health (`f32`). + - `10`: Potion effect ambient color. +3. **Player (extends LivingEntity)**: + - Additional hearts, skin customization bitmask, main hand indicator. + +### Sending Metadata Updates in Pumpkin + +Pumpkin encodes entity metadata in `pumpkin-protocol`: + +```rust +use pumpkin_protocol::VarInt; +use pumpkin_protocol::java::client::play::CSetEntityMetadata; + +// Construct metadata update for entity with ID 42 +let packet = CSetEntityMetadata::new( + VarInt(entity.entity_id), + serialized_metadata_bytes, +); + +// Broadcast to chunk observers +world.broadcast_packet_all(&packet).await; +``` + +--- + +## 2. Bedrock Edition: `CSetActorData` + +Bedrock Edition uses `CSetActorData` (`packet(39)`), storing properties in a dictionary mapping `u32` keys to strongly-typed `MetadataValue` variants. + +### Key & Flag Constants + +Bedrock exposes flags as a 64-bit integer bitfield under `entity_data_key::FLAGS`: + +- `entity_data_flag::ON_FIRE` (`0`): Entity is burning. +- `entity_data_flag::SNEAKING` (`1`): Player is sneaking. +- `entity_data_flag::RIDING` (`2`): Entity is mounted on a vehicle. +- `entity_data_flag::SPRINTING` (`3`): Player is sprinting. +- `entity_data_flag::USING_ITEM` (`4`): Player is actively charging a bow, eating food, or using a shield. +- `entity_data_flag::INVISIBLE` (`5`): Entity is invisible. +- `entity_data_flag::GLOWING` (`26`): Entity has the glowing effect outline. + +### Constructing and Updating Actor Data in Pumpkin + +In `crates/pumpkin/src/entity/living.rs`: + +```rust +use pumpkin_protocol::bedrock::client::set_actor_data::{ + CSetActorData, MetadataValue, PropertySyncData, SyncedActorDataList, + entity_data_flag, entity_data_key, +}; + +let mut actor_data = SyncedActorDataList::new(); + +// Set entity flags (e.g. actively using an item) +let mut flags: i64 = 0; +if is_using_item { + flags |= 1i64 << entity_data_flag::USING_ITEM; +} +actor_data.set(entity_data_key::FLAGS, MetadataValue::Int64(flags)); + +// Set air supply +actor_data.set(entity_data_key::AIR_SUPPLY, MetadataValue::Short(air as i16)); + +// Set custom name tag +actor_data.set(entity_data_key::NAME, MetadataValue::String(name)); + +let packet = CSetActorData { + target_runtime_id: VarULong(entity_id), + actor_data, + synced_properties: PropertySyncData::default(), + tick: VarULong(current_tick), +}; + +client.try_enqueue_client_packet(&packet); +``` + +--- + +## Multi-Version Compatibility + +Because Mojang frequently shifts metadata indices between Minecraft releases, `pumpkin-protocol` leverages translation tables from `pumpkin-data`: + +- For Java Edition, `pumpkin_data::meta_data_type::MetaDataType` remaps indices dynamically based on the client's `JavaMinecraftVersion`. +- For Bedrock Edition, `SyncedActorDataList` validates keys against the active Bedrock protocol version to prevent client crashes. diff --git a/docs/nl/developer/networking/proxy/bungeecord.md b/docs/nl/developer/networking/proxy/bungeecord.md new file mode 100644 index 0000000..cb06ff5 --- /dev/null +++ b/docs/nl/developer/networking/proxy/bungeecord.md @@ -0,0 +1,73 @@ +# BungeeCord Forwarding + +Pumpkin provides native support for BungeeCord and Waterfall proxy networks, including **BungeeGuard** authentication token verification. + +Implementation is located in [`crates/pumpkin/src/net/proxy/bungeecord.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/bungeecord.rs). + +--- + +## How BungeeCord Forwarding Operates + +Unlike modern protocols that use custom login channels, BungeeCord operates by encoding client metadata directly into the `server_address` field of the initial `SHandshake` packet. + +The string uses null character (`\0`) delimiters: + +```text +[original_host]\0[player_ip:port]\0[player_uuid]\0[properties_json] +``` + +1. **`original_host`**: The domain name the player originally typed into their client. +2. **`player_ip:port`**: The genuine IP address and ephemeral source port of the client. +3. **`player_uuid`**: The player's Mojang UUID (32 hex characters without hyphens or 36 characters with hyphens). +4. **`properties_json`**: A JSON array containing skin textures, cape textures, and Mojang cryptographic signatures. + +--- + +## The Security Risk: Proxy Bypasses + +Because BungeeCord's handshake string is unauthenticated plain text, an attacker could connect directly to an exposed backend port, inject an arbitrary UUID and operator username, and gain full server control. + +Traditionally, this required strict Linux firewall rules (`iptables` / `ufw`) to restrict port access exclusively to the proxy IP. + +--- + +## BungeeGuard Protection + +To eliminate proxy bypasses without relying solely on network firewalls, Pumpkin integrates **BungeeGuard** token verification: + +1. The BungeeCord proxy injects a shared secret into the player's profile properties under the key `bungeeguard-token`. +2. When Pumpkin processes the handshake in `bungeecord_login`: + - It checks whether `secret` is configured in `pumpkin.toml`. + - It inspects the forwarded properties for `bungeeguard-token`. + - If the token matches, Pumpkin **strips the secret property** from the player's profile (so other plugins cannot read it) and completes the login. + - If the token is missing or incorrect, Pumpkin rejects the connection with `BungeeCordError::MissingToken` or `BungeeCordError::InvalidToken`. + +```rust +// In crates/pumpkin/src/net/proxy/bungeecord.rs +if !secret.is_empty() { + let Some(token_index) = properties.iter().position(|p| p.name == BUNGEEGUARD_TOKEN_PROPERTY) else { + return Err(BungeeCordError::MissingToken); + }; + + if properties[token_index].value != secret { + return Err(BungeeCordError::InvalidToken); + } + properties.remove(token_index); +} +``` + +--- + +## Configuration in `pumpkin.toml` + +To enable BungeeCord forwarding with BungeeGuard: + +```toml +[networking.proxy] +enabled = true + +[networking.proxy.bungeecord] +enabled = true +# Optional: Configure the BungeeGuard shared secret to block direct connections +secret = "YOUR_BUNGEEGUARD_SECRET_HERE" +``` diff --git a/docs/nl/developer/networking/proxy/index.md b/docs/nl/developer/networking/proxy/index.md new file mode 100644 index 0000000..300c89e --- /dev/null +++ b/docs/nl/developer/networking/proxy/index.md @@ -0,0 +1,46 @@ +# Proxy Forwarding Overview + +In multi-server Minecraft networks, players connect through a front-facing reverse proxy (such as Velocity, BungeeCord, or Vine) that routes connections between lobbies and sub-servers without requiring players to reconnect. + +Because backend servers receive TCP connections directly from the proxy machine rather than the player, the server must be informed of the player's genuine **IP address**, **UUID**, **username**, and **skin properties**. + +Pumpkin provides native support for all three major proxy forwarding protocols in [`crates/pumpkin/src/net/proxy/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/net/proxy). + +--- + +## The Proxy Forwarding Problem + +Without secure proxy forwarding: +1. **IP Loss**: Every connected player appears with the proxy's loopback or internal IP (breaking IP bans and geo-location). +2. **UUID Spoofing & Bypasses**: Malicious users could connect directly to the backend server's port with any spoofed username or operator identity. + +To prevent proxy bypasses, Pumpkin validates cryptographic secrets, tokens, or digital signatures sent by the proxy. + +--- + +## Comparison of Supported Protocols + +| Protocol | Protocol Transport | Security / Cryptography | Replay Protection | Crate Module | +| :--- | :--- | :--- | :--- | :--- | +| **[BungeeCord](/developer/networking/proxy/bungeecord)** | `SHandshake` server address string | Optional token in profile properties (`bungeeguard-token`) | None | `pumpkin::net::proxy::bungeecord` | +| **[Velocity](/developer/networking/proxy/velocity)** | Login plugin message (`velocity:player_info`) | Symmetric HMAC-SHA256 signature | None | `pumpkin::net::proxy::velocity` | +| **[Vine](/developer/networking/proxy/vine)** | Login plugin message (`vine:player_info`) | Asymmetric **Ed25519** digital signatures | 16-byte nonce challenge + 30s timestamp window | `pumpkin::net::proxy::vine` | + +--- + +## Lifecycle Architecture + +1. **Player Authentication**: The player connects to the proxy (BungeeCord, Velocity, or Vine) and authenticates against Mojang's session servers. +2. **Backend Connection**: The proxy connects to Pumpkin over TCP and sends `SHandshake`. +3. **Data Forwarding**: + - **BungeeCord Mode**: The proxy injects the player's remote IP, UUID, and properties directly into the `SHandshake` hostname string (separated by null bytes `\0`). + - **Modern Mode (Velocity / Vine)**: Pumpkin sends `CLoginPluginRequest` on a dedicated channel (`velocity:player_info` or `vine:player_info`). The proxy signs the player profile using HMAC-SHA256 (Velocity) or Ed25519 (Vine) and replies with `SLoginPluginResponse`. +4. **Validation & Session Initialization**: Pumpkin validates the signature, binds the real IP and GameProfile to the `JavaClient`, and sends `CLoginSuccess`. + +--- + +## Proxy Documentation + +- **[BungeeCord](/developer/networking/proxy/bungeecord)**: Legacy host string injection and optional BungeeGuard token validation. +- **[Velocity Modern Forwarding](/developer/networking/proxy/velocity)**: HMAC-SHA256 signed player info during the Login state. +- **[Vine Modern Forwarding](/developer/networking/proxy/vine)**: Next-generation proxy forwarding with Ed25519 signatures and replay attack prevention. diff --git a/docs/nl/developer/networking/proxy/velocity.md b/docs/nl/developer/networking/proxy/velocity.md new file mode 100644 index 0000000..b0333e7 --- /dev/null +++ b/docs/nl/developer/networking/proxy/velocity.md @@ -0,0 +1,88 @@ +# Velocity Modern Forwarding + +[Velocity](https://papermc.io/software/velocity) is a high-performance proxy created by PaperMC. It uses modern plugin messaging during the `Login` state to securely transmit player data with **HMAC-SHA256** message authentication codes. + +Pumpkin implements Velocity modern forwarding in [`crates/pumpkin/src/net/proxy/velocity.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/velocity.rs). + +--- + +## How Velocity Forwarding Operates + +Unlike BungeeCord, which injects unauthenticated data into the handshake string, Velocity operates during the **Login** state over a dedicated plugin channel: + +1. **Handshake & Login**: The player authenticates with Velocity. Velocity initiates a standard TCP connection to Pumpkin and transmits `SHandshake` and `SLoginStart`. +2. **Channel Query**: Recognizing that Velocity modern forwarding is enabled, Pumpkin pauses the login sequence and dispatches a `CLoginPluginRequest` on channel `velocity:player_info`. +3. **Payload Generation**: Velocity bundles the player's remote address, UUID, username, and skin textures. It calculates a 32-byte HMAC-SHA256 signature across this payload using the shared secret. +4. **Signature Verification**: Velocity sends `SLoginPluginResponse`. Pumpkin validates the HMAC-SHA256 digest against its configured secret. If valid, Pumpkin overwrites the connection's network address and GameProfile and transitions to `Config` / `Play`. + +--- + +## Wire Payload Structure + +The payload in `SLoginPluginResponse` is structured as follows: + +```text +┌─────────────────────────────────────────────────────────────┐ +│ 32-byte HMAC-SHA256 Signature │ +├─────────────────────────────────────────────────────────────┤ +│ Forwarding Protocol Version (VarInt: 1 - 4) │ +├─────────────────────────────────────────────────────────────┤ +│ Client Remote IP Address (String / IPv4 / IPv6) │ +├─────────────────────────────────────────────────────────────┤ +│ Player UUID (128-bit / UUID) │ +├─────────────────────────────────────────────────────────────┤ +│ Player Username (String) │ +├─────────────────────────────────────────────────────────────┤ +│ Profile Properties Count (VarInt) │ +│ ├─ Property Name (String) │ +│ ├─ Property Value (String) │ +│ └─ Optional Signature (String) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Cryptographic Validation in Pumpkin + +Inside `crates/pumpkin/src/net/proxy/velocity.rs`: + +```rust +use hmac::{Hmac, KeyInit, Mac}; +use sha2::Sha256; + +type HmacSha256 = Hmac; + +// Extract signature and payload +let (signature, data_to_verify) = data.split_at(32); + +// Initialize HMAC with secret from pumpkin.toml +let mut mac = HmacSha256::new_from_slice(secret.as_bytes()) + .map_err(|_| VelocityError::FailedVerifyIntegrity)?; + +mac.update(data_to_verify); + +// Constant-time verification prevents timing attacks +mac.verify_slice(signature) + .map_err(|_| VelocityError::FailedVerifyIntegrity)?; +``` + +If the signature does not match (e.g., if an attacker attempts to connect directly to the backend without knowing the secret), Pumpkin drops the connection immediately. + +--- + +## Configuration in `pumpkin.toml` + +1. Open `velocity.toml` on your Velocity proxy and set: + ```toml + player-info-forwarding-mode = "modern" + forwarding-secret-file = "forwarding.secret" + ``` +2. Copy the generated secret string from `forwarding.secret` into `pumpkin.toml`: + ```toml + [networking.proxy] + enabled = true + + [networking.proxy.velocity] + enabled = true + secret = "YOUR_VELOCITY_FORWARDING_SECRET_HERE" + ``` diff --git a/docs/nl/developer/networking/proxy/vine.md b/docs/nl/developer/networking/proxy/vine.md new file mode 100644 index 0000000..fed2a8a --- /dev/null +++ b/docs/nl/developer/networking/proxy/vine.md @@ -0,0 +1,112 @@ +# Vine Modern Forwarding + +**Vine** is a next-generation, high-performance Minecraft proxy written in Rust. Designed for ultra-low latency, multi-threading, and enterprise-grade security, Vine introduces modern cryptographic forwarding based on **Ed25519 asymmetric signatures** and **replay attack protection**. + +Pumpkin provides native support for Vine forwarding in [`crates/pumpkin/src/net/proxy/vine.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/vine.rs). + +--- + +## Why Vine Forwarding? + +While Velocity's HMAC-SHA256 forwarding represents an improvement over legacy BungeeCord, it relies on a **symmetric shared secret**. If one backend server in a network is compromised, an attacker obtains the shared secret and can forge authenticated player logins across the entire server cluster. + +Vine solves this with: +1. **Asymmetric Public-Key Cryptography (Ed25519)**: The proxy signs player data using its private key. Backend servers only store the proxy's **Public Key** (`public_key`). Even if a backend server is compromised, the attacker cannot forge identities to other servers. +2. **Replay Attack Protection (16-byte Challenge Nonce)**: Pumpkin generates a cryptographic random 16-byte nonce for each connection. The proxy must sign and return this nonce. Replaying a captured login packet is impossible. +3. **Timestamp Drift Window**: Transmits a Unix timestamp that must be within ±30 seconds of the backend's system clock (`MAX_TIMESTAMP_DRIFT_SECS = 30`). + +--- + +## The Handshake Sequence + +1. **Connection**: The player connects and authenticates with Vine. Vine opens a backend connection to Pumpkin and sends `SHandshake` and `SLoginStart`. +2. **Challenge Query**: Pumpkin generates a cryptographically random 16-byte nonce and sends `CLoginPluginRequest` on the `vine:player_info` channel. +3. **Asymmetric Signing**: Vine serializes the version, timestamp, the returned 16-byte nonce, the client IP address, and the player GameProfile. It signs this byte stream with its Ed25519 private key. +4. **Tri-fold Validation**: Vine replies with `SLoginPluginResponse`. Pumpkin verifies: + - **Clock Skew**: The embedded timestamp is within ±30 seconds of UTC time. + - **Nonce Check**: The signed nonce exactly matches the 16-byte challenge generated in step 2. + - **Ed25519 Signature**: The signature is cryptographically valid against the configured `public_key`. +5. **Session Success**: If all three checks pass, Pumpkin applies the verified identity and emits `CLoginSuccess`. + +--- + +## Wire Payload Structure + +The `SLoginPluginResponse` data layout: + +```text +┌─────────────────────────────────────────────────────────────┐ +│ 64-byte Ed25519 Digital Signature │ +├─────────────────────────────────────────────────────────────┤ +│ Forwarding Protocol Version (i32: 1) │ +├─────────────────────────────────────────────────────────────┤ +│ Unix Timestamp in Seconds (i64) │ +├─────────────────────────────────────────────────────────────┤ +│ 16-byte Challenge Nonce │ +├─────────────────────────────────────────────────────────────┤ +│ Client Remote IP Address (String / IPv4 / IPv6) │ +├─────────────────────────────────────────────────────────────┤ +│ Player UUID (128-bit / UUID) │ +├─────────────────────────────────────────────────────────────┤ +│ Player Username (String) │ +├─────────────────────────────────────────────────────────────┤ +│ Profile Properties Count (VarInt) │ +│ ├─ Property Name (String) │ +│ ├─ Property Value (String) │ +│ └─ Optional Signature (String) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Verification in Pumpkin + +In `crates/pumpkin/src/net/proxy/vine.rs`: + +```rust +use ed25519_dalek::{Signature, Verifier, VerifyingKey}; + +// 1. Validate payload length (64-byte signature + 25-byte headers minimum) +if data.len() < 89 { + return Err(VineError::DataTooShort); +} + +// 2. Extract signature and signed payload +let (sig_bytes, signed_data) = data.split_at(64); +let signature = Signature::from_slice(sig_bytes) + .map_err(|_| VineError::InvalidSignature)?; + +// 3. Verify timestamp skew (30 second maximum drift) +let skew = (server_now - timestamp).abs(); +if skew > MAX_TIMESTAMP_DRIFT_SECS { + return Err(VineError::TimestampExpired(skew, MAX_TIMESTAMP_DRIFT_SECS)); +} + +// 4. Verify challenge nonce matches connection state +if received_nonce != expected_nonce { + return Err(VineError::ChallengeMismatch); +} + +// 5. Verify Ed25519 signature with proxy's public key +verifying_key.verify(signed_data, &signature) + .map_err(|_| VineError::InvalidSignature)?; +``` + +--- + +## Configuration in `pumpkin.toml` + +Configure Vine forwarding in `pumpkin.toml`: + +```toml +[networking.proxy] +enabled = true + +[networking.proxy.vine] +enabled = true +# 64-character hex-encoded Ed25519 public key of the Vine proxy +public_key = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + +# Alternatively, if secret is supplied, the public key is derived automatically +secret = "" +``` diff --git a/docs/nl/developer/networking/query.md b/docs/nl/developer/networking/query.md new file mode 100644 index 0000000..7d97a9c --- /dev/null +++ b/docs/nl/developer/networking/query.md @@ -0,0 +1,97 @@ +# GameSpy4 Query Protocol + +Pumpkin includes native support for the **GameSpy4 Query** protocol over UDP. External monitoring systems, Discord bots, and server listing platforms use Query to poll real-time player counts, MOTD, server versions, and active player lists with minimal network overhead. + +Query wire encoding is implemented in [`crates/pumpkin-protocol/src/query.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-protocol/src/query.rs), and the asynchronous UDP listener is implemented in [`crates/pumpkin/src/net/query.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/query.rs). + +--- + +## Why Query? (UDP vs. Status Ping) + +While standard Minecraft clients ping servers via the TCP `Status` state (SLP), Query operates over stateless **UDP**: + +- **No TCP Handshake**: Avoids TCP connection overhead and connection state tables. +- **Detailed Server State**: Returns comprehensive key-value metadata and a full list of connected player usernames (not available via basic TCP ping). +- **Amplification Attack Protection**: Enforces cryptographic challenge tokens so malicious actors cannot abuse the UDP port for denial-of-service reflection attacks. + +--- + +## The Handshake & Challenge Token Flow + +To prevent UDP spoofing and reflection DDoS attacks, Query requires a two-step handshake: + +1. **Handshake Challenge**: The client transmits `SHandshake` (`0xFEFD`, type `0x09`, and a 32-bit session ID). Pumpkin generates a cryptographic 32-bit integer challenge token tied to the client's `SocketAddr` and responds with `CHandshake`. +2. **Status Query**: The client sends `SStatusRequest` containing the received challenge token: + - **Basic Status** (no padding): Returns MOTD, game type (`SMP`), map name, online player count, and max player capacity. + - **Full Status** (4-byte padding): Returns detailed server metadata key-value pairs (`hostname`, `gametype`, `game_id`, `version`, `plugins`, `map`, `numplayers`, `maxplayers`, `hostport`, `hostip`) followed by an array of online player usernames. + +### Challenge Token Invalidation +In `crates/pumpkin/src/net/query.rs`, challenge tokens are stored in a thread-safe map (`Arc>>`). A background Tokio timer purges all challenge tokens every 30 seconds to prevent replay abuse: + +```rust +// Tokens are strictly bound to the client's IP and port +let valid_challenge_tokens = Arc::new(RwLock::new(HashMap::new())); + +// Background expiration loop +tokio::spawn(async move { + let mut interval = time::interval(Duration::from_secs(30)); + loop { + interval.tick().await; + valid_challenge_tokens.write().await.clear(); + } +}); +``` + +--- + +## Packet Formats + +Every incoming Query packet starts with the 2-byte magic header `0xFEFD` followed by a 1-byte packet type: + +| Type Byte | Name | Direction | Description | +| :--- | :--- | :--- | :--- | +| `0x09` | Handshake Request | Client → Server | Client asks for a challenge token. | +| `0x09` | Handshake Response | Server → Client | Returns the numeric token encoded as an ASCII string. | +| `0x00` | Stat Request | Client → Server | Requests basic or full status with challenge token. | +| `0x00` | Basic Stat Response | Server → Client | 11-byte short summary. | +| `0x00` | Full Stat Response | Server → Client | Key-value dictionary + null-separated player list. | + +--- + +## Response Payloads + +### 1. Basic Status (`CBasicStatus`) +Contains essential server fields: +- **`motd`**: Server message of the day (stripped of color codes). +- **`gametype`**: Fixed string `"SMP"`. +- **`map`**: Active world/level name (e.g., `"world"`). +- **`numplayers`**: Online player count. +- **`maxplayers`**: Maximum configured player capacity. +- **`hostport`**: Port number (`u16` Little Endian). +- **`hostip`**: Server IP string. + +### 2. Full Status (`CFullStatus`) +Returns two null-terminated sections: + +1. **Key-Value Metadata Map**: + - `hostname`: Server description / MOTD. + - `gametype`: `"SMP"`. + - `game_id`: `"MINECRAFT"`. + - `version`: Server protocol / Minecraft version string. + - `plugins`: Installed server software and plugin names. + - `map`: Level name. + - `numplayers` / `maxplayers`: Capacity stats. + - `hostport` / `hostip`: Network bindings. +2. **Player List**: + - An array of null-terminated UTF-8 strings containing the usernames of all currently connected players. + - Concludes with a terminal null byte (`\0`). + +--- + +## Configuration in `pumpkin.toml` + +```toml +[networking.query] +enabled = true +address = "0.0.0.0:25565" +``` diff --git a/docs/nl/developer/plugins/index.md b/docs/nl/developer/plugins/index.md new file mode 100644 index 0000000..8cc993f --- /dev/null +++ b/docs/nl/developer/plugins/index.md @@ -0,0 +1,56 @@ +# Plugin Engine Architecture + +Pumpkin features a modular, multi-runtime plugin engine designed for performance, safety, and cross-language interoperability. + +Unlike traditional Minecraft servers that rely on a single JVM class loader, Pumpkin isolates plugin execution through dedicated loaders supporting both **native high-performance Rust binaries** and **sandboxed WebAssembly components** compiled from Rust, Python, C#, C, Go, or Kotlin. + +--- + +## Architectural Overview + +### Architectural Breakdown + +| Subsystem | Primary Crate | Responsibility | +| :--- | :--- | :--- | +| **Plugin Manager** | `crates/pumpkin/src/plugin` | Lifecycle orchestration (load, enable, disable), event dispatching, and loader registration | +| **Native Loader** | `crates/pumpkin/src/plugin/loader/native` | Direct dynamic library loading (`.so`, `.dll`, `.dylib`) for zero-overhead Rust extensions | +| **WASM Loader** | `crates/pumpkin/src/plugin/loader/wasm` | Wasmtime execution engine for sandboxed components compiled from Rust, Python, Go, C#, C, and Kotlin | +| **Interface Specifications** | `crates/pumpkin-plugin-wit` | WebAssembly Interface Types (WIT) defining typed functions, types, and host imports | +| **Security & Verification** | `crates/pumpkin/src/plugin/loader/wasm/sign` | Cryptographic signature verification via W3C wasmsign2 / Ed25519 public keys | + +--- + +## The Dual Execution Model + +| Feature | Native Plugins | WebAssembly (Wasmtime) Plugins | +| :--- | :--- | :--- | +| **File Extension** | `.so` (Linux), `.dll` (Windows), `.dylib` (macOS) | `.wasm` (Component Model) | +| **Languages** | Rust (compiled dynamically) | Rust, Python, C#, C, Go, Kotlin | +| **Performance** | Native CPU execution (zero overhead) | JIT-compiled with near-native speed | +| **Sandboxing** | None (runs in host address space) | Memory-isolated sandboxed execution | +| **Security & Signing** | OS-level permissions | Cryptographic W3C Wasm-Sign verification | +| **Hot Unloading** | Limited (OS shared library constraints) | Fully supported memory reclamation | + +--- + +## Event Lifecycle & Dispatching + +Plugins interact with Pumpkin primarily through events: + +1. **Event Dispatch**: + - `server.plugin_manager.fire_blocking(&server, &mut event)`: Dispatches events synchronously on the executing thread (e.g., cancelling player block placement or modifying packet payloads). + - Asynchronous events run on non-blocking Tokio tasks. +2. **Cancellable Events**: + - Handlers can set `event.cancelled = true` to abort the default game logic (such as cancelling player movement, interactions, or packet processing). + +--- + +## Plugin Engine Documentation + +Explore the internal mechanics of Pumpkin's plugin system: + +- **[Adding a Plugin Loader](/developer/plugins/loaders)**: Step-by-step guide on implementing the `PluginLoader` trait for new scripting languages and runtimes. +- **[WASM Signing & Verification](/developer/plugins/wasm-signing)**: Detailed specification of Ed25519 cryptographic signing, W3C `wasmsign2` custom sections, and marketplace license verification. + +> [!NOTE] +> Looking to develop third-party plugins rather than working on Pumpkin's internal plugin engine? Check out the **[Plugin Development Guide](/plugin-dev/introduction)**. diff --git a/docs/nl/developer/plugins/loaders.md b/docs/nl/developer/plugins/loaders.md new file mode 100644 index 0000000..7e0f070 --- /dev/null +++ b/docs/nl/developer/plugins/loaders.md @@ -0,0 +1,161 @@ +# Adding a Plugin Loader + +Pumpkin's plugin architecture is runtime-agnostic. The server does not assume all plugins run in WebAssembly or native shared libraries; instead, all execution runtimes implement the unified [`PluginLoader`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/plugin/loader/mod.rs) trait. + +This guide explains how `PluginLoader` operates and demonstrates how to implement a custom loader for new script runtimes or virtual machines (such as Lua, JavaScript/Boa, or GraalVM). + +--- + +## The `PluginLoader` Trait + +All plugin loaders implement the `PluginLoader` trait located in `crates/pumpkin/src/plugin/loader/mod.rs`: + +```rust +use std::{any::Any, path::Path, pin::Pin, sync::Arc}; +use crate::plugin::{PluginMetadata, api::Plugin}; + +pub type PluginLoadFuture<'a> = Pin< + Box< + dyn Future< + Output = Result< + (Arc, PluginMetadata, Box), + LoaderError, + >, + > + Send + + 'a, + >, +>; + +pub type PluginUnloadFuture<'a> = + Pin> + Send + 'a>>; + +pub trait PluginLoader: Send + Sync { + /// Determines whether this loader handles the target file. + fn can_load(&self, path: &Path) -> bool; + + /// Loads, parses, and initializes a plugin instance asynchronously. + fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a>; + + /// Unloads the plugin and reclaims associated resources. + fn unload(&self, data: Box) -> PluginUnloadFuture<'_>; + + /// Indicates whether the runtime supports safe unloading without memory leaks. + fn can_unload(&self) -> bool; +} +``` + +--- + +## Existing Loaders in Pumpkin + +Pumpkin currently provides two official loaders: + +1. **`NativePluginLoader` (`loader/native.rs`)**: + - Matches dynamic library files (`.so` on Linux, `.dll` on Windows, `.dylib` on macOS). + - Dynamically links the library using [`libloading`](https://docs.rs/libloading/). + - Locates and invokes the extern entrypoint function (`pumpkin_plugin_init`). + - `can_unload()` returns `false` due to OS-level shared library unloading hazards. +2. **`WasmPluginLoader` (`loader/wasm/mod.rs`)**: + - Matches `.wasm` files. + - Verifies cryptographic signatures (see [WASM Signing](/developer/plugins/wasm-signing)). + - JIT-compiles WebAssembly components using [Wasmtime](https://wasmtime.dev/). + - Links host WIT exports (`pumpkin-host-bindings`). + - `can_unload()` returns `true` because Wasmtime safely drops guest memory. + +--- + +## Step-by-Step: Adding a Custom Plugin Loader + +Here is how to create a custom loader (e.g., for a scripting runtime): + +### Step 1: Define the Loader Struct +Create a new file in `crates/pumpkin/src/plugin/loader/my_loader.rs`: + +```rust +use std::{any::Any, path::Path, sync::Arc}; +use crate::plugin::loader::{LoaderError, PluginLoadFuture, PluginLoader, PluginUnloadFuture}; +use crate::plugin::{PluginMetadata, api::Plugin}; + +pub struct MyCustomLoader; + +impl MyCustomLoader { + #[must_use] + pub fn new() -> Self { + Self + } +} +``` + +### Step 2: Implement File Recognition (`can_load`) +Check the file extension or inspect header bytes: + +```rust +impl PluginLoader for MyCustomLoader { + fn can_load(&self, path: &Path) -> bool { + path.extension() + .and_then(|ext| ext.to_str()) + .is_some_and(|ext| ext.eq_ignore_ascii_case("script")) + } +``` + +### Step 3: Implement Asynchronous Loading (`load`) +The `load` method reads the script, initializes the VM, registers server host callbacks, and returns a tuple containing: +1. `Arc`: The generic plugin handle. +2. `PluginMetadata`: Name, author, version, description. +3. `Box`: Loader-specific context (e.g., VM state) retained for unloading. + +```rust + fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a> { + Box::pin(async move { + let script_content = tokio::fs::read_to_string(path).await + .map_err(|e| LoaderError::InitializationFailed(e.to_string()))?; + + // 1. Parse plugin metadata from header or accompanying manifest + let metadata = PluginMetadata { + name: "ExampleScript".into(), + version: "1.0.0".into(), + authors: vec!["Author".into()], + description: Some("Custom script plugin".into()), + }; + + // 2. Initialize runtime instance + let plugin_instance = Arc::new(MyScriptPluginWrapper::new(script_content)); + let runtime_state: Box = Box::new(()); + + Ok((plugin_instance, metadata, runtime_state)) + }) + } +``` + +### Step 4: Implement Clean Unloading (`unload` & `can_unload`) +If your runtime can cleanly terminate without leaking resources: + +```rust + fn can_unload(&self) -> bool { + true + } + + fn unload(&self, data: Box) -> PluginUnloadFuture<'_> { + Box::pin(async move { + // Drop runtime state and disconnect event listeners + drop(data); + Ok(()) + }) + } +} +``` + +### Step 5: Register the Loader in `PluginManager` + +Open `crates/pumpkin/src/plugin/mod.rs` and register your loader in `PluginManager::new()`: + +```rust +// In crates/pumpkin/src/plugin/mod.rs +let mut loaders: Vec> = Vec::new(); + +loaders.push(Box::new(NativePluginLoader::new())); +loaders.push(Box::new(WasmPluginLoader::new())); +loaders.push(Box::new(MyCustomLoader::new())); // Add custom loader +``` + +During server startup, Pumpkin iterates through all registered loaders in sequence when scanning the `plugins/` folder, dispatching each file to the first loader whose `can_load` returns `true`. diff --git a/docs/nl/developer/plugins/wasm-signing.md b/docs/nl/developer/plugins/wasm-signing.md new file mode 100644 index 0000000..40a36eb --- /dev/null +++ b/docs/nl/developer/plugins/wasm-signing.md @@ -0,0 +1,86 @@ +# WASM Plugin Signing & Verification + +Pumpkin implements a cryptographic signing and license verification system for WebAssembly plugins. This ensures plugin integrity, authenticates author identity, and protects verified or marketplace plugins against unauthorized tampering. + +The signing subsystem is implemented in [`crates/pumpkin/src/plugin/loader/wasm/wasm_host/signature.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/plugin/loader/wasm/wasm_host/signature.rs) based on the **W3C `wasmsign2`** specification using custom WebAssembly sections. + +--- + +## Why Sign WebAssembly Plugins? + +- **Tamper Protection**: Ensures that compiled bytecode has not been injected with malicious logic after compilation. +- **Author Identity**: Authenticates that a plugin was built and released by a verified developer. +- **Marketplace Licensing**: Validates license keys and entitlements for plugins distributed through the official [Pumpkin Marketplace](https://market.pumpkinmc.org). + +--- + +## Custom Sections in the WASM Binary + +Pumpkin embeds signing and verification metadata directly into two standard WebAssembly custom sections: +- **`pumpkin.metadata`**: Stores developer, publication, and license claims in JSON format. +- **`wasm_signature`**: Stores the cryptographic signature envelope compliant with the W3C `wasmsign2` standard. + +### 1. `pumpkin.metadata` Section +Contains a JSON string holding publication and license claims: + +```json +{ + "marketplace_url": "https://market.pumpkinmc.org", + "plugin_id": 1024, + "plugin_name": "EconomyPlus", + "version": "1.2.0", + "dev_id": 42, + "dev_name": "DevName", + "is_paid": true, + "user_id": 1001, + "license_key": "XXXX-XXXX-XXXX-XXXX", + "issued_at": "2026-09-14T05:00:00Z" +} +``` + +### 2. `wasm_signature` Section +Follows the W3C `wasmsign2` envelope structure: + +```json +{ + "version": 1, + "algorithm": "Ed25519", + "public_key_hex": "4a7b...", + "signature_hex": "8e1c..." +} +``` + +--- + +## The Signing Pipeline + +The signing process guarantees integrity by hashing and signing the clean WebAssembly bytecode combined with the metadata claims: + +### Signing Algorithm Steps +1. **Sanitization (`strip_pumpkin_sections`)**: Uses [`wasmparser`](https://docs.rs/wasmparser/) to strip any preexisting `pumpkin.metadata` or `wasm_signature` custom sections to avoid recursion. +2. **Payload Construction**: + ```rust + let mut sign_payload = Vec::new(); + sign_payload.extend_from_slice(&clean_wasm); + sign_payload.extend_from_slice(&metadata_json); + ``` +3. **Ed25519 Signature**: The payload is signed using [`ed25519-dalek`](https://docs.rs/ed25519-dalek/) with the developer's 32-byte secret key. +4. **Encoding**: Uses [`wasm-encoder`](https://docs.rs/wasm-encoder/) to append the `pumpkin.metadata` and `wasm_signature` custom sections to the clean binary. + +--- + +## The Verification Pipeline + +When Pumpkin boots and loads a WebAssembly plugin, the verification sequence executes before the plugin is instantiated: + +1. **Custom Section Extraction**: The `WasmPluginLoader` scans the binary for `pumpkin.metadata` and `wasm_signature`. +2. **Key Retrieval**: + - If the plugin is marked as marketplace-verified, Pumpkin verifies the public key against the Marketplace Public Key Endpoint (`https://market.pumpkinmc.org/api/v1/rest/public-key`). + - If signed by an independent developer, the key is matched against trusted developer keys configured in `pumpkin.toml`. +3. **Bytecode Verification**: + - Pumpkin strips the custom sections from the loaded binary to recover the original `clean_wasm`. + - Reconstructs the payload `(clean_wasm + metadata_json)`. + - Verifies the signature using `verifying_key.verify(&payload, &signature)`. +4. **License & Integrity Validation**: + - If verification succeeds, Wasmtime instantiates the plugin safely. + - If verification fails or the signature is invalid, loading is aborted with an error, protecting the server from running compromised code. diff --git a/docs/nl/developer/porting-versions.md b/docs/nl/developer/porting-versions.md new file mode 100644 index 0000000..0a5014a --- /dev/null +++ b/docs/nl/developer/porting-versions.md @@ -0,0 +1,287 @@ +# Porting to Newer Minecraft Versions + +Upgrading Pumpkin to support a newer Minecraft release—whether a minor patch (e.g., `1.21.4` to `1.21.5`) or a major update (e.g., `1.21` to `26.1`/`26.2`)—is a structured, multi-step process. + +> [!IMPORTANT] +> **The Extractor mod must be ported first!** +> Pumpkin cannot be ported in isolation. The primary source for Pumpkin's assets (`blocks.json`, `items.json`, `entities.json`, `packets.json`, `tracked_data.json`, `synced_registries.json`, etc.) is our dedicated Fabric mod: [Pumpkin-MC/Extractor](https://github.com/Pumpkin-MC/Extractor). You **must first port and run the Extractor on the target Minecraft version** before running `pumpkin-codegen` or updating server crates. + +### Porting Lifecycle at a Glance + +| Stage | Responsibility | Primary Crates / Tools | +| :--- | :--- | :--- | +| **1. Port Extractor** | Extract vanilla block states, entities, items, packets, and registries | `Pumpkin-MC/Extractor` (Fabric mod) | +| **2. Update Datapacks** | Ingest new data packs, tags, recipes, and Bedrock mappings | `assets/datapacks/`, `assets/bedrock` | +| **3. Version Declarations** | Register protocol version and ViaVersion/ViaBackwards remap nodes | `pumpkin-util`, `tools/pumpkin-codegen` | +| **4. Code Generation** | Regenerate zero-cost Rust data tables and registries | `pumpkin-codegen` -> `pumpkin-data` | +| **5. Compiler Fixes** | Update packet layouts, codecs, and data components | `pumpkin-protocol`, `pumpkin-inventory` | +| **6. Gameplay Logic** | Implement new block behaviors, item mechanics, and mob AI | `crates/pumpkin/src/block`, `item`, `entity` | +| **7. Multi-Version QA** | Validate backward compatibility, test suites, and client joins | `cargo test`, `pumpkin-gametest` | + +--- + +## Phase 1: Port the Extractor Mod First (`Pumpkin-MC/Extractor`) + +The [Extractor](https://github.com/Pumpkin-MC/Extractor) is a Kotlin-based Fabric mod that boots an actual Minecraft client or server instance to introspect live game registries and extract data into the exact JSON formats Pumpkin expects. + +### 1. Clone or Update the Extractor Repository +```bash +git clone https://github.com/Pumpkin-MC/Extractor +cd Extractor +``` + +### 2. Update `gradle.properties` +Bump the dependency versions to match the target Minecraft version, Yarn mappings, and Fabric ecosystem: + +```properties +minecraft_version=26.2 +yarn_mappings=26.2+build.1:v2 +loader_version=0.16.10 +fabric_version=0.115.0+26.2 +``` + +### 3. Resolve Extractor Compilation Errors +Minecraft and Fabric internal APIs often change between releases. Check and fix any broken extractors in `src/main/kotlin/de/snowii/extractor/extractors/`: +- **`Blocks.kt`**: Extracts block states, hardness, resistance, luminance, and collision box offsets. +- **`Items.kt`**: Extracts item IDs, max stack sizes, and default components. +- **`Entities.kt`**: Extracts entity IDs, dimensions (width, height), and eye heights. +- **`Packets.kt`**: Extracts packet IDs for handshake, status, login, configuration, and play states. +- **`SyncedRegistries.kt`**: Extracts dynamic registries synchronized to clients. +- **`TrackedData.kt`**: Extracts entity tracked data indices and serializers. +- **`MultiNoise.kt` & `Biome.kt`**: Extracts world generation biome trees and noise parameters. + +### 4. Run the Extractor +Execute the server runner (or client runner for translation/screen dumps): + +```bash +./gradlew runServer +``` + +Once the extraction finishes, all dumped JSON files will be located in `run/pumpkin_extractor_output/`. + +### 5. Replace the Old Asset Files in Pumpkin +Copy the newly generated JSON files from `run/pumpkin_extractor_output/` and use them to **replace the old asset files** in Pumpkin: +- Replace the core game assets in `Pumpkin/assets/` (`blocks.json`, `items.json`, `entities.json`, `packets.json`, `properties.json`, `synced_registries.json`, `tracked_data.json`, `en_us.json`, etc.). +- Replace the world generation test files in `Pumpkin/crates/pumpkin-world/assets/`. + +--- + +## Phase 2: Update Assets & Datapacks + +Once the raw extractor assets are in place, update the versioned datapacks and supplemental files in Pumpkin: + +### 1. Add the Versioned Datapack +Extract the internal vanilla data pack from the new Minecraft server JAR and place it under `assets/datapacks//`: + +``` +assets/datapacks// +└── data/ + ├── minecraft/ + │ ├── advancement/ + │ ├── chat_type/ + │ ├── damage_type/ + │ ├── dimension_type/ + │ ├── enchantment/ + │ ├── item_modifier/ + │ ├── loot_table/ + │ ├── recipe/ + │ ├── tags/ + │ └── worldgen/ + └── c/ + └── tags/ (Conventional / Common Tags) +``` + +### 2. Update Versioned Assets +Update or generate the corresponding versioned metadata files: +- `assets/registry/_synced_registries.json`: Dynamic registries streamed to the client during the `Configuration` network state. +- `assets/tags/_tags.json`: Tag collections. +- `assets/tracked_data/_tracked_data.json`: Entity metadata indices. + +### 3. Update Bedrock Mappings +If porting Bedrock Edition support simultaneously, update the files in `assets/bedrock/` using the latest mappings from [GeyserMC](https://github.com/GeyserMC/Geyser) and Bedrock Dedicated Server (BDS). + +### 4. Decompile Sources for Reverse Engineering (Optional but Recommended) +For complex changes—such as worldgen noise router formulas and custom block collision shapes—decompile the matching vanilla JAR into `minecraft-sources-/` (using Vineflower or Fabric Loom with official Mojang mappings) for reference. + +--- + +## Phase 3: Register Version Constants & ViaVersion Mappings + +Pumpkin supports multi-version clients through automated protocol remapping powered by **ViaBackwards** / **ViaRewind** mapping tables. + +### 1. Declare Version in `pumpkin-util` +Open `crates/pumpkin-util/src/version.rs` and add the new version to [`JavaMinecraftVersion`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-util/src/version.rs): + +```rust +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum JavaMinecraftVersion { + // ... existing versions ... + V_1_21_4, + V_1_21_11, + V_26_2, // New target version +} + +impl JavaMinecraftVersion { + pub const fn protocol_version(&self) -> i32 { + match self { + // ... + Self::V_26_2 => 770, // Protocol version number + } + } + + pub const fn version_name(&self) -> &'static str { + match self { + // ... + Self::V_26_2 => "26.2", + } + } +} +``` + +### 2. Declare Version in `pumpkin-codegen` +Open `tools/pumpkin-codegen/src/version.rs` and add the identical enum variant to the codegen AST. + +### 3. Add ViaVersion / ViaBackwards Mappings +To enable clients on older versions to connect to the updated server (e.g., a `1.21.4` client connecting to a `26.2` server): +1. Obtain the NBT mapping file from the [ViaBackwards repository](https://github.com/ViaVersion/ViaBackwards): + `mappings-to.nbt` +2. Save it to: + `assets/viabackwards/data/mappings-to.nbt` +3. In `tools/pumpkin-codegen/src/remap/mod.rs`, link the new node into the `remap_nodes!` macro: + +```rust +let node_26_2 = $crate::remap::MappingNode { + version: $crate::version::JavaMinecraftVersion::V_26_2, + value: "../../assets/viabackwards/data/mappings-26.2to26.1.nbt", + child: Some(&node_26_1), +}; +$remapper.process(&node_26_2) +``` + +4. Check `tools/pumpkin-codegen/src/remap/` to ensure specific remap builders (`block_state.rs`, `item_id.rs`, `entity_id.rs`, `sound_id.rs`, `argument_type.rs`) correctly process any new remapping structures introduced by ViaVersion. + +--- + +## Phase 4: Run Code Generation (`pumpkin-codegen`) + +With all extractor assets, datapacks, and mapping nodes in place, execute the codegen tool from the workspace root: + +```bash +cargo run -p pumpkin-codegen +``` + +### What Gets Regenerated? +`pumpkin-codegen` generates zero-cost Rust data tables into `crates/pumpkin-data/src/generated/`: +- **`block.rs`**: All block IDs, properties, state masks, and collision shapes. +- **`item.rs`**: Items, default components, and tool tiers. +- **`entity_type.rs` & `tracked_data.rs`**: Entity type IDs, default bounding boxes, and metadata serializers. +- **`packet.rs`**: Serverbound and clientbound packet ID constants across protocol states. +- **`recipes.rs`**: All crafting, cooking, and stonecutting recipes parsed from the new datapack. +- **`tag.rs`**: Tag lookups (`#minecraft:mineable/axe`, `#c:iron_ingots`). +- **`remap/*.rs`**: State remap lookup tables (`block_state_remap.rs`, `item_id_remap.rs`, `entity_id_remap.rs`, `sound_id_remap.rs`). + +--- + +## Phase 5: Resolve Workspace Compilation Errors + +After regenerating `pumpkin-data`, the Rust compiler will flag breaking changes across the workspace. Run: + +```bash +cargo check --workspace +``` + +### Common Compiler Breakages & Fixes: + +#### 1. Packet ID & Layout Changes (`pumpkin-protocol`) +Mojang frequently adds, moves, or alters packet IDs and payloads: +- Update `pumpkin-protocol/src/java/client/` and `pumpkin-protocol/src/java/server/`. +- Inspect packets like `LoginSuccess`, `PlayLogin`, `Respawn`, and `PlayerSpawnData` for newly added fields (e.g., new game mode flags, dimension attributes, or teleportation IDs). +- For Bedrock Edition, check `StartGame`, `NetworkItem`, and container transaction packets in `pumpkin-protocol/src/bedrock/`. + +#### 2. Data Component Changes +If items switched from older NBT structures to new Data Components (or if components were renamed/restructured): +- Update `crates/pumpkin-data/src/data_component.rs`. +- Update serialization and deserialization in `pumpkin-inventory`. + +#### 3. Registry Synchronization +During the `Configuration` phase, servers send dynamic registry contents to Java clients. If new registries were added to vanilla Minecraft (e.g., trim materials, banner patterns, instrument sounds): +- Verify `crates/pumpkin-data/src/generated/registry.rs`. +- Ensure `pumpkin/src/net/java/config.rs` streams all required registry codecs to the client. + +--- + +## Phase 6: Implement Logic for New Blocks, Items & Entities + +Once the server compiles, any newly introduced gameplay content must be implemented. + +### 1. New Blocks +1. Locate new block identifiers in `pumpkin-data::Block`. +2. Create a behavior module under `crates/pumpkin/src/block/blocks/.rs` implementing [`BlockBehaviour`](/developer/blocks/adding-blocks): + ```rust + pub struct NewBlockBehaviour; + + impl BlockBehaviour for NewBlockBehaviour { + fn on_use(&self, block: &Block, player: &Arc, location: BlockPos, world: &Arc) { + // Interaction logic + } + } + ``` +3. Register the behavior in the block registry in `crates/pumpkin/src/block/mod.rs`. + +### 2. New Items +1. Locate new item identifiers in `pumpkin-data::Item`. +2. If the item has unique right-click or use behavior (e.g., weapons, projectiles, instruments), create an item behavior in `crates/pumpkin/src/item/items/.rs` implementing [`ItemBehaviour`](/developer/items/adding-items). +3. If the item places entities (e.g., spawn eggs, boats, minecarts), configure entity spawning logic in `pumpkin/src/item/items/spawn_egg.rs`. +4. Register the behavior in `crates/pumpkin/src/item/mod.rs`. + +### 3. New Entities +1. Check `pumpkin-data::entity_type::EntityType` for new entity IDs. +2. Create an entity struct in `crates/pumpkin/src/entity/mob/` or `living/` extending [`LivingEntity`](/developer/entities/). +3. Implement `TrackedData` fields to match vanilla client expectations (e.g., poses, animations, glowing states). +4. Assign AI goals and navigation tasks using the [Mob AI & Goals system](/developer/entities/ai-and-mobs). +5. Add spawn packet construction in `crates/pumpkin/src/entity/spawning.rs`. + +--- + +## Phase 7: Multi-Version & ViaVersion Verification + +Ensure that older clients can still connect seamlessly: + +1. **Verify Generated Remap Tables**: + Check `crates/pumpkin-data/src/generated/block_state_remap.rs` and `item_id_remap.rs`. Ensure that novel block states fall back gracefully to compatible equivalents on older client versions (e.g., new wood variants mapping to oak or spruce). +2. **Packet Translation**: + Verify that packet writers in `pumpkin-protocol` apply `remap` functions when serializing data for a connection whose negotiated `protocol_version` is older than the server's native version. + +--- + +## Phase 8: Testing & Verification Checklist + +Before opening a Pull Request for a version port, complete the following QA checklist: + +- [ ] **Automated Tests**: + ```bash + cargo test --workspace + ``` +- [ ] **Game Test Suite**: + ```bash + cargo test -p pumpkin-gametest + ``` +- [ ] **Strict Linter Rules**: + ```bash + cargo clippy --workspace --all-targets -- -D warnings + ``` +- [ ] **Spellcheck**: + ```bash + typos + ``` +- [ ] **In-Game Java Client Test**: + - Connect with the native new Minecraft client version. + - Test login, world loading, chunk generation, and player movement. + - Place and break new blocks; verify collision boxes and redstone reactivity. + - Use and equip new items; test attack speed, durability, and animations. + - Spawn and fight new entities; test animations, hurt sounds, and drops. +- [ ] **In-Game Older Version Client Test**: + - Connect with an older supported client version (e.g., 1.21.x). + - Verify that blocks, items, and entities translate without causing client crashes or desyncs. +- [ ] **In-Game Bedrock Client Test**: + - Verify that Bedrock clients can authenticate, join, load world chunks, and view custom items. diff --git a/docs/nl/plugin-dev/c/first-command.md b/docs/nl/plugin-dev/c/first-command.md new file mode 100644 index 0000000..3ac3ebd --- /dev/null +++ b/docs/nl/plugin-dev/c/first-command.md @@ -0,0 +1,90 @@ +# Creating Your First Command + +Writing commands in C for Pumpkin is low-overhead, fast, and uses C WIT bindings. + +--- + +## 1. Quick Example + +Here is a complete C plugin file (`main.c`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```c [main.c] +#include +#include "pumpkin.h" + +void plugin_on_load(pumpkin_context_t ctx) { + // 1. Register permission node + pumpkin_permission_t perm = { + .node = "my_c_plugin:hello", + .description = "Allows executing the /hello command", + .default_access = PERMISSION_DEFAULT_ALLOW + }; + pumpkin_register_permission(ctx, &perm); + + // 2. Build and register command tree + const char* names[] = {"hello", "hi"}; + pumpkin_command_t cmd = pumpkin_command_new(names, 2, "Greets the player"); + + pumpkin_register_command(ctx, cmd, "my_c_plugin:hello"); +} + +int32_t plugin_handle_command(uint32_t command_id, pumpkin_sender_t sender, pumpkin_server_t server) { + pumpkin_sender_send_message(sender, "Hello from C Plugin!"); + return 1; +} + +pumpkin_metadata_t plugin_get_metadata() { + pumpkin_metadata_t meta = { + .name = "my_c_plugin", + .version = "0.1.0", + .description = "My first Pumpkin C plugin" + }; + return meta; +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Struct +Populate a `pumpkin_permission_t` struct and call `pumpkin_register_permission`: +```c +pumpkin_permission_t perm = { + .node = "my_c_plugin:hello", + .description = "Allows executing the /hello command", + .default_access = PERMISSION_DEFAULT_ALLOW +}; +pumpkin_register_permission(ctx, &perm); +``` + +### Step 2: Build the Command Tree +Create the command tree by passing name array and description: +```c +const char* names[] = {"hello", "hi"}; +pumpkin_command_t cmd = pumpkin_command_new(names, 2, "Greets the player"); +``` + +### Step 3: Register Command & Handle Execution +Pass the command handle to `pumpkin_register_command` and handle responses inside `plugin_handle_command`: +```c +pumpkin_register_command(ctx, cmd, "my_c_plugin:hello"); +``` + +::: + +--- + +## Next Steps + +- Check out the [C Quick Start Guide](./quick-start) to set up your compiler toolchain. diff --git a/docs/nl/plugin-dev/csharp/first-command.md b/docs/nl/plugin-dev/csharp/first-command.md new file mode 100644 index 0000000..c2a34ca --- /dev/null +++ b/docs/nl/plugin-dev/csharp/first-command.md @@ -0,0 +1,110 @@ +# Creating Your First Command + +Registering custom commands in Pumpkin using C# provides a strongly-typed, clean structure. + +--- + +## 1. Quick Example + +Here is a complete C# plugin file (`MyPlugin.cs`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```csharp [MyPlugin.cs] +using PluginWorld; +using PluginWorld.wit.Exports.pumpkin.plugin.v0_1_0; +using PluginWorld.wit.Imports.pumpkin.plugin.v0_1_0; + +namespace MyPumpkinPlugin; + +public class MyPlugin : IPluginWorldExports, IMetadataExports +{ + public static void InitPlugin() { } + + public static void OnLoad(IContextImports.Context context) + { + // 1. Register permission node + var perm = new IPermissionImports.Permission( + "my_csharp_plugin:hello", + "Allows executing the /hello command", + IPermissionImports.PermissionDefault.Allow, + [] + ); + IContextImports.RegisterPermission(context, perm); + + // 2. Build and register command tree + var command = ICommandImports.Command.New(["hello", "hi"], "Greets the player"); + IContextImports.RegisterCommand(context, command, "my_csharp_plugin:hello"); + } + + public static void OnUnload(IContextImports.Context context) { } + + public static IEventImports.Event HandleEvent(uint eventId, IServerImports.Server server, IEventImports.Event @event) + { + return @event; + } + + public static int HandleCommand(uint commandId, ICommandImports.CommandSender sender, IServerImports.Server server, ICommandImports.ConsumedArgs args) + { + ICommandImports.SendMessage(sender, "Hello from C# Plugin!"); + return 1; + } + + public static void HandleTask(uint handlerId, IServerImports.Server server) { } + + public static IMetadataExports.PluginMetadata Metadata() + { + return new IMetadataExports.PluginMetadata( + "my_csharp_plugin", + "0.1.0", + "My first Pumpkin C# plugin", + ["Developer"], + [] + ); + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Node +Construct an `IPermissionImports.Permission` record and register it via `IContextImports.RegisterPermission`: +```csharp +var perm = new IPermissionImports.Permission( + "my_csharp_plugin:hello", + "Allows executing the /hello command", + IPermissionImports.PermissionDefault.Allow, + [] +); +IContextImports.RegisterPermission(context, perm); +``` + +### Step 2: Build the Command Tree +Instantiate your command with primary names and description: +```csharp +var command = ICommandImports.Command.New(["hello", "hi"], "Greets the player"); +``` + +### Step 3: Register and Handle +Register the command with `IContextImports.RegisterCommand` and process incoming executions inside `HandleCommand`: +```csharp +IContextImports.RegisterCommand(context, command, "my_csharp_plugin:hello"); +``` + +::: + +--- + +## Next Steps + +- Follow the [C# Quick Start Guide](./quick-start) to configure `.csproj` and build WebAssembly outputs. diff --git a/docs/nl/plugin-dev/d/quick-start.md b/docs/nl/plugin-dev/d/quick-start.md new file mode 100644 index 0000000..64c54c4 --- /dev/null +++ b/docs/nl/plugin-dev/d/quick-start.md @@ -0,0 +1,130 @@ +# Quick Start + +Deze handleiding helpt je op weg met het schrijven van Pumpkin-serverplugins met de programmeertaal [D](https://dlang.org/). + +D-plugins voor Pumpkin worden gecompileerd naar WebAssembly-componenten (Wasm) met behulp van de officiële [`pumpkin-api-d`](https://github.com/Pumpkin-MC/pumpkin-api-d) bindings. + +--- + +## Vereisten + +Voordat je D-plugins voor Pumpkin bouwt, zorg ervoor dat het volgende is geïnstalleerd: + +- **[LDC](https://github.com/ldc-developers/ldc)**: Versie 1.43 of nieuwer (LLVM-gebaseerde D-compiler). +- **`addon-wasi`**: Het WASI-doelpakket voor LDC. +- **[DUB](https://dub.pm/)**: Het D-pakket- en buildbeheerprogramma (meestal inbegrepen bij LDC). + +--- + +## 1. Het project opzetten + +Maak een nieuwe map voor je pluginproject en voeg een `dub.json`-configuratiebestand toe: + +```bash +mkdir my-d-plugin +cd my-d-plugin +``` + +Maak `dub.json` aan in de hoofdmap van het project: + +```json [dub.json] +{ + "name": "my-d-plugin", + "description": "Een minimale Pumpkin-plugin gebouwd met D.", + "license": "proprietary", + "authors": ["JouwNaam"], + "copyright": "Copyright © 2026, JouwNaam", + + "dependencies": { + "pumpkin-api-d": "~>0.1.0" + }, + + "targetType": "executable", + "dflags": ["-Xcc=-mexec-model=reactor"], + "buildTypes": { + "debug": { + "buildOptions": ["debugMode", "debugInfo"] + }, + "release": { + "buildOptions": ["optimize", "inline"], + "dflags": ["-L-S"] + } + } +} +``` + +> [!NOTE] +> De vlag `-Xcc=-mexec-model=reactor` is vereist door de WebAssembly-linker zodat de plugin wordt behandeld als een reactor (een component met geëxporteerde toegangspunten) in plaats van een zelfstandig programma met een afsluitpunt. + +--- + +## 2. De plugin schrijven + +Maak een map `source` aan en plaats je code in `source/app.d`: + +```d [source/app.d] +module plugin; + +import wit.pumpkin.plugin.plugin; +import wit.common; +import pumpkin.register; + +struct MyPlugin { + @GetMetadata PluginMetadata getMetadata() + { + immutable(WitString)[$] authors = [ + "JouwNaam".witList + ]; + + return (immutable PluginMetadata( + name: "my-d-plugin".witList, + version_: "0.1.0".witList, + authors: authors.witList, + description: "Een voorbeeldplugin in D.".witList, + dependencies: WitList!WitString(), + permissions: WitList!WitString() + )).witClone; + } + + @OnLoad + Result!(void, WitString) onLoad(Context ctx) + { + scope (exit) + ctx.witDrop; // Binnenkomende bronnen moeten na gebruik worden vrijgegeven + + log(Level.info, "Hallo vanuit de D-plugin!".witList); + return ok!WitString; + } +} + +mixin RegisterPlugin!MyPlugin; +``` + +--- + +## 3. De plugin bouwen + +Compileer je plugin naar een WebAssembly-component voor `wasm32-wasip2`: + +```bash +dub build -b release -a wasm32-wasip2 +``` + +Dit genereert `my-d-plugin.wasm` in je projectmap. + +--- + +## 4. De plugin uitvoeren + +1. Kopieer het gecompileerde `.wasm`-bestand naar de map `plugins/` van je Pumpkin-server: + ```bash + cp my-d-plugin.wasm /pad/naar/pumpkin/plugins/ + ``` +2. Start of herstart Pumpkin: + ```bash + ./pumpkin + ``` +3. Controleer de serverlogs om te bevestigen dat je plugin is geladen: + ```text + [INFO] Hallo vanuit de D-plugin! + ``` diff --git a/docs/nl/plugin-dev/go/basic-logic.md b/docs/nl/plugin-dev/go/basic-logic.md new file mode 100644 index 0000000..7e0e4a6 --- /dev/null +++ b/docs/nl/plugin-dev/go/basic-logic.md @@ -0,0 +1,60 @@ +# Writing the basic logic + +## Plugin Entry Point + +In Go, your plugin must implement the `api.Plugin` interface and register itself in the `init()` function. You also need to import `wit_exports` for WebAssembly compatibility. + +:::code-group + +```go [main.go] +package main + +import ( + "github.com/Pumpkin-MC/pumpkin-api-go/api" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_context" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_logging" + _ "github.com/Pumpkin-MC/pumpkin-api-go/pkg/wit_exports" // Required for WASM exports +) + +type MyPlugin struct { + api.DefaultPlugin +} + +func (p *MyPlugin) Metadata() api.Metadata { + return api.Metadata{ + Name: "my-go-plugin", + Version: "0.1.0", + Authors: []string{"you"}, + } +} + +func (p *MyPlugin) OnLoad(ctx *pumpkin_plugin_context.Context) { + pumpkin_plugin_logging.Log(pumpkin_plugin_logging.LevelInfo(), "Go plugin loaded!") +} + +func init() { + api.RegisterPlugin(&MyPlugin{}) +} + +func main() {} +``` + +::: + +## Compiling the plugin + +To compile your plugin to WebAssembly, you must use **TinyGo**. The standard Go compiler does not yet support the specific WASI targets required by Pumpkin in the same way. + +Run the following command in your project folder: + +```bash +tinygo build -o my_plugin.wasm -target=wasi main.go +``` + +This will generate a `my_plugin.wasm` file that can be loaded by Pumpkin. + +## Testing the plugin + +Installing a plugin is as simple as putting the plugin binary (`.wasm`) into the `plugins/` folder of your Pumpkin server! + +When you start up the server and run the `/plugins` command, you should see your plugin listed. diff --git a/docs/nl/plugin-dev/go/first-command.md b/docs/nl/plugin-dev/go/first-command.md new file mode 100644 index 0000000..f20ffdd --- /dev/null +++ b/docs/nl/plugin-dev/go/first-command.md @@ -0,0 +1,103 @@ +# Creating Your First Command + +Registering commands in Go with Pumpkin is built on standard Go idiom using TinyGo WASM compilation. + +--- + +## 1. Quick Example + +Here is a complete Go plugin file (`main.go`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```go [main.go] +package main + +import ( + "github.com/Pumpkin-MC/pumpkin-api-go/api" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_command" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_context" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_permission" + _ "github.com/Pumpkin-MC/pumpkin-api-go/pkg/wit_exports" +) + +type MyPlugin struct { + api.DefaultPlugin +} + +func (p *MyPlugin) Metadata() api.Metadata { + return api.Metadata{ + Name: "my_go_plugin", + Version: "0.1.0", + Authors: []string{"Developer"}, + Description: "My first Pumpkin Go plugin", + } +} + +func (p *MyPlugin) OnLoad(ctx *pumpkin_plugin_context.Context) { + // 1. Register permission node + ctx.RegisterPermission(pumpkin_plugin_permission.Permission{ + Node: "my_go_plugin:hello", + Description: "Allows executing the /hello command", + Default: pumpkin_plugin_permission.PermissionDefaultAllow(), + }) + + // 2. Build and register command tree + cmd := pumpkin_plugin_command.New([]string{"hello", "hi"}, "Greets the player") + ctx.RegisterCommand(cmd, "my_go_plugin:hello") +} + +func (p *MyPlugin) HandleCommand(cmdID uint32, sender pumpkin_plugin_command.CommandSender) int32 { + sender.SendMessage("Hello from Go Plugin!") + return 1 +} + +func init() { + api.RegisterPlugin(&MyPlugin{}) +} + +func main() {} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Register Permission +Call `ctx.RegisterPermission` inside `OnLoad`: +```go +ctx.RegisterPermission(pumpkin_plugin_permission.Permission{ + Node: "my_go_plugin:hello", + Description: "Allows executing the /hello command", + Default: pumpkin_plugin_permission.PermissionDefaultAllow(), +}) +``` + +### Step 2: Build the Command Tree +Instantiate the command tree with `pumpkin_plugin_command.New`: +```go +cmd := pumpkin_plugin_command.New([]string{"hello", "hi"}, "Greets the player") +``` + +### Step 3: Register & Execute +Register the command with `ctx.RegisterCommand` and handle execution in `HandleCommand`: +```go +ctx.RegisterCommand(cmd, "my_go_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Follow the [Go Quick Start Guide](./quick-start) to set up TinyGo compilation. +- Learn more about [Basic Logic](./basic-logic). diff --git a/docs/nl/plugin-dev/go/quick-start.md b/docs/nl/plugin-dev/go/quick-start.md new file mode 100644 index 0000000..90bacb4 --- /dev/null +++ b/docs/nl/plugin-dev/go/quick-start.md @@ -0,0 +1,35 @@ +# Creating a new project + +Pumpkin plugins use the [Go](https://go.dev/) programming language and are compiled to WebAssembly. + +## Prerequisites + +Before we can start, make sure you have the following installed: +- [Go](https://go.dev/doc/install) (latest version recommended) +- [TinyGo](https://tinygo.org/getting-started/install/) (required for WASM compilation) + +## Initializing a new module + +First, create a new directory for your project and initialize a Go module: + +```bash +mkdir hello-pumpkin +cd hello-pumpkin +go mod init github.com/yourname/hello-pumpkin +``` + +Next, add the Pumpkin Go API as a dependency: + +```bash +go get github.com/Pumpkin-MC/pumpkin-api-go +``` + +## Project Structure + +Your project should have at least a `main.go` file. The structure should look like this: + +```text +├── go.mod +├── go.sum +└── main.go +``` diff --git a/docs/nl/plugin-dev/introduction.md b/docs/nl/plugin-dev/introduction.md index de69e41..3f05e45 100644 --- a/docs/nl/plugin-dev/introduction.md +++ b/docs/nl/plugin-dev/introduction.md @@ -15,4 +15,6 @@ De Pumpkin Plugin API haalt inspiratie uit de Spigot/Bukkit plugin API op veel p * [C# API](https://github.com/Pumpkin-MC/pumpkin-api-cs) * [Go API](https://github.com/Pumpkin-MC/pumpkin-api-go) * [C API](https://github.com/Pumpkin-MC/pumpkin-api-c) +* [Kotlin API](https://github.com/Pumpkin-MC/pumpkin-api-kt) +* [D API](https://github.com/Pumpkin-MC/pumpkin-api-d) diff --git a/docs/nl/plugin-dev/kotlin/first-command.md b/docs/nl/plugin-dev/kotlin/first-command.md new file mode 100644 index 0000000..12831f5 --- /dev/null +++ b/docs/nl/plugin-dev/kotlin/first-command.md @@ -0,0 +1,94 @@ +# Creating Your First Command + +Registering custom commands in Pumpkin using Kotlin is concise, idiomatic, and strongly typed. + +--- + +## 1. Quick Example + +Here is a complete Kotlin plugin (`Plugin.kt`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```kotlin [src/wasmWasiMain/kotlin/plugin/Plugin.kt] +package plugin + +import pumpkin.plugin.context.Context +import pumpkin.plugin.command.Command +import pumpkin.plugin.permission.Permission +import pumpkin.plugin.permission.PermissionDefault + +class MyPlugin { + fun onLoad(ctx: Context) { + // 1. Register permission node + ctx.registerPermission( + Permission( + node = "my_kotlin_plugin:hello", + description = "Allows executing the /hello command", + default = PermissionDefault.ALLOW, + children = emptyList() + ) + ) + + // 2. Build and register command tree + val cmd = Command( + names = listOf("hello", "hi"), + description = "Greets the player" + ) + ctx.registerCommand(cmd, "my_kotlin_plugin:hello") + } + + fun handleCommand(sender: pumpkin.plugin.command.CommandSender): Int { + sender.sendMessage("Hello from Kotlin Plugin!") + return 1 + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Node +Construct a `Permission` object and pass it to `ctx.registerPermission`: +```kotlin +ctx.registerPermission( + Permission( + node = "my_kotlin_plugin:hello", + description = "Allows executing the /hello command", + default = PermissionDefault.ALLOW, + children = emptyList() + ) +) +``` + +### Step 2: Build the Command Tree +Instantiate `Command` with primary names and aliases: +```kotlin +val cmd = Command( + names = listOf("hello", "hi"), + description = "Greets the player" +) +``` + +### Step 3: Register with Context +Register the command with `ctx.registerCommand` and handle calls inside `handleCommand`: +```kotlin +ctx.registerCommand(cmd, "my_kotlin_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Follow the [Kotlin Quick Start Guide](./quick-start) to configure your `Makefile` and Kotlin/Wasm build tools. diff --git a/docs/nl/plugin-dev/kotlin/quick-start.md b/docs/nl/plugin-dev/kotlin/quick-start.md new file mode 100644 index 0000000..acefd9c --- /dev/null +++ b/docs/nl/plugin-dev/kotlin/quick-start.md @@ -0,0 +1,81 @@ +# Quick Start + +This guide will help you get started with writing Pumpkin server plugins using Kotlin. + +:::warning +Until the Kotlin + Wasm component toolchain matures more, there will be some oddity and inconvenience. + +Bugs are also expected. +::: + +## Prerequisites + +Before you start, ensure you have the following installed: +- JDK 17 or later + - To run Gradle 9.4 +- [Rust](https://rust-lang.org/) + - This is required as a key component (wit-bindgen) is written and Rust must be built from a particular Kotlin-enabled fork. + - You only need a default Rust install for your host platform. NOT for any WebAssembly targets +- [wasm-tools](https://github.com/bytecodealliance/wasm-tools) + - To bundle the Wasm Kotlin produces into a component +- Make (e.g. [GNU Make](https://www.gnu.org/software/make/)) + - To run the convenience `Makefile`. You may opt to do without and perform the contained steps manually. + +## Setting up the project + +Unlike most of the other APIs packages available, [pumpkin-api-kt](https://github.com/Pumpkin-MC/pumpkin-api-kt) is a TEMPLATE, not e.g. a Maven package. + +To start off, clone the template (and rename it as you please): +```sh +git clone --recurse-submodules https://github.com/Pumpkin-MC/pumpkin-api-kt +mv pumpkin-api-kt my_kotlin_plugin +cd my_kotlin_plugin +``` + +Next, we'll want to update the `wit` submodule so we are using the latest version of the Pumpkin plugin API. + +```sh +cd wit +git pull origin master +cd .. +``` + +Finally, rename the Gradle project. Change the `rootProject.name` in `settings.gradle.kts`, AND the `PROJECT_NAME` in `Makefile`. They must both match. Whatever you name the project, is going to be the filename of the Wasm produced. + +## Creating your first plugin + +As part of the template, a basic plugin is implemented in `src/wasmWasiMain/kotlin/plugin/Plugin.kt`. + +Feel free to modify the metadata at the bottom. However, before changing anything else, it's recommended that you build the plugin (see next section) first so that the bindings are generated and IDE completion works for `pumpkin`. + +## Building the plugin + +To build your plugin into a WebAssembly component: + +```sh +make +``` + +The compiled `.wasm` file will be located in `build`. You can place this file in the `plugins` folder of your Pumpkin server. + +Note that doing this the first time might take awhile, while it builds `wit-bindgen` from Rust source. + +Running `make` will check for `wit-bindgen` updates each time. You may want to run instead `make componentify` after the initial setup to avoid this. + +## Troubleshooting + +### Linker errors +If you start getting errors like +``` +main ThreadId(01) pumpkin::plugin: Failed to load plugin from +"./plugins/my_plugin.wasm": Wasm plugin initialization error: plugin failed +to load with error: component imports instance 'pumpkin:plugin/gui@0.1.0', but +a matching implementation was not found in the linker +``` +or other "linker" errors when loading your plugin into Pumpkin, update the `wit` submodule. + +```sh +cd wit +git pull origin master +cd .. +``` diff --git a/docs/nl/plugin-dev/migrating-from-bukkit/commands.md b/docs/nl/plugin-dev/migrating-from-bukkit/commands.md new file mode 100644 index 0000000..8878c4a --- /dev/null +++ b/docs/nl/plugin-dev/migrating-from-bukkit/commands.md @@ -0,0 +1,171 @@ +# Commando's migreren van Bukkit + +In Bukkit worden commando's gedefinieerd in `plugin.yml` en afgehandeld via `CommandExecutor`-klassen of de Brigadier-bibliotheek. + +In Pumpkin worden commando's opgebouwd met behulp van een **op Brigadier geïnspireerde commandoboom** en programmatisch geregistreerd via `context.register_command`. Er worden geen YAML-configuratiebestanden gebruikt. + +--- + +## Belangrijkste verschillen + +| Functie | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Declaratie** | Gedeclareerd in `plugin.yml` onder `commands:` | Opgebouwd in code met behulp van `Command::new(...)` | +| **Permissies** | Gekoppeld in `plugin.yml` of gecontroleerd via `player.hasPermission()` | Expliciete permissienode gekoppeld tijdens `register_command()` | +| **Tab-aanvulling** | Handmatige string-matching in `TabCompleter` | Automatische boomgebaseerde tab-aanvulling aan de clientzijde | +| **Subcommando's** | Handmatig `if (args[0].equalsIgnoreCase("..."))` | Geneste `Command`-boomknooppunten | + +--- + +## Codevergelijking: Eenvoudig commando + +Vergelijk het registreren van een `/feed`-commando in Bukkit vs. Pumpkin: + +### 1. Bukkit-implementatie (Java) + +```java [FeedCommand.java] +// 1. Registered in plugin.yml: +// commands: +// feed: +// description: Feeds the player +// permission: myplugin.feed + +public class FeedCommand implements CommandExecutor { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("Only players can execute this command!"); + return true; + } + Player player = (Player) sender; + if (!player.hasPermission("myplugin.feed")) { + player.sendMessage("No permission!"); + return true; + } + player.setFoodLevel(20); + player.sendMessage("Your hunger has been satisfied!"); + return true; + } +} +``` + +### 2. Pumpkin-implementaties + +::: code-group + +```rust [Rust] +use pumpkin_plugin_api::{ + command::{CommandHandler, CommandSender, ConsumedArgs, CommandError, Command}, + permission::{Permission, PermissionDefault}, + Context, Plugin, PluginMetadata, Server, +}; + +struct FeedExecutor; + +impl CommandHandler for FeedExecutor { + fn handle(&self, sender: CommandSender, _server: Server, _args: ConsumedArgs) -> Result { + sender.send_message("Your hunger has been satisfied!"); + Ok(1) + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { MyPlugin } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "feed_plugin".into(), + version: "1.0.0".into(), + authors: vec!["Developer".into()], + description: "Feed command plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Register permission node (Replaces plugin.yml permission) + context.register_permission(&Permission { + node: "feed_plugin:feed".to_string(), + description: "Allows executing /feed".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), + })?; + + // 2. Build command tree + let command = Command::new(&["feed".to_string(), "eat".to_string()], "Feeds the player") + .execute(FeedExecutor); + + // 3. Register with context + context.register_command(command, "feed_plugin:feed")?; + + Ok(()) + } +} +``` + +```python [Python] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + permission, command, context, server +) + +class FeedExecutor: + def handle(self, sender: command.CommandSender, srv: server.Server, args: command.ConsumedArgs) -> int: + sender.send_message("Your hunger has been satisfied!") + return 1 + +class FeedPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata(name="feed_plugin", version="1.0.0", authors=["Dev"], description="Feed command") + + def on_load(self, ctx: context.Context) -> None: + ctx.register_permission(permission.Permission( + node="feed_plugin:feed", + description="Allows executing /feed", + default=permission.PermissionDefault.ALLOW, + children=[] + )) + + cmd = command.Command(["feed", "eat"], "Feeds the player") + cmd.execute(FeedExecutor()) + ctx.register_command(cmd, "feed_plugin:feed") + + register_plugin(FeedPlugin) +``` + +```kotlin [Kotlin] +package plugin + +import pumpkin.plugin.context.Context +import pumpkin.plugin.command.Command +import pumpkin.plugin.command.CommandSender +import pumpkin.plugin.permission.Permission +import pumpkin.plugin.permission.PermissionDefault + +class FeedPlugin { + fun onLoad(ctx: Context) { + ctx.registerPermission( + Permission( + node = "feed_plugin:feed", + description = "Allows executing /feed", + default = PermissionDefault.ALLOW, + children = emptyList() + ) + ) + + val cmd = Command( + names = listOf("feed", "eat"), + description = "Feeds the player" + ) + ctx.registerCommand(cmd, "feed_plugin:feed") + } + + fun handleCommand(sender: CommandSender): Int { + sender.sendMessage("Your hunger has been satisfied!") + return 1 + } +} +``` + +::: diff --git a/docs/nl/plugin-dev/migrating-from-bukkit/configuration.md b/docs/nl/plugin-dev/migrating-from-bukkit/configuration.md new file mode 100644 index 0000000..4b40b01 --- /dev/null +++ b/docs/nl/plugin-dev/migrating-from-bukkit/configuration.md @@ -0,0 +1,69 @@ +# Configuratie & Gegevensopslag migreren + +Bukkit-plugins leunen sterk op `config.yml`, `YamlConfiguration` en het serialisatiesysteem van Bukkit-configuraties. + +In Pumpkin draaien WebAssembly-plugins in een veilige sandboxomgeving waar configuratiebestanden native kunnen worden afgehandeld met behulp van standaardformaten zoals **TOML**, **JSON** of **YAML**. + +--- + +## Belangrijkste verschillen + +| Functie | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Configuratiebestandsformaat** | `config.yml` (YAML-formaat) | Flexibel: TOML, JSON of aangepaste bestanden | +| **Standaardconfiguratie genereren** | `saveDefaultConfig()` | Configuratie lezen uit de pluginmap of ingesloten WASM-asset | +| **Gegevens parsen** | `getConfig().getString(...)` | Native serde- / JSON- / TOML-deserialisatie | +| **Bestands-I/O sandboxing** | Directe toegang tot het bestandssysteem overal | Gesandboxte maptoegang verleend aan de WASM-plugin | + +--- + +## Codevergelijking: Configuratie laden + +### 1. Bukkit-implementatie (Java) + +```java [ConfigExample.java] +public class ConfigExample extends JavaPlugin { + @Override + public void onEnable() { + saveDefaultConfig(); // Saves config.yml from jar resources + FileConfiguration config = getConfig(); + + String welcomeMsg = config.getString("welcome-message", "Default welcome!"); + int maxItems = config.getInt("max-items", 10); + getLogger().info("Loaded message: " + welcomeMsg); + } +} +``` + +### 2. Pumpkin-implementatie (Rust) + +In Pumpkin kun je `serde::Deserialize` afleiden om overzichtelijke TOML-configuratiebestanden te parsen: + +```rust [config.rs] +use serde::Deserialize; +use std::fs; + +#[derive(Deserialize, Debug)] +pub struct PluginConfig { + pub welcome_message: String, + pub max_items: i32, +} + +impl Default for PluginConfig { + fn default() -> Self { + Self { + welcome_message: "Default welcome!".to_string(), + max_items: 10, + } + } +} + +pub fn load_config() -> PluginConfig { + let config_path = "plugins/my_plugin/config.toml"; + if let Ok(content) = fs::read_to_string(config_path) { + toml::from_str(&content).unwrap_or_default() + } else { + PluginConfig::default() + } +} +``` diff --git a/docs/nl/plugin-dev/migrating-from-bukkit/events.md b/docs/nl/plugin-dev/migrating-from-bukkit/events.md new file mode 100644 index 0000000..08c27dc --- /dev/null +++ b/docs/nl/plugin-dev/migrating-from-bukkit/events.md @@ -0,0 +1,117 @@ +# Events migreren van Bukkit + +In Bukkit worden events afgehandeld door klassen te maken die `Listener` implementeren en methoden te annoteren met `@EventHandler`. + +In Pumpkin zijn event-handlers structs of objecten die expliciet worden geregistreerd via `context.register_event_handler(...)`. Pumpkin maakt bovendien onderscheid tussen **blokkerende (blocking)** en **niet-blokkerende (non-blocking)** events om de gelijktijdigheid (concurrency) van de server te maximaliseren. + +--- + +## Belangrijkste verschillen + +| Functie | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Listener-declaratie** | `@EventHandler`-annotatie op methoden | `EventHandler` trait- / interface-implementatie | +| **Registratie** | `pm.registerEvents(listener, plugin)` | `context.register_event_handler(handler, priority, is_blocking)` | +| **Uitvoeringsmodel** | Uitvoering op hoofdthread (`EventPriority`) | Keuze tussen **Blokkerend** (sequentieel/annuleerbaar) en **Niet-blokkerend** (gelijktijdig) | +| **Annulering** | `event.setCancelled(true)` | `event.cancel()` op blokkerende events | + +--- + +## Blokkerende vs. niet-blokkerende events in Pumpkin + +In tegenstelling tot Bukkit, waar alle event-handlers sequentieel op de hoofdserverthread draaien, kun je in Pumpkin aangeven of een event-handler **blokkerend** is: + +```rust +// Registratiesignatuur in Rust: +context.register_event_handler(handler, priority, is_blocking)?; +``` + +- **Blokkerend (`is_blocking = true`)**: Wordt uitgevoerd in sequentiële prioriteitsvolgorde. Kan eventgegevens wijzigen (bijv. join-bericht aanpassen) of het event annuleren. +- **Niet-blokkerend (`is_blocking = false`)**: Wordt gelijktijdig uitgevoerd over workerthreads. Ideaal voor logging, metrics of externe notificaties waarbij annulering niet nodig is. + +--- + +## Codevergelijking: Speler-join-event + +### 1. Bukkit-implementatie (Java) + +```java [JoinListener.java] +public class JoinListener implements Listener { + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + event.setJoinMessage("Welcome " + player.getName() + " to the server!"); + } +} + +// In JavaPlugin: +// getServer().getPluginManager().registerEvents(new JoinListener(), this); +``` + +### 2. Pumpkin-implementaties + +::: code-group + +```rust [Rust] +use pumpkin_plugin_api::{ + Context, Plugin, PluginMetadata, Server, + events::{EventData, EventHandler, EventPriority, PlayerJoinEvent}, + text::TextComponent, +}; + +struct JoinHandler; + +impl EventHandler for JoinHandler { + fn handle<'a>( + &'a self, + _server: Server, + mut event: EventData, + ) -> EventData { + event.join_message = TextComponent::text("Welcome to the Pumpkin server!"); + event + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { MyPlugin } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "join_plugin".into(), + version: "1.0.0".into(), + authors: vec!["Developer".into()], + description: "Join event handler".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Register blocking event handler with Normal priority + context.register_event_handler(JoinHandler, EventPriority::Normal, true)?; + Ok(()) + } +} +``` + +```python [Python] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + event, server, context +) + +class MyPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata(name="join_plugin", version="1.0.0", authors=["Dev"], description="Join event") + + def on_load(self, ctx: context.Context) -> None: + self.register_event(ctx, event.EventType.PLAYER_JOIN_EVENT, self.on_player_join) + + def on_player_join(self, srv: server.Server, evt: event.PlayerJoinEventData) -> event.PlayerJoinEventData: + print(f"Player joined!") + return evt + +register_plugin(MyPlugin) +``` + +::: diff --git a/docs/nl/plugin-dev/migrating-from-bukkit/index.md b/docs/nl/plugin-dev/migrating-from-bukkit/index.md new file mode 100644 index 0000000..b29c11e --- /dev/null +++ b/docs/nl/plugin-dev/migrating-from-bukkit/index.md @@ -0,0 +1,27 @@ +# Migratieoverzicht & Architectuur + +De overstap van plugin-ontwikkeling voor **Bukkit / Spigot / Paper** naar Pumpkin betekent een verschuiving van een Java-georiënteerd objectgeoriënteerd model naar een gecompileerd, meertalig **WebAssembly (WASM)** componentmodel. + +--- + +De onderstaande tabel vergelijkt het verouderde Bukkit/Paper-paradigma met de moderne WebAssembly-architectuur van Pumpkin: + +| Concept | Bukkit / Spigot / Paper | Pumpkin | +| :--- | :--- | :--- | +| **Taalondersteuning** | Java / Kotlin / Scala (JVM) | Rust, Python, Kotlin, C#, Go, C | +| **Binaire uitvoer** | `.jar` Java Archive | `.wasm` WebAssembly-component | +| **Plugin-beschrijving** | `plugin.yml`-bestand | Programmatische `PluginMetadata`-struct | +| **Levenscyclus-hooks** | `onEnable()` / `onDisable()` | `on_load(context)` / `on_unload(context)` | +| **Beveiliging & Isolatie** | Onbeperkte JVM-reflectie | Gesandboxt WASM-capabilitymodel | +| **Gelijktijdigheid (Concurrency)** | Single-threaded tick-lus (`BukkitScheduler`) | Multithreaded native uitvoering met async-runtime | + +--- + +## Gedetailleerde migratieonderwerpen + +Bekijk speciale handleidingen over het migreren van elk belangrijk plugin-subsysteem: + +- [Commando's migreren](./commands) — Overstappen van `getCommand().setExecutor()` en `plugin.yml` naar Brigadier-commandobomen. +- [Events migreren](./events) — Vervanging van `@EventHandler` en `Listener`-interfaces door Pumpkin's blokkerende vs. niet-blokkerende eventsysteem. +- [Inventarissen & GUI's migreren](./inventories) — Overstappen van `Bukkit.createInventory()` naar Pumpkin-container- en venster-handlers. +- [Configuratie & Gegevens migreren](./configuration) — Vervangt `getConfig()` / `config.yml` door native TOML, JSON of aangepaste opslag. diff --git a/docs/nl/plugin-dev/migrating-from-bukkit/inventories.md b/docs/nl/plugin-dev/migrating-from-bukkit/inventories.md new file mode 100644 index 0000000..5a19768 --- /dev/null +++ b/docs/nl/plugin-dev/migrating-from-bukkit/inventories.md @@ -0,0 +1,72 @@ +# Inventarissen & GUI's migreren van Bukkit + +Het maken van inventaris-GUI's (zoals kistmenu's, winkelinterfaces of aangepaste spelervensters) is een veelvoorkomende taak bij de ontwikkeling van Minecraft-plugins. + +In Bukkit worden aangepaste GUI's gemaakt met `Bukkit.createInventory()` en afgehandeld via `InventoryClickEvent`. In Pumpkin worden inventarisvensters beheerd via **Window-API's** en container-klikpakketten. + +--- + +## Belangrijkste verschillen + +| Functie | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Inventariscreatie** | `Bukkit.createInventory(owner, size, title)` | Venstercontainer-definities (`WindowType`) | +| **Vensters openen** | `player.openInventory(inv)` | `player.open_window(window)` | +| **Klikken onderscheppen** | `InventoryClickEvent`-afhandeling | Venster-klik callback-handlers | +| **Items stapelen** | `ItemStack` met `ItemMeta` | `ItemStack` met componenten / NBT | + +--- + +## Codevergelijking: Een aangepaste GUI-menu openen + +### 1. Bukkit-implementatie (Java) + +```java [MenuGUI.java] +public class MenuGUI implements Listener { + + public void openMenu(Player player) { + Inventory inv = Bukkit.createInventory(null, 9, Component.text("Custom Menu")); + + ItemStack item = new ItemStack(Material.DIAMOND); + ItemMeta meta = item.getItemMeta(); + meta.displayName(Component.text("Click Me!")); + item.setItemMeta(meta); + + inv.setItem(4, item); + player.openInventory(inv); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (event.getView().getTitle().equals("Custom Menu")) { + event.setCancelled(true); + if (event.getSlot() == 4) { + event.getWhoClicked().sendMessage("Diamond clicked!"); + } + } + } +} +``` + +### 2. Pumpkin-implementatie (Rust) + +```rust [gui.rs] +use pumpkin_plugin_api::{ + player::Player, + item::ItemStack, + window::{Window, WindowType}, + text::TextComponent, +}; + +pub fn open_custom_menu(player: &Player) { + // 1. Create a 9-slot chest window + let mut window = Window::new(WindowType::Generic9x1, TextComponent::text("Custom Menu")); + + // 2. Set item in slot 4 + let item = ItemStack::new("minecraft:diamond", 1); + window.set_item(4, Some(item)); + + // 3. Open window for player + player.open_window(window); +} +``` diff --git a/docs/nl/plugin-dev/python/first-command.md b/docs/nl/plugin-dev/python/first-command.md new file mode 100644 index 0000000..00058f4 --- /dev/null +++ b/docs/nl/plugin-dev/python/first-command.md @@ -0,0 +1,95 @@ +# Creating Your First Command + +Registering and handling custom commands in Pumpkin with Python is designed to be straightforward and expressive. + +--- + +## 1. Quick Example + +Here is a complete, minimal Python plugin (`main.py`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```python [main.py] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + permission, command, context, server +) + +class HelloCommand: + def handle(self, sender: command.CommandSender, srv: server.Server, args: command.ConsumedArgs) -> int: + sender.send_message("Hello from Python Plugin!") + return 1 + +class MyPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata( + name="my_python_plugin", + version="0.1.0", + authors=["Developer"], + description="My first Pumpkin Python plugin" + ) + + def on_load(self, ctx: context.Context) -> None: + # 1. Register permission node + ctx.register_permission(permission.Permission( + node="my_python_plugin:hello", + description="Allows executing the /hello command", + default=permission.PermissionDefault.ALLOW, + children=[] + )) + + # 2. Build the command tree with aliases & executor + cmd = command.Command(["hello", "hi"], "Greets the player") + cmd.execute(HelloCommand()) + + # 3. Register the command + ctx.register_command(cmd, "my_python_plugin:hello") + +register_plugin(MyPlugin) +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission +Commands in Pumpkin require a permission node. Register it with `permission.PermissionDefault.ALLOW` to allow execution by default: +```python +ctx.register_permission(permission.Permission( + node="my_python_plugin:hello", + description="Allows executing the /hello command", + default=permission.PermissionDefault.ALLOW, + children=[] +)) +``` + +### Step 2: Build the Command Tree +Create a `command.Command` instance with primary names and aliases, then attach your handler with `.execute(...)`: +```python +cmd = command.Command(["hello", "hi"], "Greets the player") +cmd.execute(HelloCommand()) +``` + +### Step 3: Register with Context +Pass the command tree and permission node string to `ctx.register_command`: +```python +ctx.register_command(cmd, "my_python_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Check out [Events](./events) to handle player actions and server events in Python. +- Learn more about [Basic Logic](./basic-logic). diff --git a/docs/nl/plugin-dev/rust/command/first-command.md b/docs/nl/plugin-dev/rust/command/first-command.md new file mode 100644 index 0000000..9840346 --- /dev/null +++ b/docs/nl/plugin-dev/rust/command/first-command.md @@ -0,0 +1,115 @@ +# Creating Your First Command + +Registering and handling custom commands in Pumpkin is designed to be developer-friendly, fast, and structured like Mojang's Brigadier system. + +--- + +## 1. Quick Example + +Here is a complete, minimal Rust plugin that registers a `/hello` command with permission checks and sends a response back to the player. + +```rust [src/lib.rs] +use pumpkin_plugin_api::{ + command::{CommandHandler, CommandSender, ConsumedArgs, CommandError, Command}, + permission::{Permission, PermissionDefault}, + Context, Plugin, PluginMetadata, Server, +}; + +struct HelloExecutor; + +impl CommandHandler for HelloExecutor { + fn handle( + &self, + sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + sender.send_message("Hello from Pumpkin!"); + Ok(1) + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { + MyPlugin + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "my_plugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Developer".into()], + description: "My first Pumpkin plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Register permission node + context.register_permission(&Permission { + node: "my_plugin:hello".to_string(), + description: "Allows executing the /hello command".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), + })?; + + // 2. Build the command tree with aliases & executor + let names = ["hello".to_string(), "hi".to_string()]; + let command = Command::new(&names, "Greets the player").execute(HelloExecutor); + + // 3. Register the command + context.register_command(command, "my_plugin:hello")?; + + Ok(()) + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission +Commands in Pumpkin require a permission node. Using `PermissionDefault::Allow` allows all players to execute it by default: +```rust +context.register_permission(&Permission { + node: "my_plugin:hello".to_string(), + description: "Allows executing the /hello command".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), +})?; +``` + +### Step 2: Build the Command Tree +Use `Command::new` to define primary names and aliases, then attach your `CommandHandler` struct with `.execute(...)`: +```rust +let names = ["hello".to_string(), "hi".to_string()]; +let command = Command::new(&names, "Greets the player") + .execute(HelloExecutor); +``` + +### Step 3: Register with Context +Pass the command tree and the permission node string to `context.register_command`: +```rust +context.register_command(command, "my_plugin:hello")?; +``` + +::: + +--- + +## Next Steps + +- Explore complex subcommands and argument parsing in the [Rock-Paper-Scissors Tutorial](./rock-paper-scissors). +- Learn how to handle inventory and GUI interactions in plugin logic. diff --git a/docs/nl/plugin-dev/rust/command/rock-paper-scissors.md b/docs/nl/plugin-dev/rust/command/rock-paper-scissors.md new file mode 100644 index 0000000..c1f188b --- /dev/null +++ b/docs/nl/plugin-dev/rust/command/rock-paper-scissors.md @@ -0,0 +1,255 @@ +# Writing a Command Handler + +In Minecraft, commands are the primary way for both regular players and server operators to +interact with the game on a lower level. They can be used to perform a wide range of tasks, +from simple chat messages to complex server management commands. In this tutorial, we will +create a basic Rock-Paper-Scissors command handler that allows players to play the game +against the server. + +Pumpkin has its own system for handling commands, which is based around each command having a +'tree', which defines the exact structure of the command and its arguments. Each node in the +tree represents a command or argument, and the tree is traversed to determine the command to +execute and its parameters. + +We would also like to thank [ploxxxy](https://github.com/ploxxxy) for writing the original +[Rock-Paper-Scissors plugin](https://github.com/ploxxxy/rock-paper-scissors-mc) from which +this tutorial is based. + +## Adding the basics + +Each command in Pumpkin is defined as a structure that implements the `CommandHandler` trait. +This trait requires the implementation of a `handle` method, which takes the sender, server, +and consumed arguments as parameters, and returns a `-> Result<(), CommandError>`. Let's +define this structure now: + +```rust +use pumpkin_plugin_api::{ + Server, + command::{CommandError, CommandSender, ConsumedArgs}, + commands::CommandHandler, +}; + +struct RockPaperScissorsExecutor; +impl CommandHandler for RockPaperScissorsExecutor { + fn handle( + &self, + _sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + Ok(1) + } +} +``` + +This code defines an empty structure `RockPaperScissorsExecutor` that implements the +`CommandExecutor` trait. The `handler` method is defined to return `Ok(1)` when called. + +## Adding helper enums + +First install the `rand` crate via + +```bash +cargo add rand +``` + +To make our lives easier, we will also define a couple of enums to represent the possible +choices and outcomes of the game, as well as well as a couple functions to generate random +choices and check the outcome. Add these to your plugin's code. + +```rust +use rand::{rng, Rng}; + +#[derive(PartialEq, Debug, Clone, Copy)] +enum Choice { + Rock, + Paper, + Scissors, +} + +enum Outcome { + Win, + Lose, + Draw, +} + +impl Choice { + pub fn beats(&self, other: &Choice) -> Outcome { + if self == other { + return Outcome::Draw; + } + + match (self, other) { + (Choice::Rock, Choice::Scissors) => Outcome::Win, + (Choice::Paper, Choice::Rock) => Outcome::Win, + (Choice::Scissors, Choice::Paper) => Outcome::Win, + _ => Outcome::Lose, + } + } +} + +fn get_random_choice() -> Choice { + let choices = [Choice::Rock, Choice::Paper, Choice::Scissors]; + let index = rng().random_range(0..3); + choices[index] +} +``` + +Now we need to modify the `RockPaperScissorsExecutor` struct to accept a `Choice` parameter +and implement the game logic. + +```rust +struct RockPaperScissorsExecutor(Choice); // [!code ++] +struct RockPaperScissorsExecutor; // [!code --] + +impl CommandHandler for RockPaperScissorsExecutor { + fn handle( + &self, + sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + let player_choice = self.0; // [!code ++:3] + let computer_choice = get_random_choice(); + Ok(1) + } +} +``` + +This code will allow us to later pass in the player's choice and use it in the game logic, +as well as compare it with the computer's choice to determine the outcome of the game. + +## Implementing the Game Logic + +Now we can move on to actually implementing the game logic, and showing the outcome to the +players. + +First we will show the player their and the computer's choice. Add this code to your plugin: + +```rust +impl CommandHandler for RockPaperScissorsExecutor { + fn handle( + &self, + sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + let player_choice = self.0; + let computer_choice = get_random_choice(); + + // [!code ++:9] + let you_chose = TextComponent::text("You chose: "); + you_chose.add_child(TextComponent::text(&format!("{:?}", player_choice))); + you_chose.color_named(NamedColor::Aqua); + sender.send_message(you_chose); + + let i_chose = TextComponent::text("I chose: "); + i_chose.add_child(TextComponent::text(&format!("{:?}", computer_choice))); + i_chose.color_named(NamedColor::Gold); + sender.send_message(i_chose); + } +} +``` + +Next we can compute the game outcome and show it to the player. Add this code to your plugin: + +```rust +impl CommandHandler for RockPaperScissorsExecutor { + fn handle( + &self, + sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + // Existing Code + + match player_choice.beats(&computer_choice) { // [!code ++:17] + Outcome::Win => { + let message = TextComponent::text("You win!"); + message.color_named(NamedColor::Green); + sender.send_message(message); + } + Outcome::Lose => { + let message = TextComponent::text("You lose!"); + message.color_named(NamedColor::Red); + sender.send_message(message); + } + Outcome::Draw => { + let message = TextComponent::text("It's a tie!"); + message.color_named(NamedColor::Yellow); + sender.send_message(message); + } + } + + Ok(1) + } +} +``` + +And that's it! The core logic is done. Now we only have one last thing to do. + +## Building and registering a command tree + +As stated earlier, we need to build a command tree and register it with the server. This will +allow players to execute our plugin's commands. + +Building a command tree isn't very hard, but you have to know the exact structure of the command +and its arguments. In this case, we have a command named `rock-paper-scissors`, which will take +one required argument (the player's choice). + +The command tree is initialized using the `Command::new()` function. This function takes two +arguments: a list of names, where the first is the main command name, and the others are aliases +for the command; and a command description which is used to describe the command in the help menu. +We can then add 'branches' to the tree using the `.then()` method. This method accepts a 'leaf' +which can be built with the `CommandNode::literal()`, `CommandNode::argument()`, or +`CommandNode::require()` functions. + +For the rock-paper-scissors command, we'll create 3 separate branches, each with a `CommandNode::literal()` +leaf node for the player's choice. We will also register the command tree with the server and +with a `permission` of `hello-pumpkin:command.rockpaperscisors` which will allow anyone with that +permission to execute the command. Add the following code to your `on_load()` method: + +```rust +struct HelloPlugin; +impl Plugin for HelloPlugin { + fn new() -> Self { + HelloPlugin + } + + fn metadata(&self) -> PluginMetadata { + // Existing Code + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Existing Code + + let command = Command::new( // [!code ++:20] + &["rps".to_string(), "rockpaperscissors".to_string()], + "Play Rock Paper Scissors with the server.", + ); + command.then(CommandNode::literal("rock").execute(RockPaperScissorsExecutor(Choice::Rock))); + command + .then(CommandNode::literal("paper").execute(RockPaperScissorsExecutor(Choice::Paper))); + command.then( + CommandNode::literal("scissors").execute(RockPaperScissorsExecutor(Choice::Scissors)), + ); + + let permission = Permission { + node: "hello-pumpkin:command.rockpaperscisors".to_string(), + description: "Allows the player to play rock paper scisors".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), + }; + + context.register_permission(&permission)?; + context.register_command(command, "hello-pumpkin:command.rockpaperscisors"); + Ok(()) + } +} +``` + +And that's it! If you compile the plugin, you can test it by running the following command: + +```bash +/rps rock +``` diff --git a/docs/nl/plugin-dev/rust/join-event.md b/docs/nl/plugin-dev/rust/events.md similarity index 100% rename from docs/nl/plugin-dev/rust/join-event.md rename to docs/nl/plugin-dev/rust/events.md diff --git a/docs/nl/plugin-dev/rust/introduction.md b/docs/nl/plugin-dev/rust/introduction.md deleted file mode 100644 index 7bb6eb7..0000000 --- a/docs/nl/plugin-dev/rust/introduction.md +++ /dev/null @@ -1,9 +0,0 @@ -# Writing an Example Plugin -This tutorial will guide you through creating a new plugin. - -This tutorial expects that you have some experience with rust and with using a command line. - -Topics described in this tutorial: -- Modifying the join message -- Creating a rock-paper-scissors command (coming soon) -- Saving plugin data in the plugin's directory (coming soon) diff --git a/docs/nl/plugin-dev/rust/plugin-utils.md b/docs/nl/plugin-dev/rust/plugin-utils.md new file mode 100644 index 0000000..3adfbbe --- /dev/null +++ b/docs/nl/plugin-dev/rust/plugin-utils.md @@ -0,0 +1,348 @@ +# Licensing & Updates (`pumpkin-plugin-utils`) + +`pumpkin-plugin-utils` is an official utility crate for Pumpkin Rust plugins. It provides built-in tools for **licensing checks**, **cryptographic signature verification**, and **update checks** against the [Pumpkin Marketplace](https://market.pumpkinmc.org). + +## Features + +- **Offline Ed25519 Signature Verification**: Verifies plugin integrity and embedded marketplace metadata in `< 1ms` on startup without blocking the server. +- **Global Metadata Caching**: Call `init(&context)` once in your plugin's `on_load` lifecycle method to cache verified metadata globally. +- **Online License Verification**: Query the marketplace `/api/v1/rest/check-license` endpoint with automatic 7-day lease caching (`license_lease.json`). +- **Offline Grace Period**: Resilient offline lease evaluation ensures your plugin keeps working during marketplace maintenance or network outages. +- **Zero-Argument Update Checking**: Easily check if newer plugin releases are available on the marketplace. +- **Dynamic Public Key Resolution**: Resolves the marketplace public key dynamically via host WIT imports or HTTPS fallback without hardcoding keys. + +--- + +## Adding the Dependency + +Add `pumpkin-plugin-utils` alongside `pumpkin-plugin-api` in your plugin's `Cargo.toml`: + +:::code-group + +```toml [Cargo.toml] +[package] +name = "my-plugin" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +pumpkin-plugin-api = "0.1.0-dev+26.2-26.45" +pumpkin-plugin-utils = "0.1.0-dev+26.2-26.45" +tracing = "0.1" +``` + +::: + +--- + +## Initialization + +Before performing license or update checks, initialize the utility crate inside your plugin's `on_load` method using the plugin's `Context`: + +```rust +use pumpkin_plugin_api::{Context, Plugin, PluginMetadata}; +use tracing::{info, warn}; + +struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { + Self + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "MyPlugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Author".into()], + description: "Plugin with license and update checks".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Initialize pumpkin-plugin-utils (verifies signature & caches metadata) + let metadata = pumpkin_plugin_utils::init(&context) + .map_err(|e| format!("Initialization failed: {e}"))?; + + info!( + "Loaded plugin '{}' v{} (Dev: {})", + metadata.plugin_name, metadata.version, metadata.dev_name + ); + + Ok(()) + } +} +``` + +::: info What happens during `init`? +1. Locates the plugin's `.wasm` binary in the server directory. +2. Extracts the `pumpkin.metadata` and `wasm_signature` custom sections. +3. Cryptographically verifies the Ed25519 signature over the WASM binary and metadata. +4. Caches the verified `PumpkinMetadata` globally for subsequent calls. +::: + +--- + +## Checking for Updates + +`pumpkin-plugin-utils` provides both zero-argument automatic checking and custom manual checking. + +### 1. Automatic Update Check (Recommended) + +Once initialized, call `pumpkin_plugin_utils::check_for_updates()` without arguments. It automatically uses the cached plugin name, current version, and marketplace endpoint: + +```rust +use tracing::{info, warn}; + +fn check_updates() { + match pumpkin_plugin_utils::check_for_updates() { + Ok(update) => { + if update.update_available { + info!( + "A new update is available: {}!", + update.latest_version.as_deref().unwrap_or("unknown") + ); + } else { + info!("Plugin is up to date."); + } + } + Err(err) => { + warn!("Failed to check for updates: {err}"); + } + } +} +``` + +### 2. Manual Update Check (`UpdateChecker`) + +If you want to manually specify the plugin name, version, or a custom marketplace URL, use `UpdateChecker`: + +```rust +use pumpkin_plugin_utils::UpdateChecker; + +let checker = UpdateChecker::new(); +let response = checker.check_for_updates( + "my-plugin", + "1.0.0", + "https://market.pumpkinmc.org", +)?; + +if response.update_available { + println!("Update found: {:?}", response.latest_version); +} +``` + +### Update Response Structure + +The update check returns a `CheckUpdateResponse`: + +```rust +pub struct CheckUpdateResponse { + /// Whether a newer stable release exists on the marketplace. + pub update_available: bool, + /// The latest stable version string, if available. + pub latest_version: Option, +} +``` + +--- + +## Checking and Verifying Licenses + +`pumpkin-plugin-utils` supports online verification against the marketplace API as well as offline lease management with configurable grace periods. + +### 1. Online License Check + +To verify a license against the marketplace (`GET /api/v1/rest/check-license`): + +```rust +// Pass `None` to use the embedded license key from metadata +let response = pumpkin_plugin_utils::check_license_online(None) + .map_err(|e| format!("License verification failed: {e}"))?; + +if response.valid { + info!("License is valid (status: {})", response.status); +} else { + warn!("License is invalid: {}", response.status); + return Err(format!("Invalid license status: {}", response.status).into()); +} +``` + +#### Providing a Custom License Key Override +If your plugin allows server owners to configure a license key in a `config.toml` file, pass `Some(&key)`: + +```rust +let config_key = "LIC-CUSTOM-KEY-12345"; +let response = pumpkin_plugin_utils::check_license_online(Some(config_key))?; +``` + +::: tip 7-Day Lease Caching +Successful online checks automatically save a verified lease file (`license_lease.json`) into the plugin's data folder with a 7-day expiration. +::: + +### 2. Evaluating License with Grace Period + +To keep paid plugins functional when the server is offline or the marketplace is unreachable, use `evaluate_license`: + +```rust +use pumpkin_plugin_utils::LicenseStatus; +use tracing::{info, warn}; + +// Specify grace period duration in days (e.g., 7 days) +let grace_period_days = 7; + +match pumpkin_plugin_utils::evaluate_license(grace_period_days) { + LicenseStatus::Valid(meta) => { + info!("License valid for buyer (User ID: {})", meta.user_id); + } + LicenseStatus::GracePeriod { metadata, days_remaining, reason } => { + warn!( + "Running in offline grace period ({} days remaining). Reason: {}", + days_remaining, reason + ); + } + LicenseStatus::Invalid(reason) => { + return Err(format!("Plugin license invalid or tampered: {reason}").into()); + } + LicenseStatus::Unsigned => { + info!("Plugin is running in unsigned / development mode."); + } +} +``` + +### `LicenseStatus` Enum + +| Variant | Description | +| :--- | :--- | +| `LicenseStatus::Valid(PumpkinMetadata)` | License is valid, verified, or the plugin is free / open-source. | +| `LicenseStatus::GracePeriod { metadata, days_remaining, reason }` | Operating offline with a previously validated lease that is within the allowable grace period. | +| `LicenseStatus::Invalid(String)` | Signature is invalid, expired, revoked, or metadata was tampered with. | +| `LicenseStatus::Unsigned` | Binary does not contain marketplace metadata/signature (useful during local development). | + +--- + +## Accessing Plugin Metadata + +You can access the verified plugin metadata anywhere in your codebase using `get_metadata` or `metadata()`: + +```rust +if let Some(meta) = pumpkin_plugin_utils::get_metadata() { + println!("Plugin Name: {}", meta.plugin_name); + println!("Plugin Version: {}", meta.version); + println!("Is Paid: {}", meta.is_paid); + println!("License Key: {:?}", meta.license_key); + println!("Buyer User ID: {}", meta.user_id); + println!("Developer: {} (ID: {})", meta.dev_name, meta.dev_id); + println!("Marketplace URL: {}", meta.marketplace_url); + println!("Issued At: {}", meta.issued_at); +} +``` + +### `PumpkinMetadata` Fields + +| Field | Type | Description | +| :--- | :--- | :--- | +| `plugin_name` | `String` | Canonical plugin name on the marketplace. | +| `version` | `String` | SemVer version string of the plugin binary. | +| `plugin_id` | `i64` | Unique plugin identifier on the marketplace. | +| `dev_id` | `i64` | Developer account ID. | +| `dev_name` | `String` | Developer display name. | +| `is_paid` | `bool` | `true` if this is a premium/commercial plugin. | +| `user_id` | `i64` | Buyer / licensee user ID (`0` for free plugins). | +| `license_key` | `Option` | Unique license key issued to the licensee. | +| `marketplace_url` | `String` | Base marketplace URL (`https://market.pumpkinmc.org`). | +| `issued_at` | `String` | ISO-8601 timestamp when the binary/license was issued. | + +--- + +## Complete Example + +Here is a full plugin implementation combining initialization, online licensing checks, offline grace periods, and update checking: + +:::code-group + +```rust:line-numbers [src/lib.rs] +use pumpkin_plugin_api::{Context, Plugin, PluginMetadata}; +use pumpkin_plugin_utils::LicenseStatus; +use tracing::{error, info, warn}; + +struct CommercialPlugin; + +impl Plugin for CommercialPlugin { + fn new() -> Self { + Self + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "CommercialPlugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Developer".into()], + description: "A commercial Pumpkin plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Initialize plugin-utils (verifies Ed25519 signature & caches metadata) + let meta = pumpkin_plugin_utils::init(&context).map_err(|err| { + error!("Signature verification failed: {err}"); + format!("Security verification error: {err}") + })?; + + info!("Loaded {} v{}", meta.plugin_name, meta.version); + + // 2. Validate license (offline evaluation + online check) + match pumpkin_plugin_utils::evaluate_license(7) { + LicenseStatus::Valid(_) => { + info!("Local license status: Valid"); + } + LicenseStatus::GracePeriod { days_remaining, .. } => { + warn!("Local license in grace period: {days_remaining} days remaining"); + } + LicenseStatus::Unsigned => { + info!("Running in development mode (unsigned binary)"); + } + LicenseStatus::Invalid(reason) => { + error!("Invalid license: {reason}"); + return Err(format!("License verification failed: {reason}").into()); + } + } + + // 3. Online license check if paid + if meta.is_paid { + if let Ok(check) = pumpkin_plugin_utils::check_license_online(None) { + if !check.valid { + return Err(format!("Online license rejected: {}", check.status).into()); + } + info!("Online license check confirmed: {}", check.status); + } else { + warn!("Online license verification unreachable, falling back to local lease"); + } + } + + // 4. Check for updates + if let Ok(update) = pumpkin_plugin_utils::check_for_updates() { + if update.update_available { + info!( + "A new version is available on the marketplace: {}", + update.latest_version.as_deref().unwrap_or("unknown") + ); + } + } + + Ok(()) + } + + fn on_unload(&mut self, _context: Context) -> pumpkin_plugin_api::Result<()> { + info!("CommercialPlugin unloaded."); + Ok(()) + } +} + +pumpkin_plugin_api::register_plugin!(CommercialPlugin); +``` + +::: diff --git a/docs/nl/troubleshooting/common_issues.md b/docs/nl/troubleshooting/common_issues.md index 54248e5..9fd0c3b 100644 --- a/docs/nl/troubleshooting/common_issues.md +++ b/docs/nl/troubleshooting/common_issues.md @@ -7,3 +7,14 @@ **Oorzaak:** Dit heeft te maken met authenticatie, meestal met de instelling om proxyverbindingen te voorkomen. **Oplossing:** Schakel `prevent_proxy_connections` uit in `pumpkin.toml` + +2. ### Kan geen verbinding maken via 0.0.0.0 + + **Probleem:** Spelers die verbinding proberen te maken met een server op hun eigen computer kunnen geen verbinding krijgen (bijv. foutmelding "Connection refused") bij het invoeren van `0.0.0.0` of `0.0.0.0:25565`. + + **Oorzaak:** In `pumpkin.toml` geeft `0.0.0.0` aan dat Pumpkin moet luisteren op alle netwerkinterfaces. Dit is een bind-adres voor de server, geen geldig doeladres waarmee een Minecraft-client verbinding kan maken. + + **Oplossing:** + - Als je vanaf **dezelfde computer** verbindt, gebruik dan `localhost` of `127.0.0.1`. + - Als je verbindt vanaf een **ander apparaat op hetzelfde lokale netwerk (LAN)**, gebruik dan het lokale IP-adres van de host (bijv. `192.168.x.x`). + - Als spelers via het **internet** verbinden, moeten ze je openbare IP-adres of domeinnaam gebruiken (met poortdoorschakeling/port forwarding ingeschakeld op de router). diff --git a/docs/pt/admin/introduction.md b/docs/pt/admin/introduction.md new file mode 100644 index 0000000..2b729b5 --- /dev/null +++ b/docs/pt/admin/introduction.md @@ -0,0 +1,25 @@ +# Visão Geral da Administração + +Bem-vindo à **Seção de Administração** para administradores do servidor Pumpkin. O Pumpkin é um servidor de Minecraft multithread de alto desempenho escrito em Rust, projetado para suportar grandes quantidades de jogadores com consumo mínimo de recursos. + +--- + +## Principais Recursos para Administradores de Servidores + +- **Multithreading Extremo**: Projetado do zero para processadores modernos multi-core. +- **Arquitetura de Plugins WASM**: Execução de plugins segura e em sandbox em Rust, Python, Kotlin, C#, Go ou C sem o overhead da JVM. +- **Suporte Nativo a Cross-Proxy**: Suporte integrado para Velocity, BungeeCord e encaminhamento moderno de proxies. +- **Configuração Baseada em TOML**: Arquivos de configuração limpos e legíveis para humanos localizados em `pumpkin.toml` e configurações TOML específicas de recursos. + +--- + +## Guias e Tópicos de Administração + +Explore os seguintes guias de administração: + +- [Migrando de um Servidor Bukkit / Paper / Spigot](./migrating-from-bukkit) — Principais diferenças na administração do servidor, plugins, armazenamento de mundos e desempenho. +- [Configuração do Servidor](../config/introduction) — Detalhamento minucioso das configurações do `pumpkin.toml`. +- [Configuração de Proxy](../config/proxy) — Configurando o encaminhamento de jogadores do BungeeCord e Velocity. +- [Comandos e Permissões](../config/commands) — Gerenciamento de comandos de operador e permissões no jogo. +- [Autenticação](../config/authentication) — Modo online vs. offline e configurações do Yggdrasil. +- [Solução de Problemas e Questões Comuns](../troubleshooting/common_issues) — Resolvendo problemas de vinculação de portas, memória e carregamento de plugins. diff --git a/docs/pt/admin/migrating-from-bukkit.md b/docs/pt/admin/migrating-from-bukkit.md new file mode 100644 index 0000000..d933c48 --- /dev/null +++ b/docs/pt/admin/migrating-from-bukkit.md @@ -0,0 +1,126 @@ +# Migrando do Bukkit / Spigot / Paper para o Pumpkin + +Este guia aborda as diferenças arquiteturais e de configuração ao transicionar um servidor do Bukkit, Spigot ou Paper para o Pumpkin. + +--- + +## 1. Diferenças Arquiteturais + +| Aspecto | Bukkit / Spigot / Paper | Pumpkin | +| :--- | :--- | :--- | +| **Ambiente de Execução** | Java Virtual Machine (requer Java 17/21+) | Executável nativo (Rust compilado, sem necessidade de Java) | +| **Comando de Inicialização** | `java [flags] -jar server.jar` | `./pumpkin` | +| **Loop de Ticks** | Loop de ticks single-threaded com tarefas assíncronas | Execução de ticks multithread entre núcleos de CPU | +| **Formato de Plugins** | Bytecode Java (`.jar`) direcionado à API Bukkit | WebAssembly (`.wasm`), ou `.jar` via PatchBukkit | +| **Linguagens de Plugins** | Java, Kotlin, Scala | Rust, Python, Kotlin, C#, Go, C | +| **Configuração** | `server.properties`, `paper.yml`, `spigot.yml` | `pumpkin.toml` (formato TOML) | +| **Gerenciamento de Memória** | JVM Garbage Collection | Alocação nativa de memória pelo SO | + +--- + +## 2. Plugins e Extensões + +### Plugins em WebAssembly +O formato primário de plugins do Pumpkin é o WebAssembly (`.wasm`): +- **Isolamento**: Os plugins são executados em um runtime WebAssembly em sandbox. +- **Suporte a Linguagens**: Os plugins podem ser compilados para WASM a partir de Rust, Python, Go, C#, C ou Kotlin. +- **Plugins Personalizados**: Para portar plugins Bukkit próprios para o Pumpkin, consulte o [Guia de Migração para Desenvolvedores](../plugin-dev/migrating-from-bukkit/index). + +### Compatibilidade com Plugins Bukkit: PatchBukkit +Para servidores que dependem de plugins Bukkit/Spigot `.jar` existentes, o projeto **PatchBukkit** fornece uma camada de compatibilidade: +- **Arquitetura**: O PatchBukkit incorpora uma JVM dentro do Pumpkin e reimplementa partes da API Bukkit, fazendo a ponte com o Pumpkin via JNI e FFI. +- **Localização dos Plugins**: Plugins `.jar` compatíveis são colocados em `patchbukkit/patchbukkit-plugins/`. +- **Status e Limitações**: O PatchBukkit está em desenvolvimento ativo. Chamadas padrão da API Bukkit são suportadas; plugins com forte dependência de classes internas do NMS (`net.minecraft.server`), reflexão do CraftBukkit ou manipulação de bytecode podem não funcionar corretamente. +- **Requisito de Java**: A execução do PatchBukkit requer um runtime Java (JRE/JDK) instalado no sistema host para rodar a JVM incorporada. + +--- + +## 3. Configuração e Propriedades + +O Pumpkin utiliza arquivos de configuração TOML em vez de `server.properties` e YAML: + +| Paper / Spigot (`server.properties`) | Pumpkin (`pumpkin.toml`) | +| :--- | :--- | +| `server-port=25565` | `server_address = "0.0.0.0:25565"` | +| `motd=...` | `motd = "A Pumpkin Server"` | +| `max-players=20` | `max_players = 20` | +| `online-mode=true` | `online_mode = true` | +| `view-distance=10` | `view_distance = 10` | +| `simulation-distance=8` | `simulation_distance = 8` | + +--- + +## 4. Armazenamento de Mundos e Dados + +- **Compatibilidade de Formato**: O Pumpkin lê o formato padrão de chunks Anvil (arquivos `.mca`). +- **Estrutura de Diretórios**: Certifique-se de que os diretórios de dimensões (`world`, `world_nether`, `world_the_end`) correspondam à estrutura esperada do Pumpkin ao copiar mundos de instalações existentes do Paper/Spigot. +- **Formato Mais Recente de Mundo e Datapack Necessário**: Ao menos por enquanto, o Pumpkin suporta apenas o formato de mundo e o formato de datapack mais recentes da versão suportada do Minecraft. Formatos antigos de mundo ou versões legadas de datapack não são convertidos automaticamente em tempo de execução. + - **Atualizando Mundos no Cliente**: Se estiver migrando um mundo antigo, atualize-o antes de carregá-lo no Pumpkin. Você pode atualizar um mundo existente diretamente no cliente oficial do Minecraft: abra o jogo na versão mais recente compatível, vá em **Um Jogador**, selecione o mundo, clique em **Editar** e escolha **Otimizar Mundo**. Como alternativa, carregue e salve o mundo uma vez em um servidor vanilla na versão mais recente. + - **Datapacks**: Certifique-se de que todos os datapacks em `world/datapacks` estejam atualizados para o formato esperado pela versão de destino. + +--- + +## 5. Inicialização e Flags da JVM + +### Nenhuma Instalação de Java Necessária +O Pumpkin puro é um executável nativo autocontido. Não é necessário ter um runtime Java (JRE/JDK) instalado no sistema ou no contêiner para executar o servidor. + +### Comandos de Inicialização +Servidores Paper e Spigot frequentemente utilizam scripts de inicialização JVM com flags de alocação de heap e garbage collection (como as Aikar's Flags): + +:::code-group + +```bash [Paper (JVM)] +# Comando típico de inicialização do Paper com Aikar's Flags: +java -Xms10G -Xmx10G \ + -XX:+UseG1GC \ + -XX:+ParallelRefProcEnabled \ + -XX:MaxGCPauseMillis=200 \ + -XX:+UnlockExperimentalVMOptions \ + -XX:+DisableExplicitGC \ + -XX:+AlwaysPreTouch \ + -XX:G1NewSizePercent=30 \ + -XX:G1MaxNewSizePercent=40 \ + -XX:G1HeapRegionSize=8M \ + -XX:G1ReservePercent=20 \ + -XX:G1HeapWastePercent=5 \ + -XX:G1MixedGCCountTarget=4 \ + -XX:InitiatingHeapOccupancyPercent=15 \ + -XX:G1MixedGCLiveThresholdPercent=90 \ + -XX:G1RSetUpdatingPauseTimePercent=5 \ + -XX:SurvivorRatio=32 \ + -XX:+PerfDisableSharedMem \ + -XX:MaxTenuringThreshold=1 \ + -Dusing.aikars.flags=https://mcflags.emc.gs \ + -Daikars.new.flags=true \ + -jar paper.jar --nogui +``` + +```bash [Pumpkin (Nativo)] +# Linux / macOS: +./pumpkin + +# Windows: +./pumpkin.exe + +# Docker: +docker run -p 25565:25565 -v ./data:/data pumpkinmc/pumpkin:latest +``` + +::: + +### Diferenças de Memória +- **Sem Coletor de Lixo**: O Pumpkin é escrito em Rust e utiliza gerenciamento determinístico de memória em tempo de compilação (RAII). Não há coletor de lixo em execução nem necessidade de ajustar pausas de GC. +- **Sem Alocação Fixa de Heap**: Flags como `-Xms` e `-Xmx` não existem em binários nativos. A memória é alocada dinamicamente pelo sistema operacional conforme chunks e entidades são carregados, e liberada no descarregamento. +- **Flags da JVM Não Se Aplicam**: Parâmetros de ajuste de estratégias de GC, tamanhos de gerações ou detalhes internos da JVM não são necessários nem suportados. + +--- + +## Checklist do Administrador + +1. [ ] Faça backup dos dados e arquivos de mundo existentes, garantindo que o mundo e os datapacks estejam atualizados para o formato mais recente (ex.: via "Otimizar Mundo" no cliente). +2. [ ] Mapeie as configurações do `server.properties` para o `pumpkin.toml`. +3. [ ] Identifique os plugins necessários e busque equivalentes em WebAssembly (`.wasm`) ou teste com o [PatchBukkit](#compatibilidade-com-plugins-bukkit-patchbukkit). +4. [ ] Configure o suporte a proxy (`Velocity` / `BungeeCord`) no `pumpkin.toml` se utilizar uma rede de servidores. +5. [ ] Atualize os scripts de inicialização para executar `./pumpkin` diretamente sem argumentos da JVM. +6. [ ] Inicie o servidor e verifique a conectividade e os logs. diff --git a/docs/pt/config/bedrock.md b/docs/pt/config/bedrock.md new file mode 100644 index 0000000..a3b37ad --- /dev/null +++ b/docs/pt/config/bedrock.md @@ -0,0 +1,50 @@ +# Bedrock e NetherNet + +O Pumpkin inclui suporte nativo para clientes Minecraft Bedrock Edition, incluindo o transporte WebRTC/ICE do NetherNet e personalização de nomes de usuário. + +## Configuração + +:::code-group + +```toml [pumpkin.toml] +[networking.bedrock] +enabled = true +online_mode = true +max_players = 1000 +view_distance = 16 +simulation_distance = 10 +motd = "A blazingly fast Pumpkin server!" +username_prefix = "" +replace_username_spaces = true +chunk_caching = true + +[networking.bedrock.nethernet] +enabled = true +address = "0.0.0.0:19132" +identity_key = "nethernet-key.der" +stun_servers = [] +``` + +::: + +### Opções do Bedrock + +- **`enabled`**: Se conexões de clientes Bedrock Edition são aceitas (padrão: `true`). +- **`online_mode`**: Se a autenticação Xbox Live é exigida para jogadores Bedrock (padrão: `true`). +- **`max_players`**: Número máximo de jogadores Bedrock simultâneos (`0` desativa o limite, padrão: `1000`). +- **`view_distance`**: Distância máxima de renderização de chunks enviada para clientes Bedrock (padrão: `16`). +- **`simulation_distance`**: Distância máxima de simulação de ticks para jogadores Bedrock (padrão: `10`). +- **`motd`**: Mensagem do dia (MOTD) exibida na lista de servidores Bedrock. +- **`username_prefix`**: Prefixo opcional adicionado antes da gamertag do jogador Bedrock (por exemplo, `"."` ou `"*"`), evitando colisões de nomes com contas Java Edition em servidores cross-play (padrão: `""`). +- **`replace_username_spaces`**: Se os espaços nas gamertags do Bedrock são substituídos automaticamente por sublinhados `_`, garantindo que os nomes dos jogadores possam ser referenciados em comandos slash do Minecraft (padrão: `true`). +- **`chunk_caching`**: Se o cache de blobs de chunks no cliente está habilitado para economizar largura de banda de rede (padrão: `true`). + +### Configurações de Transporte do NetherNet + +O NetherNet é o transporte de rede moderno baseado em WebRTC/ICE do Minecraft Bedrock: + +- **`enabled`**: Se os clientes podem se conectar usando o NetherNet (padrão: `true`). +- **`address`**: Endereço de socket de rede para sinalização TCP e multiplexação UDP ICE (padrão: `"0.0.0.0:19132"`). +- **`external_ip`**: Endereço IP público opcional anunciado quando o servidor estiver hospedado atrás de NAT. +- **`identity_key`**: Caminho do arquivo para a chave de identidade privada PKCS#8 P-384 retida entre reinicializações para Confiança no Primeiro Uso (TOFU - Trust On First Use) do cliente (padrão: `"nethernet-key.der"`). +- **`stun_servers`**: Lista opcional de URLs de servidores STUN para travessia NAT via ICE. diff --git a/docs/pt/config/chat.md b/docs/pt/config/chat.md new file mode 100644 index 0000000..b5dcc63 --- /dev/null +++ b/docs/pt/config/chat.md @@ -0,0 +1,39 @@ +# Chat e Anti-Spam + +O Pumpkin oferece formatação de chat personalizável no jogo e proteção anti-spam integrada na seção `[chat]` do `pumpkin.toml`. + +## Configuração + +:::code-group + +```toml [pumpkin.toml] +[chat] +format = "<{DISPLAYNAME}> {MESSAGE}" + +[chat.anti_spam] +enabled = true +spam_threshold = 200 +message_cost = 20 +decay_per_tick = 1 +ops_bypass = true +``` + +::: + +### Configurações de Chat + +- **`format`**: A string de formato da mensagem de chat (padrão: `"<{DISPLAYNAME}> {MESSAGE}"`). + - `{DISPLAYNAME}`: Nome de exibição do jogador. + - `{MESSAGE}`: Texto da mensagem de chat. + > [!NOTE] + > A formatação personalizada de chat não se aplica quando o relatório seguro de chat (`allow_chat_reports = true`) estiver habilitado. + +### Proteção Anti-Spam + +O Pumpkin implementa um contador de spam do tipo leaky-bucket (balde furado) para proteger o servidor contra spam de jogadores e inundações de bots: + +- **`enabled`**: Se a proteção anti-spam está ativa (padrão: `true`). +- **`spam_threshold`**: O limite do contador de spam em ticks no qual um jogador será expulso (kicked) (padrão: `200` ticks). +- **`message_cost`**: A quantidade adicionada ao contador de spam do jogador para cada mensagem de chat ou comando enviado (padrão: `20` ticks). +- **`decay_per_tick`**: A quantidade decaída do contador de spam do jogador a cada tick do servidor (padrão: `1` tick). +- **`ops_bypass`**: Se os operadores do servidor ignoram as verificações anti-spam (padrão: `true`). diff --git a/docs/pt/config/packet-limiter.md b/docs/pt/config/packet-limiter.md new file mode 100644 index 0000000..f939d7b --- /dev/null +++ b/docs/pt/config/packet-limiter.md @@ -0,0 +1,30 @@ +# Limitador de Pacotes + +O Pumpkin possui um limitador de taxa de pacotes integrado para proteger o servidor contra inundações de pacotes (packet flooding), ataques de spam e exploits do lado do cliente. Os limites de taxa de pacotes podem ser configurados independentemente para clientes Java Edition e Bedrock Edition. + +## Configuração + +:::code-group + +```toml [pumpkin.toml] +[networking.java.packet_limiter] +enabled = true +max_packet_rate = 500.0 +burst_capacity = 500.0 +kick_message = "Kicked for spamming packets" + +[networking.bedrock.packet_limiter] +enabled = true +max_packet_rate = 500.0 +burst_capacity = 500.0 +kick_message = "Kicked for spamming packets" +``` + +::: + +### Opções de Configuração + +- **`enabled`**: Se o limitador de taxa de pacotes está habilitado para esta edição de cliente (padrão: `true`). +- **`max_packet_rate`**: Taxa máxima permitida de pacotes recebidos por segundo por conexão de cliente (padrão: `500.0`). Defina como `<= 0.0` para desativar o limite de taxa sem desativar o módulo do limitador. +- **`burst_capacity`**: Capacidade de tolerância de surto (burst) do token bucket para picos breves de pacotes do cliente (padrão: `500.0`). +- **`kick_message`**: A mensagem de desconexão exibida ao jogador quando sua conexão ultrapassar o limite de pacotes (padrão: `"Kicked for spamming packets"`). diff --git a/docs/pt/config/player-data.md b/docs/pt/config/player-data.md new file mode 100644 index 0000000..abc82f2 --- /dev/null +++ b/docs/pt/config/player-data.md @@ -0,0 +1,41 @@ +# Dados do Jogador e Jogabilidade + +As configurações para salvar dados persistentes dos jogadores, conquistas (advancements), sincronização de receitas e recursos sazonais divertidos são definidas no `pumpkin.toml`. + +## Configuração + +:::code-group + +```toml [pumpkin.toml] +[player_data] +save_player_data = true +save_player_cron_interval = 300 + +[advancement] +save_advancements = true + +[recipe] +send_recipes = true + +[fun] +april_fools = true +``` + +::: + +### Dados do Jogador + +- **`save_player_data`**: Se os dados persistentes dos jogadores (inventário, localização, vida) são salvos em disco (padrão: `true`). +- **`save_player_cron_interval`**: Intervalo de tempo em segundos entre salvamentos periódicos automáticos dos dados de jogadores online (padrão: `300` segundos / 5 minutos). + +### Conquistas (Advancements) + +- **`save_advancements`**: Se o progresso de conquistas dos jogadores é monitorado e salvo em disco (padrão: `true`). + +### Receitas + +- **`send_recipes`**: Se as receitas de criação (crafting) e fundição são sincronizadas com os clientes conectados, habilitando o livro de receitas no cliente (padrão: `true`). + +### Recursos Divertidos + +- **`april_fools`**: Se os easter eggs e recursos sazonais do Dia da Mentira (1º de abril) estão ativados (padrão: `true`). diff --git a/docs/pt/config/plugins.md b/docs/pt/config/plugins.md new file mode 100644 index 0000000..f7aa80d --- /dev/null +++ b/docs/pt/config/plugins.md @@ -0,0 +1,59 @@ +# Plugins + +O Pumpkin fornece um runtime de plugins em WebAssembly (WASM) com segurança baseada em recursos (capabilities). O comportamento dos plugins, verificação de assinaturas e permissões são configurados em `[plugins]` no `pumpkin.toml`. + +## Configuração + +:::code-group + +```toml [pumpkin.toml] +[plugins] +enabled = true +hot_reload = false +ask_permission_confirmation = true +allow_unsigned = true +allowed_permissions = [] +blocked_permissions = [] +inherit_env = false +loopback_only = false +verify_signatures = true + +# Sobrescritas opcionais por plugin +[plugins.overrides.my_plugin] +enabled = true +allow_unsigned = true +max_memory_mb = 128 +allowed_permissions = ["fs:read:data"] +blocked_permissions = ["network:outbound"] +loopback_only = true + +[plugins.overrides.my_plugin.environment] +API_KEY = "example_secret" +``` + +::: + +### Configurações Globais de Plugins + +- **`enabled`**: Chave mestre para habilitar ou desabilitar o runtime de plugins (padrão: `true`). +- **`hot_reload`**: Monitora o diretório `plugins/` e recarrega automaticamente plugins modificados em tempo de execução (padrão: `false`). +- **`ask_permission_confirmation`**: Pergunta no console do servidor quando um plugin solicitar recursos/permissões não aprovadas (padrão: `true`). +- **`allow_unsigned`**: Permite carregar plugins WASM não assinados (padrão: `true`). +- **`allowed_permissions`**: Lista de permissões pré-aprovadas globalmente para todos os plugins, ignorando solicitações interativas no console (padrão: `[]`). +- **`blocked_permissions`**: Lista de permissões negadas globalmente para todos os plugins (padrão: `[]`). +- **`inherit_env`**: Se as variáveis de ambiente do host são herdadas nas sandboxes WASI dos plugins por padrão (padrão: `false`). +- **`loopback_only`**: Se as conexões de rede de saída dos plugins são restritas a `127.0.0.1` / localhost (padrão: `false`). +- **`max_memory_mb`**: (Opcional) Limite global máximo de memória em megabytes (MB) por instância de plugin. +- **`verify_signatures`**: Se o Pumpkin verifica assinaturas criptográficas em plugins WASM antes de carregá-los (padrão: `true`). + +### Sobrescritas por Plugin + +Ajuste permissões e variáveis de ambiente para plugins específicos sob `[plugins.overrides.]`: + +- **`enabled`**: Habilita ou desabilita este plugin específico (padrão: `true`). +- **`allow_unsigned`**: Sobrescreve se este plugin específico pode executar sem assinatura (`true`/`false`). +- **`max_memory_mb`**: Limite máximo de memória em MB alocado especificamente para este plugin. +- **`allowed_permissions`**: Permissões pré-aprovadas para este plugin. +- **`blocked_permissions`**: Permissões explicitamente bloqueadas para este plugin. +- **`loopback_only`**: Sobrescreve a restrição de rede de loopback para este plugin. +- **`environment`**: Tabela de variáveis de ambiente personalizadas repassadas diretamente para o ambiente WASI do plugin. diff --git a/docs/pt/config/server-links.md b/docs/pt/config/server-links.md new file mode 100644 index 0000000..e086938 --- /dev/null +++ b/docs/pt/config/server-links.md @@ -0,0 +1,50 @@ +# Links do Servidor + +Os clientes de Minecraft (desde a versão 1.21) podem exibir links clicáveis do servidor no menu de pausa e nos menus do jogo. Configure links padrão e personalizados do servidor sob `[server_links]` no `pumpkin.toml`. + +## Configuração + +:::code-group + +```toml [pumpkin.toml] +[server_links] +enabled = true +bug_report = "https://github.com/Pumpkin-MC/Pumpkin/issues" +support = "" +status = "" +feedback = "" +community = "" +website = "" +forums = "" +news = "" +announcements = "" + +[server_links.custom] +# "Store" = "https://store.example.com" +# "Discord" = "https://discord.gg/example" +``` + +::: + +### Opções de Configuração + +- **`enabled`**: Chave mestre para habilitar a transmissão de links do servidor para clientes que estão se conectando (padrão: `true`). +- **`bug_report`**: Link para o rastreador de bugs (bug tracker) do servidor ou do projeto (padrão: `"https://github.com/Pumpkin-MC/Pumpkin/issues"`). +- **`support`**: Link para suporte técnico ou central de ajuda. +- **`status`**: Link para a página de status/uptime do servidor. +- **`feedback`**: Link para a página de feedback ou sugestões dos jogadores. +- **`community`**: Link para fóruns ou grupo da comunidade. +- **`website`**: Link para o site oficial. +- **`forums`**: Link para fóruns de discussão. +- **`news`**: Link para o blog ou página de notícias do servidor. +- **`announcements`**: Link para a página de anúncios. + +### Links Personalizados + +Defina links arbitrários de chave-valor exibidos nos menus dos clientes em `[server_links.custom]`: + +```toml +[server_links.custom] +"Store" = "https://store.example.com" +"Discord" = "https://discord.gg/example" +``` diff --git a/docs/pt/config/world.md b/docs/pt/config/world.md new file mode 100644 index 0000000..35cddfc --- /dev/null +++ b/docs/pt/config/world.md @@ -0,0 +1,41 @@ +# Mundo + +O Pumpkin permite ajustar com precisão os formatos de armazenamento do mundo, os intervalos de salvamento automático (autosave) e os modos de iluminação em `[world]` no `pumpkin.toml`. + +## Configuração + +:::code-group + +```toml [pumpkin.toml] +[world] +lighting = "default" +autosave_ticks = 6000 + +[world.chunk] +type = "anvil" +write_in_place = false + +[world.chunk.compression] +algorithm = "LZ4" +level = 6 +``` + +::: + +### Configurações de Mundo + +- **`lighting`**: Modo de cálculo de propagação do motor de iluminação. + - `"default"`: Propagação de luz padrão do Vanilla Minecraft. + - `"full"`: Luz solar total em todos os lugares, sem sombras. + - `"dark"`: Iluminação completamente escura em todos os lugares (luz zero). +- **`autosave_ticks`**: Número de ticks do servidor entre salvamentos automáticos do mundo (padrão: `6000`, equivalente a 5 minutos a 20 TPS). Definir como `0` desativa o salvamento automático do mundo. + +### Configurações de Armazenamento de Chunks + +- **`type`**: O formato de armazenamento de chunks a ser utilizado: + - `"anvil"`: Formato padrão de arquivos de região Anvil do Minecraft (`.mca`). + - `"linear"`: Formato de armazenamento de região Linear que usa compressão rápida para menor pegada de disco. + - `"pump"`: Formato nativo otimizado de mundo do Pumpkin. +- **`write_in_place`**: Se os chunks devem ser reescritos no mesmo local dentro dos arquivos de região existentes em vez de realocar novos chunks livres (padrão: `false`). +- **`compression.algorithm`**: Algoritmo de compressão utilizado para dados de chunks (`"LZ4"`, `"ZLib"`, `"GZip"`, `"Custom"`). +- **`compression.level`**: Nível de compressão para o armazenamento de chunks (padrão: `6`). diff --git a/docs/pt/developer/architecture.md b/docs/pt/developer/architecture.md new file mode 100644 index 0000000..19c26b1 --- /dev/null +++ b/docs/pt/developer/architecture.md @@ -0,0 +1,139 @@ +# Pumpkin Architecture & Crates + +Pumpkin is organized as a modular Cargo workspace composed of specialized crates. Each crate has distinct responsibilities, strictly defined boundaries, and minimal cross-dependencies. + +This architecture enables high parallel compilation speeds, isolated testing, zero-cost data sharing, and clean separation between network protocols, game logic, and world management. + +--- + +## Workspace Crate Overview + +### Workspace Architecture at a Glance + +| Layer | Crates | Responsibilities | +| :--- | :--- | :--- | +| **Core Runtime** | `pumpkin` | Game loop, world ticking, player sessions, entity management, and event loops | +| **Networking & Protocols** | `pumpkin-protocol`, `pumpkin-auth`, `pumpkin-nbt` | Java/Bedrock packet serialization, Yggdrasil & Xbox Live authentication, SNBT | +| **World & Gameplay** | `pumpkin-world`, `pumpkin-inventory`, `pumpkin-command` | Region I/O (Anvil/Linear), terrain generation, block ticking, containers, Brigadier tree | +| **Extensibility** | `pumpkin-plugin-runtime`, `pumpkin-host-bindings`, `pumpkin-plugin-api`, `pumpkin-plugin-wit` | WebAssembly plugin execution via Wasmtime, WIT host APIs, native and WASM loaders | +| **Foundations** | `pumpkin-data`, `pumpkin-config`, `pumpkin-util` | Code-generated registries, typed configuration files, mathematical vectors, permissions | + +--- + +## Detailed Crate Breakdown + +### 1. `pumpkin` (Core Server) +- **Path**: `crates/pumpkin` +- **Purpose**: The main server binary and centralized orchestration engine. +- **Responsibilities**: + - Initializes configuration, logging (`tracing`), and telemetry. + - Spawns Tokio network listeners for Minecraft: Java Edition, Bedrock Edition, RCON, Query, and LAN broadcast. + - Manages the server tick loop (`Server::tick`), coordinating world updates, player actions, entity movements, and chunk broadcasting. + - Dispatches incoming client packets through `crates/pumpkin/src/net/java/` and `net/bedrock/`. + - Integrates the plugin manager, firing event handlers before and after packet processing. + +### 2. `pumpkin-protocol` (Network Protocol) +- **Path**: `crates/pumpkin-protocol` +- **Purpose**: High-speed network wire format serialization and deserialization for Minecraft Java and Bedrock editions. +- **Responsibilities**: + - Implements the `ClientPacket` (serialization) and `ServerPacket` (deserialization) traits. + - Handles protocol primitives: `VarInt`, `VarLong`, UUIDs, NBT compounds, and string codecs. + - Implements packets across all states: `Handshake`, `Status`, `Login`, `Config`, and `Play`. + - Provides packet compression (ZLib/Fast compression) and packet encryption (AES-128/CFB8). + - Encapsulates RCON and GameSpy4 Query protocols. + +### 3. `pumpkin-world` (World & Chunks) +- **Path**: `crates/pumpkin-world` +- **Purpose**: World loading, saving, terrain generation, and chunk management. +- **Responsibilities**: + - Supports multiple chunk storage formats: + - **Vanilla Anvil (`.mca`)**: Reading and writing standard region files. + - **Linear (`.linear`)**: High-efficiency zstd-compressed region format saving 50–95% disk space. + - **Slime (`.slime`)**: Single-file world format. + - **Pump**: Native Pumpkin chunk storage format. + - Implements world generation pipelines: Perlin noise heightmaps, 3D noise routers, biome climate parameters, placed features, carvers, and superflat terrain. + - Manages block lighting and sky lighting propagation. + - Coordinates chunk loading, unloading, and streaming via cylindrical chunk iterators. + +### 4. `pumpkin-data` (Minecraft Datasets & Registries) +- **Path**: `crates/pumpkin-data` +- **Purpose**: Compile-time static registries and data tables generated directly from vanilla Minecraft assets and reports. +- **Responsibilities**: + - Contains complete ID mappings, properties, and definitions for blocks, items, biomes, entities, sounds, particles, enchantments, and recipes. + - Packet ID lookups across multiple Minecraft protocol versions. + - Zero-cost lookup tables generated by `tools/pumpkin-codegen`. + +### 5. `pumpkin-config` (Configuration Management) +- **Path**: `crates/pumpkin-config` +- **Purpose**: TOML configuration parsing, serialization, and validation. +- **Responsibilities**: + - Loads and writes `pumpkin.toml`. + - Configures server ports, compression thresholds, proxy modes, resource packs, logging levels, world settings, and Bedrock integration. + - Provides safe defaults and schema validations. + +### 6. `pumpkin-inventory` (Inventory & Containers) +- **Path**: `crates/pumpkin-inventory` +- **Purpose**: Server-side container window handlers and inventory mechanics. +- **Responsibilities**: + - Manages player inventory, chest containers, crafting tables, furnaces, and villager trading screens. + - Validates client slot clicks, drag operations, item stacking, and item splitting. + - Coordinates window packet synchronization to ensure client inventory fidelity. + +### 7. `pumpkin-command` (Command Dispatcher) +- **Path**: `crates/pumpkin-command` +- **Purpose**: Brigadier-inspired hierarchical command tree dispatcher and parser. +- **Responsibilities**: + - Constructs modular command trees with typed arguments (`Integer`, `Float`, `Entity`, `BlockPos`, etc.). + - Executes permission-checked commands. + - Provides real-time autocompletion suggestions sent to connected clients. + +### 8. `pumpkin-auth` (Authentication & Session Verification) +- **Path**: `crates/pumpkin-auth` +- **Purpose**: Cryptographic authentication and player identity verification. +- **Responsibilities**: + - Validates client sessions with Mojang's Yggdrasil authentication servers. + - Handles RSA keypair generation and shared secret exchange during the Login state. + - Supports offline-mode UUID generation and custom authentication endpoints. + - Implements proxy forwarding protocols: [BungeeCord](https://github.com/SpigotMC/BungeeCord), [BungeeGuard](https://github.com/lucko/BungeeGuard), and [Velocity](https://github.com/PaperMC/Velocity) modern forwarding with HMAC-SHA256 signatures. + +### 9. `pumpkin-nbt` (Named Binary Tag Codec) +- **Path**: `crates/pumpkin-nbt` +- **Purpose**: High-throughput binary NBT parser and serializer. +- **Responsibilities**: + - Parses and encodes all standard NBT tag types (`Byte`, `Short`, `Int`, `Long`, `Float`, `Double`, `ByteArray`, `String`, `List`, `Compound`, `IntArray`, `LongArray`). + - Supports compression layers: Gzip, Zlib, and uncompressed Network NBT. + +### 10. `pumpkin-plugin-*` (Extensibility Infrastructure) +- **Paths**: + - `crates/pumpkin-plugin-api`: The Rust developer SDK for compiling native plugins. + - `crates/pumpkin-plugin-runtime`: Wasmtime-powered WebAssembly execution host. + - `crates/pumpkin-plugin-wit`: WebAssembly Interface Type (WIT) specifications. + - `crates/pumpkin-host-bindings`: Auto-generated host bindings connecting Wasmtime and the server core. +- **Purpose**: Enables safe, polyglot plugin execution (Rust, Python, C#, C, Go, Kotlin) with sandboxed memory and near-native performance. + +### 11. `pumpkin-util` & `pumpkin-macros` (Foundations) +- **Paths**: `crates/pumpkin-util`, `crates/pumpkin-macros`, `crates/pumpkin-api-macros` +- **Purpose**: Foundational types, shared algorithms, and procedural macros. +- **Responsibilities**: + - Mathematical utilities: `Vector2`, `Vector3`, `BoundingBox`, and angle conversions. + - Text and chat formatting: `TextComponent` and JSON component serialization. + - Procedural macros such as `#[java_packet(...)]` that bind packet structs to protocol IDs and packet traits. + +### 12. `tools/` (Developer Tooling) +- **`tools/pumpkin-codegen`**: Automated code generator that parses Minecraft version jars, data reports, and asset archives to emit Rust source files into `pumpkin-data`. +- **`tools/pumpkin-fuzzer`**: Fuzz testing suites for network packet parsers, NBT readers, and region file decoders to identify potential crash vectors or memory anomalies. + +--- + +## Concurrency & Threading Model + +| Component | Runtime / Pool | Primary Workload | Communication Mechanism | +| :--- | :--- | :--- | :--- | +| **Network & Socket I/O** | Tokio Async Runtime | TCP packet reads, Bedrock UDP packets, framing, session handshakes | Async tasks (`tokio::spawn`), non-blocking reactors | +| **Heavy Compute** | Rayon Thread Pool | Terrain generation, noise routers, light propagation, chunk compression | Worker threads via crossbeam / Rayon iterators | +| **Inter-Thread Bridge** | Tokio Channels | Dispatching completed chunks and encoded packets back to network tasks | Non-blocking `tokio::sync::mpsc` channels | + +Pumpkin strictly adheres to this separation of concerns: +- **I/O Bound**: Managed on Tokio asynchronous tasks. +- **CPU Bound**: Managed on Rayon thread pools. +- **Communication**: Coordinated via cross-thread channels (`tokio::sync::mpsc`) without ever blocking the Tokio reactor loop. diff --git a/docs/pt/developer/blocks/adding-blocks.md b/docs/pt/developer/blocks/adding-blocks.md new file mode 100644 index 0000000..0770ed8 --- /dev/null +++ b/docs/pt/developer/blocks/adding-blocks.md @@ -0,0 +1,182 @@ +# Adding a Block Behavior + +This guide walks through implementing interactive behavior for a block in Pumpkin. + +--- + +## When Is a Behavior Needed? + +- **Non-Interactive Blocks** (e.g., stone, dirt, terracotta): Do **not** require any custom Rust code. Their breaking speeds, hardness, and placement properties are automatically derived from `pumpkin-data`. +- **Interactive or Functional Blocks** (e.g., doors, chests, levers, crops, campfires, TNT): Require an implementation of the `BlockBehaviour` trait to respond to player clicks, redstone signals, physics steps, or timer ticks. + +--- + +## Step 1: Create the Behavior Struct + +Create a new file under `crates/pumpkin/src/block/blocks//.rs` (or directly in `crates/pumpkin/src/block/blocks/`). + +Use the `#[pumpkin_block(...)]` macro to associate your behavior struct with one or more vanilla block identifiers: + +```rust +use std::sync::Arc; +use pumpkin_data::Block; +use pumpkin_data::block_properties::{DoorLikeProperties, Facing}; +use pumpkin_macros::pumpkin_block; +use pumpkin_util::math::position::BlockPos; +use crate::block::registry::BlockActionResult; +use crate::block::{ + BlockBehaviour, NormalUseArgs, OnNeighborUpdateArgs, PlacedArgs, UseWithItemArgs, +}; +use crate::world::World; + +/// Associates this behavior struct with one or multiple block IDs. +#[pumpkin_block([ + "minecraft:oak_door", + "minecraft:iron_door", + "minecraft:spruce_door", + "minecraft:birch_door" +])] +pub struct DoorBlock; + +impl BlockBehaviour for DoorBlock { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state_id = args.world.get_block_state(args.location).await; + + // Iron doors require redstone power and cannot be opened manually by hand + if args.block.id == pumpkin_data::Block::IRON_DOOR.id { + return BlockActionResult::Pass; + } + + // Toggle open state + let is_open = DoorLikeProperties::open(state_id); + let new_state = DoorLikeProperties::with_open(state_id, !is_open); + args.world.set_block_state(args.location, new_state).await; + + BlockActionResult::Consume + } + + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + // Handle neighbor redstone signals or support block destruction + } +} +``` + +--- + +## Step 2: `BlockBehaviour` Hook Reference + +The `BlockBehaviour` trait exposes lifecycle hooks modeled on vanilla mechanics: + +### Player & Item Interaction Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `normal_use` | `(&self, args: NormalUseArgs<'_>) -> BlockActionResult` | Triggered when right-clicking the block with an empty hand. | +| `use_with_item` | `(&self, args: UseWithItemArgs<'_>) -> BlockActionResult` | Triggered when right-clicking the block while holding an item. | +| `get_screen_handler_factory` | `(&self, args: GetScreenHandlerFactoryArgs<'_>) -> Option>` | Returns an inventory container factory to open a UI screen (e.g., Chests, Crafting Tables). | + +### World Lifecycle & Physics Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `placed` | `(&self, args: PlacedArgs<'_>)` | Triggered immediately after the block is placed in the world. | +| `broken` | `(&self, args: BrokenArgs<'_>)` | Triggered when broken by a player or game mechanics. | +| `on_entity_collision` | `(&self, args: OnEntityCollisionArgs<'_>)` | Triggered when an entity walks inside, steps on, or falls into the block. | +| `on_neighbor_update` | `(&self, args: OnNeighborUpdateArgs<'_>)` | Triggered when an adjacent block state updates or is destroyed. | +| `on_projectile_hit` | `(&self, args: OnProjectileHitArgs<'_>)` | Triggered when an arrow, snowball, or projectile strikes the block. | + +### Redstone & Power Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `emits_redstone_power` | `(&self, state_id: BlockStateId) -> bool` | Declares whether the block emits redstone power to neighbors. | +| `get_weak_redstone_power` | `(&self, ...) -> u8` | Returns emitted weak power level (0–15). | +| `get_strong_redstone_power` | `(&self, ...) -> u8` | Returns emitted strong direct power level (0–15). | + +### Foliage & Bonemeal Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `is_valid_bonemeal_target` | `(&self, args: BonemealArgs<'_>) -> bool` | Checks if bone meal can be applied (e.g., crop not fully grown). | +| `is_bonemeal_success` | `(&self, args: BonemealArgs<'_>) -> bool` | Evaluates random chance roll for bone meal success. | +| `perform_bonemeal` | `(&self, args: BonemealArgs<'_>)` | Advances growth stages or spawns adjacent flora. | + +--- + +## Step 3: Modifying Block States + +All block properties are strongly typed in `pumpkin_data::block_properties`: + +```rust +use pumpkin_data::block_properties::{BedLikeProperties, Part}; + +// 1. Read typed properties from the numeric BlockStateId +let part = BedLikeProperties::part(state_id); // Part::Head or Part::Foot +let is_occupied = BedLikeProperties::occupied(state_id); + +// 2. Generate a new BlockStateId with updated properties +let updated_state = BedLikeProperties::with_occupied(state_id, true); + +// 3. Commit the updated state to the world +world.set_block_state(pos, updated_state).await; +``` + +--- + +## Step 4: Adding a Block Entity (Tile Entity) + +If your block needs to store persistent custom state (inventories, custom text, timers): + +1. Create a block entity struct in `crates/pumpkin/src/block/entities/`: + ```rust + use pumpkin_util::math::position::BlockPos; + use crate::block::entities::BlockEntity; + + pub struct CustomBlockEntity { + pub pos: BlockPos, + pub custom_counter: u32, + } + + impl BlockEntity for CustomBlockEntity { + fn to_nbt(&self) -> pumpkin_nbt::compound::NbtCompound { + let mut compound = pumpkin_nbt::compound::NbtCompound::new(); + compound.put_int("Counter", self.custom_counter as i32); + compound + } + + fn from_nbt(&mut self, tag: &pumpkin_nbt::compound::NbtCompound) { + if let Some(val) = tag.get_int("Counter") { + self.custom_counter = val as u32; + } + } + + fn tick(&mut self, world: &std::sync::Arc, pos: &BlockPos) { + // Periodic logic per game tick + } + } + ``` + +2. Link the block entity in your `BlockBehaviour`: + ```rust + impl BlockBehaviour for CustomBlock { + fn create_block_entity(&self, pos: BlockPos) -> Option>> { + Some(Arc::new(Mutex::new(CustomBlockEntity::new(pos)))) + } + } + ``` + +--- + +## Step 5: Registering the Block + +1. Export your module in `crates/pumpkin/src/block/blocks/mod.rs`: + ```rust + pub mod doors; + ``` + +2. Register the behavior struct in `crates/pumpkin/src/block/registry.rs`: + ```rust + manager.register(DoorBlock); + ``` + +At startup, `manager.register` automatically reads the block IDs declared in `#[pumpkin_block(...)]` and routes interactions to your implementation. diff --git a/docs/pt/developer/blocks/index.md b/docs/pt/developer/blocks/index.md new file mode 100644 index 0000000..525a200 --- /dev/null +++ b/docs/pt/developer/blocks/index.md @@ -0,0 +1,104 @@ +# Block Architecture & State System + +Blocks form the static and dynamic foundation of Minecraft worlds. In Pumpkin, block handling is cleanly bifurcated into two distinct layers: + +1. **Static Data Layer ([`pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data))**: Compile-time generated metadata including numeric block IDs, block state IDs, hardness, blast resistance, light emission, opacity, collision shapes, and block state properties generated by [`pumpkin-codegen`](/developer/codegen). +2. **Dynamic Behavior Layer ([`pumpkin::block`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/block))**: Server-side behavioral hooks—such as opening containers, toggling redstone signals, growing crops, breaking triggers, and collision effects—handled via the `BlockBehaviour` trait. + +--- + +## Static Blocks vs. Interactive Behaviors + +Because Minecraft contains thousands of block state combinations, Pumpkin avoids allocating runtime behavior structs for simple, non-interactive blocks: + +| Category | Description | Examples | Runtime Behavior Required? | +| :--- | :--- | :--- | :--- | +| **Static Blocks** | Rigid solid voxels without interactive logic or custom physics | `stone`, `dirt`, `cobblestone`, `bedrock`, `obsidian` | No — Placement, breaking times, and tool requirements are handled entirely by `pumpkin-data`. | +| **Interactive Blocks** | Blocks triggered by right-clicks or tool actions | `door`, `trapdoor`, `lever`, `button`, `crafting_table` | Yes — Implements `BlockBehaviour::normal_use` or `use_with_item`. | +| **Collision Blocks** | Blocks that modify entity movement or apply forces | `honey_block`, `slime_block`, `soul_sand`, `powder_snow` | Yes — Implements `on_entity_collision` or `on_landed_upon`. | +| **Redstone Components** | Power sources, conductors, and logic gates | `redstone_wire`, `repeater`, `comparator`, `observer` | Yes — Implements `emits_redstone_power` and `on_neighbor_update`. | +| **Block Entities** | Blocks with persistent, serialized custom states | `chest`, `furnace`, `sign`, `decorated_pot`, `spawner` | Yes — Implements `create_block_entity` and links a `BlockEntity`. | + +--- + +## Block IDs, States, and Properties + +Minecraft distinguishes between a **Block** type and a specific **Block State**: + +### 1. `BlockId` and `BlockStateId` + +- **`BlockId`**: An identifier representing a base block type (e.g., `minecraft:oak_door`). +- **`BlockStateId`**: A 16-bit integer representing a unique combination of block properties (e.g., `minecraft:oak_door[facing=north,half=lower,hinge=left,open=false,powered=false]`). + +In Pumpkin, block data is looked up from precomputed lookup tables in `pumpkin-data`: + +```rust +use pumpkin_data::Block; +use pumpkin_data::BlockStateId; + +// Access static properties directly from generated data +let door_block = Block::OAK_DOOR; +let default_state_id: BlockStateId = door_block.default_state_id; + +let hardness = door_block.hardness; +let blast_resistance = door_block.blast_resistance; +let is_air = door_block.is_air; +``` + +### 2. Generated State Property Helpers + +Rather than parsing raw strings or bitmasks manually, [`pumpkin-codegen`](/developer/codegen) generates strongly typed property helpers in `pumpkin_data::block_properties`: + +```rust +use pumpkin_data::block_properties::{DoorLikeProperties, DoubleBlockHalf, Facing}; + +// Read typed properties from a numeric BlockStateId +let is_open = DoorLikeProperties::open(state_id); +let half = DoorLikeProperties::half(state_id); +let facing = DoorLikeProperties::facing(state_id); + +// Generate a new BlockStateId with mutated properties +let toggled_state_id = DoorLikeProperties::with_open(state_id, !is_open); +``` + +--- + +## Physics, Bounding Boxes & Raycasting + +Pumpkin calculates collision and raycasting bounding boxes directly using pre-baked shapes in `pumpkin-data`: + +- **Solid Collisions**: During physics ticks, an entity's axis-aligned bounding box (AABB) is tested against surrounding block collision voxels. +- **Raycasting**: When a player targets a block to place or break, client raycasts are validated against the server-side block outline shapes. +- **Custom Movement Hooks**: Blocks like slime blocks or honey blocks override vertical rebound or horizontal velocity: + - `bounce_entity_after_fall`: Dampens fall damage and multiplies rebound velocity. + - `stop_vertical_movement_after_fall`: Zeros fall velocity upon contact. + +--- + +## Block Entities (Tile Entities) + +When a block must store arbitrary dynamic data that cannot fit into standard block state bits (such as container inventories, custom sign text, brewing timers, or mob spawner delays), it uses a **Block Entity**: + +| Component | Responsibility | Implementation File | +| :--- | :--- | :--- | +| `BlockEntity` Trait | Defines NBT serialization (`to_nbt`, `from_nbt`) and tick execution | [`crates/pumpkin/src/block/entities/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/block/entities) | +| `create_block_entity` | Hook in `BlockBehaviour` called when placing the block | Attached to relevant block behaviors | +| Chunk Entity Storage | Manages active block entities and coordinates within chunk sections | [`crates/pumpkin-world`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-world) | + +--- + +## The Block Registry & Dispatch System + +Block interaction events are routed through the central `BlockRegistry` in [`crates/pumpkin/src/block/registry.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/block/registry.rs): + +1. **Registration**: At server initialization, all behavior structs are registered into the `BlockManager`. +2. **Mapping**: The manager maps numeric `BlockId` keys to thread-safe behavior instances (`Arc`). +3. **Dispatch**: When the network layer receives block action packets (such as `CUseItemOn` or block dig packets), the server looks up the behavior in $O(1)$ time and invokes the relevant hook. + +--- + +## Next Steps + +To implement a new interactive block behavior, continue to the practical guide: + +- **[Adding a Block](/developer/blocks/adding-blocks)**: Complete walk-through of declaring attributes, implementing interaction hooks, creating block entities, and registering behaviors. diff --git a/docs/pt/developer/codegen.md b/docs/pt/developer/codegen.md new file mode 100644 index 0000000..3908d04 --- /dev/null +++ b/docs/pt/developer/codegen.md @@ -0,0 +1,66 @@ +# Code Generation (`pumpkin-codegen`) + +Minecraft is a massive game with thousands of block states, items, packet IDs, sound events, recipes, and biome parameters. These registries frequently change between Minecraft updates. + +To maintain 100% fidelity without manually maintaining tens of thousands of error-prone constants, Pumpkin utilizes an automated code generation tool: [`tools/pumpkin-codegen`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/tools/pumpkin-codegen). + +--- + +## Purpose & Workflow + +`pumpkin-codegen` parses official Minecraft server data generator reports, asset files, and version specifications, and outputs zero-cost, strongly-typed Rust files directly into [`crates/pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data). + +The pipeline operates in three distinct stages: +1. **Ingestion**: Reads raw JSON assets and vanilla server reports from `assets/` and `assets/datapacks/`. +2. **Transformation**: Normalizes states, calculates bitsets, processes ViaVersion remappings, and optimizes lookup tables. +3. **Emission**: Emits compiled Rust constants and structs into [`crates/pumpkin-data/src/generated`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data/src/generated). + +--- + +## Generated Datasets + +`pumpkin-codegen` generates the following components in `pumpkin-data`: + +| Category | Description | Generated Source | +| :--- | :--- | :--- | +| **Packets** | Serverbound and clientbound packet IDs for all protocol states across supported Minecraft versions. | `packet.rs` | +| **Blocks** | Block definitions, default states, state properties (facing, powered, waterlogged, etc.), and block collision boxes. | `block.rs` | +| **Items** | Item definitions, stack sizes, durability, data components, food properties, and equipment slots. | `item.rs` | +| **Registries** | Dynamic registries for biomes, dimensions, chat types, damage types, trim materials, and banner patterns. | `registry.rs` | +| **Biomes & Noise** | Climate parameters, noise router configurations, 3D noise settings, and surface rules. | `biome.rs`, `noise_router.rs` | +| **Entities** | Entity types, default bounding boxes, attributes, and tracked entity metadata schemas. | `entity_type.rs`, `tracked_data.rs` | +| **Recipes & Crafting** | Shaped, shapeless, smelting, blasting, smoking, and stonecutting recipes. | `recipes.rs` | +| **Tags** | Block tags, item tags, fluid tags, and entity type tags (e.g., `#minecraft:mineable/pickaxe`). | `tag.rs` | +| **Enchantments & Potions** | Enchantment definitions, potion effects, and brewing stand recipes. | `enchantments.rs`, `potion.rs` | +| **Translations** | Official localization keys for chat messages and item names. | `translations.rs` | +| **Plugin WIT** | WebAssembly Component Model WIT files defining plugin interfaces. | `wit/` | + +--- + +## Running Code Generation + +To run the code generator locally: + +```bash +# Navigate to the workspace root +cd Pumpkin + +# Run the codegen tool +cargo run -p pumpkin-codegen +``` + +### When Should You Run Codegen? + +- **Upgrading to a New Minecraft Version**: When porting Pumpkin to a new minor or major Minecraft release. +- **Adding Protocol Mappings**: When adding support for an older or newer client protocol version. +- **Updating WIT Definitions**: When adding new functions or types to the WebAssembly plugin API. + +--- + +## Data Sources + +`pumpkin-codegen` reads data from several primary sources: + +1. **Minecraft Data Generator Reports**: Generated by running `java -DbundlerMainClass=net.minecraft.data.Main -jar server.jar --reports`. +2. **Minecraft Client Assets**: Extracted from client `.jar` files for translation keys, model shapes, and sound mappings. +3. **Minecraft Protocol Wiki**: Cross-referenced with protocol specifications for packet IDs. diff --git a/docs/pt/developer/commands/adding-commands.md b/docs/pt/developer/commands/adding-commands.md new file mode 100644 index 0000000..eda739a --- /dev/null +++ b/docs/pt/developer/commands/adding-commands.md @@ -0,0 +1,246 @@ +# Adding a Command + +This guide explains how to implement and register a new command in the Pumpkin server codebase. + +Commands in Pumpkin are implemented in the `crates/pumpkin/src/command/commands/` directory and registered into the central [`CommandDispatcher`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/dispatcher.rs) during server initialization. + +--- + +## Anatomy of a Pumpkin Command + +A typical command file contains: +1. **Metadata Constants**: Command description, permission node identifier, and argument names. +2. **Permission Registration**: Declaring default OP requirements in the server's [`PermissionRegistry`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-util/src/permission.rs). +3. **Command Executors**: Structs implementing the [`CommandExecutor`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/mod.rs) trait for each terminal node or subcommand. +4. **Registration Function**: Constructing the argument tree using fluent builders (`command`, `literal`, `argument`). + +--- + +## Step-by-Step Implementation: Creating `/heal` + +Let's implement a complete `/heal` command that can heal either the executing player or a set of target players: + +``` +/heal -> Heals the executing sender +/heal -> Heals target player(s) +/heal -> Heals target player(s) by a specified amount +``` + +### 1. Create the File + +Create `crates/pumpkin/src/command/commands/heal.rs`: + +```rust +use std::sync::Arc; + +use crate::command::argument_builder::{ArgumentBuilder, argument, command}; +use crate::command::argument_types::core::float::FloatArgumentType; +use crate::command::argument_types::entity::EntityArgumentType; +use crate::command::context::command_context::CommandContext; +use crate::command::errors::error_types::CommandErrorType; +use crate::command::node::dispatcher::CommandDispatcher; +use crate::command::node::{CommandExecutor, CommandExecutorResult}; +use crate::entity::EntityBase; +use pumpkin_data::translation; +use pumpkin_util::PermissionLvl; +use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry}; +use pumpkin_util::text::TextComponent; + +const DESCRIPTION: &str = "Restores health to target entities."; +const PERMISSION: &str = "pumpkin:command.heal"; + +const ARG_TARGETS: &str = "targets"; +const ARG_AMOUNT: &str = "amount"; + +const ERROR_NOT_PLAYER: CommandErrorType<0> = CommandErrorType::new( + translation::java::PERMISSIONS_REQUIRES_PLAYER, + translation::java::PERMISSIONS_REQUIRES_PLAYER, +); +``` + +### 2. Implement the Executors + +Command execution logic is encapsulated in structs implementing [`CommandExecutor`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/mod.rs): + +```rust +/// Heals the executing player to full health. +struct SelfHealExecutor; + +impl CommandExecutor for SelfHealExecutor { + fn execute(&self, context: &CommandContext) -> CommandExecutorResult { + let player = context + .source + .output + .as_player() + .ok_or_else(|| ERROR_NOT_PLAYER.create_without_context())?; + + // Restore player health + player.living_entity.set_health(20.0); + + context.source.send_feedback( + TextComponent::text("Fully restored your health!"), + true, // Broadcast to operators if configured + ); + + // Return number of affected entities + Ok(1) + } +} + +/// Heals specified target players. +struct TargetsHealExecutor { + has_amount: bool, +} + +impl CommandExecutor for TargetsHealExecutor { + fn execute(&self, context: &CommandContext) -> CommandExecutorResult { + let targets = EntityArgumentType::get_players(context, ARG_TARGETS)?; + + let amount = if self.has_amount { + FloatArgumentType::get(context, ARG_AMOUNT)? + } else { + 20.0 + }; + + let count = targets.len(); + for target in &targets { + let current = target.living_entity.health.load(); + target.living_entity.set_health((current + amount).min(20.0)); + } + + let msg = if count == 1 { + TextComponent::text(format!("Healed {} by {} HP", targets[0].gameprofile.name, amount)) + } else { + TextComponent::text(format!("Healed {} players by {} HP", count, amount)) + }; + + context.source.send_feedback(msg, true); + + Ok(count as i32) + } +} +``` + +### 3. Build the Command Tree + +Use the fluent builder functions `command`, `literal`, and `argument` to assemble the syntax tree and register the permission: + +```rust +pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) { + // 1. Register permission node with default requirement (OP level 2) + registry.register_permission_or_panic(Permission::new( + PERMISSION, + DESCRIPTION, + PermissionDefault::Op(PermissionLvl::Two), + )); + + // 2. Assemble command tree + dispatcher.register( + command("heal", DESCRIPTION) + .requires(PERMISSION) + .executes(SelfHealExecutor) + .then( + argument(ARG_TARGETS, EntityArgumentType::Players) + .executes(TargetsHealExecutor { has_amount: false }) + .then( + argument(ARG_AMOUNT, FloatArgumentType::new(1.0, 100.0)) + .executes(TargetsHealExecutor { has_amount: true }), + ), + ), + ); +} +``` + +--- + +## Command Structure Patterns + +### Adding Subcommand Literals +Use `literal("subcommand_name")` to create keyword branches: + +```rust +use crate::command::argument_builder::{argument, command, literal}; + +dispatcher.register( + command("weather", "Set the world weather") + .requires("minecraft:command.weather") + .then(literal("clear").executes(ClearWeatherExecutor)) + .then(literal("rain").executes(RainWeatherExecutor)) + .then(literal("thunder").executes(ThunderWeatherExecutor)) +); +``` + +### Cross-Platform Translation Feedback +Pumpkin servers serve both Java and Bedrock Edition players simultaneously. Whenever possible, use vanilla translation keys with `TextComponent::translate_cross` so messages render localized on the client: + +```rust +use pumpkin_data::translation; +use pumpkin_util::text::TextComponent; + +let msg = TextComponent::translate_cross( + translation::java::COMMANDS_KILL_SUCCESS_SINGLE, + translation::bedrock::COMMANDS_KILL_SUCCESSFUL, + [target.get_display_name()], +); + +context.source.send_feedback(msg, true); +``` + +### Error Handling in Executors +Executors must return `CommandExecutorResult`, which resolves to `Result`. If validation fails, return an error cleanly without panicking: + +```rust +use crate::command::errors::error_types::CommandErrorType; + +const ERROR_INVALID_VALUE: CommandErrorType<0> = CommandErrorType::new( + "Amount must be greater than zero.", + "Amount must be greater than zero.", +); + +if amount <= 0.0 { + return Err(ERROR_INVALID_VALUE.create_without_context()); +} +``` + +--- + +## Registering in `default_dispatcher` + +Once your command is implemented, register it in `crates/pumpkin/src/command/commands/mod.rs`: + +1. **Declare the Module**: + ```rust + // In crates/pumpkin/src/command/commands/mod.rs + mod heal; + ``` + +2. **Invoke `register` in `default_dispatcher`**: + ```rust + pub fn default_dispatcher( + permission_manager: &PermissionManager, + commands_config: &CommandsConfig, + ) -> CommandDispatcher { + let registry = &permission_manager.registry; + let mut dispatcher = CommandDispatcher::new(); + + // ... existing registrations ... + heal::register(&mut dispatcher, registry); + + dispatcher + } + ``` + +--- + +## Best Practices Checklist + +- [ ] **No Panics or Unwraps**: Use `?` or return descriptive `CommandSyntaxError`s when arguments are invalid. +- [ ] **Permission Nodes**: Prefix permission strings with appropriate namespaces (`minecraft:command.` for vanilla commands, `pumpkin:command.` for server-specific commands). +- [ ] **Return Success Counts**: The returned `i32` represents the command's exit code or number of affected objects for redstone command blocks and `/execute store result`. +- [ ] **Respect GameRules**: When notifying players other than the sender, check if `send_command_feedback` is enabled: + ```rust + let server = context.source.server(); + if server.level_info.load().game_rules.send_command_feedback { + target.send_system_message(&feedback); + } + ``` diff --git a/docs/pt/developer/commands/arguments-and-suggestions.md b/docs/pt/developer/commands/arguments-and-suggestions.md new file mode 100644 index 0000000..6794ae1 --- /dev/null +++ b/docs/pt/developer/commands/arguments-and-suggestions.md @@ -0,0 +1,159 @@ +# Argument Types & Suggestions + +Pumpkin provides a comprehensive suite of type-safe command arguments through `pumpkin-command` and the main `pumpkin` server crate. Arguments deserialize raw token streams into structured Rust types, validate ranges, and furnish tab-completion suggestions to clients. + +--- + +## Standard Argument Types + +Argument parsers implement the [`ArgumentType`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/argument_types/argument_type.rs) trait. The following built-in types are available for command builders: + +### 1. Primitive & Scalar Types + +| Argument Type | Import Path | Deserialized Type | Description | +| :--- | :--- | :--- | :--- | +| **`IntegerArgumentType`** | `...::core::integer` | `i32` | Standard 32-bit integer with optional `[min, max]` bounds. | +| **`FloatArgumentType`** | `...::core::float` | `f32` | 32-bit floating point number with bounds. | +| **`DoubleArgumentType`** | `...::core::double` | `f64` | 64-bit floating point number with bounds. | +| **`LongArgumentType`** | `...::core::long` | `i64` | 64-bit integer with bounds. | +| **`BoolArgumentType`** | `...::core::bool` | `bool` | Boolean value (`true` or `false`). | +| **`StringArgumentType`** | `...::core::string` | `String` | String tokens based on quoting rules. | + +#### String Argument Behaviors +- **`StringArgumentType::Word`**: Parses a single word without whitespace. +- **`StringArgumentType::Quotable`**: Parses either a single word or a quoted phrase (`"hello world"`). +- **`StringArgumentType::Greedy`**: Consumes all remaining characters in the input stream (ideal for chat/broadcast commands like `/say` and `/me`). + +--- + +## Coordinate Argument Types + +Minecraft coordinates support absolute positions, relative offsets (`~`), and local caret directions (`^`): + +```rust +use crate::command::argument_builder::argument; +use crate::command::argument_types::coordinates::block_pos::BlockPosArgumentType; +use crate::command::argument_types::coordinates::vec3::Vec3ArgumentType; + +// Block position (integer BlockPos) +argument("pos", BlockPosArgumentType) + +// Precise entity coordinates (f64 Vector3) +argument("location", Vec3ArgumentType) +``` + +### Retrieving Parsed Coordinates +Inside an executor, coordinate arguments resolve relative offsets against the sender's current position and orientation in the [`CommandSource`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/command/context/command_source.rs): + +```rust +// Extracts centered f64 coordinates (handling ~ and ^ relative to sender) +let pos: Vector3 = Vec3ArgumentType::get_coordinates(context, "location")?; + +// Extracts integer block position +let block_pos: BlockPos = BlockPosArgumentType::get_block_pos(context, "pos")?; +``` + +--- + +## Entity Selectors (`EntityArgumentType`) + +Entity selectors allow players and command blocks to target entities using player names, UUIDs, or vanilla target selectors (`@p`, `@r`, `@a`, `@e`, `@s`). + +In Pumpkin, entity selectors are accessed via [`EntityArgumentType`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/command/argument_types/entity.rs): + +```rust +pub enum EntityArgumentType { + /// Matches exactly one player + Player, + /// Matches zero or more players + Players, + /// Matches exactly one entity + Entity, + /// Matches zero or more entities + Entities, +} +``` + +### Resolution in Executors + +```rust +// 1. Target single player +let player: Arc = EntityArgumentType::get_player(context, "target")?; + +// 2. Target multiple players +let players: Vec> = EntityArgumentType::get_players(context, "targets")?; + +// 3. Target single living entity or mob +let entity: Arc = EntityArgumentType::get_entity(context, "target")?; + +// 4. Target multiple entities +let entities: Vec> = EntityArgumentType::get_entities(context, "targets")?; +``` + +### Supported Selector Syntaxes +Pumpkin's entity selector parser (`crates/pumpkin/src/command/argument_types/entity_selector/`) parses and validates: +- `@p`: Nearest player relative to execution coordinates. +- `@r`: Random player. +- `@a`: All online players. +- `@e`: All entities in loaded world chunks. +- `@s`: Executing entity (`context.source.entity`). +- **Bracket Arguments**: Filter queries like `@e[type=zombie,distance=..10,limit=5,sort=nearest]`. + +--- + +## Registry & Game Mechanic Types + +Pumpkin provides specialized argument parsers for Minecraft data types: + +- **`GameModeArgumentType`**: Resolves `survival`, `creative`, `adventure`, `spectator` or numeric IDs `0`–`3`. +- **`BlockArgumentType`**: Parses block identifiers and block states (e.g., `minecraft:chest[facing=north]`). +- **`ItemArgumentType`**: Parses item identifiers and optional data components (e.g., `minecraft:diamond_sword`). +- **`NbtArgumentType`**: Parses SNBT (Stringified NBT) tags into Pumpkin's NBT representation. +- **`TimeArgumentType`**: Parses durations with suffixes (e.g., `10s`, `20t`, `1d`). + +--- + +## Suggestion Providers & Tab Completion + +Arguments can supply dynamic tab-completion candidates to connected players using the [`SuggestionProvider`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/suggestion/mod.rs) trait: + +```rust +use crate::command::context::command_context::CommandContext; +use crate::command::suggestion::provider::SuggestionProvider; +use pumpkin_command::suggestion::suggestions::{Suggestions, SuggestionsBuilder}; + +struct CustomSuggestionProvider; + +impl SuggestionProvider for CustomSuggestionProvider { + fn suggest( + &self, + context: &CommandContext, + mut builder: SuggestionsBuilder, + ) -> Suggestions { + // Retrieve already typed token prefix + let remaining = builder.remaining(); + + let candidates = ["alpha", "bravo", "charlie"]; + for candidate in candidates { + if candidate.starts_with(remaining) { + builder.suggest(candidate); + } + } + + builder.build() + } +} +``` + +### Attaching Suggestions to Arguments +Use the `.suggests(...)` method on `RequiredArgumentBuilder`: + +```rust +argument("code", StringArgumentType::Word) + .suggests(CustomSuggestionProvider) + .executes(MyExecutor) +``` + +### Client Completion Protocol +- **Java Edition**: When a player presses `Tab`, the client transmits a `ServerboundCommandSuggestion` packet. Pumpkin matches the cursor position within the `CommandDispatcher`, executes the target node's `SuggestionProvider`, and responds with `ClientboundCommandSuggestions` containing completion ranges and tooltip tooltips. +- **Bedrock Edition**: Suggestion options for literals and enums are serialized ahead of time in the `CAvailableCommands` packet as dynamic enum value tables. diff --git a/docs/pt/developer/commands/execution-and-context.md b/docs/pt/developer/commands/execution-and-context.md new file mode 100644 index 0000000..6f5f044 --- /dev/null +++ b/docs/pt/developer/commands/execution-and-context.md @@ -0,0 +1,118 @@ +# Execution & Context + +In Pumpkin, command execution separates the **physical origin** of a command from its **runtime context**. This design enables complex command routing, dimension shifts, conditional execution, and vanilla-compliant `/execute` chaining. + +--- + +## `CommandSender` vs. `CommandSource` + +Understanding the distinction between `CommandSender` and `CommandSource` is fundamental to developing commands in Pumpkin: + +| Attribute | `CommandSender` | `CommandSource` | +| :--- | :--- | :--- | +| **Concept** | The physical origin and network destination | The mutable runtime execution environment | +| **Mutability** | Immutable throughout command execution | Cloned and modified across `/execute` branches | +| **Key Fields** | Terminal socket, connection handle, RCON buffer | World, position coordinates, rotation, entity, silent flag | +| **Creation** | Instantiated on network connection or terminal input | Constructed via `sender.into_source(&server)` | + +### 1. `CommandSender` (Physical Caller) +Represents the entity or socket that initiated the command and receives its output: + +```rust +pub enum CommandSender { + Console, + Rcon(Arc>>), + Player(Arc), + CommandBlock(Arc, Arc), + Dummy, +} +``` + +- **Output Target**: Determines where messages formatted with `send_message` are routed (e.g., standard out, RCON buffer, player packet, or block entity NBT). +- **Hard Permissions**: Holds fixed base permissions (e.g., Console has level 4 bypass; Command Blocks have level 2). + +### 2. `CommandSource` (Execution Environment) +Represents the mutable runtime state during command evaluation: + +```rust +pub struct CommandSource { + pub output: CommandSender, + pub world: Option>, + pub entity: Option>, + pub position: Vector3, + pub rotation: Vector2, + pub name: String, + pub display_name: TextComponent, + pub server: Option>, + pub silent: bool, + pub command_result_taker: ResultValueTaker, + pub entity_anchor: EntityAnchor, +} +``` + +A sender converts into a `CommandSource` via `sender.into_source(&server)`. + +--- + +## The `/execute` Command & Context Redirection + +The `/execute` command manipulates `CommandSource` properties before passing control to a downstream command. + +### Branching Modifiers +- **`as `**: Changes `context.source.entity` to target entities without altering position or rotation. +- **`at `**: Updates `context.source.world`, `position`, and `rotation` to match the target entity. +- **`positioned `**: Modifies `context.source.position` to new coordinates. +- **`in `**: Changes `context.source.world` to the specified dimension (Overworld, Nether, The End). +- **`anchored (feet|eyes)`**: Adjusts coordinate offsets for line-of-sight and facing checks. + +### Context Forking & Chaining +When `/execute` matches multiple targets (e.g., `/execute as @e run ...`), `pumpkin-command` uses **forking**: +1. The dispatcher forks the execution context once per matched target. +2. The child command executes independently for each cloned `CommandSource`. +3. The cumulative success count is aggregated across all executions. + +--- + +## Result Values & Success Counts + +Vanilla commands communicate results back through integer exit codes and return values. + +### The Return Value Callback +Executors return `CommandExecutorResult` (`Result`). The returned integer signifies: +- **0**: Failure or zero entities affected. +- **> 0**: Number of entities affected, blocks changed, or items cleared. + +For Command Blocks, this value is persisted into the block entity's `SuccessCount`: + +```rust +pub fn set_success_count(&self, count: u32) { + if let Self::CommandBlock(c, _) = self { + c.success_count.store(count, Ordering::SeqCst); + } +} +``` + +### `/execute store` +When chained with `/execute store (result|success) ...`, Pumpkin intercepts the return value via `ReturnValueCallback` and writes it to scoreboards, bossbars, or entity NBT tags. + +--- + +## Feedback & GameRules Integration + +Sending output to the command sender is governed by server configuration and world game rules: + +```rust +context.source.send_feedback( + TextComponent::text("Command executed successfully!"), + true, // Broadcast to server operators +); +``` + +### Output Rules Matrix + +| Condition | Feedback Behavior | +| :--- | :--- | +| `silent == true` | Output is suppressed completely. | +| `send_command_feedback == false` | Player senders do not receive success feedback (errors are still displayed). | +| `command_block_output == false` | Command block execution output is not broadcast to online operators. | +| `broadcast_console_to_ops == true` | Server console and RCON commands emit a copy of their feedback to all online OP level 2+ players. | diff --git a/docs/pt/developer/commands/index.md b/docs/pt/developer/commands/index.md new file mode 100644 index 0000000..3b31eb9 --- /dev/null +++ b/docs/pt/developer/commands/index.md @@ -0,0 +1,142 @@ +# Commands Architecture & Dispatcher + +Pumpkin features a high-performance, pure-Rust implementation of the **Brigadier** command tree specification through the dedicated `pumpkin-command` crate. It coordinates command parsing, requirement resolution, type-safe argument deserialization, execution branching, and dual-edition client tree synchronization for both **Minecraft: Java Edition** and **Minecraft: Bedrock Edition**. + +### Command Processing Pipeline + +Every command executed in Pumpkin flows through the following discrete stages: + +1. **Input Normalization**: Strips single leading slashes while preserving plugin multi-slash conventions (such as `//set`). +2. **Availability Check**: Rejects commands disabled via server configuration with standard `unknown command` syntax errors. +3. **Permission Evaluation**: Validates requirements (`PermissionLvl` or custom registry nodes) against the executing sender. +4. **Tokenization & Parsing**: `StringReader` recursively walks the `Tree`, matching literal keywords and parsing typed argument parameters. +5. **Context Dispatch**: Invokes the matched `CommandExecutor` callback with a typed `CommandContext`. +6. **Feedback & Result Consumption**: Formats cross-platform translation components, notifies observers, and writes exit codes back to the caller. + +--- + +## The `pumpkin-command` Crate + +Pumpkin decouples command parsing and tree representation from the main server runtime into the modular `pumpkin-command` crate (`crates/pumpkin-command`). + +Key components of `pumpkin-command` include: +- **`Tree`**: Contiguous vector-backed storage of attached command nodes (`AttachedNode`). +- **`CommandDispatcher`**: The central registry and dispatch engine. It processes raw command strings, parses tokens with `StringReader`, traverses the command tree, and executes target callbacks. +- **`ArgumentBuilder`**: Fluent builder API (`command`, `literal`, `argument`) modeled after Mojang's Brigadier library. +- **`CommandSource`**: Generic execution state representing the current position, rotation, dimension, entity, and feedback output stream. + +--- + +## The Command Node Hierarchy + +Commands in Pumpkin are modeled as directed graphs of nodes. To optimize memory allocations and avoid circular reference issues during building, the system separates nodes into **detached** and **attached** states: + +### 1. Detached Nodes (`DetachedNode`) +Constructed using fluent builders (`CommandArgumentBuilder`, `LiteralArgumentBuilder`, `RequiredArgumentBuilder`) before registration: +- **`CommandDetachedNode`**: Represents a root command entrypoint with an identifier and a description. +- **`LiteralDetachedNode`**: Represents a literal keyword in a subcommand branch (e.g., `set` in `/gamemode creative`). +- **`ArgumentDetachedNode`**: Represents a typed parameter parsed by an `ArgumentType` (e.g., coordinates, entity selectors, integer ranges). + +### 2. Attached Nodes (`AttachedNode`) +Once registered into the `CommandDispatcher`, detached trees are flattened into an indexed `Tree` structure: +```rust +pub enum AttachedNode { + Root(RootAttachedNode), + Literal(LiteralAttachedNode), + Argument(ArgumentAttachedNode), + Command(CommandAttachedNode), +} +``` +Each attached node has: +- **`NodeId`**: A zero-cost typed index (`NonZero`) into the contiguous node arena. +- **`children`**: Fast lookup map (`FxHashMap`) for literals and child arguments. +- **`requirements`**: Predicates determining whether a sender has permission to view or execute the node. +- **`command`**: Optional `CommandExecutor` callback invoked when parsing terminates on this node. +- **`redirect`**: Optional `Redirection` pointer to loop back or redirect to another sub-tree (used heavily by `/execute`). + +--- + +## Dispatch & Execution Pipeline + +When a command arrives at the server, `CommandDispatcher::handle_command` processes it through several stages: + +```rust +pub fn handle_command<'a>(&'a self, source: &S, mut input: &'a str) { + // 1. Strip single leading slash (preserving double-slash commands like //set) + if let Some(sliced) = input.strip_prefix('/') { + let first_token = input.split_whitespace().next().unwrap_or(""); + let sliced_token = sliced.split_whitespace().next().unwrap_or(""); + if !self.has_command(first_token) + && (self.has_command(sliced_token) || !first_token.starts_with("//")) + { + input = sliced; + } + } + + // 2. Check disabled commands from configuration + if self.is_disabled(Self::command_name(input)) { + let reader = StringReader::new(input); + Self::send_error_to_source(source, DISPATCHER_UNKNOWN_COMMAND.create(&reader), input); + return; + } + + // 3. Parse and execute + let output = self.execute_input(input, source); + if let Err(error) = output { + Self::send_error_to_source(source, error, input); + } +} +``` + +### Syntax Error Formatting +When parsing encounters invalid syntax, unexpected tokens, or out-of-range arguments, Pumpkin constructs a `CommandSyntaxError`. If context is available, the dispatcher formats a visual pointer highlighting exactly where the parser failed: + +``` +Unknown or incomplete command, see below for error +/gamemode creatve<--[HERE] +``` + +In the Java Edition client, this error text is annotated with a `ClickEvent::SuggestCommand`, allowing the player to click the error in chat to copy the failed command into their input box for quick editing. + +--- + +## Dual-Edition Network Synchronization + +Unlike vanilla servers which only serialize command graphs for Java Edition, Pumpkin dynamically synchronizes the registered command tree across both Java and Bedrock clients: + +### 1. Java Edition: `CCommands` Packet +When a player joins or their permission level updates (e.g., via `/op` or `/deop`), Pumpkin builds and sends a `CCommands` packet (`pumpkin_protocol::java::client::play::CCommands`): +- Converts `AttachedNode`s into a flat array of `ProtoNode` entries. +- Filters out disabled commands and nodes where `requirements.evaluate(source)` returns `false`. +- Serializes argument parsers (`brigadier:string`, `brigadier:integer`, `minecraft:entity`, etc.) and client-side suggestion identifiers. + +### 2. Bedrock Edition: `CAvailableCommands` Packet +Bedrock Edition uses an overload-and-enum format rather than Brigadier's recursive graph. In `client_suggestions.rs`, Pumpkin traverses the `AttachedNode` tree to synthesize: +- **`CommandData`**: Root command name, description, permission flags, and aliases. +- **`OverloadData`**: Flattened permutations of argument chains leading to executable endpoints. +- **`ParamData`**: Bedrock parameter types, bitflags (`ARG_FLAG_VALID`, `ARG_FLAG_ENUM`), and dynamically collected enum value tables. + +--- + +## Universal Execution Targets + +Pumpkin commands can be executed across diverse senders: + +| Sender | Source Representation | Output Destination | Default Permission | +| :--- | :--- | :--- | :--- | +| **Player** | `CommandSender::Player(Arc)` | Client system chat packet | Loaded from permission level / permissions file | +| **Server Console** | `CommandSender::Console` | Standard stdout (`tracing::info!`) | Level 4 (Full administrative bypass) | +| **RCON Console** | `CommandSender::Rcon(Arc>>)` | Network response packet payload | Level 4 | +| **Command Block** | `CommandSender::CommandBlock(Arc, Arc)` | NBT `LastOutput` field | Level 2 | +| **Datapack Function** | `CommandSender` inherited from caller | Inherited | Inherited | +| **WASM Plugin** | Invoked through WIT `execute_command` host call | Captured via `ResultDeferrer` | Configurable | + +--- + +## Command Configuration & Disabling + +Pumpkin allows server administrators to disable individual commands via `config/commands.toml` (`CommandsConfig`). When a command is disabled: +1. It is omitted from the `CCommands` packet for Java clients. +2. It is omitted from the `CAvailableCommands` packet for Bedrock clients. +3. Tab completion ignores the command entirely. +4. Any attempt to invoke the command returns `DISPATCHER_UNKNOWN_COMMAND`. diff --git a/docs/pt/developer/entities/ai-and-mobs.md b/docs/pt/developer/entities/ai-and-mobs.md new file mode 100644 index 0000000..bb1652f --- /dev/null +++ b/docs/pt/developer/entities/ai-and-mobs.md @@ -0,0 +1,111 @@ +# Mob AI & Goal System + +Pumpkin implements a modular, priority-driven Artificial Intelligence (AI) goal system for computer-controlled mobs, located in [`crates/pumpkin/src/entity/ai/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity/ai) and [`crates/pumpkin/src/entity/mob/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity/mob). + +--- + +## The Goal System Architecture + +Mob behaviors are decomposed into independent modular tasks called **Goals**. Each mob possesses a **GoalSelector** that maintains an ordered collection of goals evaluated on every server tick. + +### Goal Priority Hierarchy + +Goals are assigned an integer priority (where lower numbers represent higher precedence). During each tick, higher-priority actions preempt lower-priority background tasks: + +| Priority | Goal Type | Evaluation Condition | Behavior | +| :--- | :--- | :--- | :--- | +| **1 (Highest)** | `SwimGoal` | Entity is submerged in water | Jump repeatedly to swim to the water surface and avoid drowning. | +| **2** | `MeleeAttackGoal` | Valid hostile target in sight/range | Navigate towards target entity and execute melee attacks. | +| **3** | `WanderAroundGoal` | No active combat target | Pick a random nearby navigable voxel and walk to it. | +| **4 (Lowest)** | `LookAtEntityGoal` | Player or mob within observation radius | Rotate head and body pitch/yaw towards the target entity. | + +### Execution Flow + +1. **Evaluation**: On every game tick, `GoalSelector::tick()` iterates through registered goals in order of priority. +2. **Activation**: If an inactive goal satisfies `can_start()`, it is activated via `start()`. +3. **Execution**: Active goals run their per-tick logic in `tick()`. +4. **Preemption & Termination**: If `should_continue()` returns `false`, or a mutually exclusive goal with higher priority activates, `stop()` is called and the goal resets. + +### The `Goal` Lifecycle + +Every AI behavior implements the `Goal` trait: + +```rust +pub trait Goal: Send + Sync { + /// Evaluates preconditions to determine if this goal can activate. + fn can_start(&self) -> bool; + + /// Called once when the goal transitions from inactive to active. + fn start(&mut self) {} + + /// Evaluates whether the goal should continue executing on subsequent ticks. + fn should_continue(&self) -> bool { + self.can_start() + } + + /// Executes the continuous behavior on each server tick. + fn tick(&mut self) {} + + /// Called when the goal completes, fails, or is preempted by a higher priority goal. + fn stop(&mut self) {} +} +``` + +--- + +## Standard Built-in Goals + +Pumpkin provides standard vanilla AI behaviors in `crates/pumpkin/src/entity/ai/`: + +| Goal | Description | Target Mobs | +| :--- | :--- | :--- | +| **`SwimGoal`** | Jumps repeatedly while submerged in water to prevent drowning. | Almost all land mobs | +| **`MeleeAttackGoal`** | Navigates towards an attack target and executes a strike when within reach. | Zombies, Spiders, Iron Golems | +| **`RangedAttackGoal`** | Stays at range and shoots projectiles (arrows, snowballs). | Skeletons, Pillagers, Blazes | +| **`FleeSunGoal`** | Seeks shade or water during daytime to prevent combustion. | Zombies, Skeletons | +| **`PanicGoal`** | Sprints in random directions when taking damage. | Cows, Pigs, Sheep, Chickens | +| **`WanderAroundGoal`** | Selects random accessible coordinates nearby when idle. | Passive & neutral mobs | +| **`LookAtEntityGoal`** | Rotates head to face nearby players within sensory range. | Villagers, Mobs | + +--- + +## Navigation & Pathfinding + +Mobs navigate the block world using Pumpkin's 3D grid pathfinder: + +1. **Path Calculation**: Resolves a path from the mob's origin to target coordinates using an A* algorithm optimized for Minecraft block geometries. +2. **Obstacle Avoidance**: + - Avoids hazardous blocks (lava, fire, sweet berry bushes). + - Prevents walking off ledges higher than the mob's fall threshold. + - Computes jumping steps over 1-block obstacles. +3. **Waypoint Steering**: The navigation engine updates the mob's velocity vectors per tick to smoothly guide the entity along waypoints. + +--- + +## Entity Attributes System + +Mob capabilities and stats are governed by the **Attributes** system in [`crates/pumpkin/src/entity/attributes.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/entity/attributes.rs): + +```rust +use pumpkin_data::entity::attribute::EntityAttribute; + +// Common vanilla attributes +EntityAttribute::GENERIC_MAX_HEALTH; // Base hit points (e.g. 20.0 for Zombies) +EntityAttribute::GENERIC_MOVEMENT_SPEED; // Base walking velocity (e.g. 0.23) +EntityAttribute::GENERIC_ATTACK_DAMAGE; // Melee attack damage (e.g. 3.0) +EntityAttribute::GENERIC_FOLLOW_RANGE; // Sensory detection range in blocks (e.g. 35.0) +EntityAttribute::GENERIC_KNOCKBACK_RESISTANCE; // Resistance against knockback (0.0 to 1.0) +``` + +### Modifiers +Attributes support dynamic modifiers with additive or multiplicative calculations (e.g., speed bonuses from Swiftness potions or armor attribute boosts). + +--- + +## Ageable & Passive Entities + +Passive animals (cows, sheep, pigs) implement the `Ageable` trait: + +- **Baby State**: Renders with smaller collision boxes and oversized heads. +- **Growing Age**: A negative tick timer counting upward to `0`. When it reaches `0`, the entity matures into an adult. +- **Breeding Cooldown**: Positive tick timer tracking cooldowns between successful matings. diff --git a/docs/pt/developer/entities/index.md b/docs/pt/developer/entities/index.md new file mode 100644 index 0000000..53623ee --- /dev/null +++ b/docs/pt/developer/entities/index.md @@ -0,0 +1,77 @@ +# Entity Architecture & Hierarchy + +Entities in Minecraft represent all dynamic, living and non-living objects that exist within a world—ranging from dropped item stacks and arrows to zombies, villagers, and players. + +In Pumpkin, entity systems are implemented in [`crates/pumpkin/src/entity/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity). The architecture leverages object-oriented trait composition on top of concurrency-safe, cache-friendly data structures. + +--- + +## Entity Trait Hierarchy + +Pumpkin structures entities into an inheritance hierarchy using Rust traits: + +| Trait / Type | Parent Trait | Core Capabilities & State | +| :--- | :--- | :--- | +| `Entity` | *(None / Base Struct)* | Entity ID, UUID, atomic position and velocity vectors, bounding box, pose | +| `EntityBase` | *(Root Trait)* | Dynamic runtime downcasting (`get_living_entity`, etc.), world tick, damage, teleportation | +| `LivingEntity` | `EntityBase` | Health, absorption, potion effects, equipment slots, attribute containers | +| `Mob` | `LivingEntity` | AI goal selector, navigation, pathfinding, target tracking, death loot | +| `Player` | `LivingEntity` | Network client session, inventory, crafting screens, hunger, advancements | + +### 1. `Entity` (Base Struct) +Contains fundamental physics, geometry, and spatial state common to all entities: +- **`entity_id`**: Fast 32-bit unique integer identifier used across network packets. +- **`uuid`**: 128-bit persistent UUID. +- **`position` & `velocity`**: Thread-safe vectors (`AtomicCell>`) allowing concurrent reading without lock contention. +- **`bounding_box`**: 3D axis-aligned bounding box (AABB) used for collision resolution and raycast targeting. +- **`pose`**: Current pose (`Standing`, `Crouching`, `Sleeping`, `FallFlying`, etc.). + +### 2. `EntityBase` (Foundation Trait) +The root dynamic trait implemented by every entity in the server: +- Supports safe runtime downcasting via helper methods (`get_living_entity()`, `get_mob()`, `get_player()`). +- Provides hooks for world interaction: `tick()`, `damage()`, `teleport()`, `on_lightning_strike()`, `is_immune_to_explosion()`. + +### 3. `LivingEntity` (Biological Trait) +Extends `EntityBase` for entities with health, combat, and animation states: +- Manages health bars, absorption, invulnerability ticks, and fall damage. +- Tracks active potion effects (`crates/pumpkin/src/entity/effect/`). +- Handles equipment slots (`MainHand`, `OffHand`, `Helmet`, `Chestplate`, `Leggings`, `Boots`). +- Encapsulates attribute containers (max health, movement speed, knockback resistance). + +### 4. `Mob` (AI Trait) +Extends `LivingEntity` for computer-controlled mobs: +- Manages AI goal selectors (attacking, wandering, fleeing, looking around). +- Controls navigation and pathfinding over blocks. +- Spawns configured loot tables upon death. + +### 5. `Player` (Human Client) +Represents a human player connected through a game client: +- Links to network socket handlers (`JavaClient` or `BedrockClient`). +- Manages container screen handlers, ender chests, player inventory, and crafting. +- Tracks hunger, saturation, exhaustion, experience levels, and advancement criteria. + +--- + +## Physics & Movement Simulation + +On every server tick, Pumpkin simulates entity physics through a deterministic sequence: + +1. **Apply Velocity & Gravity**: Evaluates current velocity and adds downward gravitational acceleration unless `has_gravity()` is false or the entity is flying. +2. **Resolve Block Collisions**: Checks the entity's 3D axis-aligned bounding box (`bounding_box`) against solid block voxels in the surrounding chunk; velocity along colliding axes is dampened or zeroed. +3. **Apply Fluid Drag & Buoyancy**: Evaluates whether the entity intersects water or lava fluids, applying drag coefficients and buoyant upward force. +4. **Synchronize & Broadcast**: Persists the updated coordinates in atomic position cells and broadcasts relative delta movement packets to observing players. +5. **Void Death**: Non-player entities falling below $Y = -128$ are automatically despawned. + +--- + +## Combat & Damage System + +Damage calculations reside in [`crates/pumpkin/src/entity/combat.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/entity/combat.rs): + +1. **Damage Types**: Damage is categorized by `DamageType` (e.g., `GENERIC`, `PLAYER_ATTACK`, `FALL`, `FIRE`, `MAGIC`, `EXPLOSION`, `LIGHTNING_BOLT`). +2. **Damage Scaling**: + - Armor defense points mitigate incoming physical damage according to the vanilla formula: + $$\text{damage} = \text{damage} \times \left(1 - \frac{\min(20, \max(\text{armor} / 5, \text{armor} - \frac{\text{damage}}{2 + \text{toughness} / 4}))}{25}\right)$$ + - Resistance status effects and Protection enchantments apply secondary reductions. +3. **Knockback**: When damage is dealt, a directional impulse vector is added to the victim's velocity, modulated by their knockback resistance attribute. +4. **Hurt Animations**: Triggers hurt sounds and broadcasts red-tint flash metadata to all observers. diff --git a/docs/pt/developer/entities/spawning-and-tracking.md b/docs/pt/developer/entities/spawning-and-tracking.md new file mode 100644 index 0000000..82a3d7b --- /dev/null +++ b/docs/pt/developer/entities/spawning-and-tracking.md @@ -0,0 +1,95 @@ +# Spawning & Network Tracking + +To maintain high tick rates with hundreds of active entities, Pumpkin utilizes a chunk-based entity tracking and streaming system. + +Entities are held in chunk-level collections inside [`crates/pumpkin-world`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-world) and synchronized across clients via edition-specific network packets. + +--- + +## The Spawning Pipeline + +Spawning an entity involves creating an instance, allocating its network IDs, registering it with the world, and broadcasting spawn packets to chunk observers: + +1. **Construct Entity Instance**: Instantiate the specific entity struct (such as `ZombieEntity`, `ArrowEntity`, or `ItemEntity`) initialized with its starting world and spatial coordinates. +2. **Allocate Unique Network Identifiers**: Assign an atomic 32-bit `entity_id` for network protocol packets and a 128-bit persistent UUID. +3. **Register in World & Chunk Map**: Insert the entity into the world's entity storage and index it within its corresponding chunk coordinate entry. +4. **Query Chunk Observers**: Look up all connected players within tracking range whose view distance includes the containing chunk. +5. **Broadcast Edition-Specific Spawn Packets**: Transmit the relevant spawn and metadata packets tailored to each observing client's edition (Java or Bedrock). + +### Spawning via the World API + +```rust +use std::sync::Arc; +use pumpkin_data::entity::EntityType; +use pumpkin_util::math::vector3::Vector3; +use crate::entity::Entity; +use crate::entity::mob::zombie::ZombieEntity; + +// 1. Create base entity with target world and position +let position = Vector3::new(100.5, 64.0, -200.5); +let base_entity = Entity::new(world.clone(), position, &EntityType::ZOMBIE); + +// 2. Wrap in specific entity implementation +let zombie = Arc::new(ZombieEntity::new(base_entity)); + +// 3. Register into the world +world.spawn_entity(zombie); +``` + +--- + +## Network Packet Synchronization + +When a player enters a chunk containing entities—or when an entity moves into an observed chunk—Pumpkin dispatches spawn packets tailored to the client's edition: + +### Java Edition Spawning +1. **`CSpawnEntity`**: Declares the entity ID, UUID, entity type, position, and initial pitch/yaw. +2. **`CSetEntityMetadata`**: Sends initial visual states (custom name tags, burning flags, baby/adult state). +3. **`CEntityVelocity`**: Synchronizes initial momentum. +4. **`CUpdateEntityRot` & `CHeadRot`**: Synchronizes head orientation independently from body yaw. + +### Bedrock Edition Spawning +1. **`CAddActor`**: Declares the runtime ID, actor type identifier, position, rotation, and embedded `SyncedActorDataList`. +2. **`CSetActorMotion`**: Sets initial velocity vector. + +--- + +## Movement & Position Synchronization + +Transmitting absolute 64-bit coordinates every tick would congest network bandwidth. Instead, Pumpkin dynamically switches between compact relative delta updates and authoritative full synchronization based on movement distance: + +| Metric / Scenario | Threshold | Java Protocol Packet | Bedrock Protocol Packet | Bandwidth Impact | +| :--- | :--- | :--- | :--- | :--- | +| **Incremental Movement** | $\Delta < 8$ blocks per tick | `CUpdateEntityPos` / `CUpdateEntityPosRot` | `CMoveActorDelta` | Minimal (fixed-point byte deltas) | +| **Teleport / Long Distance** | $\Delta \ge 8$ blocks or chunk warp | `CTeleportEntity` / `CEntityPositionSync` | `CMoveActorAbsolute` | Full 64-bit float coordinate payload | +| **Rotation Only** | Position unchanged | `CUpdateEntityRot` & `CHeadRot` | `CMoveActorDelta` (rot flags only) | Minimal (byte angles) | + +### 1. Relative Delta Movement +If the entity moved less than 8 blocks in a single tick, Pumpkin sends delta packets using fixed-point encoded increments: +- **Java**: `CUpdateEntityPos` (XYZ delta), `CUpdateEntityRot` (yaw/pitch delta), or `CUpdateEntityPosRot` (both). +- **Bedrock**: `CMoveActorDelta` with bitflags indicating which coordinates changed (`MOVE_ACTOR_DELTA_FLAG_HAS_X`, `HAS_Y`, `HAS_Z`). + +### 2. Full Position Synchronization +When an entity teleports, crosses chunk boundaries, or accumulates floating-point drift: +- **Java**: Sends `CEntityPositionSync` containing absolute coordinates, velocities, and on-ground flags. +- **Bedrock**: Sends `CMoveActorAbsolute`. + +--- + +## Passenger & Vehicle Mounting + +When an entity mounts another entity (e.g., a player mounting a horse, boat, or minecart): + +- **Java Edition**: Sends `CSetPassengers` containing the vehicle's `VarInt` entity ID and an array of passenger entity IDs. +- **Bedrock Edition**: Sends `CSetActorLink` defining the rider and vehicle runtime IDs and link type (`Rider`, `Passenger`). + +--- + +## Despawning & Entity Removal + +When an entity dies, is removed from the world, or steps out of a player's tracking radius: + +- **Java Edition**: Sends `CRemoveEntities` with an array of entity IDs. +- **Bedrock Edition**: Sends `CRemoveActor` with the entity's runtime ID. + +The client immediately frees its local rendering and audio memory for the removed entity. diff --git a/docs/pt/developer/extractor.md b/docs/pt/developer/extractor.md new file mode 100644 index 0000000..2b09f8f --- /dev/null +++ b/docs/pt/developer/extractor.md @@ -0,0 +1,144 @@ +# Vanilla Data Extractor + +The [Pumpkin Extractor](https://github.com/Pumpkin-MC/Extractor) is a dedicated Fabric mod written in Kotlin that introspects a running vanilla Minecraft server or client to dump authoritative game registries and runtime metadata into structured JSON files. + +These JSON dumps are consumed by [`pumpkin-codegen`](/developer/codegen) to generate strongly-typed Rust code in `pumpkin-data` and `pumpkin-world`. + +--- + +## Datapacks vs. Extractor: Our Data Philosophy + +Before creating a new extractor, it is crucial to understand Pumpkin's data hierarchy: + +> [!IMPORTANT] +> **We preferably use vanilla datapacks whenever possible.** +> If vanilla Minecraft already exposes a registry or system through official datapack JSON files, we consume the datapack directly rather than writing custom extractor code. + +### When to Use Datapacks vs. When to Use the Extractor + +| Source | When to Use | Examples | +| :--- | :--- | :--- | +| **Vanilla Datapacks** *(Preferred)* | Any data officially serialized as JSON by Mojang in `data/minecraft/`. | Recipes, tags (`blocks`, `items`, `fluids`), biomes, dimension types, damage types, trim materials, worldgen noise parameters. | +| **Fabric Extractor** *(Only when necessary)* | Game data **hardcoded in Java bytecode** that cannot be exported from raw datapack files. | Block collision shapes, AABB bounding boxes, block hardness, packet ID-to-state mappings, entity tracked data IDs, fuel burn durations, composter chances, and flower pot conversion tables. | + +--- + +## Extractor Mod Architecture + +The Extractor runs on top of the Fabric loader using official Fabric Yarn mappings: + +- **Mod Entry Point (`src/main/kotlin/de/snowii/extractor/Extractor.kt`)**: Registers with Fabric's `ServerLifecycleEvents.SERVER_STARTED` event. When the headless server finishes booting, it invokes all registered extractors and dumps files to `run/pumpkin_extractor_output/`. +- **Extractor Interface**: Every data extractor implements the `Extractor.Extractor` interface: + ```kotlin + interface Extractor { + fun fileName(): String + + @Throws(Exception::class) + fun extract(server: MinecraftServer): JsonElement + + // Returns true if output belongs in tests/ rather than the root output folder + fun isTest(): Boolean = false + } + ``` +- **Output Directory**: JSON files are saved to `pumpkin_extractor_output/` encoded in UTF-8 without HTML escaping. + +--- + +## How to Add a New Extractor + +Follow this step-by-step workflow when you need to extract new hardcoded vanilla mechanics: + +### Step 1: Verify the Data Cannot Be Sourced from Datapacks +Inspect the official Minecraft client/server JAR or Mojang's data reports: +- Check `data/minecraft/` for existing registries, tags, or JSON definitions. +- If the data is present in the datapack, add it directly to `Pumpkin/assets/datapacks/` instead of writing an extractor. + +### Step 2: Create the Extractor Class +If the data is hardcoded in Java methods, create a new Kotlin class in: +```text +src/main/kotlin/de/snowii/extractor/extractors/.kt +``` + +Implement the `Extractor.Extractor` interface: + +```kotlin +package de.snowii.extractor.extractors + +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import de.snowii.extractor.Extractor +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.server.MinecraftServer +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack + +class Fuels : Extractor.Extractor { + override fun fileName(): String { + return "fuels.json" + } + + override fun extract(server: MinecraftServer): JsonElement { + val fuelsJson = JsonObject() + + // Query vanilla runtime methods + server.fuelValues().fuelItems().forEach { fuel -> + val itemId = BuiltInRegistries.ITEM.getKey(fuel).toString() + val burnDuration = server.fuelValues().burnDuration(ItemStack(fuel)) + fuelsJson.add(itemId, JsonPrimitive(burnDuration)) + } + + return fuelsJson + } +} +``` + +### Step 3: Register the Extractor +Open `src/main/kotlin/de/snowii/extractor/Extractor.kt` and add an instance of your class to the `extractors` array: + +```kotlin +val extractors = arrayOf( + // ... existing extractors ... + Blocks(), + Items(), + Entities(), + Fuels(), // [!code ++] +) +``` + +### Step 4: Run the Extractor +Execute the headless server task in the Extractor repository: + +```bash +./gradlew runServer +``` + +Once the extraction finishes, locate your new JSON file in: +```text +run/pumpkin_extractor_output/.json +``` + +### Step 5: Integrate with `pumpkin-codegen` +1. Move the generated JSON file into Pumpkin: + ```bash + cp run/pumpkin_extractor_output/fuels.json ../Pumpkin/assets/ + ``` +2. In the `Pumpkin` repository, open `tools/pumpkin-codegen`: + - Create a parser for `.json`. + - Write a code generation template to produce the corresponding Rust structs or static lookup tables in `crates/pumpkin-data`. +3. Run code generation: + ```bash + cargo run -p pumpkin-codegen + ``` +4. Verify that `pumpkin-data` compiles and passes all unit tests: + ```bash + cargo test -p pumpkin-data + ``` + +--- + +## Best Practices for Extractor Contributions + +- **Sort Keys Deterministically**: When generating JSON objects or lists, sort entries by numeric registry ID or alphabetically by identifier (`minecraft:stone`, `minecraft:dirt`). This prevents noisy git diffs when re-running extraction across updates. +- **Prefer Standard Types**: Use vanilla identifiers (`net.minecraft.resources.Identifier`) as string keys instead of arbitrary integers where possible. +- **Access Wideners**: If private vanilla fields or methods need to be accessed, declare an access widener in `src/main/resources/extractor.accesswidener` rather than resorting to brittle runtime reflection. diff --git a/docs/pt/developer/items/adding-items.md b/docs/pt/developer/items/adding-items.md new file mode 100644 index 0000000..6335d14 --- /dev/null +++ b/docs/pt/developer/items/adding-items.md @@ -0,0 +1,121 @@ +# Adding an Item Behavior + +This guide details how to implement custom interactive mechanics for an item in Pumpkin. + +--- + +## When Is an Item Behavior Needed? + +- **Passive Items** (e.g., diamonds, gold ingots, sticks): Do **not** require custom Rust code. Their stack sizes, tool categories, and crafting recipes are handled automatically by `pumpkin-data` and `pumpkin-inventory`. +- **Interactive Items** (e.g., snowballs, ender pearls, hoes, bows, potions): Require an implementation of `ItemBehaviour` to handle right-click events, cooldowns, animations, and entity spawns. + +--- + +## Step 1: Define the Item Struct & Metadata + +Create a new file under `crates/pumpkin/src/item/items/.rs` (e.g., `items/snowball.rs`): + +```rust +use std::sync::Arc; +use pumpkin_data::entity::EntityType; +use pumpkin_data::item::Item; +use pumpkin_data::sound::{Sound, SoundCategory}; +use crate::entity::Entity; +use crate::entity::player::Player; +use crate::entity::projectile::snowball::SnowballEntity; +use crate::item::{ItemBehaviour, ItemMetadata}; + +pub struct SnowBallItem; + +// 1. Declare the associated numeric Item ID(s) +impl ItemMetadata for SnowBallItem { + fn ids() -> Box<[u16]> { + [Item::SNOWBALL.id].into() + } +} + +// 2. Implement the interaction behavior +impl ItemBehaviour for SnowBallItem { + fn normal_use(&self, _item: &Item, player: &Player) { + let position = player.position(); + let world = player.world(); + + // Play throw sound effect + world.play_sound( + Sound::EntitySnowballThrow, + SoundCategory::Neutral, + &position, + ); + + // Construct and launch the projectile entity + let entity = Entity::new(world.clone(), position, &EntityType::SNOWBALL); + let snowball = SnowballEntity::new_shot(entity, player.get_entity()); + let (yaw, pitch) = player.rotation(); + snowball.thrown.set_velocity_from(pitch, yaw, 0.0, 1.5, 1.0); + world.spawn_entity(Arc::new(snowball)); + + // Safely decrement held item unless in Creative mode + let mut held = player.inventory.held_item(); + held.decrement_unless_creative(player.gamemode.load(), 1); + player.inventory.set_held_item(held); + } +} +``` + +--- + +## Step 2: `ItemBehaviour` Hook Reference + +The `ItemBehaviour` trait provides comprehensive hooks for different interaction models: + +### Instant Single-Use Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `normal_use` | `(&self, item: &Item, player: &Player)` | Triggered on right-clicking into open air. | +| `normal_use_with_rotation` | `(&self, item: &Item, player: &Player, yaw: f32, pitch: f32)` | Provides client-reported view angles for precise raycasting and aiming. | +| `use_on_block` | `(&self, item: &mut ItemStack, player: &Player, location: BlockPos, face: BlockDirection, cursor_pos: Vector3, block: &Block, server: &Server) -> BlockActionResult` | Triggered when right-clicking a block face (e.g., stripping logs with an axe, tilling farmland with a hoe). | +| `use_on_entity` | `(&self, item: &mut ItemStack, player: &Player, entity: Arc)` | Triggered when right-clicking another living entity (e.g., shearing sheep, nametags). | + +### Channeled & Timed Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `get_use_duration` | `(&self) -> i32` | Duration in game ticks required to complete the action (e.g., `32` ticks for food, `72000` ticks for bows). | +| `on_use_tick` | `(&self, stack: &ItemStack, player: &Player, remaining_use_ticks: i32)` | Executed on each tick while right-click is held. | +| `on_stopped_using` | `(&self, stack: &ItemStack, player: &Player)` | Triggered when the player releases right-click (e.g., firing a charged bow). | + +--- + +## Step 3: Durability & Cooldowns + +### Decrementing Stacks Safely +Always use `decrement_unless_creative` to ensure survival players consume items while creative players retain them: + +```rust +item_stack.decrement_unless_creative(player.gamemode.load(), 1); +``` + +### Applying Cooldowns +To prevent action spamming (e.g., Ender Pearls, Chorus Fruit): + +```rust +// Start a 20-tick (1-second) cooldown on Ender Pearls +player.start_cooldown(Item::ENDER_PEARL.registry_key.to_string(), 20); +``` + +--- + +## Step 4: Registering the Item + +1. Export your module in `crates/pumpkin/src/item/items/mod.rs`: + ```rust + pub mod snowball; + ``` + +2. Register the struct in `crates/pumpkin/src/item/registry.rs`: + ```rust + items.register(SnowBallItem); + ``` + +When the server initializes, `items.register` maps the IDs declared in `ItemMetadata::ids()` to your behavior instance. diff --git a/docs/pt/developer/items/index.md b/docs/pt/developer/items/index.md new file mode 100644 index 0000000..70f9f9b --- /dev/null +++ b/docs/pt/developer/items/index.md @@ -0,0 +1,71 @@ +# Item Architecture & Data Components + +Items represent portable game objects held in player inventories, hotbars, armor slots, and world containers. In Pumpkin, item handling is divided into two primary subsystems: + +1. **Static Data Layer ([`pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data))**: Compile-time generated metadata including item IDs, max stack sizes, max durability, tool tier properties, food nutrition, and data components generated by [`pumpkin-codegen`](/developer/codegen). +2. **Dynamic Behavior Layer ([`pumpkin::item`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/item))**: Interactive right-click actions, projectile launching, eating durations, bow charging, and block interactions handled via the `ItemBehaviour` trait. + +--- + +## Static Items vs. Actionable Items + +| Item Type | Description | Examples | Runtime Behavior Struct Needed? | +| :--- | :--- | :--- | :--- | +| **Passive Items** | Items without active right-click effects | `diamond`, `stick`, `iron_ingot`, `feather` | No — Stacking, moving, crafting, and dropping are handled automatically by `pumpkin-inventory`. | +| **Standard Block Items** | Items corresponding directly to placeable blocks | `stone`, `oak_planks`, `glass` | No — Placement logic queries the target block's data. | +| **Projectiles & Throwables** | Items spawned into physical entities when thrown | `snowball`, `egg`, `ender_pearl`, `splash_potion` | Yes — Implements `ItemBehaviour::normal_use`. | +| **Channeled / Charged Items** | Items with use durations or charging animations | `bow`, `crossbow`, `spyglass`, `trident` | Yes — Implements `get_use_duration`, `on_use_tick`, `on_stopped_using`. | +| **Tools with Block Interactions**| Items modifying existing blocks in the world | `hoe` (tilling), `axe` (stripping), `shovel` (pathmaking) | Yes — Implements `ItemBehaviour::use_on_block`. | + +--- + +## The `ItemStack` Structure + +An `ItemStack` represents an instance of an item in an inventory container. It encapsulates: + +- **`item`**: Reference to the static [`Item`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-data/src/item.rs) definition. +- **`item_count`**: Current stack size ($1 \le \text{count} \le \text{max\_stack\_size}$). +- **Data Components**: Dynamic properties attached to the item stack (custom names, lore, damage values, enchantments, potion effects). + +```rust +use pumpkin_data::item::Item; +use pumpkin_data::item_stack::ItemStack; + +// Create a new item stack +let mut stack = ItemStack::new(1, Item::DIAMOND_SWORD); + +// Safely decrement stack count, respecting creative mode +stack.decrement_unless_creative(player.gamemode.load(), 1); +``` + +--- + +## Data Components System + +Modern Minecraft represents item properties via modular **Data Components**: + +| Component | Responsibility | Description | +| :--- | :--- | :--- | +| `FoodImpl` | Nutrition & Saturation | Controls hunger points restored and saturation ratio. | +| `ConsumableImpl` | Consumption Mechanics | Controls eating duration, sound effects, and animation types. | +| `Damageable` | Durability | Tracks current damage points and breaks when max durability is reached. | +| `UseCooldown` | Cooldown Group | Prevents item re-use for a specified duration in game ticks. | +| `Enchantments` | Enchantment Levels | Key-value mapping of applied enchantments and levels. | + +--- + +## The Item Registry & Dispatch Architecture + +Item interactions are managed by the `ItemRegistry` in [`crates/pumpkin/src/item/registry.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/item/registry.rs): + +1. **Registration**: At server startup, all `ItemBehaviour` implementations are registered along with their target numeric item IDs. +2. **Interaction Routing**: When a client sends use packets (`CUseItem`, `CUseItemOn`), Pumpkin evaluates cooldowns and routes execution to the registered `ItemBehaviour`. +3. **Cooldown System**: Players track active cooldowns keyed by cooldown group strings. If an item is on cooldown, interactions are canceled before calling the behavior. + +--- + +## Next Steps + +To implement interactive mechanics for a new item, proceed to the practical guide: + +- **[Adding an Item](/developer/items/adding-items)**: Step-by-step instructions for implementing `ItemMetadata`, `ItemBehaviour`, managing cooldowns, and registering items. diff --git a/docs/pt/developer/networking/bedrock/adding-packets.md b/docs/pt/developer/networking/bedrock/adding-packets.md new file mode 100644 index 0000000..f68b888 --- /dev/null +++ b/docs/pt/developer/networking/bedrock/adding-packets.md @@ -0,0 +1,182 @@ +# Adding a Bedrock Edition Packet + +This guide explains how to implement, encode, decode, and handle a new Minecraft: Bedrock Edition packet in Pumpkin. + +--- + +## Naming Conventions + +- **Clientbound packets** (Server → Client) must start with **`C`** (e.g., `CChunkRadiusUpdated`, `CContainerOpen`). +- **Serverbound packets** (Client → Server) must start with **`S`** (e.g., `SRequestChunkRadius`, `SPlayerAction`). + +--- + +## Step 1: Identifying the Packet ID + +In the Bedrock protocol, packet IDs are fixed integer constants assigned by Mojang. Verify the numeric ID from the official protocol documentation or network packet traces. + +For example: +- `Disconnect` packet ID: `5` +- `RequestChunkRadius` packet ID: `69` + +--- + +## Step 2: Implementing a Clientbound Packet + +Clientbound packets implement the `PacketWrite` trait from `pumpkin-protocol::serial`. + +### 1. Create the Packet File +Place the file in `crates/pumpkin-protocol/src/bedrock/client/.rs`: + +```rust +use std::io::{Error, Write}; +use pumpkin_macros::packet; +use crate::{codec::var_int::VarInt, serial::PacketWrite}; + +#[packet(5)] +pub struct CDisconnect { + pub reason: VarInt, + pub skip_message: bool, + pub message: String, + pub filtered_message: String, +} + +impl CDisconnect { + #[must_use] + pub const fn new(reason: i32, message: String) -> Self { + Self { + reason: VarInt(reason), + skip_message: message.is_empty(), + message, + filtered_message: String::new(), + } + } +} + +impl PacketWrite for CDisconnect { + fn write(&self, writer: &mut W) -> Result<(), Error> { + self.reason.write(writer)?; + self.skip_message.write(writer)?; + if !self.skip_message { + self.message.write(writer)?; + self.filtered_message.write(writer)?; + } + Ok(()) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/bedrock/client/mod.rs`: + +```rust +pub mod disconnect; +pub use disconnect::*; +``` + +--- + +## Step 3: Implementing a Serverbound Packet + +Serverbound packets implement the `PacketRead` trait. Pumpkin provides a procedural derive macro `#[derive(PacketRead)]` for automatic deserialization when fields implement `PacketRead`. + +### 1. Create the Packet File +Place the file in `crates/pumpkin-protocol/src/bedrock/server/.rs`: + +```rust +use pumpkin_macros::packet; +use crate::{codec::var_int::VarInt, serial::PacketRead}; + +#[derive(PacketRead, Debug)] +#[packet(69)] +pub struct SRequestChunkRadius { + pub chunk_radius: VarInt, + pub max_chunk_radius: u8, +} +``` + +If a packet requires custom parsing logic, implement `PacketRead` manually: + +```rust +use std::io::{Error, Read}; +use crate::serial::PacketRead; + +impl PacketRead for SMyCustomPacket { + fn read(reader: &mut R) -> Result { + let chunk_radius = VarInt::read(reader)?; + let max_chunk_radius = u8::read(reader)?; + Ok(Self { chunk_radius, max_chunk_radius }) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/bedrock/server/mod.rs`: + +```rust +pub mod request_chunk_radius; +pub use request_chunk_radius::*; +``` + +--- + +## Step 4: Dispatching Incoming Packets in `pumpkin` + +Incoming packets are dispatched in `crates/pumpkin/src/net/bedrock/mod.rs`: + +```rust +// Inside BedrockClient::handle_game_packet +match packet.id { + SRequestChunkRadius::PACKET_ID => { + let packet = SRequestChunkRadius::read(reader)?; + self.handle_request_chunk_radius(player, &packet); + } + _ => {} +} +``` + +--- + +## Step 5: Implementing the Handler Function + +Implement the handler logic in `crates/pumpkin/src/net/bedrock/play/.rs`: + +```rust +use super::*; +use pumpkin_protocol::bedrock::server::request_chunk_radius::SRequestChunkRadius; + +impl BedrockClient { + pub fn handle_request_chunk_radius(&self, player: &Arc, packet: &SRequestChunkRadius) { + let requested_radius = packet.chunk_radius.0; + + // Clamp view distance within server configuration limits + let clamped = requested_radius.clamp(2, 16); + + // Notify client that view radius was accepted + self.try_enqueue_client_packet(&CChunkRadiusUpdated { + chunk_radius: VarInt(clamped), + }); + } +} +``` + +Remember to register the new module in `crates/pumpkin/src/net/bedrock/play/mod.rs`: + +```rust +pub mod request_chunk_radius; +``` + +--- + +## Step 6: Sending Outgoing Packets + +To send a Bedrock packet to a connected client: + +```rust +// Enqueues the packet into the client's outgoing batch queue +self.try_enqueue_client_packet(&CChunkRadiusUpdated { + chunk_radius: VarInt(view_distance), +}); +``` + +Batched packets are automatically compressed, framed with `0xfe`, and transmitted over the RakNet/NetherNet socket. diff --git a/docs/pt/developer/networking/bedrock/authentication.md b/docs/pt/developer/networking/bedrock/authentication.md new file mode 100644 index 0000000..1571a73 --- /dev/null +++ b/docs/pt/developer/networking/bedrock/authentication.md @@ -0,0 +1,61 @@ +# Bedrock Edition Authentication + +Minecraft: Bedrock Edition implements a certificate chain authentication protocol based on JSON Web Tokens (JWT) cryptographically anchored to Microsoft / Xbox Live root public keys. + +Authentication handling is powered by the [`pumpkin-auth`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-auth) crate and processed during the Bedrock login handshake in `crates/pumpkin/src/net/bedrock/login/`. + +--- + +## The Handshake Flow + +When a Bedrock client initiates connection, it sends an `SLogin` packet containing the client's protocol version, an **Identity Chain**, and **Client Data**: + +1. **Token Acquisition**: The Bedrock client signs into Xbox Live, obtaining a chained JSON Web Token (JWT) certificate signed by Microsoft's CA. +2. **Login Transmission**: The client packages this identity chain alongside a separate `ClientData` JWT into `SLogin`. +3. **Chain Validation**: Pumpkin walks the certificate chain, verifying each token's signature against Mojang's public root keys and extracting the player's Xbox User ID (XUID), Gamertag, and ephemeral session public key. +4. **Hardware Verification**: Pumpkin verifies the `ClientData` payload using the client's ephemeral public key, extracting geometry, skins, and device OS telemetry. +5. **Session Authorization**: Once validated, Pumpkin emits `CPlayStatus` (Login Success) and progresses to resource pack negotiation. + +--- + +## Token Payloads + +### 1. Identity Chain (`Chain`) +An array of JWT tokens forming an unbroken chain of trust: +- **Root Token**: Cryptographically signed by Mojang's official public root certificate authority. +- **Intermediate Tokens**: Issued by Xbox Live services, verifying the player's Xbox Live User ID (**XUID**), gamertag, and generating an ephemeral ECDSA public key for the session. +- **Claims**: Includes `extraData` with `displayName`, `identity` (UUID), `XUID`, and `titleId`. + +### 2. Client Data (`ClientData`) +A separate JWT signed with the client's ephemeral private key corresponding to the public key in the identity chain: +- **Hardware & Environment**: `DeviceOS`, `DeviceModel`, `CurrentInputMode`, `DefaultInputMode`, `ClientRandomId`. +- **Game Version**: `GameVersion` string. +- **Player Cosmetics**: Full skin texture bitmaps, cape data, geometry names, and persona definitions. + +--- + +## Verification in `pumpkin-auth` + +Pumpkin verifies token signatures using **NIST P-384 ECDSA** cryptography: + +```rust +// In crates/pumpkin/src/net/bedrock/login/mod.rs +match AuthPayload::verify(&jwt_payload) { + Ok(profile) => { + // Successfully verified against Mojang public keys + info!("Authenticated Bedrock player: {} (XUID: {})", profile.name, profile.xuid); + } + Err(LoginError::ChainValidationFailed(err)) => { + warn!("Bedrock JWT chain validation failed: {err}"); + self.try_kick(DisconnectReason::Kicked, "Authentication failed".into()); + } + Err(LoginError::SelfSignedNotAllowed) => { + warn!("Rejected unauthenticated self-signed Bedrock client"); + self.try_kick(DisconnectReason::Kicked, "Server enforces Xbox Live authentication".into()); + } +} +``` + +### Online vs. Offline Mode +- **Online Mode** (`online_mode = true` in `pumpkin.toml`): The chain must successfully validate against Mojang's public keys. Self-signed or unsigned tokens are immediately rejected. +- **Offline Mode** (`online_mode = false`): Pumpkin accepts self-signed client chains, generating offline UUIDs while extracting cosmetic skins and device metadata. diff --git a/docs/pt/developer/networking/bedrock/nethernet.md b/docs/pt/developer/networking/bedrock/nethernet.md new file mode 100644 index 0000000..88c92f0 --- /dev/null +++ b/docs/pt/developer/networking/bedrock/nethernet.md @@ -0,0 +1,61 @@ +# NetherNet WebRTC Transport + +**NetherNet** is Mojang's modern, WebRTC-based transport protocol for Minecraft: Bedrock Edition. Designed for high-resilience peer-to-peer and relayed communication, NetherNet enables seamless cross-platform multiplayer (mobile, console, desktop) without requiring server operators or players to configure router port-forwarding. + +Pumpkin provides a native, zero-dependency NetherNet implementation in [`crates/pumpkin/src/net/bedrock/nethernet.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/bedrock/nethernet.rs). + +--- + +## Architectural Layers + +| Layer | Implementation | Responsibility | +| :--- | :--- | :--- | +| **HTTP Signaling** | Axum HTTP Router (`ice_router.rs`) | Handles SDP Offer (`POST /offer`), SDP Answer (`GET /answer`), and ICE Candidates (`POST /candidate`) | +| **NAT Traversal** | ICE Agent | Coordinates STUN/TURN binding discovery and hole punching | +| **Transport Security** | DTLS Handshake | Generates ephemeral session keys and secures the WebRTC transport using ECDSA P-384 keys | +| **Data Delivery** | WebRTC DataChannels | Unordered/ordered datagram channels streaming standard Bedrock game batches to the dispatcher | + +--- + +## Core Components in Pumpkin + +Pumpkin's NetherNet subsystem is divided into four distinct modules: + +### 1. Cryptographic Identity (`nethernet-key.der`) +- Pumpkin generates and loads a **NIST P-384 ECDSA private key** (`nethernet-key.der`). +- Used to cryptographically sign session descriptions (SDP offers and answers), authenticating the server's identity to connecting clients. +- Encoded using standard PKCS#8 DER formatting. + +### 2. HTTP Signaling Router (`ice_router.rs`) +Signaling is handled over HTTP via an [Axum](https://github.com/tokio-rs/axum) router: + +- **`POST /offer`**: The client submits an SDP offer describing its WebRTC codecs, transport profiles, and network interfaces. +- **`GET /answer`**: Pumpkin generates a corresponding local SDP answer containing its negotiated capabilities. +- **`POST /candidate`**: Both endpoints exchange trickle ICE candidates to discover the fastest direct network path (host, server-reflexive, or relay). + +### 3. Local Network Discovery (`discovery.rs`) +- Listens and broadcasts availability on the local subnet via UDP multicast. +- Allows Bedrock clients on the same Wi-Fi or LAN to discover and connect to the Pumpkin server automatically without entering an IP address. + +### 4. DataChannel Packet Streaming +- Once the WebRTC peer connection transitions to the `Connected` state, an ordered, reliable **WebRTC DataChannel** is opened. +- Bedrock batch packets (`0xfe`) flow bidirectionally through the DataChannel directly into Pumpkin's `incoming_game_packet_send` channel. + +--- + +## Configuration in `pumpkin.toml` + +NetherNet is configured under `[networking.bedrock.nethernet]` in `pumpkin.toml`: + +```toml +[networking.bedrock] +enabled = true +address = "0.0.0.0:19132" + +[networking.bedrock.nethernet] +enabled = true +signaling_address = "0.0.0.0:19133" +ice_servers = [ + "stun:stun.l.google.com:19302" +] +``` diff --git a/docs/pt/developer/networking/bedrock/overview.md b/docs/pt/developer/networking/bedrock/overview.md new file mode 100644 index 0000000..ae0d615 --- /dev/null +++ b/docs/pt/developer/networking/bedrock/overview.md @@ -0,0 +1,80 @@ +# Bedrock Edition Networking Overview + +Pumpkin features native support for **Minecraft: Bedrock Edition** clients (iOS, Android, Windows, Xbox, PlayStation, Switch). + +Bedrock protocol structures and wire codecs reside in `pumpkin-protocol::bedrock`, while connection management and gameplay handlers reside in `pumpkin::net::bedrock`. + +--- + +## Transport Layers: RakNet & NetherNet + +Unlike Java Edition, which communicates exclusively over TCP streams, Bedrock Edition operates over UDP using two primary transport layers: + +1. **RakNet (Port 19132)**: + - A reliable, sequenced UDP networking library originally developed by Jenkins Software. + - Provides connection handshakes, packet acknowledgment (ACK/NACK), MTU negotiation, and packet splitting for payloads larger than the network MTU. +2. **NetherNet**: + - Modern peer-to-peer and relayed transport protocol used by modern Bedrock versions. + - Integrated in Pumpkin via `crates/pumpkin/src/net/bedrock/nethernet.rs`. + +--- + +## Packet Framing & Batching (`0xfe`) + +Bedrock does not send individual game packets over the network directly. Instead, multiple game packets are bundled into a single **batch packet**: + +1. **Header Byte**: Every Bedrock game packet batch starts with `0xfe`. +2. **Compression**: The payload following `0xfe` is compressed using **Deflate** (or ZLib) above a negotiated threshold. +3. **Sub-packet Framing**: When decompressed, the batch contains one or more sequential game packets, each prefixed by its length encoded as a `VarUInt`. + +In Pumpkin, batch decoding and assembly are managed by `BedrockBatchDecoder` and `BedrockBatchEncoder` in `crates/pumpkin-protocol/src/bedrock/`. + +--- + +## Connection Lifecycle & State Progression + +Bedrock does not have explicit protocol states like Java Edition. Instead, it follows a strict sequence of packets: + +| Step | Packet Exchanged | Purpose | +| :--- | :--- | :--- | +| **1. Network Settings** | `SRequestNetworkSettings` -> `CNetworkSettings` | Negotiates compression algorithm (Deflate/Snappy) and payload threshold | +| **2. Authentication** | `SLogin` -> `CPlayStatus` | Transmits JWT identity chain and client hardware data; verifies Xbox Live tokens | +| **3. Resource Negotiation** | `CResourcePacksInfo` -> `SResourcePackClientResponse` | Negotiates server resource packs and download behavior | +| **4. World Initialization** | `CStartGame` + `CItemRegistry` + `CCreativeContent` | Streams world spawn position, dimension settings, runtime player entity ID, and item palettes | +| **5. Ready to Spawn** | `SSetLocalPlayerAsInitialized` -> `CPlayStatus` | Signals client loading completion and transitions to active in-game play | + +--- + +## Packet Dispatching in `pumpkin` + +Inside `crates/pumpkin/src/net/bedrock/mod.rs`, decompressed sub-packets are dispatched based on their numeric packet ID: + +```rust +match packet.id { + SPlayerAuthInput::PACKET_ID => { + let packet = SPlayerAuthInput::read(reader)?; + self.handle_player_auth_input(player, packet, server); + } + SRequestChunkRadius::PACKET_ID => { + let packet = SRequestChunkRadius::read(reader)?; + self.handle_request_chunk_radius(player, &packet); + } + SInventoryTransaction::PACKET_ID => { + let packet = SInventoryTransaction::read(reader)?; + self.handle_inventory_action(player, packet); + } + _ => { + trace!("Unhandled Bedrock packet ID: {}", packet.id); + } +} +``` + +--- + +## Authentication & NetherNet + +- **JWT Authentication**: Bedrock verifies player identities via certificate chains signed by Microsoft / Xbox Live root authorities. +- **NetherNet WebRTC**: High-performance peer-to-peer transport using WebRTC DataChannels, ICE NAT traversal, and P-384 ECDSA keys. + +For complete implementation details, see **[Bedrock Authentication & NetherNet](/developer/networking/bedrock/authentication)**. + diff --git a/docs/pt/developer/networking/index.md b/docs/pt/developer/networking/index.md new file mode 100644 index 0000000..0685c45 --- /dev/null +++ b/docs/pt/developer/networking/index.md @@ -0,0 +1,49 @@ +# Networking Overview + +Pumpkin is built with a high-performance, asynchronous networking stack capable of servicing both **Minecraft: Java Edition** and **Minecraft: Bedrock Edition** clients simultaneously. + +All protocol definitions, serialization traits, and wire-level codecs are isolated in the [`pumpkin-protocol`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-protocol) crate, while client connection management, session state machines, and gameplay packet dispatching are managed in [`pumpkin/src/net`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/net). + +--- + +## Supported Editions + +Pumpkin separates network implementations by edition due to their fundamentally distinct transport layers, packet encodings, and state models: + +| Feature | Minecraft: Java Edition | Minecraft: Bedrock Edition | +| :--- | :--- | :--- | +| **Transport Layer** | TCP socket streams | UDP via RakNet & NetherNet | +| **Framing** | VarInt length-prefixed frames | Batched packets encapsulated in `0xfe` headers | +| **Encryption** | AES-128 / CFB8 stream cipher | Cryptographic session handshake | +| **Compression** | Per-packet ZLib (above configurable threshold) | Per-batch Deflate / ZLib compression | +| **State Model** | Explicit states: Handshake, Status, Login, Config, Play | Packet-driven state progression | +| **Protocol Crate** | `pumpkin-protocol::java` | `pumpkin-protocol::bedrock` | +| **Server Net Module** | `pumpkin::net::java` | `pumpkin::net::bedrock` | + +--- + +## Networking Architecture + +The networking pipeline routes incoming connections through specialized transport and codec layers before delegating to the unified server game loop: + +1. **Transport Listeners**: Independent listeners bind to their respective ports—Tokio TCP for Java Edition (`:25565`) and UDP/RakNet/NetherNet for Bedrock Edition (`:19132`). +2. **Session Abstraction**: Each accepted connection spawns an isolated session task (`JavaClient` or `BedrockClient`) managing wire framing and encryption. +3. **Codec Deserialization**: Wire bytes are deserialized into typed packet structs via `pumpkin-protocol::java` (`ClientPacket`/`ServerPacket`) and `pumpkin-protocol::bedrock` (`PacketWrite`/`PacketRead`). +4. **Unified Gameplay Dispatch**: Decoded packets are dispatched into the unified game loop, updating player entity states and streaming world chunks. + +--- + +## Navigating the Networking Docs + +Explore the documentation for each edition: + +- **[Entity Metadata & Synced Data](/developer/networking/metadata)**: Synchronization of visual entity states (DataWatchers in Java, Actor Data in Bedrock). +- **[Java Edition](/developer/networking/java/overview)**: + - **[Architecture & Lifecycle](/developer/networking/java/overview)**: State machine, `JavaClient` vs. `Player`, and multi-version negotiation. + - **[How to Add a Packet](/developer/networking/java/adding-packets)**: Guide to defining, serializing, and dispatching Java Edition packets. + - **[Authentication](/developer/networking/java/authentication)**: Mojang Yggdrasil validation, GameProfiles, and proxy forwarding. +- **[Bedrock Edition](/developer/networking/bedrock/overview)**: + - **[Architecture & Transports](/developer/networking/bedrock/overview)**: RakNet and NetherNet integration, batching, and session handling. + - **[How to Add a Packet](/developer/networking/bedrock/adding-packets)**: Guide to defining and handling Bedrock Edition packets. + - **[Authentication & NetherNet](/developer/networking/bedrock/authentication)**: JWT certificate chain verification, WebRTC DataChannels, P-384 keys, and signaling. +- **[RCON](/developer/networking/rcon)**: Implementation of Valve's Remote Console protocol for administrative management. diff --git a/docs/pt/developer/networking/java/adding-packets.md b/docs/pt/developer/networking/java/adding-packets.md new file mode 100644 index 0000000..1800d06 --- /dev/null +++ b/docs/pt/developer/networking/java/adding-packets.md @@ -0,0 +1,205 @@ +# Adding a Java Edition Packet + +This guide walks you through implementing, serializing, dispatching, and sending a new Java Edition packet in Pumpkin. + +--- + +## Naming Conventions + +To keep packet definitions uniform across the codebase, follow these naming conventions: + +- **Clientbound packets** (Server → Client) must start with **`C`** (e.g., `CBlockUpdate`, `CPlayDisconnect`). +- **Serverbound packets** (Client → Server) must start with **`S`** (e.g., `SChatCommand`, `SPlayerPosition`). +- If a packet exists in multiple states, prefix the state name before the packet action (e.g., `CLoginDisconnect`, `CConfigDisconnect`, and `CPlayDisconnect`). + +--- + +## Step 1: Find the Packet ID in `pumpkin-data` + +Packet IDs across all supported Minecraft versions are maintained in `pumpkin-data`. Import the corresponding constant: + +```rust +// Clientbound Play state disconnect +use pumpkin_data::packet::clientbound::play::DISCONNECT; + +// Serverbound Play state player position +use pumpkin_data::packet::serverbound::play::MOVE_PLAYER_POS; +``` + +--- + +## Step 2: Implementing a Clientbound Packet + +Clientbound packets are sent from Pumpkin to the Minecraft client. They implement the `ClientPacket` trait from `pumpkin-protocol`. + +### 1. Create the Struct File +Place the file in `crates/pumpkin-protocol/src/java/client//.rs` (e.g., `play/disconnect.rs`): + +```rust +use std::io::Write; +use pumpkin_data::packet::clientbound::play::DISCONNECT; +use pumpkin_macros::java_packet; +use pumpkin_protocol::ClientPacket; +use pumpkin_protocol::ser::{NetworkWriteExt, WritingError}; +use pumpkin_util::text::TextComponent; +use pumpkin_util::version::JavaMinecraftVersion; + +/// Displays a disconnect screen with a message to the player in Play state. +#[java_packet(DISCONNECT)] +pub struct CPlayDisconnect<'a> { + pub reason: &'a TextComponent, +} + +impl<'a> CPlayDisconnect<'a> { + #[must_use] + pub const fn new(reason: &'a TextComponent) -> Self { + Self { reason } + } +} + +impl ClientPacket for CPlayDisconnect<'_> { + fn write_packet_data( + &self, + mut write: impl Write, + version: &JavaMinecraftVersion, + ) -> Result<(), WritingError> { + // Use NetworkWriteExt methods to serialize fields + write.write_component(self.reason, version) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/java/client//mod.rs`: + +```rust +mod disconnect; +pub use disconnect::*; +``` + +--- + +## Step 3: Implementing a Serverbound Packet + +Serverbound packets are received by Pumpkin from the game client. They implement the `ServerPacket` trait from `pumpkin-protocol`. + +### 1. Create the Struct File +Place the file in `crates/pumpkin-protocol/src/java/server//.rs` (e.g., `play/player_position.rs`): + +```rust +use pumpkin_data::packet::serverbound::play::MOVE_PLAYER_POS; +use pumpkin_macros::java_packet; +use pumpkin_protocol::ServerPacket; +use pumpkin_protocol::ser::{NetworkReadExt, ReadingError}; +use pumpkin_util::math::vector3::Vector3; +use pumpkin_util::version::JavaMinecraftVersion; + +#[java_packet(MOVE_PLAYER_POS)] +pub struct SPlayerPosition { + pub position: Vector3, + pub collision: u8, +} + +impl<'a> ServerPacket<'a> for SPlayerPosition { + fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result { + let x = bytebuf.get_f64_be()?; + let y = bytebuf.get_f64_be()?; + + // Handle version-specific fields if necessary + if *version <= JavaMinecraftVersion::V_1_7_6 { + let _stance = bytebuf.get_f64_be()?; + } + + let z = bytebuf.get_f64_be()?; + let collision = bytebuf.get_u8()?; + + Ok(Self { + position: Vector3::new(x, y, z), + collision, + }) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/java/server//mod.rs`: + +```rust +mod player_position; +pub use player_position::*; +``` + +--- + +## Step 4: Network Codec Extension Helpers + +`NetworkWriteExt` and `NetworkReadExt` provide helper methods for standard Minecraft types: + +| Data Type | Reading Method | Writing Method | +| :--- | :--- | :--- | +| **`VarInt`** | `bytebuf.get_var_int()?` | `write.write_var_int(val)?` | +| **`VarLong`** | `bytebuf.get_var_long()?` | `write.write_var_long(val)?` | +| **`String`** | `bytebuf.get_string()?` | `write.write_string(val)?` | +| **`UUID`** | `bytebuf.get_uuid()?` | `write.write_uuid(&val)?` | +| **`TextComponent`** | `bytebuf.get_component()?` | `write.write_component(val, version)?` | +| **`Position`** | `bytebuf.get_block_pos()?` | `write.write_block_pos(val)?` | +| **`NBT`** | `bytebuf.get_nbt()?` | `write.write_nbt(&val)?` | + +--- + +## Step 5: Handling Incoming Packets in `pumpkin` + +Incoming serverbound packets must be handled in the appropriate state handler in `crates/pumpkin/src/net/java/`. + +For packets in the **Play state**, open `crates/pumpkin/src/net/java/mod.rs` and update `handle_play_packet`: + +```rust +// In crates/pumpkin/src/net/java/mod.rs +pub fn handle_play_packet( + &self, + player: &Arc, + server: &Arc, + packet: &RawPacket, +) -> Result<(), Box> { + let version = self.version.load(); + let mut payload = &packet.payload[..]; + + match packet.id { + // Match against version-specific packet ID + id if id == SPlayerPosition::to_id(version) => { + let packet = SPlayerPosition::read(&mut payload, &version)?; + self.handle_player_position(player, &packet); + } + _ => {} + } + Ok(()) +} +``` + +Then implement the handler function: + +```rust +impl JavaClient { + pub fn handle_player_position(&self, player: &Arc, packet: &SPlayerPosition) { + // Validate coordinates, update player position, check collisions + player.set_position(packet.position); + } +} +``` + +--- + +## Step 6: Sending Outgoing Packets + +To send a clientbound packet to a player: + +```rust +// 1. Send directly to a player's connection +player.client.send_packet(&CPlayDisconnect::new(&reason)); + +// 2. Broadcast to all players watching a chunk +world.broadcast_packet_except( + &[player.gameprofile.id], + &CBlockUpdate::new(block_pos, block_id), +).await; +``` diff --git a/docs/pt/developer/networking/java/authentication.md b/docs/pt/developer/networking/java/authentication.md new file mode 100644 index 0000000..861da51 --- /dev/null +++ b/docs/pt/developer/networking/java/authentication.md @@ -0,0 +1,83 @@ +# Java Edition Authentication + +Authentication verifies the identity of players connecting to Pumpkin to prevent username spoofing and account impersonation. + +Pumpkin handles authentication in the [`pumpkin-auth`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-auth) crate and orchestrates it during the `Login` state in `crates/pumpkin/src/net/java/login/`. + +--- + +## Why Authentication Matters + +Offline (non-authenticated) accounts generate player UUIDs strictly based on their offline username string (`UUID::new_v3(...)`). Without verification, any user can join using the username of an operator or administrator and gain privileged access. + +In online mode (`online_mode = true` in `pumpkin.toml`), Pumpkin cryptographically verifies with Mojang's session servers that the connecting client owns the requested account. + +--- + +## How Yggdrasil Authentication Works + +The authentication exchange between the client, Pumpkin, and Mojang's Yggdrasil servers follows these steps: + +1. **Initiation**: The client sends `SLoginStart` declaring its desired username and optional offline UUID. +2. **Key Exchange**: Pumpkin replies with `CEncryptionBegin`, providing a random 4-byte verify token, the server ID string, and Pumpkin's 1024-bit RSA public key. +3. **Session Join**: The client generates a random 16-byte shared secret (`AES-128`), computes a SHA-1 server hash, and calls Mojang's session server (`POST /session/minecraft/join`) using its authenticated Microsoft access token. +4. **Verification**: The client sends `SEncryptionResponse` containing the RSA-encrypted shared secret and encrypted verify token. Pumpkin decrypts the token with its RSA private key, recalculates the SHA-1 digest, and queries Mojang (`GET /session/minecraft/hasJoined`). +5. **Profile Retrieval**: Mojang validates that the client and server hashes match, returning the authenticated player's UUID, username, and signed skin/cape texture properties (`GameProfile`). +6. **Encryption Activation**: Pumpkin activates full-stream symmetric encryption (`AES-128/CFB8`) and completes the login sequence by sending `CLoginSuccess`. + +--- + +## Game Profile Structure + +When Mojang's `hasJoined` endpoint validates a session, it returns a `GameProfile` containing the player's authenticated UUID, username, and signed texture properties: + +```rust +pub struct GameProfile { + pub id: Uuid, + pub name: String, + pub properties: Vec, + pub profile_actions: Option>, +} + +pub struct Property { + pub name: String, + pub value: String, // Base64 encoded texture data + pub signature: Option, // Cryptographic signature verifying authenticity +} +``` + +Pumpkin verifies that skin and cape signatures are cryptographically signed with Mojang's public key to prevent clients from injecting malicious payloads or unauthorized textures. + +--- + +## Custom Authentication Endpoints + +For private networks or enterprise environments running custom Yggdrasil-compatible authentication servers, endpoints can be customized in `pumpkin.toml`: + +```toml +[networking.java.authentication] +enabled = true +fallbacks = [] +connect_timeout = 5000 +read_timeout = 5000 +prevent_proxy_connections = false + +[networking.java.authentication.player_profile] +allow_banned_players = false +allowed_actions = ["FORCED_NAME_CHANGE", "USING_BANNED_SKIN"] + +[networking.java.authentication.textures] +enabled = true +allowed_url_schemes = ["http", "https"] +allowed_url_domains = [".minecraft.net", ".mojang.com"] +``` + +--- + +## Proxy Authentication Forwarding + +When running Pumpkin behind proxies, client connections terminate at the proxy. Pumpkin supports the three primary proxy forwarding standards: + +- **Velocity Modern Forwarding**: Validates player data forwarded with HMAC-SHA256 secret tokens (`[networking.proxy.velocity]`). +- **BungeeGuard**: Token-based authentication header validation for BungeeCord setups. +- **Legacy BungeeCord**: Handshake forwarding appending IP, UUID, and properties to the server host string. diff --git a/docs/pt/developer/networking/java/overview.md b/docs/pt/developer/networking/java/overview.md new file mode 100644 index 0000000..e79753c --- /dev/null +++ b/docs/pt/developer/networking/java/overview.md @@ -0,0 +1,74 @@ +# Java Edition Networking Overview + +The Java Edition network stack handles standard Minecraft PC clients connecting over TCP. Wire-level structures and serialization logic reside in `pumpkin-protocol::java`, while connection management and gameplay packet handlers reside in `pumpkin::net::java`. + +--- + +## The Five Protocol States + +The Minecraft Java protocol operates as an explicit finite state machine. Every connection begins in the `Handshake` state and transitions according to client requests and server responses: + +| State | Transition Trigger | Purpose & Next State | +| :--- | :--- | :--- | +| **Handshake** | Client initiates connection | Inspects protocol version and transitions to `Status`, `Login`, or `Transfer` | +| **Status** | `NextState = 1` | Streams MOTD, active player counts, favicon, and ping response | +| **Login** | `NextState = 2` | Authenticates account (Yggdrasil), negotiates encryption (AES-128), transitions to `Config` | +| **Transfer** | `NextState = 3` | Modern proxy / server-transfer negotiation | +| **Config** | Authentication success | Exchanges dynamic registries, data tags, feature flags, and resource packs | +| **Play** | Configuration acknowledged | Primary bidirectional gameplay loop (chunks, movement, combat, chat) | + +1. **Handshake (`0x00`)**: Always the first packet sent by the client. It declares the client's protocol version, server address, port, and desired next state (`Status`, `Login`, or `Transfer`). +2. **Status**: Lightweight query state. The client requests a server list ping, and Pumpkin responds with the server version, player counts, favicon, and MOTD. +3. **Login**: Authenticates the player. If `online_mode` is enabled, Pumpkin performs an encryption handshake (AES-128/CFB8) and verifies the player's account with Mojang's Yggdrasil session servers. +4. **Config**: Introduced in modern Minecraft (1.20.2+). The server sends feature flags, known registries, resource packs, and custom server links before the player joins the physical world. +5. **Play**: The primary gameplay state. Used for all in-game actions including block breaking, entity movement, combat, chat, and chunk streaming. + +--- + +## Connection Representation: `JavaClient` vs. `Player` + +To keep memory usage minimal and prevent unnecessary allocations, Pumpkin cleanly separates pre-play connections from active players: + +### `JavaClient` +- **Scope**: Exists across `Handshake`, `Status`, `Login`, and `Config` states. +- **Location**: `crates/pumpkin/src/net/java/mod.rs`. +- **Characteristics**: + - Contains connection metadata: client protocol version, remote address, encryption/compression state. + - Has no world coordinates, no physics, no inventory, and no living entity. + - Very low memory footprint. + +### `Player` +- **Scope**: Exists solely during the `Play` state. +- **Location**: `crates/pumpkin/src/entity/player/`. +- **Characteristics**: + - Implements `EntityBase`, representing a living entity inside a `World` and `Level`. + - Holds inventory screens, health, hunger, velocities, and chunk view subscription queues. + - Contains an `Arc` reference for network transmission. + +--- + +## Multi-Version Support (`JavaMinecraftVersion`) + +Pumpkin supports multiple Minecraft client versions. Protocol differences are handled cleanly via `pumpkin_util::version::JavaMinecraftVersion`: + +- Packet IDs may differ across Minecraft versions. The `#[java_packet(ID)]` macro works with `pumpkin-data` to resolve the correct ID for the target version. +- When reading or writing packets, methods accept a `&JavaMinecraftVersion` reference to conditionally include or omit fields that changed in specific Minecraft updates. + +```rust +if *version <= JavaMinecraftVersion::V_1_7_6 { + // Read legacy stance coordinate present in early Minecraft versions + let _stance = bytebuf.get_f64_be()?; +} +``` + +--- + +## The Packet Pipeline + +Incoming Java packets pass through a structured pipeline: + +1. **Length Framing**: `tokio_util::codec` decodes VarInt frame lengths. +2. **Decompression & Decryption**: If enabled, the raw buffer is decrypted via AES/CFB8 and decompressed via ZLib. +3. **Plugin Event Interception**: A `PacketReceivedEvent` is dispatched to the plugin manager. Plugins can inspect or cancel the packet before server processing. +4. **ID Dispatching**: The packet ID is matched against version-specific constants in `crates/pumpkin/src/net/java/mod.rs` (or the respective state module). +5. **Handling**: The packet struct is deserialized and processed by state-specific handler functions. diff --git a/docs/pt/developer/networking/metadata.md b/docs/pt/developer/networking/metadata.md new file mode 100644 index 0000000..9def4ae --- /dev/null +++ b/docs/pt/developer/networking/metadata.md @@ -0,0 +1,123 @@ +# Entity Metadata & Synced Data + +Minecraft synchronizes persistent entity visual states, animation flags, poses, and properties via **Entity Metadata** (historically called *DataWatchers* in Java Edition and *Actor Data* in Bedrock Edition). + +Instead of transmitting full state packets whenever an entity crouches, catches fire, or takes damage, the server broadcasts compact metadata updates to nearby observers. + +--- + +## Edition Comparison + +| Feature | Minecraft: Java Edition | Minecraft: Bedrock Edition | +| :--- | :--- | :--- | +| **Packet Name** | `CSetEntityMetadata` (`SET_ENTITY_DATA`) | `CSetActorData` (Packet ID `39`) | +| **Storage Structure** | `TrackedData` list terminating with `0xFF` | `SyncedActorDataList` (`HashMap`) | +| **Key Type** | Index (`u8`) mapped to typed `MetaDataType` | Enum keys (`entity_data_key::*`) | +| **Flags** | Bitfield byte (fire, crouching, sprinting, glowing) | Bitfield int64 (`entity_data_flag::*`) | +| **Dynamic Properties** | N/A (baked into tracked indices) | `PropertySyncData` (client/server sync) | +| **Crate Location** | `crates/pumpkin-protocol/src/java/client/play/entity_metadata.rs` | `crates/pumpkin-protocol/src/bedrock/client/set_actor_data.rs` | + +--- + +## 1. Java Edition: `CSetEntityMetadata` + +In Java Edition, entity metadata consists of numbered index entries. Each entry defines an index, a `MetaDataType` ID, and the encoded value. The list terminates with a single `0xFF` byte. + +### Key Index Hierarchy + +Entity metadata follows an object-oriented inheritance model: + +1. **Entity (Base)**: + - `0`: State bitfield (flags: bit `0` = on fire, bit `1` = crouching, bit `3` = sprinting, bit `5` = invisible, bit `6` = glowing). + - `1`: Air supply (`VarInt`). + - `2`: Custom name (optional `TextComponent`). + - `3`: Custom name visible (`bool`). + - `4`: Silent (`bool`). + - `5`: No gravity (`bool`). + - `6`: Pose (`Standing`, `FallFlying`, `Sleeping`, `Swimming`, `SpinAttack`, `Crouching`, `Dying`). +2. **LivingEntity (extends Entity)**: + - `8`: Hand states (using item, active hand). + - `9`: Health (`f32`). + - `10`: Potion effect ambient color. +3. **Player (extends LivingEntity)**: + - Additional hearts, skin customization bitmask, main hand indicator. + +### Sending Metadata Updates in Pumpkin + +Pumpkin encodes entity metadata in `pumpkin-protocol`: + +```rust +use pumpkin_protocol::VarInt; +use pumpkin_protocol::java::client::play::CSetEntityMetadata; + +// Construct metadata update for entity with ID 42 +let packet = CSetEntityMetadata::new( + VarInt(entity.entity_id), + serialized_metadata_bytes, +); + +// Broadcast to chunk observers +world.broadcast_packet_all(&packet).await; +``` + +--- + +## 2. Bedrock Edition: `CSetActorData` + +Bedrock Edition uses `CSetActorData` (`packet(39)`), storing properties in a dictionary mapping `u32` keys to strongly-typed `MetadataValue` variants. + +### Key & Flag Constants + +Bedrock exposes flags as a 64-bit integer bitfield under `entity_data_key::FLAGS`: + +- `entity_data_flag::ON_FIRE` (`0`): Entity is burning. +- `entity_data_flag::SNEAKING` (`1`): Player is sneaking. +- `entity_data_flag::RIDING` (`2`): Entity is mounted on a vehicle. +- `entity_data_flag::SPRINTING` (`3`): Player is sprinting. +- `entity_data_flag::USING_ITEM` (`4`): Player is actively charging a bow, eating food, or using a shield. +- `entity_data_flag::INVISIBLE` (`5`): Entity is invisible. +- `entity_data_flag::GLOWING` (`26`): Entity has the glowing effect outline. + +### Constructing and Updating Actor Data in Pumpkin + +In `crates/pumpkin/src/entity/living.rs`: + +```rust +use pumpkin_protocol::bedrock::client::set_actor_data::{ + CSetActorData, MetadataValue, PropertySyncData, SyncedActorDataList, + entity_data_flag, entity_data_key, +}; + +let mut actor_data = SyncedActorDataList::new(); + +// Set entity flags (e.g. actively using an item) +let mut flags: i64 = 0; +if is_using_item { + flags |= 1i64 << entity_data_flag::USING_ITEM; +} +actor_data.set(entity_data_key::FLAGS, MetadataValue::Int64(flags)); + +// Set air supply +actor_data.set(entity_data_key::AIR_SUPPLY, MetadataValue::Short(air as i16)); + +// Set custom name tag +actor_data.set(entity_data_key::NAME, MetadataValue::String(name)); + +let packet = CSetActorData { + target_runtime_id: VarULong(entity_id), + actor_data, + synced_properties: PropertySyncData::default(), + tick: VarULong(current_tick), +}; + +client.try_enqueue_client_packet(&packet); +``` + +--- + +## Multi-Version Compatibility + +Because Mojang frequently shifts metadata indices between Minecraft releases, `pumpkin-protocol` leverages translation tables from `pumpkin-data`: + +- For Java Edition, `pumpkin_data::meta_data_type::MetaDataType` remaps indices dynamically based on the client's `JavaMinecraftVersion`. +- For Bedrock Edition, `SyncedActorDataList` validates keys against the active Bedrock protocol version to prevent client crashes. diff --git a/docs/pt/developer/networking/proxy/bungeecord.md b/docs/pt/developer/networking/proxy/bungeecord.md new file mode 100644 index 0000000..cb06ff5 --- /dev/null +++ b/docs/pt/developer/networking/proxy/bungeecord.md @@ -0,0 +1,73 @@ +# BungeeCord Forwarding + +Pumpkin provides native support for BungeeCord and Waterfall proxy networks, including **BungeeGuard** authentication token verification. + +Implementation is located in [`crates/pumpkin/src/net/proxy/bungeecord.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/bungeecord.rs). + +--- + +## How BungeeCord Forwarding Operates + +Unlike modern protocols that use custom login channels, BungeeCord operates by encoding client metadata directly into the `server_address` field of the initial `SHandshake` packet. + +The string uses null character (`\0`) delimiters: + +```text +[original_host]\0[player_ip:port]\0[player_uuid]\0[properties_json] +``` + +1. **`original_host`**: The domain name the player originally typed into their client. +2. **`player_ip:port`**: The genuine IP address and ephemeral source port of the client. +3. **`player_uuid`**: The player's Mojang UUID (32 hex characters without hyphens or 36 characters with hyphens). +4. **`properties_json`**: A JSON array containing skin textures, cape textures, and Mojang cryptographic signatures. + +--- + +## The Security Risk: Proxy Bypasses + +Because BungeeCord's handshake string is unauthenticated plain text, an attacker could connect directly to an exposed backend port, inject an arbitrary UUID and operator username, and gain full server control. + +Traditionally, this required strict Linux firewall rules (`iptables` / `ufw`) to restrict port access exclusively to the proxy IP. + +--- + +## BungeeGuard Protection + +To eliminate proxy bypasses without relying solely on network firewalls, Pumpkin integrates **BungeeGuard** token verification: + +1. The BungeeCord proxy injects a shared secret into the player's profile properties under the key `bungeeguard-token`. +2. When Pumpkin processes the handshake in `bungeecord_login`: + - It checks whether `secret` is configured in `pumpkin.toml`. + - It inspects the forwarded properties for `bungeeguard-token`. + - If the token matches, Pumpkin **strips the secret property** from the player's profile (so other plugins cannot read it) and completes the login. + - If the token is missing or incorrect, Pumpkin rejects the connection with `BungeeCordError::MissingToken` or `BungeeCordError::InvalidToken`. + +```rust +// In crates/pumpkin/src/net/proxy/bungeecord.rs +if !secret.is_empty() { + let Some(token_index) = properties.iter().position(|p| p.name == BUNGEEGUARD_TOKEN_PROPERTY) else { + return Err(BungeeCordError::MissingToken); + }; + + if properties[token_index].value != secret { + return Err(BungeeCordError::InvalidToken); + } + properties.remove(token_index); +} +``` + +--- + +## Configuration in `pumpkin.toml` + +To enable BungeeCord forwarding with BungeeGuard: + +```toml +[networking.proxy] +enabled = true + +[networking.proxy.bungeecord] +enabled = true +# Optional: Configure the BungeeGuard shared secret to block direct connections +secret = "YOUR_BUNGEEGUARD_SECRET_HERE" +``` diff --git a/docs/pt/developer/networking/proxy/index.md b/docs/pt/developer/networking/proxy/index.md new file mode 100644 index 0000000..300c89e --- /dev/null +++ b/docs/pt/developer/networking/proxy/index.md @@ -0,0 +1,46 @@ +# Proxy Forwarding Overview + +In multi-server Minecraft networks, players connect through a front-facing reverse proxy (such as Velocity, BungeeCord, or Vine) that routes connections between lobbies and sub-servers without requiring players to reconnect. + +Because backend servers receive TCP connections directly from the proxy machine rather than the player, the server must be informed of the player's genuine **IP address**, **UUID**, **username**, and **skin properties**. + +Pumpkin provides native support for all three major proxy forwarding protocols in [`crates/pumpkin/src/net/proxy/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/net/proxy). + +--- + +## The Proxy Forwarding Problem + +Without secure proxy forwarding: +1. **IP Loss**: Every connected player appears with the proxy's loopback or internal IP (breaking IP bans and geo-location). +2. **UUID Spoofing & Bypasses**: Malicious users could connect directly to the backend server's port with any spoofed username or operator identity. + +To prevent proxy bypasses, Pumpkin validates cryptographic secrets, tokens, or digital signatures sent by the proxy. + +--- + +## Comparison of Supported Protocols + +| Protocol | Protocol Transport | Security / Cryptography | Replay Protection | Crate Module | +| :--- | :--- | :--- | :--- | :--- | +| **[BungeeCord](/developer/networking/proxy/bungeecord)** | `SHandshake` server address string | Optional token in profile properties (`bungeeguard-token`) | None | `pumpkin::net::proxy::bungeecord` | +| **[Velocity](/developer/networking/proxy/velocity)** | Login plugin message (`velocity:player_info`) | Symmetric HMAC-SHA256 signature | None | `pumpkin::net::proxy::velocity` | +| **[Vine](/developer/networking/proxy/vine)** | Login plugin message (`vine:player_info`) | Asymmetric **Ed25519** digital signatures | 16-byte nonce challenge + 30s timestamp window | `pumpkin::net::proxy::vine` | + +--- + +## Lifecycle Architecture + +1. **Player Authentication**: The player connects to the proxy (BungeeCord, Velocity, or Vine) and authenticates against Mojang's session servers. +2. **Backend Connection**: The proxy connects to Pumpkin over TCP and sends `SHandshake`. +3. **Data Forwarding**: + - **BungeeCord Mode**: The proxy injects the player's remote IP, UUID, and properties directly into the `SHandshake` hostname string (separated by null bytes `\0`). + - **Modern Mode (Velocity / Vine)**: Pumpkin sends `CLoginPluginRequest` on a dedicated channel (`velocity:player_info` or `vine:player_info`). The proxy signs the player profile using HMAC-SHA256 (Velocity) or Ed25519 (Vine) and replies with `SLoginPluginResponse`. +4. **Validation & Session Initialization**: Pumpkin validates the signature, binds the real IP and GameProfile to the `JavaClient`, and sends `CLoginSuccess`. + +--- + +## Proxy Documentation + +- **[BungeeCord](/developer/networking/proxy/bungeecord)**: Legacy host string injection and optional BungeeGuard token validation. +- **[Velocity Modern Forwarding](/developer/networking/proxy/velocity)**: HMAC-SHA256 signed player info during the Login state. +- **[Vine Modern Forwarding](/developer/networking/proxy/vine)**: Next-generation proxy forwarding with Ed25519 signatures and replay attack prevention. diff --git a/docs/pt/developer/networking/proxy/velocity.md b/docs/pt/developer/networking/proxy/velocity.md new file mode 100644 index 0000000..b0333e7 --- /dev/null +++ b/docs/pt/developer/networking/proxy/velocity.md @@ -0,0 +1,88 @@ +# Velocity Modern Forwarding + +[Velocity](https://papermc.io/software/velocity) is a high-performance proxy created by PaperMC. It uses modern plugin messaging during the `Login` state to securely transmit player data with **HMAC-SHA256** message authentication codes. + +Pumpkin implements Velocity modern forwarding in [`crates/pumpkin/src/net/proxy/velocity.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/velocity.rs). + +--- + +## How Velocity Forwarding Operates + +Unlike BungeeCord, which injects unauthenticated data into the handshake string, Velocity operates during the **Login** state over a dedicated plugin channel: + +1. **Handshake & Login**: The player authenticates with Velocity. Velocity initiates a standard TCP connection to Pumpkin and transmits `SHandshake` and `SLoginStart`. +2. **Channel Query**: Recognizing that Velocity modern forwarding is enabled, Pumpkin pauses the login sequence and dispatches a `CLoginPluginRequest` on channel `velocity:player_info`. +3. **Payload Generation**: Velocity bundles the player's remote address, UUID, username, and skin textures. It calculates a 32-byte HMAC-SHA256 signature across this payload using the shared secret. +4. **Signature Verification**: Velocity sends `SLoginPluginResponse`. Pumpkin validates the HMAC-SHA256 digest against its configured secret. If valid, Pumpkin overwrites the connection's network address and GameProfile and transitions to `Config` / `Play`. + +--- + +## Wire Payload Structure + +The payload in `SLoginPluginResponse` is structured as follows: + +```text +┌─────────────────────────────────────────────────────────────┐ +│ 32-byte HMAC-SHA256 Signature │ +├─────────────────────────────────────────────────────────────┤ +│ Forwarding Protocol Version (VarInt: 1 - 4) │ +├─────────────────────────────────────────────────────────────┤ +│ Client Remote IP Address (String / IPv4 / IPv6) │ +├─────────────────────────────────────────────────────────────┤ +│ Player UUID (128-bit / UUID) │ +├─────────────────────────────────────────────────────────────┤ +│ Player Username (String) │ +├─────────────────────────────────────────────────────────────┤ +│ Profile Properties Count (VarInt) │ +│ ├─ Property Name (String) │ +│ ├─ Property Value (String) │ +│ └─ Optional Signature (String) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Cryptographic Validation in Pumpkin + +Inside `crates/pumpkin/src/net/proxy/velocity.rs`: + +```rust +use hmac::{Hmac, KeyInit, Mac}; +use sha2::Sha256; + +type HmacSha256 = Hmac; + +// Extract signature and payload +let (signature, data_to_verify) = data.split_at(32); + +// Initialize HMAC with secret from pumpkin.toml +let mut mac = HmacSha256::new_from_slice(secret.as_bytes()) + .map_err(|_| VelocityError::FailedVerifyIntegrity)?; + +mac.update(data_to_verify); + +// Constant-time verification prevents timing attacks +mac.verify_slice(signature) + .map_err(|_| VelocityError::FailedVerifyIntegrity)?; +``` + +If the signature does not match (e.g., if an attacker attempts to connect directly to the backend without knowing the secret), Pumpkin drops the connection immediately. + +--- + +## Configuration in `pumpkin.toml` + +1. Open `velocity.toml` on your Velocity proxy and set: + ```toml + player-info-forwarding-mode = "modern" + forwarding-secret-file = "forwarding.secret" + ``` +2. Copy the generated secret string from `forwarding.secret` into `pumpkin.toml`: + ```toml + [networking.proxy] + enabled = true + + [networking.proxy.velocity] + enabled = true + secret = "YOUR_VELOCITY_FORWARDING_SECRET_HERE" + ``` diff --git a/docs/pt/developer/networking/proxy/vine.md b/docs/pt/developer/networking/proxy/vine.md new file mode 100644 index 0000000..fed2a8a --- /dev/null +++ b/docs/pt/developer/networking/proxy/vine.md @@ -0,0 +1,112 @@ +# Vine Modern Forwarding + +**Vine** is a next-generation, high-performance Minecraft proxy written in Rust. Designed for ultra-low latency, multi-threading, and enterprise-grade security, Vine introduces modern cryptographic forwarding based on **Ed25519 asymmetric signatures** and **replay attack protection**. + +Pumpkin provides native support for Vine forwarding in [`crates/pumpkin/src/net/proxy/vine.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/vine.rs). + +--- + +## Why Vine Forwarding? + +While Velocity's HMAC-SHA256 forwarding represents an improvement over legacy BungeeCord, it relies on a **symmetric shared secret**. If one backend server in a network is compromised, an attacker obtains the shared secret and can forge authenticated player logins across the entire server cluster. + +Vine solves this with: +1. **Asymmetric Public-Key Cryptography (Ed25519)**: The proxy signs player data using its private key. Backend servers only store the proxy's **Public Key** (`public_key`). Even if a backend server is compromised, the attacker cannot forge identities to other servers. +2. **Replay Attack Protection (16-byte Challenge Nonce)**: Pumpkin generates a cryptographic random 16-byte nonce for each connection. The proxy must sign and return this nonce. Replaying a captured login packet is impossible. +3. **Timestamp Drift Window**: Transmits a Unix timestamp that must be within ±30 seconds of the backend's system clock (`MAX_TIMESTAMP_DRIFT_SECS = 30`). + +--- + +## The Handshake Sequence + +1. **Connection**: The player connects and authenticates with Vine. Vine opens a backend connection to Pumpkin and sends `SHandshake` and `SLoginStart`. +2. **Challenge Query**: Pumpkin generates a cryptographically random 16-byte nonce and sends `CLoginPluginRequest` on the `vine:player_info` channel. +3. **Asymmetric Signing**: Vine serializes the version, timestamp, the returned 16-byte nonce, the client IP address, and the player GameProfile. It signs this byte stream with its Ed25519 private key. +4. **Tri-fold Validation**: Vine replies with `SLoginPluginResponse`. Pumpkin verifies: + - **Clock Skew**: The embedded timestamp is within ±30 seconds of UTC time. + - **Nonce Check**: The signed nonce exactly matches the 16-byte challenge generated in step 2. + - **Ed25519 Signature**: The signature is cryptographically valid against the configured `public_key`. +5. **Session Success**: If all three checks pass, Pumpkin applies the verified identity and emits `CLoginSuccess`. + +--- + +## Wire Payload Structure + +The `SLoginPluginResponse` data layout: + +```text +┌─────────────────────────────────────────────────────────────┐ +│ 64-byte Ed25519 Digital Signature │ +├─────────────────────────────────────────────────────────────┤ +│ Forwarding Protocol Version (i32: 1) │ +├─────────────────────────────────────────────────────────────┤ +│ Unix Timestamp in Seconds (i64) │ +├─────────────────────────────────────────────────────────────┤ +│ 16-byte Challenge Nonce │ +├─────────────────────────────────────────────────────────────┤ +│ Client Remote IP Address (String / IPv4 / IPv6) │ +├─────────────────────────────────────────────────────────────┤ +│ Player UUID (128-bit / UUID) │ +├─────────────────────────────────────────────────────────────┤ +│ Player Username (String) │ +├─────────────────────────────────────────────────────────────┤ +│ Profile Properties Count (VarInt) │ +│ ├─ Property Name (String) │ +│ ├─ Property Value (String) │ +│ └─ Optional Signature (String) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Verification in Pumpkin + +In `crates/pumpkin/src/net/proxy/vine.rs`: + +```rust +use ed25519_dalek::{Signature, Verifier, VerifyingKey}; + +// 1. Validate payload length (64-byte signature + 25-byte headers minimum) +if data.len() < 89 { + return Err(VineError::DataTooShort); +} + +// 2. Extract signature and signed payload +let (sig_bytes, signed_data) = data.split_at(64); +let signature = Signature::from_slice(sig_bytes) + .map_err(|_| VineError::InvalidSignature)?; + +// 3. Verify timestamp skew (30 second maximum drift) +let skew = (server_now - timestamp).abs(); +if skew > MAX_TIMESTAMP_DRIFT_SECS { + return Err(VineError::TimestampExpired(skew, MAX_TIMESTAMP_DRIFT_SECS)); +} + +// 4. Verify challenge nonce matches connection state +if received_nonce != expected_nonce { + return Err(VineError::ChallengeMismatch); +} + +// 5. Verify Ed25519 signature with proxy's public key +verifying_key.verify(signed_data, &signature) + .map_err(|_| VineError::InvalidSignature)?; +``` + +--- + +## Configuration in `pumpkin.toml` + +Configure Vine forwarding in `pumpkin.toml`: + +```toml +[networking.proxy] +enabled = true + +[networking.proxy.vine] +enabled = true +# 64-character hex-encoded Ed25519 public key of the Vine proxy +public_key = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + +# Alternatively, if secret is supplied, the public key is derived automatically +secret = "" +``` diff --git a/docs/pt/developer/networking/query.md b/docs/pt/developer/networking/query.md new file mode 100644 index 0000000..7d97a9c --- /dev/null +++ b/docs/pt/developer/networking/query.md @@ -0,0 +1,97 @@ +# GameSpy4 Query Protocol + +Pumpkin includes native support for the **GameSpy4 Query** protocol over UDP. External monitoring systems, Discord bots, and server listing platforms use Query to poll real-time player counts, MOTD, server versions, and active player lists with minimal network overhead. + +Query wire encoding is implemented in [`crates/pumpkin-protocol/src/query.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-protocol/src/query.rs), and the asynchronous UDP listener is implemented in [`crates/pumpkin/src/net/query.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/query.rs). + +--- + +## Why Query? (UDP vs. Status Ping) + +While standard Minecraft clients ping servers via the TCP `Status` state (SLP), Query operates over stateless **UDP**: + +- **No TCP Handshake**: Avoids TCP connection overhead and connection state tables. +- **Detailed Server State**: Returns comprehensive key-value metadata and a full list of connected player usernames (not available via basic TCP ping). +- **Amplification Attack Protection**: Enforces cryptographic challenge tokens so malicious actors cannot abuse the UDP port for denial-of-service reflection attacks. + +--- + +## The Handshake & Challenge Token Flow + +To prevent UDP spoofing and reflection DDoS attacks, Query requires a two-step handshake: + +1. **Handshake Challenge**: The client transmits `SHandshake` (`0xFEFD`, type `0x09`, and a 32-bit session ID). Pumpkin generates a cryptographic 32-bit integer challenge token tied to the client's `SocketAddr` and responds with `CHandshake`. +2. **Status Query**: The client sends `SStatusRequest` containing the received challenge token: + - **Basic Status** (no padding): Returns MOTD, game type (`SMP`), map name, online player count, and max player capacity. + - **Full Status** (4-byte padding): Returns detailed server metadata key-value pairs (`hostname`, `gametype`, `game_id`, `version`, `plugins`, `map`, `numplayers`, `maxplayers`, `hostport`, `hostip`) followed by an array of online player usernames. + +### Challenge Token Invalidation +In `crates/pumpkin/src/net/query.rs`, challenge tokens are stored in a thread-safe map (`Arc>>`). A background Tokio timer purges all challenge tokens every 30 seconds to prevent replay abuse: + +```rust +// Tokens are strictly bound to the client's IP and port +let valid_challenge_tokens = Arc::new(RwLock::new(HashMap::new())); + +// Background expiration loop +tokio::spawn(async move { + let mut interval = time::interval(Duration::from_secs(30)); + loop { + interval.tick().await; + valid_challenge_tokens.write().await.clear(); + } +}); +``` + +--- + +## Packet Formats + +Every incoming Query packet starts with the 2-byte magic header `0xFEFD` followed by a 1-byte packet type: + +| Type Byte | Name | Direction | Description | +| :--- | :--- | :--- | :--- | +| `0x09` | Handshake Request | Client → Server | Client asks for a challenge token. | +| `0x09` | Handshake Response | Server → Client | Returns the numeric token encoded as an ASCII string. | +| `0x00` | Stat Request | Client → Server | Requests basic or full status with challenge token. | +| `0x00` | Basic Stat Response | Server → Client | 11-byte short summary. | +| `0x00` | Full Stat Response | Server → Client | Key-value dictionary + null-separated player list. | + +--- + +## Response Payloads + +### 1. Basic Status (`CBasicStatus`) +Contains essential server fields: +- **`motd`**: Server message of the day (stripped of color codes). +- **`gametype`**: Fixed string `"SMP"`. +- **`map`**: Active world/level name (e.g., `"world"`). +- **`numplayers`**: Online player count. +- **`maxplayers`**: Maximum configured player capacity. +- **`hostport`**: Port number (`u16` Little Endian). +- **`hostip`**: Server IP string. + +### 2. Full Status (`CFullStatus`) +Returns two null-terminated sections: + +1. **Key-Value Metadata Map**: + - `hostname`: Server description / MOTD. + - `gametype`: `"SMP"`. + - `game_id`: `"MINECRAFT"`. + - `version`: Server protocol / Minecraft version string. + - `plugins`: Installed server software and plugin names. + - `map`: Level name. + - `numplayers` / `maxplayers`: Capacity stats. + - `hostport` / `hostip`: Network bindings. +2. **Player List**: + - An array of null-terminated UTF-8 strings containing the usernames of all currently connected players. + - Concludes with a terminal null byte (`\0`). + +--- + +## Configuration in `pumpkin.toml` + +```toml +[networking.query] +enabled = true +address = "0.0.0.0:25565" +``` diff --git a/docs/pt/developer/plugins/index.md b/docs/pt/developer/plugins/index.md new file mode 100644 index 0000000..8cc993f --- /dev/null +++ b/docs/pt/developer/plugins/index.md @@ -0,0 +1,56 @@ +# Plugin Engine Architecture + +Pumpkin features a modular, multi-runtime plugin engine designed for performance, safety, and cross-language interoperability. + +Unlike traditional Minecraft servers that rely on a single JVM class loader, Pumpkin isolates plugin execution through dedicated loaders supporting both **native high-performance Rust binaries** and **sandboxed WebAssembly components** compiled from Rust, Python, C#, C, Go, or Kotlin. + +--- + +## Architectural Overview + +### Architectural Breakdown + +| Subsystem | Primary Crate | Responsibility | +| :--- | :--- | :--- | +| **Plugin Manager** | `crates/pumpkin/src/plugin` | Lifecycle orchestration (load, enable, disable), event dispatching, and loader registration | +| **Native Loader** | `crates/pumpkin/src/plugin/loader/native` | Direct dynamic library loading (`.so`, `.dll`, `.dylib`) for zero-overhead Rust extensions | +| **WASM Loader** | `crates/pumpkin/src/plugin/loader/wasm` | Wasmtime execution engine for sandboxed components compiled from Rust, Python, Go, C#, C, and Kotlin | +| **Interface Specifications** | `crates/pumpkin-plugin-wit` | WebAssembly Interface Types (WIT) defining typed functions, types, and host imports | +| **Security & Verification** | `crates/pumpkin/src/plugin/loader/wasm/sign` | Cryptographic signature verification via W3C wasmsign2 / Ed25519 public keys | + +--- + +## The Dual Execution Model + +| Feature | Native Plugins | WebAssembly (Wasmtime) Plugins | +| :--- | :--- | :--- | +| **File Extension** | `.so` (Linux), `.dll` (Windows), `.dylib` (macOS) | `.wasm` (Component Model) | +| **Languages** | Rust (compiled dynamically) | Rust, Python, C#, C, Go, Kotlin | +| **Performance** | Native CPU execution (zero overhead) | JIT-compiled with near-native speed | +| **Sandboxing** | None (runs in host address space) | Memory-isolated sandboxed execution | +| **Security & Signing** | OS-level permissions | Cryptographic W3C Wasm-Sign verification | +| **Hot Unloading** | Limited (OS shared library constraints) | Fully supported memory reclamation | + +--- + +## Event Lifecycle & Dispatching + +Plugins interact with Pumpkin primarily through events: + +1. **Event Dispatch**: + - `server.plugin_manager.fire_blocking(&server, &mut event)`: Dispatches events synchronously on the executing thread (e.g., cancelling player block placement or modifying packet payloads). + - Asynchronous events run on non-blocking Tokio tasks. +2. **Cancellable Events**: + - Handlers can set `event.cancelled = true` to abort the default game logic (such as cancelling player movement, interactions, or packet processing). + +--- + +## Plugin Engine Documentation + +Explore the internal mechanics of Pumpkin's plugin system: + +- **[Adding a Plugin Loader](/developer/plugins/loaders)**: Step-by-step guide on implementing the `PluginLoader` trait for new scripting languages and runtimes. +- **[WASM Signing & Verification](/developer/plugins/wasm-signing)**: Detailed specification of Ed25519 cryptographic signing, W3C `wasmsign2` custom sections, and marketplace license verification. + +> [!NOTE] +> Looking to develop third-party plugins rather than working on Pumpkin's internal plugin engine? Check out the **[Plugin Development Guide](/plugin-dev/introduction)**. diff --git a/docs/pt/developer/plugins/loaders.md b/docs/pt/developer/plugins/loaders.md new file mode 100644 index 0000000..7e0f070 --- /dev/null +++ b/docs/pt/developer/plugins/loaders.md @@ -0,0 +1,161 @@ +# Adding a Plugin Loader + +Pumpkin's plugin architecture is runtime-agnostic. The server does not assume all plugins run in WebAssembly or native shared libraries; instead, all execution runtimes implement the unified [`PluginLoader`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/plugin/loader/mod.rs) trait. + +This guide explains how `PluginLoader` operates and demonstrates how to implement a custom loader for new script runtimes or virtual machines (such as Lua, JavaScript/Boa, or GraalVM). + +--- + +## The `PluginLoader` Trait + +All plugin loaders implement the `PluginLoader` trait located in `crates/pumpkin/src/plugin/loader/mod.rs`: + +```rust +use std::{any::Any, path::Path, pin::Pin, sync::Arc}; +use crate::plugin::{PluginMetadata, api::Plugin}; + +pub type PluginLoadFuture<'a> = Pin< + Box< + dyn Future< + Output = Result< + (Arc, PluginMetadata, Box), + LoaderError, + >, + > + Send + + 'a, + >, +>; + +pub type PluginUnloadFuture<'a> = + Pin> + Send + 'a>>; + +pub trait PluginLoader: Send + Sync { + /// Determines whether this loader handles the target file. + fn can_load(&self, path: &Path) -> bool; + + /// Loads, parses, and initializes a plugin instance asynchronously. + fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a>; + + /// Unloads the plugin and reclaims associated resources. + fn unload(&self, data: Box) -> PluginUnloadFuture<'_>; + + /// Indicates whether the runtime supports safe unloading without memory leaks. + fn can_unload(&self) -> bool; +} +``` + +--- + +## Existing Loaders in Pumpkin + +Pumpkin currently provides two official loaders: + +1. **`NativePluginLoader` (`loader/native.rs`)**: + - Matches dynamic library files (`.so` on Linux, `.dll` on Windows, `.dylib` on macOS). + - Dynamically links the library using [`libloading`](https://docs.rs/libloading/). + - Locates and invokes the extern entrypoint function (`pumpkin_plugin_init`). + - `can_unload()` returns `false` due to OS-level shared library unloading hazards. +2. **`WasmPluginLoader` (`loader/wasm/mod.rs`)**: + - Matches `.wasm` files. + - Verifies cryptographic signatures (see [WASM Signing](/developer/plugins/wasm-signing)). + - JIT-compiles WebAssembly components using [Wasmtime](https://wasmtime.dev/). + - Links host WIT exports (`pumpkin-host-bindings`). + - `can_unload()` returns `true` because Wasmtime safely drops guest memory. + +--- + +## Step-by-Step: Adding a Custom Plugin Loader + +Here is how to create a custom loader (e.g., for a scripting runtime): + +### Step 1: Define the Loader Struct +Create a new file in `crates/pumpkin/src/plugin/loader/my_loader.rs`: + +```rust +use std::{any::Any, path::Path, sync::Arc}; +use crate::plugin::loader::{LoaderError, PluginLoadFuture, PluginLoader, PluginUnloadFuture}; +use crate::plugin::{PluginMetadata, api::Plugin}; + +pub struct MyCustomLoader; + +impl MyCustomLoader { + #[must_use] + pub fn new() -> Self { + Self + } +} +``` + +### Step 2: Implement File Recognition (`can_load`) +Check the file extension or inspect header bytes: + +```rust +impl PluginLoader for MyCustomLoader { + fn can_load(&self, path: &Path) -> bool { + path.extension() + .and_then(|ext| ext.to_str()) + .is_some_and(|ext| ext.eq_ignore_ascii_case("script")) + } +``` + +### Step 3: Implement Asynchronous Loading (`load`) +The `load` method reads the script, initializes the VM, registers server host callbacks, and returns a tuple containing: +1. `Arc`: The generic plugin handle. +2. `PluginMetadata`: Name, author, version, description. +3. `Box`: Loader-specific context (e.g., VM state) retained for unloading. + +```rust + fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a> { + Box::pin(async move { + let script_content = tokio::fs::read_to_string(path).await + .map_err(|e| LoaderError::InitializationFailed(e.to_string()))?; + + // 1. Parse plugin metadata from header or accompanying manifest + let metadata = PluginMetadata { + name: "ExampleScript".into(), + version: "1.0.0".into(), + authors: vec!["Author".into()], + description: Some("Custom script plugin".into()), + }; + + // 2. Initialize runtime instance + let plugin_instance = Arc::new(MyScriptPluginWrapper::new(script_content)); + let runtime_state: Box = Box::new(()); + + Ok((plugin_instance, metadata, runtime_state)) + }) + } +``` + +### Step 4: Implement Clean Unloading (`unload` & `can_unload`) +If your runtime can cleanly terminate without leaking resources: + +```rust + fn can_unload(&self) -> bool { + true + } + + fn unload(&self, data: Box) -> PluginUnloadFuture<'_> { + Box::pin(async move { + // Drop runtime state and disconnect event listeners + drop(data); + Ok(()) + }) + } +} +``` + +### Step 5: Register the Loader in `PluginManager` + +Open `crates/pumpkin/src/plugin/mod.rs` and register your loader in `PluginManager::new()`: + +```rust +// In crates/pumpkin/src/plugin/mod.rs +let mut loaders: Vec> = Vec::new(); + +loaders.push(Box::new(NativePluginLoader::new())); +loaders.push(Box::new(WasmPluginLoader::new())); +loaders.push(Box::new(MyCustomLoader::new())); // Add custom loader +``` + +During server startup, Pumpkin iterates through all registered loaders in sequence when scanning the `plugins/` folder, dispatching each file to the first loader whose `can_load` returns `true`. diff --git a/docs/pt/developer/plugins/wasm-signing.md b/docs/pt/developer/plugins/wasm-signing.md new file mode 100644 index 0000000..40a36eb --- /dev/null +++ b/docs/pt/developer/plugins/wasm-signing.md @@ -0,0 +1,86 @@ +# WASM Plugin Signing & Verification + +Pumpkin implements a cryptographic signing and license verification system for WebAssembly plugins. This ensures plugin integrity, authenticates author identity, and protects verified or marketplace plugins against unauthorized tampering. + +The signing subsystem is implemented in [`crates/pumpkin/src/plugin/loader/wasm/wasm_host/signature.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/plugin/loader/wasm/wasm_host/signature.rs) based on the **W3C `wasmsign2`** specification using custom WebAssembly sections. + +--- + +## Why Sign WebAssembly Plugins? + +- **Tamper Protection**: Ensures that compiled bytecode has not been injected with malicious logic after compilation. +- **Author Identity**: Authenticates that a plugin was built and released by a verified developer. +- **Marketplace Licensing**: Validates license keys and entitlements for plugins distributed through the official [Pumpkin Marketplace](https://market.pumpkinmc.org). + +--- + +## Custom Sections in the WASM Binary + +Pumpkin embeds signing and verification metadata directly into two standard WebAssembly custom sections: +- **`pumpkin.metadata`**: Stores developer, publication, and license claims in JSON format. +- **`wasm_signature`**: Stores the cryptographic signature envelope compliant with the W3C `wasmsign2` standard. + +### 1. `pumpkin.metadata` Section +Contains a JSON string holding publication and license claims: + +```json +{ + "marketplace_url": "https://market.pumpkinmc.org", + "plugin_id": 1024, + "plugin_name": "EconomyPlus", + "version": "1.2.0", + "dev_id": 42, + "dev_name": "DevName", + "is_paid": true, + "user_id": 1001, + "license_key": "XXXX-XXXX-XXXX-XXXX", + "issued_at": "2026-09-14T05:00:00Z" +} +``` + +### 2. `wasm_signature` Section +Follows the W3C `wasmsign2` envelope structure: + +```json +{ + "version": 1, + "algorithm": "Ed25519", + "public_key_hex": "4a7b...", + "signature_hex": "8e1c..." +} +``` + +--- + +## The Signing Pipeline + +The signing process guarantees integrity by hashing and signing the clean WebAssembly bytecode combined with the metadata claims: + +### Signing Algorithm Steps +1. **Sanitization (`strip_pumpkin_sections`)**: Uses [`wasmparser`](https://docs.rs/wasmparser/) to strip any preexisting `pumpkin.metadata` or `wasm_signature` custom sections to avoid recursion. +2. **Payload Construction**: + ```rust + let mut sign_payload = Vec::new(); + sign_payload.extend_from_slice(&clean_wasm); + sign_payload.extend_from_slice(&metadata_json); + ``` +3. **Ed25519 Signature**: The payload is signed using [`ed25519-dalek`](https://docs.rs/ed25519-dalek/) with the developer's 32-byte secret key. +4. **Encoding**: Uses [`wasm-encoder`](https://docs.rs/wasm-encoder/) to append the `pumpkin.metadata` and `wasm_signature` custom sections to the clean binary. + +--- + +## The Verification Pipeline + +When Pumpkin boots and loads a WebAssembly plugin, the verification sequence executes before the plugin is instantiated: + +1. **Custom Section Extraction**: The `WasmPluginLoader` scans the binary for `pumpkin.metadata` and `wasm_signature`. +2. **Key Retrieval**: + - If the plugin is marked as marketplace-verified, Pumpkin verifies the public key against the Marketplace Public Key Endpoint (`https://market.pumpkinmc.org/api/v1/rest/public-key`). + - If signed by an independent developer, the key is matched against trusted developer keys configured in `pumpkin.toml`. +3. **Bytecode Verification**: + - Pumpkin strips the custom sections from the loaded binary to recover the original `clean_wasm`. + - Reconstructs the payload `(clean_wasm + metadata_json)`. + - Verifies the signature using `verifying_key.verify(&payload, &signature)`. +4. **License & Integrity Validation**: + - If verification succeeds, Wasmtime instantiates the plugin safely. + - If verification fails or the signature is invalid, loading is aborted with an error, protecting the server from running compromised code. diff --git a/docs/pt/developer/porting-versions.md b/docs/pt/developer/porting-versions.md new file mode 100644 index 0000000..0a5014a --- /dev/null +++ b/docs/pt/developer/porting-versions.md @@ -0,0 +1,287 @@ +# Porting to Newer Minecraft Versions + +Upgrading Pumpkin to support a newer Minecraft release—whether a minor patch (e.g., `1.21.4` to `1.21.5`) or a major update (e.g., `1.21` to `26.1`/`26.2`)—is a structured, multi-step process. + +> [!IMPORTANT] +> **The Extractor mod must be ported first!** +> Pumpkin cannot be ported in isolation. The primary source for Pumpkin's assets (`blocks.json`, `items.json`, `entities.json`, `packets.json`, `tracked_data.json`, `synced_registries.json`, etc.) is our dedicated Fabric mod: [Pumpkin-MC/Extractor](https://github.com/Pumpkin-MC/Extractor). You **must first port and run the Extractor on the target Minecraft version** before running `pumpkin-codegen` or updating server crates. + +### Porting Lifecycle at a Glance + +| Stage | Responsibility | Primary Crates / Tools | +| :--- | :--- | :--- | +| **1. Port Extractor** | Extract vanilla block states, entities, items, packets, and registries | `Pumpkin-MC/Extractor` (Fabric mod) | +| **2. Update Datapacks** | Ingest new data packs, tags, recipes, and Bedrock mappings | `assets/datapacks/`, `assets/bedrock` | +| **3. Version Declarations** | Register protocol version and ViaVersion/ViaBackwards remap nodes | `pumpkin-util`, `tools/pumpkin-codegen` | +| **4. Code Generation** | Regenerate zero-cost Rust data tables and registries | `pumpkin-codegen` -> `pumpkin-data` | +| **5. Compiler Fixes** | Update packet layouts, codecs, and data components | `pumpkin-protocol`, `pumpkin-inventory` | +| **6. Gameplay Logic** | Implement new block behaviors, item mechanics, and mob AI | `crates/pumpkin/src/block`, `item`, `entity` | +| **7. Multi-Version QA** | Validate backward compatibility, test suites, and client joins | `cargo test`, `pumpkin-gametest` | + +--- + +## Phase 1: Port the Extractor Mod First (`Pumpkin-MC/Extractor`) + +The [Extractor](https://github.com/Pumpkin-MC/Extractor) is a Kotlin-based Fabric mod that boots an actual Minecraft client or server instance to introspect live game registries and extract data into the exact JSON formats Pumpkin expects. + +### 1. Clone or Update the Extractor Repository +```bash +git clone https://github.com/Pumpkin-MC/Extractor +cd Extractor +``` + +### 2. Update `gradle.properties` +Bump the dependency versions to match the target Minecraft version, Yarn mappings, and Fabric ecosystem: + +```properties +minecraft_version=26.2 +yarn_mappings=26.2+build.1:v2 +loader_version=0.16.10 +fabric_version=0.115.0+26.2 +``` + +### 3. Resolve Extractor Compilation Errors +Minecraft and Fabric internal APIs often change between releases. Check and fix any broken extractors in `src/main/kotlin/de/snowii/extractor/extractors/`: +- **`Blocks.kt`**: Extracts block states, hardness, resistance, luminance, and collision box offsets. +- **`Items.kt`**: Extracts item IDs, max stack sizes, and default components. +- **`Entities.kt`**: Extracts entity IDs, dimensions (width, height), and eye heights. +- **`Packets.kt`**: Extracts packet IDs for handshake, status, login, configuration, and play states. +- **`SyncedRegistries.kt`**: Extracts dynamic registries synchronized to clients. +- **`TrackedData.kt`**: Extracts entity tracked data indices and serializers. +- **`MultiNoise.kt` & `Biome.kt`**: Extracts world generation biome trees and noise parameters. + +### 4. Run the Extractor +Execute the server runner (or client runner for translation/screen dumps): + +```bash +./gradlew runServer +``` + +Once the extraction finishes, all dumped JSON files will be located in `run/pumpkin_extractor_output/`. + +### 5. Replace the Old Asset Files in Pumpkin +Copy the newly generated JSON files from `run/pumpkin_extractor_output/` and use them to **replace the old asset files** in Pumpkin: +- Replace the core game assets in `Pumpkin/assets/` (`blocks.json`, `items.json`, `entities.json`, `packets.json`, `properties.json`, `synced_registries.json`, `tracked_data.json`, `en_us.json`, etc.). +- Replace the world generation test files in `Pumpkin/crates/pumpkin-world/assets/`. + +--- + +## Phase 2: Update Assets & Datapacks + +Once the raw extractor assets are in place, update the versioned datapacks and supplemental files in Pumpkin: + +### 1. Add the Versioned Datapack +Extract the internal vanilla data pack from the new Minecraft server JAR and place it under `assets/datapacks//`: + +``` +assets/datapacks// +└── data/ + ├── minecraft/ + │ ├── advancement/ + │ ├── chat_type/ + │ ├── damage_type/ + │ ├── dimension_type/ + │ ├── enchantment/ + │ ├── item_modifier/ + │ ├── loot_table/ + │ ├── recipe/ + │ ├── tags/ + │ └── worldgen/ + └── c/ + └── tags/ (Conventional / Common Tags) +``` + +### 2. Update Versioned Assets +Update or generate the corresponding versioned metadata files: +- `assets/registry/_synced_registries.json`: Dynamic registries streamed to the client during the `Configuration` network state. +- `assets/tags/_tags.json`: Tag collections. +- `assets/tracked_data/_tracked_data.json`: Entity metadata indices. + +### 3. Update Bedrock Mappings +If porting Bedrock Edition support simultaneously, update the files in `assets/bedrock/` using the latest mappings from [GeyserMC](https://github.com/GeyserMC/Geyser) and Bedrock Dedicated Server (BDS). + +### 4. Decompile Sources for Reverse Engineering (Optional but Recommended) +For complex changes—such as worldgen noise router formulas and custom block collision shapes—decompile the matching vanilla JAR into `minecraft-sources-/` (using Vineflower or Fabric Loom with official Mojang mappings) for reference. + +--- + +## Phase 3: Register Version Constants & ViaVersion Mappings + +Pumpkin supports multi-version clients through automated protocol remapping powered by **ViaBackwards** / **ViaRewind** mapping tables. + +### 1. Declare Version in `pumpkin-util` +Open `crates/pumpkin-util/src/version.rs` and add the new version to [`JavaMinecraftVersion`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-util/src/version.rs): + +```rust +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum JavaMinecraftVersion { + // ... existing versions ... + V_1_21_4, + V_1_21_11, + V_26_2, // New target version +} + +impl JavaMinecraftVersion { + pub const fn protocol_version(&self) -> i32 { + match self { + // ... + Self::V_26_2 => 770, // Protocol version number + } + } + + pub const fn version_name(&self) -> &'static str { + match self { + // ... + Self::V_26_2 => "26.2", + } + } +} +``` + +### 2. Declare Version in `pumpkin-codegen` +Open `tools/pumpkin-codegen/src/version.rs` and add the identical enum variant to the codegen AST. + +### 3. Add ViaVersion / ViaBackwards Mappings +To enable clients on older versions to connect to the updated server (e.g., a `1.21.4` client connecting to a `26.2` server): +1. Obtain the NBT mapping file from the [ViaBackwards repository](https://github.com/ViaVersion/ViaBackwards): + `mappings-to.nbt` +2. Save it to: + `assets/viabackwards/data/mappings-to.nbt` +3. In `tools/pumpkin-codegen/src/remap/mod.rs`, link the new node into the `remap_nodes!` macro: + +```rust +let node_26_2 = $crate::remap::MappingNode { + version: $crate::version::JavaMinecraftVersion::V_26_2, + value: "../../assets/viabackwards/data/mappings-26.2to26.1.nbt", + child: Some(&node_26_1), +}; +$remapper.process(&node_26_2) +``` + +4. Check `tools/pumpkin-codegen/src/remap/` to ensure specific remap builders (`block_state.rs`, `item_id.rs`, `entity_id.rs`, `sound_id.rs`, `argument_type.rs`) correctly process any new remapping structures introduced by ViaVersion. + +--- + +## Phase 4: Run Code Generation (`pumpkin-codegen`) + +With all extractor assets, datapacks, and mapping nodes in place, execute the codegen tool from the workspace root: + +```bash +cargo run -p pumpkin-codegen +``` + +### What Gets Regenerated? +`pumpkin-codegen` generates zero-cost Rust data tables into `crates/pumpkin-data/src/generated/`: +- **`block.rs`**: All block IDs, properties, state masks, and collision shapes. +- **`item.rs`**: Items, default components, and tool tiers. +- **`entity_type.rs` & `tracked_data.rs`**: Entity type IDs, default bounding boxes, and metadata serializers. +- **`packet.rs`**: Serverbound and clientbound packet ID constants across protocol states. +- **`recipes.rs`**: All crafting, cooking, and stonecutting recipes parsed from the new datapack. +- **`tag.rs`**: Tag lookups (`#minecraft:mineable/axe`, `#c:iron_ingots`). +- **`remap/*.rs`**: State remap lookup tables (`block_state_remap.rs`, `item_id_remap.rs`, `entity_id_remap.rs`, `sound_id_remap.rs`). + +--- + +## Phase 5: Resolve Workspace Compilation Errors + +After regenerating `pumpkin-data`, the Rust compiler will flag breaking changes across the workspace. Run: + +```bash +cargo check --workspace +``` + +### Common Compiler Breakages & Fixes: + +#### 1. Packet ID & Layout Changes (`pumpkin-protocol`) +Mojang frequently adds, moves, or alters packet IDs and payloads: +- Update `pumpkin-protocol/src/java/client/` and `pumpkin-protocol/src/java/server/`. +- Inspect packets like `LoginSuccess`, `PlayLogin`, `Respawn`, and `PlayerSpawnData` for newly added fields (e.g., new game mode flags, dimension attributes, or teleportation IDs). +- For Bedrock Edition, check `StartGame`, `NetworkItem`, and container transaction packets in `pumpkin-protocol/src/bedrock/`. + +#### 2. Data Component Changes +If items switched from older NBT structures to new Data Components (or if components were renamed/restructured): +- Update `crates/pumpkin-data/src/data_component.rs`. +- Update serialization and deserialization in `pumpkin-inventory`. + +#### 3. Registry Synchronization +During the `Configuration` phase, servers send dynamic registry contents to Java clients. If new registries were added to vanilla Minecraft (e.g., trim materials, banner patterns, instrument sounds): +- Verify `crates/pumpkin-data/src/generated/registry.rs`. +- Ensure `pumpkin/src/net/java/config.rs` streams all required registry codecs to the client. + +--- + +## Phase 6: Implement Logic for New Blocks, Items & Entities + +Once the server compiles, any newly introduced gameplay content must be implemented. + +### 1. New Blocks +1. Locate new block identifiers in `pumpkin-data::Block`. +2. Create a behavior module under `crates/pumpkin/src/block/blocks/.rs` implementing [`BlockBehaviour`](/developer/blocks/adding-blocks): + ```rust + pub struct NewBlockBehaviour; + + impl BlockBehaviour for NewBlockBehaviour { + fn on_use(&self, block: &Block, player: &Arc, location: BlockPos, world: &Arc) { + // Interaction logic + } + } + ``` +3. Register the behavior in the block registry in `crates/pumpkin/src/block/mod.rs`. + +### 2. New Items +1. Locate new item identifiers in `pumpkin-data::Item`. +2. If the item has unique right-click or use behavior (e.g., weapons, projectiles, instruments), create an item behavior in `crates/pumpkin/src/item/items/.rs` implementing [`ItemBehaviour`](/developer/items/adding-items). +3. If the item places entities (e.g., spawn eggs, boats, minecarts), configure entity spawning logic in `pumpkin/src/item/items/spawn_egg.rs`. +4. Register the behavior in `crates/pumpkin/src/item/mod.rs`. + +### 3. New Entities +1. Check `pumpkin-data::entity_type::EntityType` for new entity IDs. +2. Create an entity struct in `crates/pumpkin/src/entity/mob/` or `living/` extending [`LivingEntity`](/developer/entities/). +3. Implement `TrackedData` fields to match vanilla client expectations (e.g., poses, animations, glowing states). +4. Assign AI goals and navigation tasks using the [Mob AI & Goals system](/developer/entities/ai-and-mobs). +5. Add spawn packet construction in `crates/pumpkin/src/entity/spawning.rs`. + +--- + +## Phase 7: Multi-Version & ViaVersion Verification + +Ensure that older clients can still connect seamlessly: + +1. **Verify Generated Remap Tables**: + Check `crates/pumpkin-data/src/generated/block_state_remap.rs` and `item_id_remap.rs`. Ensure that novel block states fall back gracefully to compatible equivalents on older client versions (e.g., new wood variants mapping to oak or spruce). +2. **Packet Translation**: + Verify that packet writers in `pumpkin-protocol` apply `remap` functions when serializing data for a connection whose negotiated `protocol_version` is older than the server's native version. + +--- + +## Phase 8: Testing & Verification Checklist + +Before opening a Pull Request for a version port, complete the following QA checklist: + +- [ ] **Automated Tests**: + ```bash + cargo test --workspace + ``` +- [ ] **Game Test Suite**: + ```bash + cargo test -p pumpkin-gametest + ``` +- [ ] **Strict Linter Rules**: + ```bash + cargo clippy --workspace --all-targets -- -D warnings + ``` +- [ ] **Spellcheck**: + ```bash + typos + ``` +- [ ] **In-Game Java Client Test**: + - Connect with the native new Minecraft client version. + - Test login, world loading, chunk generation, and player movement. + - Place and break new blocks; verify collision boxes and redstone reactivity. + - Use and equip new items; test attack speed, durability, and animations. + - Spawn and fight new entities; test animations, hurt sounds, and drops. +- [ ] **In-Game Older Version Client Test**: + - Connect with an older supported client version (e.g., 1.21.x). + - Verify that blocks, items, and entities translate without causing client crashes or desyncs. +- [ ] **In-Game Bedrock Client Test**: + - Verify that Bedrock clients can authenticate, join, load world chunks, and view custom items. diff --git a/docs/pt/plugin-dev/c/first-command.md b/docs/pt/plugin-dev/c/first-command.md new file mode 100644 index 0000000..3ac3ebd --- /dev/null +++ b/docs/pt/plugin-dev/c/first-command.md @@ -0,0 +1,90 @@ +# Creating Your First Command + +Writing commands in C for Pumpkin is low-overhead, fast, and uses C WIT bindings. + +--- + +## 1. Quick Example + +Here is a complete C plugin file (`main.c`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```c [main.c] +#include +#include "pumpkin.h" + +void plugin_on_load(pumpkin_context_t ctx) { + // 1. Register permission node + pumpkin_permission_t perm = { + .node = "my_c_plugin:hello", + .description = "Allows executing the /hello command", + .default_access = PERMISSION_DEFAULT_ALLOW + }; + pumpkin_register_permission(ctx, &perm); + + // 2. Build and register command tree + const char* names[] = {"hello", "hi"}; + pumpkin_command_t cmd = pumpkin_command_new(names, 2, "Greets the player"); + + pumpkin_register_command(ctx, cmd, "my_c_plugin:hello"); +} + +int32_t plugin_handle_command(uint32_t command_id, pumpkin_sender_t sender, pumpkin_server_t server) { + pumpkin_sender_send_message(sender, "Hello from C Plugin!"); + return 1; +} + +pumpkin_metadata_t plugin_get_metadata() { + pumpkin_metadata_t meta = { + .name = "my_c_plugin", + .version = "0.1.0", + .description = "My first Pumpkin C plugin" + }; + return meta; +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Struct +Populate a `pumpkin_permission_t` struct and call `pumpkin_register_permission`: +```c +pumpkin_permission_t perm = { + .node = "my_c_plugin:hello", + .description = "Allows executing the /hello command", + .default_access = PERMISSION_DEFAULT_ALLOW +}; +pumpkin_register_permission(ctx, &perm); +``` + +### Step 2: Build the Command Tree +Create the command tree by passing name array and description: +```c +const char* names[] = {"hello", "hi"}; +pumpkin_command_t cmd = pumpkin_command_new(names, 2, "Greets the player"); +``` + +### Step 3: Register Command & Handle Execution +Pass the command handle to `pumpkin_register_command` and handle responses inside `plugin_handle_command`: +```c +pumpkin_register_command(ctx, cmd, "my_c_plugin:hello"); +``` + +::: + +--- + +## Next Steps + +- Check out the [C Quick Start Guide](./quick-start) to set up your compiler toolchain. diff --git a/docs/pt/plugin-dev/csharp/first-command.md b/docs/pt/plugin-dev/csharp/first-command.md new file mode 100644 index 0000000..c2a34ca --- /dev/null +++ b/docs/pt/plugin-dev/csharp/first-command.md @@ -0,0 +1,110 @@ +# Creating Your First Command + +Registering custom commands in Pumpkin using C# provides a strongly-typed, clean structure. + +--- + +## 1. Quick Example + +Here is a complete C# plugin file (`MyPlugin.cs`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```csharp [MyPlugin.cs] +using PluginWorld; +using PluginWorld.wit.Exports.pumpkin.plugin.v0_1_0; +using PluginWorld.wit.Imports.pumpkin.plugin.v0_1_0; + +namespace MyPumpkinPlugin; + +public class MyPlugin : IPluginWorldExports, IMetadataExports +{ + public static void InitPlugin() { } + + public static void OnLoad(IContextImports.Context context) + { + // 1. Register permission node + var perm = new IPermissionImports.Permission( + "my_csharp_plugin:hello", + "Allows executing the /hello command", + IPermissionImports.PermissionDefault.Allow, + [] + ); + IContextImports.RegisterPermission(context, perm); + + // 2. Build and register command tree + var command = ICommandImports.Command.New(["hello", "hi"], "Greets the player"); + IContextImports.RegisterCommand(context, command, "my_csharp_plugin:hello"); + } + + public static void OnUnload(IContextImports.Context context) { } + + public static IEventImports.Event HandleEvent(uint eventId, IServerImports.Server server, IEventImports.Event @event) + { + return @event; + } + + public static int HandleCommand(uint commandId, ICommandImports.CommandSender sender, IServerImports.Server server, ICommandImports.ConsumedArgs args) + { + ICommandImports.SendMessage(sender, "Hello from C# Plugin!"); + return 1; + } + + public static void HandleTask(uint handlerId, IServerImports.Server server) { } + + public static IMetadataExports.PluginMetadata Metadata() + { + return new IMetadataExports.PluginMetadata( + "my_csharp_plugin", + "0.1.0", + "My first Pumpkin C# plugin", + ["Developer"], + [] + ); + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Node +Construct an `IPermissionImports.Permission` record and register it via `IContextImports.RegisterPermission`: +```csharp +var perm = new IPermissionImports.Permission( + "my_csharp_plugin:hello", + "Allows executing the /hello command", + IPermissionImports.PermissionDefault.Allow, + [] +); +IContextImports.RegisterPermission(context, perm); +``` + +### Step 2: Build the Command Tree +Instantiate your command with primary names and description: +```csharp +var command = ICommandImports.Command.New(["hello", "hi"], "Greets the player"); +``` + +### Step 3: Register and Handle +Register the command with `IContextImports.RegisterCommand` and process incoming executions inside `HandleCommand`: +```csharp +IContextImports.RegisterCommand(context, command, "my_csharp_plugin:hello"); +``` + +::: + +--- + +## Next Steps + +- Follow the [C# Quick Start Guide](./quick-start) to configure `.csproj` and build WebAssembly outputs. diff --git a/docs/pt/plugin-dev/d/quick-start.md b/docs/pt/plugin-dev/d/quick-start.md new file mode 100644 index 0000000..b4ecc80 --- /dev/null +++ b/docs/pt/plugin-dev/d/quick-start.md @@ -0,0 +1,130 @@ +# Início Rápido + +Este guia ajudará você a começar a criar plugins para o servidor Pumpkin utilizando a linguagem de programação [D](https://dlang.org/). + +Os plugins em D para o Pumpkin são compilados em componentes WebAssembly (Wasm) utilizando os bindings oficiais [`pumpkin-api-d`](https://github.com/Pumpkin-MC/pumpkin-api-d). + +--- + +## Pré-requisitos + +Antes de iniciar, certifique-se de ter as seguintes ferramentas instaladas: + +- **[LDC](https://github.com/ldc-developers/ldc)**: Versão 1.43 ou posterior (compilador D baseado em LLVM). +- **`addon-wasi`**: O pacote de destino WASI para o LDC. +- **[DUB](https://dub.pm/)**: O gerenciador de pacotes e compilação de D (geralmente incluso no LDC). + +--- + +## 1. Configurando o Projeto + +Crie um novo diretório para o seu projeto de plugin e adicione o arquivo de configuração `dub.json`: + +```bash +mkdir my-d-plugin +cd my-d-plugin +``` + +Crie o arquivo `dub.json` na raiz do projeto: + +```json [dub.json] +{ + "name": "my-d-plugin", + "description": "Um plugin mínimo para o Pumpkin feito em D.", + "license": "proprietary", + "authors": ["SeuNome"], + "copyright": "Copyright © 2026, SeuNome", + + "dependencies": { + "pumpkin-api-d": "~>0.1.0" + }, + + "targetType": "executable", + "dflags": ["-Xcc=-mexec-model=reactor"], + "buildTypes": { + "debug": { + "buildOptions": ["debugMode", "debugInfo"] + }, + "release": { + "buildOptions": ["optimize", "inline"], + "dflags": ["-L-S"] + } + } +} +``` + +> [!NOTE] +> A flag `-Xcc=-mexec-model=reactor` é exigida pelo linker do WebAssembly para que o plugin seja tratado como um reator (um componente que expõe pontos de entrada) em vez de um programa executável com ponto de saída imediato. + +--- + +## 2. Escrevendo o Plugin + +Crie o diretório `source` e adicione o seu código em `source/app.d`: + +```d [source/app.d] +module plugin; + +import wit.pumpkin.plugin.plugin; +import wit.common; +import pumpkin.register; + +struct MyPlugin { + @GetMetadata PluginMetadata getMetadata() + { + immutable(WitString)[$] authors = [ + "SeuNome".witList + ]; + + return (immutable PluginMetadata( + name: "my-d-plugin".witList, + version_: "0.1.0".witList, + authors: authors.witList, + description: "Um exemplo de plugin em D.".witList, + dependencies: WitList!WitString(), + permissions: WitList!WitString() + )).witClone; + } + + @OnLoad + Result!(void, WitString) onLoad(Context ctx) + { + scope (exit) + ctx.witDrop; // Recursos de entrada devem ser liberados após o uso + + log(Level.info, "Olá do plugin em D!".witList); + return ok!WitString; + } +} + +mixin RegisterPlugin!MyPlugin; +``` + +--- + +## 3. Compilando o Plugin + +Compile o seu plugin como um componente WebAssembly com o alvo `wasm32-wasip2`: + +```bash +dub build -b release -a wasm32-wasip2 +``` + +Isso gerará o arquivo `my-d-plugin.wasm` na raiz do seu projeto. + +--- + +## 4. Executando o Plugin + +1. Copie o arquivo `.wasm` gerado para a pasta `plugins/` do seu servidor Pumpkin: + ```bash + cp my-d-plugin.wasm /caminho/para/pumpkin/plugins/ + ``` +2. Inicie ou reinicie o servidor Pumpkin: + ```bash + ./pumpkin + ``` +3. Verifique os logs do servidor para confirmar o carregamento: + ```text + [INFO] Olá do plugin em D! + ``` diff --git a/docs/pt/plugin-dev/go/basic-logic.md b/docs/pt/plugin-dev/go/basic-logic.md new file mode 100644 index 0000000..7e0e4a6 --- /dev/null +++ b/docs/pt/plugin-dev/go/basic-logic.md @@ -0,0 +1,60 @@ +# Writing the basic logic + +## Plugin Entry Point + +In Go, your plugin must implement the `api.Plugin` interface and register itself in the `init()` function. You also need to import `wit_exports` for WebAssembly compatibility. + +:::code-group + +```go [main.go] +package main + +import ( + "github.com/Pumpkin-MC/pumpkin-api-go/api" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_context" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_logging" + _ "github.com/Pumpkin-MC/pumpkin-api-go/pkg/wit_exports" // Required for WASM exports +) + +type MyPlugin struct { + api.DefaultPlugin +} + +func (p *MyPlugin) Metadata() api.Metadata { + return api.Metadata{ + Name: "my-go-plugin", + Version: "0.1.0", + Authors: []string{"you"}, + } +} + +func (p *MyPlugin) OnLoad(ctx *pumpkin_plugin_context.Context) { + pumpkin_plugin_logging.Log(pumpkin_plugin_logging.LevelInfo(), "Go plugin loaded!") +} + +func init() { + api.RegisterPlugin(&MyPlugin{}) +} + +func main() {} +``` + +::: + +## Compiling the plugin + +To compile your plugin to WebAssembly, you must use **TinyGo**. The standard Go compiler does not yet support the specific WASI targets required by Pumpkin in the same way. + +Run the following command in your project folder: + +```bash +tinygo build -o my_plugin.wasm -target=wasi main.go +``` + +This will generate a `my_plugin.wasm` file that can be loaded by Pumpkin. + +## Testing the plugin + +Installing a plugin is as simple as putting the plugin binary (`.wasm`) into the `plugins/` folder of your Pumpkin server! + +When you start up the server and run the `/plugins` command, you should see your plugin listed. diff --git a/docs/pt/plugin-dev/go/first-command.md b/docs/pt/plugin-dev/go/first-command.md new file mode 100644 index 0000000..f20ffdd --- /dev/null +++ b/docs/pt/plugin-dev/go/first-command.md @@ -0,0 +1,103 @@ +# Creating Your First Command + +Registering commands in Go with Pumpkin is built on standard Go idiom using TinyGo WASM compilation. + +--- + +## 1. Quick Example + +Here is a complete Go plugin file (`main.go`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```go [main.go] +package main + +import ( + "github.com/Pumpkin-MC/pumpkin-api-go/api" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_command" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_context" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_permission" + _ "github.com/Pumpkin-MC/pumpkin-api-go/pkg/wit_exports" +) + +type MyPlugin struct { + api.DefaultPlugin +} + +func (p *MyPlugin) Metadata() api.Metadata { + return api.Metadata{ + Name: "my_go_plugin", + Version: "0.1.0", + Authors: []string{"Developer"}, + Description: "My first Pumpkin Go plugin", + } +} + +func (p *MyPlugin) OnLoad(ctx *pumpkin_plugin_context.Context) { + // 1. Register permission node + ctx.RegisterPermission(pumpkin_plugin_permission.Permission{ + Node: "my_go_plugin:hello", + Description: "Allows executing the /hello command", + Default: pumpkin_plugin_permission.PermissionDefaultAllow(), + }) + + // 2. Build and register command tree + cmd := pumpkin_plugin_command.New([]string{"hello", "hi"}, "Greets the player") + ctx.RegisterCommand(cmd, "my_go_plugin:hello") +} + +func (p *MyPlugin) HandleCommand(cmdID uint32, sender pumpkin_plugin_command.CommandSender) int32 { + sender.SendMessage("Hello from Go Plugin!") + return 1 +} + +func init() { + api.RegisterPlugin(&MyPlugin{}) +} + +func main() {} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Register Permission +Call `ctx.RegisterPermission` inside `OnLoad`: +```go +ctx.RegisterPermission(pumpkin_plugin_permission.Permission{ + Node: "my_go_plugin:hello", + Description: "Allows executing the /hello command", + Default: pumpkin_plugin_permission.PermissionDefaultAllow(), +}) +``` + +### Step 2: Build the Command Tree +Instantiate the command tree with `pumpkin_plugin_command.New`: +```go +cmd := pumpkin_plugin_command.New([]string{"hello", "hi"}, "Greets the player") +``` + +### Step 3: Register & Execute +Register the command with `ctx.RegisterCommand` and handle execution in `HandleCommand`: +```go +ctx.RegisterCommand(cmd, "my_go_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Follow the [Go Quick Start Guide](./quick-start) to set up TinyGo compilation. +- Learn more about [Basic Logic](./basic-logic). diff --git a/docs/pt/plugin-dev/go/quick-start.md b/docs/pt/plugin-dev/go/quick-start.md new file mode 100644 index 0000000..90bacb4 --- /dev/null +++ b/docs/pt/plugin-dev/go/quick-start.md @@ -0,0 +1,35 @@ +# Creating a new project + +Pumpkin plugins use the [Go](https://go.dev/) programming language and are compiled to WebAssembly. + +## Prerequisites + +Before we can start, make sure you have the following installed: +- [Go](https://go.dev/doc/install) (latest version recommended) +- [TinyGo](https://tinygo.org/getting-started/install/) (required for WASM compilation) + +## Initializing a new module + +First, create a new directory for your project and initialize a Go module: + +```bash +mkdir hello-pumpkin +cd hello-pumpkin +go mod init github.com/yourname/hello-pumpkin +``` + +Next, add the Pumpkin Go API as a dependency: + +```bash +go get github.com/Pumpkin-MC/pumpkin-api-go +``` + +## Project Structure + +Your project should have at least a `main.go` file. The structure should look like this: + +```text +├── go.mod +├── go.sum +└── main.go +``` diff --git a/docs/pt/plugin-dev/introduction.md b/docs/pt/plugin-dev/introduction.md index 6a3c547..5935b9b 100644 --- a/docs/pt/plugin-dev/introduction.md +++ b/docs/pt/plugin-dev/introduction.md @@ -16,3 +16,5 @@ A API de Plugins Pumpkin se inspira na API de plugins do Spigot/Bukkit em divers * [C# API](https://github.com/Pumpkin-MC/pumpkin-api-cs) * [Go API](https://github.com/Pumpkin-MC/pumpkin-api-go) * [C API](https://github.com/Pumpkin-MC/pumpkin-api-c) +* [Kotlin API](https://github.com/Pumpkin-MC/pumpkin-api-kt) +* [D API](https://github.com/Pumpkin-MC/pumpkin-api-d) diff --git a/docs/pt/plugin-dev/kotlin/first-command.md b/docs/pt/plugin-dev/kotlin/first-command.md new file mode 100644 index 0000000..12831f5 --- /dev/null +++ b/docs/pt/plugin-dev/kotlin/first-command.md @@ -0,0 +1,94 @@ +# Creating Your First Command + +Registering custom commands in Pumpkin using Kotlin is concise, idiomatic, and strongly typed. + +--- + +## 1. Quick Example + +Here is a complete Kotlin plugin (`Plugin.kt`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```kotlin [src/wasmWasiMain/kotlin/plugin/Plugin.kt] +package plugin + +import pumpkin.plugin.context.Context +import pumpkin.plugin.command.Command +import pumpkin.plugin.permission.Permission +import pumpkin.plugin.permission.PermissionDefault + +class MyPlugin { + fun onLoad(ctx: Context) { + // 1. Register permission node + ctx.registerPermission( + Permission( + node = "my_kotlin_plugin:hello", + description = "Allows executing the /hello command", + default = PermissionDefault.ALLOW, + children = emptyList() + ) + ) + + // 2. Build and register command tree + val cmd = Command( + names = listOf("hello", "hi"), + description = "Greets the player" + ) + ctx.registerCommand(cmd, "my_kotlin_plugin:hello") + } + + fun handleCommand(sender: pumpkin.plugin.command.CommandSender): Int { + sender.sendMessage("Hello from Kotlin Plugin!") + return 1 + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Node +Construct a `Permission` object and pass it to `ctx.registerPermission`: +```kotlin +ctx.registerPermission( + Permission( + node = "my_kotlin_plugin:hello", + description = "Allows executing the /hello command", + default = PermissionDefault.ALLOW, + children = emptyList() + ) +) +``` + +### Step 2: Build the Command Tree +Instantiate `Command` with primary names and aliases: +```kotlin +val cmd = Command( + names = listOf("hello", "hi"), + description = "Greets the player" +) +``` + +### Step 3: Register with Context +Register the command with `ctx.registerCommand` and handle calls inside `handleCommand`: +```kotlin +ctx.registerCommand(cmd, "my_kotlin_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Follow the [Kotlin Quick Start Guide](./quick-start) to configure your `Makefile` and Kotlin/Wasm build tools. diff --git a/docs/pt/plugin-dev/kotlin/quick-start.md b/docs/pt/plugin-dev/kotlin/quick-start.md new file mode 100644 index 0000000..acefd9c --- /dev/null +++ b/docs/pt/plugin-dev/kotlin/quick-start.md @@ -0,0 +1,81 @@ +# Quick Start + +This guide will help you get started with writing Pumpkin server plugins using Kotlin. + +:::warning +Until the Kotlin + Wasm component toolchain matures more, there will be some oddity and inconvenience. + +Bugs are also expected. +::: + +## Prerequisites + +Before you start, ensure you have the following installed: +- JDK 17 or later + - To run Gradle 9.4 +- [Rust](https://rust-lang.org/) + - This is required as a key component (wit-bindgen) is written and Rust must be built from a particular Kotlin-enabled fork. + - You only need a default Rust install for your host platform. NOT for any WebAssembly targets +- [wasm-tools](https://github.com/bytecodealliance/wasm-tools) + - To bundle the Wasm Kotlin produces into a component +- Make (e.g. [GNU Make](https://www.gnu.org/software/make/)) + - To run the convenience `Makefile`. You may opt to do without and perform the contained steps manually. + +## Setting up the project + +Unlike most of the other APIs packages available, [pumpkin-api-kt](https://github.com/Pumpkin-MC/pumpkin-api-kt) is a TEMPLATE, not e.g. a Maven package. + +To start off, clone the template (and rename it as you please): +```sh +git clone --recurse-submodules https://github.com/Pumpkin-MC/pumpkin-api-kt +mv pumpkin-api-kt my_kotlin_plugin +cd my_kotlin_plugin +``` + +Next, we'll want to update the `wit` submodule so we are using the latest version of the Pumpkin plugin API. + +```sh +cd wit +git pull origin master +cd .. +``` + +Finally, rename the Gradle project. Change the `rootProject.name` in `settings.gradle.kts`, AND the `PROJECT_NAME` in `Makefile`. They must both match. Whatever you name the project, is going to be the filename of the Wasm produced. + +## Creating your first plugin + +As part of the template, a basic plugin is implemented in `src/wasmWasiMain/kotlin/plugin/Plugin.kt`. + +Feel free to modify the metadata at the bottom. However, before changing anything else, it's recommended that you build the plugin (see next section) first so that the bindings are generated and IDE completion works for `pumpkin`. + +## Building the plugin + +To build your plugin into a WebAssembly component: + +```sh +make +``` + +The compiled `.wasm` file will be located in `build`. You can place this file in the `plugins` folder of your Pumpkin server. + +Note that doing this the first time might take awhile, while it builds `wit-bindgen` from Rust source. + +Running `make` will check for `wit-bindgen` updates each time. You may want to run instead `make componentify` after the initial setup to avoid this. + +## Troubleshooting + +### Linker errors +If you start getting errors like +``` +main ThreadId(01) pumpkin::plugin: Failed to load plugin from +"./plugins/my_plugin.wasm": Wasm plugin initialization error: plugin failed +to load with error: component imports instance 'pumpkin:plugin/gui@0.1.0', but +a matching implementation was not found in the linker +``` +or other "linker" errors when loading your plugin into Pumpkin, update the `wit` submodule. + +```sh +cd wit +git pull origin master +cd .. +``` diff --git a/docs/pt/plugin-dev/migrating-from-bukkit/commands.md b/docs/pt/plugin-dev/migrating-from-bukkit/commands.md new file mode 100644 index 0000000..7b2d17a --- /dev/null +++ b/docs/pt/plugin-dev/migrating-from-bukkit/commands.md @@ -0,0 +1,171 @@ +# Migrando Comandos do Bukkit + +No Bukkit, os comandos são definidos no `plugin.yml` e manipulados através de classes `CommandExecutor` ou da biblioteca Brigadier. + +No Pumpkin, os comandos são construídos usando uma **árvore de comandos inspirada no Brigadier** e registrados programaticamente via `context.register_command`. Nenhum arquivo de configuração YAML é utilizado. + +--- + +## Principais Diferenças + +| Recurso | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Declaração** | Declarado no `plugin.yml` sob `commands:` | Construído no código usando `Command::new(...)` | +| **Permissões** | Vinculado no `plugin.yml` ou verificado via `player.hasPermission()` | Nó de permissão explícito vinculado durante o `register_command()` | +| **Autocompletar (Tab Completion)** | Comparação manual de strings no `TabCompleter` | Autocompletar automático no lado do cliente baseado na árvore | +| **Subcomandos** | `if (args[0].equalsIgnoreCase("..."))` manual | Nós de árvore `Command` aninhados | + +--- + +## Comparação de Código: Comando Simples + +Compare o registro de um comando `/feed` no Bukkit vs. Pumpkin: + +### 1. Implementação no Bukkit (Java) + +```java [FeedCommand.java] +// 1. Registered in plugin.yml: +// commands: +// feed: +// description: Feeds the player +// permission: myplugin.feed + +public class FeedCommand implements CommandExecutor { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("Only players can execute this command!"); + return true; + } + Player player = (Player) sender; + if (!player.hasPermission("myplugin.feed")) { + player.sendMessage("No permission!"); + return true; + } + player.setFoodLevel(20); + player.sendMessage("Your hunger has been satisfied!"); + return true; + } +} +``` + +### 2. Implementações no Pumpkin + +::: code-group + +```rust [Rust] +use pumpkin_plugin_api::{ + command::{CommandHandler, CommandSender, ConsumedArgs, CommandError, Command}, + permission::{Permission, PermissionDefault}, + Context, Plugin, PluginMetadata, Server, +}; + +struct FeedExecutor; + +impl CommandHandler for FeedExecutor { + fn handle(&self, sender: CommandSender, _server: Server, _args: ConsumedArgs) -> Result { + sender.send_message("Your hunger has been satisfied!"); + Ok(1) + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { MyPlugin } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "feed_plugin".into(), + version: "1.0.0".into(), + authors: vec!["Developer".into()], + description: "Feed command plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Register permission node (Replaces plugin.yml permission) + context.register_permission(&Permission { + node: "feed_plugin:feed".to_string(), + description: "Allows executing /feed".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), + })?; + + // 2. Build command tree + let command = Command::new(&["feed".to_string(), "eat".to_string()], "Feeds the player") + .execute(FeedExecutor); + + // 3. Register with context + context.register_command(command, "feed_plugin:feed")?; + + Ok(()) + } +} +``` + +```python [Python] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + permission, command, context, server +) + +class FeedExecutor: + def handle(self, sender: command.CommandSender, srv: server.Server, args: command.ConsumedArgs) -> int: + sender.send_message("Your hunger has been satisfied!") + return 1 + +class FeedPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata(name="feed_plugin", version="1.0.0", authors=["Dev"], description="Feed command") + + def on_load(self, ctx: context.Context) -> None: + ctx.register_permission(permission.Permission( + node="feed_plugin:feed", + description="Allows executing /feed", + default=permission.PermissionDefault.ALLOW, + children=[] + )) + + cmd = command.Command(["feed", "eat"], "Feeds the player") + cmd.execute(FeedExecutor()) + ctx.register_command(cmd, "feed_plugin:feed") + +register_plugin(FeedPlugin) +``` + +```kotlin [Kotlin] +package plugin + +import pumpkin.plugin.context.Context +import pumpkin.plugin.command.Command +import pumpkin.plugin.command.CommandSender +import pumpkin.plugin.permission.Permission +import pumpkin.plugin.permission.PermissionDefault + +class FeedPlugin { + fun onLoad(ctx: Context) { + ctx.registerPermission( + Permission( + node = "feed_plugin:feed", + description = "Allows executing /feed", + default = PermissionDefault.ALLOW, + children = emptyList() + ) + ) + + val cmd = Command( + names = listOf("feed", "eat"), + description = "Feeds the player" + ) + ctx.registerCommand(cmd, "feed_plugin:feed") + } + + fun handleCommand(sender: CommandSender): Int { + sender.sendMessage("Your hunger has been satisfied!") + return 1 + } +} +``` + +::: diff --git a/docs/pt/plugin-dev/migrating-from-bukkit/configuration.md b/docs/pt/plugin-dev/migrating-from-bukkit/configuration.md new file mode 100644 index 0000000..f3468cd --- /dev/null +++ b/docs/pt/plugin-dev/migrating-from-bukkit/configuration.md @@ -0,0 +1,69 @@ +# Migrando Configuração e Armazenamento de Dados + +Plugins Bukkit dependem fortemente de `config.yml`, `YamlConfiguration` e do sistema de serialização de configuração do Bukkit. + +No Pumpkin, plugins WebAssembly rodam em um ambiente de sandbox seguro onde arquivos de configuração podem ser manipulados nativamente usando formatos padrão como **TOML**, **JSON** ou **YAML**. + +--- + +## Principais Diferenças + +| Recurso | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Formato do Arquivo de Configuração** | `config.yml` (formato YAML) | Flexível: TOML, JSON ou arquivos personalizados | +| **Geração de Configuração Padrão** | `saveDefaultConfig()` | Leitura de configuração a partir do diretório do plugin ou recurso WASM integrado | +| **Análise (Parsing) de Dados** | `getConfig().getString(...)` | Desserialização nativa com serde / JSON / TOML | +| **Sandbox de E/S de Arquivos (File I/O)** | Acesso direto ao sistema de arquivos em qualquer lugar | Acesso restrito em sandbox a diretórios concedido ao plugin WASM | + +--- + +## Comparação de Código: Carregando Configuração + +### 1. Implementação no Bukkit (Java) + +```java [ConfigExample.java] +public class ConfigExample extends JavaPlugin { + @Override + public void onEnable() { + saveDefaultConfig(); // Saves config.yml from jar resources + FileConfiguration config = getConfig(); + + String welcomeMsg = config.getString("welcome-message", "Default welcome!"); + int maxItems = config.getInt("max-items", 10); + getLogger().info("Loaded message: " + welcomeMsg); + } +} +``` + +### 2. Implementação no Pumpkin (Rust) + +No Pumpkin, você pode derivar `serde::Deserialize` para analisar arquivos de configuração TOML limpos: + +```rust [config.rs] +use serde::Deserialize; +use std::fs; + +#[derive(Deserialize, Debug)] +pub struct PluginConfig { + pub welcome_message: String, + pub max_items: i32, +} + +impl Default for PluginConfig { + fn default() -> Self { + Self { + welcome_message: "Default welcome!".to_string(), + max_items: 10, + } + } +} + +pub fn load_config() -> PluginConfig { + let config_path = "plugins/my_plugin/config.toml"; + if let Ok(content) = fs::read_to_string(config_path) { + toml::from_str(&content).unwrap_or_default() + } else { + PluginConfig::default() + } +} +``` diff --git a/docs/pt/plugin-dev/migrating-from-bukkit/events.md b/docs/pt/plugin-dev/migrating-from-bukkit/events.md new file mode 100644 index 0000000..e187a42 --- /dev/null +++ b/docs/pt/plugin-dev/migrating-from-bukkit/events.md @@ -0,0 +1,117 @@ +# Migrando Eventos do Bukkit + +No Bukkit, os eventos são manipulados criando classes que implementam `Listener` e anotando métodos com `@EventHandler`. + +No Pumpkin, manipuladores de eventos (event handlers) são structs ou objetos registrados explicitamente via `context.register_event_handler(...)`. O Pumpkin também diferencia entre eventos **Bloqueantes** (Blocking) e **Não Bloqueantes** (Non-blocking) para maximizar a concorrência do servidor. + +--- + +## Principais Diferenças + +| Recurso | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Declaração do Listener** | Anotação `@EventHandler` em métodos | Implementação de trait / interface `EventHandler` | +| **Registro** | `pm.registerEvents(listener, plugin)` | `context.register_event_handler(handler, priority, is_blocking)` | +| **Modelo de Execução** | Execução na thread principal (`EventPriority`) | Escolha entre **Bloqueante** (sequencial/cancelável) e **Não Bloqueante** (concorrente) | +| **Cancelamento** | `event.setCancelled(true)` | `event.cancel()` em eventos bloqueantes | + +--- + +## Eventos Bloqueantes vs. Não Bloqueantes no Pumpkin + +Ao contrário do Bukkit, onde todos os manipuladores de eventos são executados sequencialmente na thread principal do servidor, o Pumpkin permite especificar se um manipulador de eventos é **bloqueante**: + +```rust +// Assinatura de registro em Rust: +context.register_event_handler(handler, priority, is_blocking)?; +``` + +- **Bloqueante (`is_blocking = true`)**: Executa em ordem de prioridade sequencial. Pode modificar dados de eventos (por exemplo, editar mensagem de entrada) ou cancelar o evento. +- **Não Bloqueante (`is_blocking = false`)**: Executa concorrentemente através de worker threads. Ideal para logging, métricas ou notificações externas onde o cancelamento não é necessário. + +--- + +## Comparação de Código: Evento de Entrada de Jogador (Player Join) + +### 1. Implementação no Bukkit (Java) + +```java [JoinListener.java] +public class JoinListener implements Listener { + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + event.setJoinMessage("Welcome " + player.getName() + " to the server!"); + } +} + +// In JavaPlugin: +// getServer().getPluginManager().registerEvents(new JoinListener(), this); +``` + +### 2. Implementações no Pumpkin + +::: code-group + +```rust [Rust] +use pumpkin_plugin_api::{ + Context, Plugin, PluginMetadata, Server, + events::{EventData, EventHandler, EventPriority, PlayerJoinEvent}, + text::TextComponent, +}; + +struct JoinHandler; + +impl EventHandler for JoinHandler { + fn handle<'a>( + &'a self, + _server: Server, + mut event: EventData, + ) -> EventData { + event.join_message = TextComponent::text("Welcome to the Pumpkin server!"); + event + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { MyPlugin } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "join_plugin".into(), + version: "1.0.0".into(), + authors: vec!["Developer".into()], + description: "Join event handler".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Register blocking event handler with Normal priority + context.register_event_handler(JoinHandler, EventPriority::Normal, true)?; + Ok(()) + } +} +``` + +```python [Python] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + event, server, context +) + +class MyPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata(name="join_plugin", version="1.0.0", authors=["Dev"], description="Join event") + + def on_load(self, ctx: context.Context) -> None: + self.register_event(ctx, event.EventType.PLAYER_JOIN_EVENT, self.on_player_join) + + def on_player_join(self, srv: server.Server, evt: event.PlayerJoinEventData) -> event.PlayerJoinEventData: + print(f"Player joined!") + return evt + +register_plugin(MyPlugin) +``` + +::: diff --git a/docs/pt/plugin-dev/migrating-from-bukkit/index.md b/docs/pt/plugin-dev/migrating-from-bukkit/index.md new file mode 100644 index 0000000..ccdc883 --- /dev/null +++ b/docs/pt/plugin-dev/migrating-from-bukkit/index.md @@ -0,0 +1,27 @@ +# Visão Geral e Arquitetura de Migração + +Migrar do desenvolvimento de plugins para **Bukkit / Spigot / Paper** para o Pumpkin envolve uma mudança de um modelo orientado a objetos centrado em Java para um modelo de componentes compilado e multilíngue em **WebAssembly (WASM)**. + +--- + +A tabela abaixo compara o paradigma legado do Bukkit/Paper com a arquitetura moderna em WebAssembly do Pumpkin: + +| Conceito | Bukkit / Spigot / Paper | Pumpkin | +| :--- | :--- | :--- | +| **Suporte a Linguagens** | Java / Kotlin / Scala (JVM) | Rust, Python, Kotlin, C#, Go, C | +| **Saída Binária** | Arquivo Java `.jar` | Componente WebAssembly `.wasm` | +| **Descritor do Plugin** | Arquivo `plugin.yml` | Struct programática `PluginMetadata` | +| **Ganchos de Ciclo de Vida (Hooks)** | `onEnable()` / `onDisable()` | `on_load(context)` / `on_unload(context)` | +| **Segurança e Isolamento** | Reflexão (Reflection) irrestrita da JVM | Modelo de recursos (capabilities) WASM em sandbox | +| **Concorrência** | Loop de tick single-threaded (`BukkitScheduler`) | Execução nativa multithread com runtime assíncrono (Async) | + +--- + +## Tópicos Detalhados de Migração + +Explore guias dedicados sobre a migração de cada subsistema principal de plugins: + +- [Migrando Comandos](./commands) — Fazendo a transição de `getCommand().setExecutor()` e `plugin.yml` para árvores de comandos Brigadier. +- [Migrando Eventos](./events) — Substituindo as interfaces `@EventHandler` e `Listener` pelo sistema de eventos bloqueantes vs. não bloqueantes do Pumpkin. +- [Migrando Inventários e GUIs](./inventories) — Mudando de `Bukkit.createInventory()` para manipuladores de contêineres e janelas do Pumpkin. +- [Migrando Configuração e Dados](./configuration) — Substituindo `getConfig()` / `config.yml` por armazenamento nativo em TOML, JSON ou personalizado. diff --git a/docs/pt/plugin-dev/migrating-from-bukkit/inventories.md b/docs/pt/plugin-dev/migrating-from-bukkit/inventories.md new file mode 100644 index 0000000..0f67bb6 --- /dev/null +++ b/docs/pt/plugin-dev/migrating-from-bukkit/inventories.md @@ -0,0 +1,72 @@ +# Migrando Inventários e GUIs do Bukkit + +Criar GUIs de inventário (como menus de baú, interfaces de loja ou janelas personalizadas de jogador) é uma tarefa comum no desenvolvimento de plugins para Minecraft. + +No Bukkit, GUIs personalizadas são criadas usando `Bukkit.createInventory()` e manipuladas via `InventoryClickEvent`. No Pumpkin, janelas de inventário são gerenciadas através das **Window APIs** e de pacotes de clique em contêineres. + +--- + +## Principais Diferenças + +| Recurso | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Criação de Inventário** | `Bukkit.createInventory(owner, size, title)` | Definições de contêiner de janela (`WindowType`) | +| **Abertura de Janelas** | `player.openInventory(inv)` | `player.open_window(window)` | +| **Interceptação de Cliques** | Manipulação via `InventoryClickEvent` | Handlers de callback de clique na janela | +| **Empilhamento de Itens** | `ItemStack` com `ItemMeta` | `ItemStack` com componentes / NBT | + +--- + +## Comparação de Código: Abrindo uma GUI de Menu Personalizada + +### 1. Implementação no Bukkit (Java) + +```java [MenuGUI.java] +public class MenuGUI implements Listener { + + public void openMenu(Player player) { + Inventory inv = Bukkit.createInventory(null, 9, Component.text("Custom Menu")); + + ItemStack item = new ItemStack(Material.DIAMOND); + ItemMeta meta = item.getItemMeta(); + meta.displayName(Component.text("Click Me!")); + item.setItemMeta(meta); + + inv.setItem(4, item); + player.openInventory(inv); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (event.getView().getTitle().equals("Custom Menu")) { + event.setCancelled(true); + if (event.getSlot() == 4) { + event.getWhoClicked().sendMessage("Diamond clicked!"); + } + } + } +} +``` + +### 2. Implementação no Pumpkin (Rust) + +```rust [gui.rs] +use pumpkin_plugin_api::{ + player::Player, + item::ItemStack, + window::{Window, WindowType}, + text::TextComponent, +}; + +pub fn open_custom_menu(player: &Player) { + // 1. Create a 9-slot chest window + let mut window = Window::new(WindowType::Generic9x1, TextComponent::text("Custom Menu")); + + // 2. Set item in slot 4 + let item = ItemStack::new("minecraft:diamond", 1); + window.set_item(4, Some(item)); + + // 3. Open window for player + player.open_window(window); +} +``` diff --git a/docs/pt/plugin-dev/python/first-command.md b/docs/pt/plugin-dev/python/first-command.md new file mode 100644 index 0000000..00058f4 --- /dev/null +++ b/docs/pt/plugin-dev/python/first-command.md @@ -0,0 +1,95 @@ +# Creating Your First Command + +Registering and handling custom commands in Pumpkin with Python is designed to be straightforward and expressive. + +--- + +## 1. Quick Example + +Here is a complete, minimal Python plugin (`main.py`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```python [main.py] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + permission, command, context, server +) + +class HelloCommand: + def handle(self, sender: command.CommandSender, srv: server.Server, args: command.ConsumedArgs) -> int: + sender.send_message("Hello from Python Plugin!") + return 1 + +class MyPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata( + name="my_python_plugin", + version="0.1.0", + authors=["Developer"], + description="My first Pumpkin Python plugin" + ) + + def on_load(self, ctx: context.Context) -> None: + # 1. Register permission node + ctx.register_permission(permission.Permission( + node="my_python_plugin:hello", + description="Allows executing the /hello command", + default=permission.PermissionDefault.ALLOW, + children=[] + )) + + # 2. Build the command tree with aliases & executor + cmd = command.Command(["hello", "hi"], "Greets the player") + cmd.execute(HelloCommand()) + + # 3. Register the command + ctx.register_command(cmd, "my_python_plugin:hello") + +register_plugin(MyPlugin) +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission +Commands in Pumpkin require a permission node. Register it with `permission.PermissionDefault.ALLOW` to allow execution by default: +```python +ctx.register_permission(permission.Permission( + node="my_python_plugin:hello", + description="Allows executing the /hello command", + default=permission.PermissionDefault.ALLOW, + children=[] +)) +``` + +### Step 2: Build the Command Tree +Create a `command.Command` instance with primary names and aliases, then attach your handler with `.execute(...)`: +```python +cmd = command.Command(["hello", "hi"], "Greets the player") +cmd.execute(HelloCommand()) +``` + +### Step 3: Register with Context +Pass the command tree and permission node string to `ctx.register_command`: +```python +ctx.register_command(cmd, "my_python_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Check out [Events](./events) to handle player actions and server events in Python. +- Learn more about [Basic Logic](./basic-logic). diff --git a/docs/pt/plugin-dev/rust/command/first-command.md b/docs/pt/plugin-dev/rust/command/first-command.md new file mode 100644 index 0000000..9840346 --- /dev/null +++ b/docs/pt/plugin-dev/rust/command/first-command.md @@ -0,0 +1,115 @@ +# Creating Your First Command + +Registering and handling custom commands in Pumpkin is designed to be developer-friendly, fast, and structured like Mojang's Brigadier system. + +--- + +## 1. Quick Example + +Here is a complete, minimal Rust plugin that registers a `/hello` command with permission checks and sends a response back to the player. + +```rust [src/lib.rs] +use pumpkin_plugin_api::{ + command::{CommandHandler, CommandSender, ConsumedArgs, CommandError, Command}, + permission::{Permission, PermissionDefault}, + Context, Plugin, PluginMetadata, Server, +}; + +struct HelloExecutor; + +impl CommandHandler for HelloExecutor { + fn handle( + &self, + sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + sender.send_message("Hello from Pumpkin!"); + Ok(1) + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { + MyPlugin + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "my_plugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Developer".into()], + description: "My first Pumpkin plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Register permission node + context.register_permission(&Permission { + node: "my_plugin:hello".to_string(), + description: "Allows executing the /hello command".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), + })?; + + // 2. Build the command tree with aliases & executor + let names = ["hello".to_string(), "hi".to_string()]; + let command = Command::new(&names, "Greets the player").execute(HelloExecutor); + + // 3. Register the command + context.register_command(command, "my_plugin:hello")?; + + Ok(()) + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission +Commands in Pumpkin require a permission node. Using `PermissionDefault::Allow` allows all players to execute it by default: +```rust +context.register_permission(&Permission { + node: "my_plugin:hello".to_string(), + description: "Allows executing the /hello command".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), +})?; +``` + +### Step 2: Build the Command Tree +Use `Command::new` to define primary names and aliases, then attach your `CommandHandler` struct with `.execute(...)`: +```rust +let names = ["hello".to_string(), "hi".to_string()]; +let command = Command::new(&names, "Greets the player") + .execute(HelloExecutor); +``` + +### Step 3: Register with Context +Pass the command tree and the permission node string to `context.register_command`: +```rust +context.register_command(command, "my_plugin:hello")?; +``` + +::: + +--- + +## Next Steps + +- Explore complex subcommands and argument parsing in the [Rock-Paper-Scissors Tutorial](./rock-paper-scissors). +- Learn how to handle inventory and GUI interactions in plugin logic. diff --git a/docs/pt/plugin-dev/rust/command/rock-paper-scissors.md b/docs/pt/plugin-dev/rust/command/rock-paper-scissors.md new file mode 100644 index 0000000..c1f188b --- /dev/null +++ b/docs/pt/plugin-dev/rust/command/rock-paper-scissors.md @@ -0,0 +1,255 @@ +# Writing a Command Handler + +In Minecraft, commands are the primary way for both regular players and server operators to +interact with the game on a lower level. They can be used to perform a wide range of tasks, +from simple chat messages to complex server management commands. In this tutorial, we will +create a basic Rock-Paper-Scissors command handler that allows players to play the game +against the server. + +Pumpkin has its own system for handling commands, which is based around each command having a +'tree', which defines the exact structure of the command and its arguments. Each node in the +tree represents a command or argument, and the tree is traversed to determine the command to +execute and its parameters. + +We would also like to thank [ploxxxy](https://github.com/ploxxxy) for writing the original +[Rock-Paper-Scissors plugin](https://github.com/ploxxxy/rock-paper-scissors-mc) from which +this tutorial is based. + +## Adding the basics + +Each command in Pumpkin is defined as a structure that implements the `CommandHandler` trait. +This trait requires the implementation of a `handle` method, which takes the sender, server, +and consumed arguments as parameters, and returns a `-> Result<(), CommandError>`. Let's +define this structure now: + +```rust +use pumpkin_plugin_api::{ + Server, + command::{CommandError, CommandSender, ConsumedArgs}, + commands::CommandHandler, +}; + +struct RockPaperScissorsExecutor; +impl CommandHandler for RockPaperScissorsExecutor { + fn handle( + &self, + _sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + Ok(1) + } +} +``` + +This code defines an empty structure `RockPaperScissorsExecutor` that implements the +`CommandExecutor` trait. The `handler` method is defined to return `Ok(1)` when called. + +## Adding helper enums + +First install the `rand` crate via + +```bash +cargo add rand +``` + +To make our lives easier, we will also define a couple of enums to represent the possible +choices and outcomes of the game, as well as well as a couple functions to generate random +choices and check the outcome. Add these to your plugin's code. + +```rust +use rand::{rng, Rng}; + +#[derive(PartialEq, Debug, Clone, Copy)] +enum Choice { + Rock, + Paper, + Scissors, +} + +enum Outcome { + Win, + Lose, + Draw, +} + +impl Choice { + pub fn beats(&self, other: &Choice) -> Outcome { + if self == other { + return Outcome::Draw; + } + + match (self, other) { + (Choice::Rock, Choice::Scissors) => Outcome::Win, + (Choice::Paper, Choice::Rock) => Outcome::Win, + (Choice::Scissors, Choice::Paper) => Outcome::Win, + _ => Outcome::Lose, + } + } +} + +fn get_random_choice() -> Choice { + let choices = [Choice::Rock, Choice::Paper, Choice::Scissors]; + let index = rng().random_range(0..3); + choices[index] +} +``` + +Now we need to modify the `RockPaperScissorsExecutor` struct to accept a `Choice` parameter +and implement the game logic. + +```rust +struct RockPaperScissorsExecutor(Choice); // [!code ++] +struct RockPaperScissorsExecutor; // [!code --] + +impl CommandHandler for RockPaperScissorsExecutor { + fn handle( + &self, + sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + let player_choice = self.0; // [!code ++:3] + let computer_choice = get_random_choice(); + Ok(1) + } +} +``` + +This code will allow us to later pass in the player's choice and use it in the game logic, +as well as compare it with the computer's choice to determine the outcome of the game. + +## Implementing the Game Logic + +Now we can move on to actually implementing the game logic, and showing the outcome to the +players. + +First we will show the player their and the computer's choice. Add this code to your plugin: + +```rust +impl CommandHandler for RockPaperScissorsExecutor { + fn handle( + &self, + sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + let player_choice = self.0; + let computer_choice = get_random_choice(); + + // [!code ++:9] + let you_chose = TextComponent::text("You chose: "); + you_chose.add_child(TextComponent::text(&format!("{:?}", player_choice))); + you_chose.color_named(NamedColor::Aqua); + sender.send_message(you_chose); + + let i_chose = TextComponent::text("I chose: "); + i_chose.add_child(TextComponent::text(&format!("{:?}", computer_choice))); + i_chose.color_named(NamedColor::Gold); + sender.send_message(i_chose); + } +} +``` + +Next we can compute the game outcome and show it to the player. Add this code to your plugin: + +```rust +impl CommandHandler for RockPaperScissorsExecutor { + fn handle( + &self, + sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + // Existing Code + + match player_choice.beats(&computer_choice) { // [!code ++:17] + Outcome::Win => { + let message = TextComponent::text("You win!"); + message.color_named(NamedColor::Green); + sender.send_message(message); + } + Outcome::Lose => { + let message = TextComponent::text("You lose!"); + message.color_named(NamedColor::Red); + sender.send_message(message); + } + Outcome::Draw => { + let message = TextComponent::text("It's a tie!"); + message.color_named(NamedColor::Yellow); + sender.send_message(message); + } + } + + Ok(1) + } +} +``` + +And that's it! The core logic is done. Now we only have one last thing to do. + +## Building and registering a command tree + +As stated earlier, we need to build a command tree and register it with the server. This will +allow players to execute our plugin's commands. + +Building a command tree isn't very hard, but you have to know the exact structure of the command +and its arguments. In this case, we have a command named `rock-paper-scissors`, which will take +one required argument (the player's choice). + +The command tree is initialized using the `Command::new()` function. This function takes two +arguments: a list of names, where the first is the main command name, and the others are aliases +for the command; and a command description which is used to describe the command in the help menu. +We can then add 'branches' to the tree using the `.then()` method. This method accepts a 'leaf' +which can be built with the `CommandNode::literal()`, `CommandNode::argument()`, or +`CommandNode::require()` functions. + +For the rock-paper-scissors command, we'll create 3 separate branches, each with a `CommandNode::literal()` +leaf node for the player's choice. We will also register the command tree with the server and +with a `permission` of `hello-pumpkin:command.rockpaperscisors` which will allow anyone with that +permission to execute the command. Add the following code to your `on_load()` method: + +```rust +struct HelloPlugin; +impl Plugin for HelloPlugin { + fn new() -> Self { + HelloPlugin + } + + fn metadata(&self) -> PluginMetadata { + // Existing Code + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Existing Code + + let command = Command::new( // [!code ++:20] + &["rps".to_string(), "rockpaperscissors".to_string()], + "Play Rock Paper Scissors with the server.", + ); + command.then(CommandNode::literal("rock").execute(RockPaperScissorsExecutor(Choice::Rock))); + command + .then(CommandNode::literal("paper").execute(RockPaperScissorsExecutor(Choice::Paper))); + command.then( + CommandNode::literal("scissors").execute(RockPaperScissorsExecutor(Choice::Scissors)), + ); + + let permission = Permission { + node: "hello-pumpkin:command.rockpaperscisors".to_string(), + description: "Allows the player to play rock paper scisors".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), + }; + + context.register_permission(&permission)?; + context.register_command(command, "hello-pumpkin:command.rockpaperscisors"); + Ok(()) + } +} +``` + +And that's it! If you compile the plugin, you can test it by running the following command: + +```bash +/rps rock +``` diff --git a/docs/pt/plugin-dev/rust/join-event.md b/docs/pt/plugin-dev/rust/events.md similarity index 100% rename from docs/pt/plugin-dev/rust/join-event.md rename to docs/pt/plugin-dev/rust/events.md diff --git a/docs/pt/plugin-dev/rust/introduction.md b/docs/pt/plugin-dev/rust/introduction.md deleted file mode 100644 index 46376fd..0000000 --- a/docs/pt/plugin-dev/rust/introduction.md +++ /dev/null @@ -1,11 +0,0 @@ -# Escrevendo um Plugin de Exemplo - -Este tutorial irá guiá-lo na criação de um novo plugin. - -Este tutorial assume que você tem alguma experiência com Rust e linha de comando. - -Tópicos descritos neste tutorial: - -- Modificando a mensagem de entrada -- Criando um comando de pedra-papel-tesoura (em breve) -- Salvando dados do plugin em diretório (em breve) diff --git a/docs/pt/plugin-dev/rust/plugin-utils.md b/docs/pt/plugin-dev/rust/plugin-utils.md new file mode 100644 index 0000000..3adfbbe --- /dev/null +++ b/docs/pt/plugin-dev/rust/plugin-utils.md @@ -0,0 +1,348 @@ +# Licensing & Updates (`pumpkin-plugin-utils`) + +`pumpkin-plugin-utils` is an official utility crate for Pumpkin Rust plugins. It provides built-in tools for **licensing checks**, **cryptographic signature verification**, and **update checks** against the [Pumpkin Marketplace](https://market.pumpkinmc.org). + +## Features + +- **Offline Ed25519 Signature Verification**: Verifies plugin integrity and embedded marketplace metadata in `< 1ms` on startup without blocking the server. +- **Global Metadata Caching**: Call `init(&context)` once in your plugin's `on_load` lifecycle method to cache verified metadata globally. +- **Online License Verification**: Query the marketplace `/api/v1/rest/check-license` endpoint with automatic 7-day lease caching (`license_lease.json`). +- **Offline Grace Period**: Resilient offline lease evaluation ensures your plugin keeps working during marketplace maintenance or network outages. +- **Zero-Argument Update Checking**: Easily check if newer plugin releases are available on the marketplace. +- **Dynamic Public Key Resolution**: Resolves the marketplace public key dynamically via host WIT imports or HTTPS fallback without hardcoding keys. + +--- + +## Adding the Dependency + +Add `pumpkin-plugin-utils` alongside `pumpkin-plugin-api` in your plugin's `Cargo.toml`: + +:::code-group + +```toml [Cargo.toml] +[package] +name = "my-plugin" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +pumpkin-plugin-api = "0.1.0-dev+26.2-26.45" +pumpkin-plugin-utils = "0.1.0-dev+26.2-26.45" +tracing = "0.1" +``` + +::: + +--- + +## Initialization + +Before performing license or update checks, initialize the utility crate inside your plugin's `on_load` method using the plugin's `Context`: + +```rust +use pumpkin_plugin_api::{Context, Plugin, PluginMetadata}; +use tracing::{info, warn}; + +struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { + Self + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "MyPlugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Author".into()], + description: "Plugin with license and update checks".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Initialize pumpkin-plugin-utils (verifies signature & caches metadata) + let metadata = pumpkin_plugin_utils::init(&context) + .map_err(|e| format!("Initialization failed: {e}"))?; + + info!( + "Loaded plugin '{}' v{} (Dev: {})", + metadata.plugin_name, metadata.version, metadata.dev_name + ); + + Ok(()) + } +} +``` + +::: info What happens during `init`? +1. Locates the plugin's `.wasm` binary in the server directory. +2. Extracts the `pumpkin.metadata` and `wasm_signature` custom sections. +3. Cryptographically verifies the Ed25519 signature over the WASM binary and metadata. +4. Caches the verified `PumpkinMetadata` globally for subsequent calls. +::: + +--- + +## Checking for Updates + +`pumpkin-plugin-utils` provides both zero-argument automatic checking and custom manual checking. + +### 1. Automatic Update Check (Recommended) + +Once initialized, call `pumpkin_plugin_utils::check_for_updates()` without arguments. It automatically uses the cached plugin name, current version, and marketplace endpoint: + +```rust +use tracing::{info, warn}; + +fn check_updates() { + match pumpkin_plugin_utils::check_for_updates() { + Ok(update) => { + if update.update_available { + info!( + "A new update is available: {}!", + update.latest_version.as_deref().unwrap_or("unknown") + ); + } else { + info!("Plugin is up to date."); + } + } + Err(err) => { + warn!("Failed to check for updates: {err}"); + } + } +} +``` + +### 2. Manual Update Check (`UpdateChecker`) + +If you want to manually specify the plugin name, version, or a custom marketplace URL, use `UpdateChecker`: + +```rust +use pumpkin_plugin_utils::UpdateChecker; + +let checker = UpdateChecker::new(); +let response = checker.check_for_updates( + "my-plugin", + "1.0.0", + "https://market.pumpkinmc.org", +)?; + +if response.update_available { + println!("Update found: {:?}", response.latest_version); +} +``` + +### Update Response Structure + +The update check returns a `CheckUpdateResponse`: + +```rust +pub struct CheckUpdateResponse { + /// Whether a newer stable release exists on the marketplace. + pub update_available: bool, + /// The latest stable version string, if available. + pub latest_version: Option, +} +``` + +--- + +## Checking and Verifying Licenses + +`pumpkin-plugin-utils` supports online verification against the marketplace API as well as offline lease management with configurable grace periods. + +### 1. Online License Check + +To verify a license against the marketplace (`GET /api/v1/rest/check-license`): + +```rust +// Pass `None` to use the embedded license key from metadata +let response = pumpkin_plugin_utils::check_license_online(None) + .map_err(|e| format!("License verification failed: {e}"))?; + +if response.valid { + info!("License is valid (status: {})", response.status); +} else { + warn!("License is invalid: {}", response.status); + return Err(format!("Invalid license status: {}", response.status).into()); +} +``` + +#### Providing a Custom License Key Override +If your plugin allows server owners to configure a license key in a `config.toml` file, pass `Some(&key)`: + +```rust +let config_key = "LIC-CUSTOM-KEY-12345"; +let response = pumpkin_plugin_utils::check_license_online(Some(config_key))?; +``` + +::: tip 7-Day Lease Caching +Successful online checks automatically save a verified lease file (`license_lease.json`) into the plugin's data folder with a 7-day expiration. +::: + +### 2. Evaluating License with Grace Period + +To keep paid plugins functional when the server is offline or the marketplace is unreachable, use `evaluate_license`: + +```rust +use pumpkin_plugin_utils::LicenseStatus; +use tracing::{info, warn}; + +// Specify grace period duration in days (e.g., 7 days) +let grace_period_days = 7; + +match pumpkin_plugin_utils::evaluate_license(grace_period_days) { + LicenseStatus::Valid(meta) => { + info!("License valid for buyer (User ID: {})", meta.user_id); + } + LicenseStatus::GracePeriod { metadata, days_remaining, reason } => { + warn!( + "Running in offline grace period ({} days remaining). Reason: {}", + days_remaining, reason + ); + } + LicenseStatus::Invalid(reason) => { + return Err(format!("Plugin license invalid or tampered: {reason}").into()); + } + LicenseStatus::Unsigned => { + info!("Plugin is running in unsigned / development mode."); + } +} +``` + +### `LicenseStatus` Enum + +| Variant | Description | +| :--- | :--- | +| `LicenseStatus::Valid(PumpkinMetadata)` | License is valid, verified, or the plugin is free / open-source. | +| `LicenseStatus::GracePeriod { metadata, days_remaining, reason }` | Operating offline with a previously validated lease that is within the allowable grace period. | +| `LicenseStatus::Invalid(String)` | Signature is invalid, expired, revoked, or metadata was tampered with. | +| `LicenseStatus::Unsigned` | Binary does not contain marketplace metadata/signature (useful during local development). | + +--- + +## Accessing Plugin Metadata + +You can access the verified plugin metadata anywhere in your codebase using `get_metadata` or `metadata()`: + +```rust +if let Some(meta) = pumpkin_plugin_utils::get_metadata() { + println!("Plugin Name: {}", meta.plugin_name); + println!("Plugin Version: {}", meta.version); + println!("Is Paid: {}", meta.is_paid); + println!("License Key: {:?}", meta.license_key); + println!("Buyer User ID: {}", meta.user_id); + println!("Developer: {} (ID: {})", meta.dev_name, meta.dev_id); + println!("Marketplace URL: {}", meta.marketplace_url); + println!("Issued At: {}", meta.issued_at); +} +``` + +### `PumpkinMetadata` Fields + +| Field | Type | Description | +| :--- | :--- | :--- | +| `plugin_name` | `String` | Canonical plugin name on the marketplace. | +| `version` | `String` | SemVer version string of the plugin binary. | +| `plugin_id` | `i64` | Unique plugin identifier on the marketplace. | +| `dev_id` | `i64` | Developer account ID. | +| `dev_name` | `String` | Developer display name. | +| `is_paid` | `bool` | `true` if this is a premium/commercial plugin. | +| `user_id` | `i64` | Buyer / licensee user ID (`0` for free plugins). | +| `license_key` | `Option` | Unique license key issued to the licensee. | +| `marketplace_url` | `String` | Base marketplace URL (`https://market.pumpkinmc.org`). | +| `issued_at` | `String` | ISO-8601 timestamp when the binary/license was issued. | + +--- + +## Complete Example + +Here is a full plugin implementation combining initialization, online licensing checks, offline grace periods, and update checking: + +:::code-group + +```rust:line-numbers [src/lib.rs] +use pumpkin_plugin_api::{Context, Plugin, PluginMetadata}; +use pumpkin_plugin_utils::LicenseStatus; +use tracing::{error, info, warn}; + +struct CommercialPlugin; + +impl Plugin for CommercialPlugin { + fn new() -> Self { + Self + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "CommercialPlugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Developer".into()], + description: "A commercial Pumpkin plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Initialize plugin-utils (verifies Ed25519 signature & caches metadata) + let meta = pumpkin_plugin_utils::init(&context).map_err(|err| { + error!("Signature verification failed: {err}"); + format!("Security verification error: {err}") + })?; + + info!("Loaded {} v{}", meta.plugin_name, meta.version); + + // 2. Validate license (offline evaluation + online check) + match pumpkin_plugin_utils::evaluate_license(7) { + LicenseStatus::Valid(_) => { + info!("Local license status: Valid"); + } + LicenseStatus::GracePeriod { days_remaining, .. } => { + warn!("Local license in grace period: {days_remaining} days remaining"); + } + LicenseStatus::Unsigned => { + info!("Running in development mode (unsigned binary)"); + } + LicenseStatus::Invalid(reason) => { + error!("Invalid license: {reason}"); + return Err(format!("License verification failed: {reason}").into()); + } + } + + // 3. Online license check if paid + if meta.is_paid { + if let Ok(check) = pumpkin_plugin_utils::check_license_online(None) { + if !check.valid { + return Err(format!("Online license rejected: {}", check.status).into()); + } + info!("Online license check confirmed: {}", check.status); + } else { + warn!("Online license verification unreachable, falling back to local lease"); + } + } + + // 4. Check for updates + if let Ok(update) = pumpkin_plugin_utils::check_for_updates() { + if update.update_available { + info!( + "A new version is available on the marketplace: {}", + update.latest_version.as_deref().unwrap_or("unknown") + ); + } + } + + Ok(()) + } + + fn on_unload(&mut self, _context: Context) -> pumpkin_plugin_api::Result<()> { + info!("CommercialPlugin unloaded."); + Ok(()) + } +} + +pumpkin_plugin_api::register_plugin!(CommercialPlugin); +``` + +::: diff --git a/docs/pt/troubleshooting/common_issues.md b/docs/pt/troubleshooting/common_issues.md index 495b19b..63b56b5 100644 --- a/docs/pt/troubleshooting/common_issues.md +++ b/docs/pt/troubleshooting/common_issues.md @@ -7,3 +7,14 @@ **Causa:** Isso está relacionado à autenticação, geralmente com a configuração `prevent_proxy_connections`. **Solução:** Desative a opção `prevent_proxy_connections` em `pumpkin.toml`. + +2. ### Não é possível conectar usando 0.0.0.0 + + **Problema:** Jogadores tentando se conectar ao servidor na mesma máquina não conseguem se conectar (ex.: erro "Connection refused") ao inserir `0.0.0.0` ou `0.0.0.0:25565`. + + **Causa:** No `pumpkin.toml`, `0.0.0.0` instrui o Pumpkin a escutar em todas as interfaces de rede locais. É um endereço de vinculação (bind) para o servidor, não um endereço de destino válido para o cliente do Minecraft se conectar. + + **Solução:** + - Se estiver se conectando do **mesmo computador**, use `localhost` ou `127.0.0.1`. + - Se estiver se conectando de **outro dispositivo na mesma rede local (LAN)**, use o endereço IP local do host (ex.: `192.168.x.x`). + - Se estiver se conectando pela **internet**, outros jogadores devem usar seu endereço IP público ou domínio (com o encaminhamento de portas configurado no roteador). diff --git a/docs/tr_TR/admin/introduction.md b/docs/tr_TR/admin/introduction.md new file mode 100644 index 0000000..5cb5228 --- /dev/null +++ b/docs/tr_TR/admin/introduction.md @@ -0,0 +1,25 @@ +# Yönetim Genel Bakış + +Pumpkin sunucu yöneticileri için **Yönetici Bölümüne** hoş geldiniz. Pumpkin; minimum kaynak tüketimiyle yüksek oyuncu sayılarını desteklemek üzere tasarlanmış, Rust ile yazılmış, yüksek performanslı ve çok iş parçacıklı (multithreaded) bir Minecraft sunucusudur. + +--- + +## Sunucu Yöneticileri İçin Temel Özellikler + +- **Gelişmiş Çoklu İş Parçacığı (Extreme Multithreading)**: Modern çok çekirdekli işlemciler için sıfırdan tasarlandı. +- **WASM Eklenti Mimarisi**: JVM ek yükü (overhead) olmaksızın Rust, Python, Kotlin, C#, Go veya C ile güvenli ve yalıtılmış (sandboxed) eklenti çalıştırma. +- **Yerel Proxy Desteği**: Velocity, BungeeCord ve modern proxy yönlendirmeleri için kutudan çıktığı haliyle yerel destek. +- **TOML Tabanlı Yapılandırma**: `pumpkin.toml` ve özelliğe özel TOML yapılandırmalarında yer alan temiz ve okunabilir yapılandırma dosyaları. + +--- + +## Yönetici Kılavuzları ve Konuları + +Aşağıdaki yönetim kılavuzlarını inceleyin: + +- [Bukkit / Paper / Spigot Sunucusundan Geçiş](./migrating-from-bukkit) — Sunucu yönetimi, eklentiler, dünya depolaması ve performanstaki temel farklar. +- [Sunucu Yapılandırması](../config/introduction) — `pumpkin.toml` ayarlarının ayrıntılı açıklaması. +- [Proxy Kurulumu](../config/proxy) — BungeeCord ve Velocity oyuncu yönlendirmesini yapılandırma. +- [Komutlar ve İzinler](../config/commands) — Oyun içi yönetici (operatör) komutlarını ve izinlerini yönetme. +- [Kimlik Doğrulama](../config/authentication) — Çevrimiçi mod (online-mode), çevrimdışı mod (offline-mode) ve Yggdrasil ayarları. +- [Sorun Giderme ve Yaygın Sorunlar](../troubleshooting/common_issues) — Port bağlama, bellek ve eklenti yükleme sorunlarını çözme. diff --git a/docs/tr_TR/admin/migrating-from-bukkit.md b/docs/tr_TR/admin/migrating-from-bukkit.md new file mode 100644 index 0000000..19aea50 --- /dev/null +++ b/docs/tr_TR/admin/migrating-from-bukkit.md @@ -0,0 +1,126 @@ +# Bukkit / Spigot / Paper'dan Pumpkin'a Geçiş + +Bu kılavuz, bir sunucuyu Bukkit, Spigot veya Paper'dan Pumpkin'a taşırken dikkate alınması gereken mimari ve yapılandırma farklılıklarını kapsar. + +--- + +## 1. Mimari Farklılıklar + +| Boyut | Bukkit / Spigot / Paper | Pumpkin | +| :--- | :--- | :--- | +| **Çalışma Zamanı Ortamı** | Java Sanal Makinesi (Java 17/21+ gerektirir) | Yerel yürütülebilir dosya (derlenmiş Rust, Java gerektirmez) | +| **Başlatma Komutu** | `java [flags] -jar server.jar` | `./pumpkin` | +| **Tick Döngüsü** | Asenkron iş parçacığı aktarımıyla tek iş parçacıklı tick döngüsü | CPU çekirdekleri arasında çok iş parçacıklı tick yürütümü | +| **Eklenti Biçimi** | Bukkit API hedefli Java bayt kodu (`.jar`) | WebAssembly (`.wasm`) veya PatchBukkit ile `.jar` | +| **Eklenti Dilleri** | Java, Kotlin, Scala | Rust, Python, Kotlin, C#, Go, C | +| **Yapılandırma** | `server.properties`, `paper.yml`, `spigot.yml` | `pumpkin.toml` (TOML biçimi) | +| **Bellek Yönetimi** | JVM Çöp Toplama (Garbage Collection) | Yerel işletim sistemi bellek tahsisi | + +--- + +## 2. Eklentiler ve Uzantılar + +### WebAssembly Eklentileri +Pumpkin'ın birincil eklenti biçimi WebAssembly (`.wasm`)'dir: +- **Yalıtım**: Eklentiler yalıtılmış bir WebAssembly çalışma zamanında çalışır. +- **Dil Desteği**: Eklentiler Rust, Python, Go, C#, C veya Kotlin dillerinden WASM'a derlenebilir. +- **Özel Eklentiler**: Özel Bukkit eklentilerinizi uyarlamak için [Geliştirici Geçiş Kılavuzu](../plugin-dev/migrating-from-bukkit/index) belgesine bakın. + +### Bukkit Eklenti Uyumluluğu: PatchBukkit +Mevcut Bukkit/Spigot `.jar` eklentilerine ihtiyaç duyan sunucular için **PatchBukkit** projesi bir uyumluluk katmanı sağlar: +- **Mimari**: PatchBukkit, Pumpkin içine bir JVM gömer, Bukkit API'sinin parçalarını yeniden uygular ve çağrıları JNI ve FFI aracılığıyla Pumpkin'a iletir. +- **Eklenti Konumu**: Uyumlu `.jar` eklentileri `patchbukkit/patchbukkit-plugins/` dizinine yerleştirilir. +- **Durum ve Kısıtlamalar**: PatchBukkit aktif geliştirme aşamasındadır. Standart Bukkit API çağrıları desteklenir; dahili NMS (`net.minecraft.server`) sınıflarına, CraftBukkit dahili bileşenlerine veya bayt kodu manipülasyonuna dayanan eklentiler düzgün çalışmayabilir. +- **Java Gereksinimi**: PatchBukkit çalıştırmak, gömülü JVM'yi yürütmek için ana sistemde bir Java çalışma zamanı (JRE/JDK) bulunmasını gerektirir. + +--- + +## 3. Yapılandırma ve Özellikler + +Pumpkin, `server.properties` ve YAML dosyaları yerine TOML yapılandırma dosyalarını kullanır: + +| Paper / Spigot (`server.properties`) | Pumpkin (`pumpkin.toml`) | +| :--- | :--- | +| `server-port=25565` | `server_address = "0.0.0.0:25565"` | +| `motd=...` | `motd = "A Pumpkin Server"` | +| `max-players=20` | `max_players = 20` | +| `online-mode=true` | `online_mode = true` | +| `view-distance=10` | `view_distance = 10` | +| `simulation-distance=8` | `simulation_distance = 8` | + +--- + +## 4. Dünya ve Veri Depolama + +- **Biçim Uyumluluğu**: Pumpkin, standart Anvil chunk biçimini (`.mca` dosyaları) okur. +- **Dizin Düzeni**: Mevcut Paper/Spigot kurulumlarından dünyaları kopyalarken boyut dizinlerinin (`world`, `world_nether`, `world_the_end`) Pumpkin'ın beklediği düzenle eşleştiğinden emin olun. +- **En Son Dünya ve Datapack Biçimi Zorunluluğu**: En azından şimdilik Pumpkin, yalnızca desteklenen Minecraft sürümünün en son dünya ve datapack biçimini destekler. Eski dünya biçimleri veya eski datapack sürümleri çalışma zamanında otomatik olarak dönüştürülmez. + - **Dünyaları İstemcide Güncelleme**: Eski bir dünyayı taşıyorsanız Pumpkin'a yüklemeden önce güncelleyin. Mevcut bir dünyayı doğrudan resmi Minecraft istemcisinde güncelleyebilirsiniz: İstemciyi eşleşen en son sürümde açın, **Tek Oyunculu** menüsüne gidin, dünyayı seçin, **Düzenle**'ye tıklayın ve **Dünyayı İyileştir** (Optimize World) seçeneğini seçin. Alternatif olarak dünyayı en son sürümdeki bir vanilla sunucusunda bir kez açıp kaydedebilirsiniz. + - **Datapack'ler**: `world/datapacks` içinde bulunan tüm datapack'lerin hedef sürümün beklediği biçime güncellendiğinden emin olun. + +--- + +## 5. Başlatma ve JVM Bayrakları + +### Java Kurulumu Gerekmez +Saf Pumpkin bağımsız bir yerel ikili dosyadır. Sunucuyu çalıştırmak için işletim sisteminizde veya konteynerinizde Java çalışma zamanına (JRE/JDK) ihtiyacınız yoktur. + +### Başlatma Komutları +Paper ve Spigot sunucuları genellikle bellek tahsisi ve çöp toplama ayarlarına yönelik başlatma komut dosyaları (Aikar's Flags gibi) kullanır: + +:::code-group + +```bash [Paper (JVM)] +# Aikar's Flags içeren tipik Paper başlatma komutu: +java -Xms10G -Xmx10G \ + -XX:+UseG1GC \ + -XX:+ParallelRefProcEnabled \ + -XX:MaxGCPauseMillis=200 \ + -XX:+UnlockExperimentalVMOptions \ + -XX:+DisableExplicitGC \ + -XX:+AlwaysPreTouch \ + -XX:G1NewSizePercent=30 \ + -XX:G1MaxNewSizePercent=40 \ + -XX:G1HeapRegionSize=8M \ + -XX:G1ReservePercent=20 \ + -XX:G1HeapWastePercent=5 \ + -XX:G1MixedGCCountTarget=4 \ + -XX:InitiatingHeapOccupancyPercent=15 \ + -XX:G1MixedGCLiveThresholdPercent=90 \ + -XX:G1RSetUpdatingPauseTimePercent=5 \ + -XX:SurvivorRatio=32 \ + -XX:+PerfDisableSharedMem \ + -XX:MaxTenuringThreshold=1 \ + -Dusing.aikars.flags=https://mcflags.emc.gs \ + -Daikars.new.flags=true \ + -jar paper.jar --nogui +``` + +```bash [Pumpkin (Yerel)] +# Linux / macOS: +./pumpkin + +# Windows: +./pumpkin.exe + +# Docker: +docker run -p 25565:25565 -v ./data:/data pumpkinmc/pumpkin:latest +``` + +::: + +### Bellek Farklılıkları +- **Çöp Toplayıcı Yoktur**: Pumpkin Rust dilinde yazılmıştır ve derleme zamanında deterministik bellek yönetimi (RAII) kullanır. Çalışma zamanı çöp toplayıcısı veya GC duraklama optimizasyonu bulunmaz. +- **Sabit Yığın Tahsisi Yoktur**: `-Xms` ve `-Xmx` gibi bayraklar yerel ikili dosyalarda bulunmaz. Bellek, chunk'lar ve varlıklar yüklendikçe işletim sisteminden dinamik olarak ayrılır ve kaldırıldıklarında iade edilir. +- **JVM Bayrakları Geçerli Değildir**: Çöp toplama stratejilerini, nesil boyutlandırmalarını veya JVM iç mekanizmalarını ayarlayan bayraklara gerek yoktur ve bunlar programa iletilemez. + +--- + +## Yönetici Kontrol Listesi + +1. [ ] Mevcut sunucu verilerinizi ve dünya dosyalarınızı yedekleyin, dünyanın ve datapack'lerin en son biçime güncellendiğinden emin olun (örn. istemcide "Optimize World" kullanarak). +2. [ ] `server.properties` ayarlarını `pumpkin.toml` dosyasına aktarın. +3. [ ] Gerekli eklentileri belirleyin ve WebAssembly (`.wasm`) karşılıklarını arayın veya [PatchBukkit](#bukkit-eklenti-uyumlulugu-patchbukkit) ile test edin. +4. [ ] Bir ağ kullanıyorsanız `pumpkin.toml` dosyasında proxy ayarlarını (`Velocity` / `BungeeCord`) yapılandırın. +5. [ ] Başlatma betiklerinizi JVM argümanları olmadan doğrudan `./pumpkin` çalıştıracak şekilde güncelleyin. +6. [ ] Sunucuyu başlatın, bağlantıları ve günlükleri kontrol edin. diff --git a/docs/tr_TR/config/bedrock.md b/docs/tr_TR/config/bedrock.md new file mode 100644 index 0000000..45510f8 --- /dev/null +++ b/docs/tr_TR/config/bedrock.md @@ -0,0 +1,50 @@ +# Bedrock ve NetherNet + +Pumpkin, NetherNet WebRTC/ICE aktarımı ve kullanıcı adı özelleştirmesi dahil olmak üzere Minecraft Bedrock Edition istemcileri için yerel destek içerir. + +## Yapılandırma + +:::code-group + +```toml [pumpkin.toml] +[networking.bedrock] +enabled = true +online_mode = true +max_players = 1000 +view_distance = 16 +simulation_distance = 10 +motd = "A blazingly fast Pumpkin server!" +username_prefix = "" +replace_username_spaces = true +chunk_caching = true + +[networking.bedrock.nethernet] +enabled = true +address = "0.0.0.0:19132" +identity_key = "nethernet-key.der" +stun_servers = [] +``` + +::: + +### Bedrock Seçenekleri + +- **`enabled`**: Bedrock Edition istemcilerinden gelen bağlantıların kabul edilip edilmeyeceği (varsayılan: `true`). +- **`online_mode`**: Bedrock oyuncuları için Xbox Live kimlik doğrulamasının zorunlu olup olmadığı (varsayılan: `true`). +- **`max_players`**: Eşzamanlı maksimum Bedrock oyuncusu sayısı (`0` sınırı devre dışı bırakır, varsayılan: `1000`). +- **`view_distance`**: Bedrock istemcilerine gönderilen maksimum chunk görüş mesafesi (varsayılan: `16`). +- **`simulation_distance`**: Bedrock oyuncuları için maksimum tick simülasyon mesafesi (varsayılan: `10`). +- **`motd`**: Bedrock sunucu listesinde görüntülenen Günün Mesajı (Message of the Day). +- **`username_prefix`**: Bedrock oyuncu etiketlerinin (gamertag) başına eklenen isteğe bağlı önek (ör. `"."` veya `"*"`), çapraz platform (cross-play) sunucularında Java Edition hesaplarıyla isim çakışmalarını önler (varsayılan: `""`). +- **`replace_username_spaces`**: Bedrock oyuncu etiketlerindeki boşlukların otomatik olarak alt çizgi `_` ile değiştirilip değiştirilmeyeceği; bu sayede oyuncu adlarının Minecraft komutlarında sorunsuzca kullanılabilmesini sağlar (varsayılan: `true`). +- **`chunk_caching`**: Ağ bant genişliğinden tasarruf etmek için istemci tarafı chunk blob önbelleğe almanın etkin olup olmadığı (varsayılan: `true`). + +### NetherNet Aktarım Ayarları + +NetherNet, Minecraft Bedrock'ın modern WebRTC/ICE ağ aktarım protokolüdür: + +- **`enabled`**: İstemcilerin NetherNet kullanarak bağlanıp bağlanamayacağı (varsayılan: `true`). +- **`address`**: TCP sinyalleşmesi ve UDP ICE çoğullaması (multiplexing) için ağ soket adresi (varsayılan: `"0.0.0.0:19132"`). +- **`external_ip`**: Sunucu NAT arkasında barındırıldığında bildirilen isteğe bağlı genel (public) IP adresi. +- **`identity_key`**: İstemcinin İlk Kullanımda Güven (TOFU - Trust On First Use) mekanizması için yeniden başlatmalar arasında korunan PKCS#8 P-384 özel kimlik anahtarı dosyasının yolu (varsayılan: `"nethernet-key.der"`). +- **`stun_servers`**: ICE NAT geçişi için isteğe bağlı STUN sunucusu URL'leri listesi. diff --git a/docs/tr_TR/config/chat.md b/docs/tr_TR/config/chat.md new file mode 100644 index 0000000..047e4fb --- /dev/null +++ b/docs/tr_TR/config/chat.md @@ -0,0 +1,39 @@ +# Sohbet ve Anti-Spam + +Pumpkin, `pumpkin.toml` dosyasındaki `[chat]` altında özelleştirilebilir oyun içi sohbet biçimlendirmesi ve yerleşik anti-spam koruması sağlar. + +## Yapılandırma + +:::code-group + +```toml [pumpkin.toml] +[chat] +format = "<{DISPLAYNAME}> {MESSAGE}" + +[chat.anti_spam] +enabled = true +spam_threshold = 200 +message_cost = 20 +decay_per_tick = 1 +ops_bypass = true +``` + +::: + +### Sohbet Ayarları + +- **`format`**: Sohbet mesajı biçim dizesi (varsayılan: `"<{DISPLAYNAME}> {MESSAGE}"`). + - `{DISPLAYNAME}`: Oyuncunun görünen adı. + - `{MESSAGE}`: Sohbet mesajı metni. + > [!NOTE] + > Güvenli sohbet raporlama (`allow_chat_reports = true`) etkinleştirildiğinde özel sohbet biçimlendirmesi uygulanmaz. + +### Anti-Spam Koruması + +Pumpkin, sunucuyu spam yapan oyunculardan ve bot akınlarından korumak için sızdıran kova (leaky-bucket) tabanlı bir spam sayacı uygular: + +- **`enabled`**: Anti-spam korumasının etkin olup olmadığı (varsayılan: `true`). +- **`spam_threshold`**: Bir oyuncunun sunucudan atılmasına (kick) neden olacak tick cinsinden spam sayacı eşiği (varsayılan: `200` tick). +- **`message_cost`**: Gönderilen her sohbet mesajı veya komut için oyuncunun spam sayacına eklenen miktar (varsayılan: `20` tick). +- **`decay_per_tick`**: Her sunucu tick'inde oyuncunun spam sayacından düşülen miktar (varsayılan: `1` tick). +- **`ops_bypass`**: Sunucu operatörlerinin anti-spam kontrollerini atlayıp atlamayacağı (varsayılan: `true`). diff --git a/docs/tr_TR/config/packet-limiter.md b/docs/tr_TR/config/packet-limiter.md new file mode 100644 index 0000000..5445774 --- /dev/null +++ b/docs/tr_TR/config/packet-limiter.md @@ -0,0 +1,30 @@ +# Paket Sınırlayıcı + +Pumpkin; sunucuyu paket taşması (packet flooding), spam saldırıları ve istemci tarafı açıklardan korumak için yerleşik bir paket oranı sınırlayıcısına sahiptir. Paket oranı sınırları, Java Edition ve Bedrock Edition istemcileri için bağımsız olarak yapılandırılabilir. + +## Yapılandırma + +:::code-group + +```toml [pumpkin.toml] +[networking.java.packet_limiter] +enabled = true +max_packet_rate = 500.0 +burst_capacity = 500.0 +kick_message = "Kicked for spamming packets" + +[networking.bedrock.packet_limiter] +enabled = true +max_packet_rate = 500.0 +burst_capacity = 500.0 +kick_message = "Kicked for spamming packets" +``` + +::: + +### Yapılandırma Seçenekleri + +- **`enabled`**: Bu istemci sürümü için paket oranı sınırlayıcısının etkin olup olmadığı (varsayılan: `true`). +- **`max_packet_rate`**: İstemci bağlantısı başına saniyede izin verilen maksimum gelen paket sayısı (varsayılan: `500.0`). Sınırlayıcı modülünü devre dışı bırakmadan hız sınırını kapatmak için `<= 0.0` olarak ayarlayın. +- **`burst_capacity`**: İstemci paketlerindeki kısa süreli ani artışlar için belirteç kovası (token bucket) ani yük kapasitesi (varsayılan: `500.0`). +- **`kick_message`**: Bağlantısı paket sınırını aştığında oyuncuya gösterilecek bağlantı kesme mesajı (varsayılan: `"Kicked for spamming packets"`). diff --git a/docs/tr_TR/config/player-data.md b/docs/tr_TR/config/player-data.md new file mode 100644 index 0000000..c1f64bc --- /dev/null +++ b/docs/tr_TR/config/player-data.md @@ -0,0 +1,41 @@ +# Oyuncu Verileri ve Oynanış + +Kalıcı oyuncu verilerini, başarımları, tarif senkronizasyonunu ve eğlenceli sezonluk özellikleri kaydetmeye yönelik ayarlar `pumpkin.toml` dosyasında yapılandırılır. + +## Yapılandırma + +:::code-group + +```toml [pumpkin.toml] +[player_data] +save_player_data = true +save_player_cron_interval = 300 + +[advancement] +save_advancements = true + +[recipe] +send_recipes = true + +[fun] +april_fools = true +``` + +::: + +### Oyuncu Verileri + +- **`save_player_data`**: Kalıcı oyuncu verilerinin (envanter, konum, can) diske kaydedilip kaydedilmeyeceği (varsayılan: `true`). +- **`save_player_cron_interval`**: Çevrimiçi oyuncu verilerinin otomatik periyodik kayıtları arasındaki saniye cinsinden zaman aralığı (varsayılan: `300` saniye / 5 dakika). + +### Başarımlar + +- **`save_advancements`**: Oyuncu başarım ilerlemesinin takip edilip diske kaydedilip kaydedilmeyeceği (varsayılan: `true`). + +### Tarifler + +- **`send_recipes`**: Üretim (crafting) ve eritme (smelting) tariflerinin bağlı istemcilerle senkronize edilerek istemci tarafındaki tarif kitabının etkinleştirilip etkinleştirilmeyeceği (varsayılan: `true`). + +### Eğlenceli Özellikler + +- **`april_fools`**: Eğlenceli ve sezonluk 1 Nisan sürpriz yumurtalarının (easter eggs) ve özelliklerinin etkin olup olmadığı (varsayılan: `true`). diff --git a/docs/tr_TR/config/plugins.md b/docs/tr_TR/config/plugins.md new file mode 100644 index 0000000..7c60387 --- /dev/null +++ b/docs/tr_TR/config/plugins.md @@ -0,0 +1,59 @@ +# Eklentiler + +Pumpkin, yetenek tabanlı (capability-based) güvenliğe sahip bir WebAssembly (WASM) eklenti çalışma zamanı sunar. Eklenti davranışı, imza doğrulaması ve izinler `pumpkin.toml` dosyasındaki `[plugins]` altında yapılandırılır. + +## Yapılandırma + +:::code-group + +```toml [pumpkin.toml] +[plugins] +enabled = true +hot_reload = false +ask_permission_confirmation = true +allow_unsigned = true +allowed_permissions = [] +blocked_permissions = [] +inherit_env = false +loopback_only = false +verify_signatures = true + +# Optional per-plugin overrides +[plugins.overrides.my_plugin] +enabled = true +allow_unsigned = true +max_memory_mb = 128 +allowed_permissions = ["fs:read:data"] +blocked_permissions = ["network:outbound"] +loopback_only = true + +[plugins.overrides.my_plugin.environment] +API_KEY = "example_secret" +``` + +::: + +### Genel Eklenti Ayarları + +- **`enabled`**: Eklenti çalışma zamanını etkinleştiren veya devre dışı bırakan ana anahtar (varsayılan: `true`). +- **`hot_reload`**: `plugins/` dizinini izler ve çalışma zamanında değiştirilen eklentileri otomatik olarak yeniden yükler (varsayılan: `false`). +- **`ask_permission_confirmation`**: Bir eklenti onaylanmamış yetenekler/izinler istediğinde sunucu konsolunda onay istemi görüntüler (varsayılan: `true`). +- **`allow_unsigned`**: İmzasız WASM eklentilerinin yüklenmesine izin verir (varsayılan: `true`). +- **`allowed_permissions`**: Etkileşimli konsol istemlerini atlayarak tüm eklentiler için genel olarak önceden onaylanmış izinlerin listesi (varsayılan: `[]`). +- **`blocked_permissions`**: Tüm eklentiler için genel olarak engellenen izinlerin listesi (varsayılan: `[]`). +- **`inherit_env`**: Ana makine ortam değişkenlerinin varsayılan olarak eklenti WASI yalıtım alanlarına (sandbox) aktarılıp aktarılmayacağı (varsayılan: `false`). +- **`loopback_only`**: Eklentilerden giden ağ bağlantılarının yalnızca `127.0.0.1` / localhost ile sınırlandırılıp sınırlandırılmayacağı (varsayılan: `false`). +- **`max_memory_mb`**: (İsteğe bağlı) Eklenti örneği başına megabayt (MB) cinsinden genel maksimum bellek sınırı. +- **`verify_signatures`**: Pumpkin'ın yüklemeden önce WASM eklentilerindeki şifreleme imzalarını doğrulayıp doğrulamayacağı (varsayılan: `true`). + +### Eklenti Başına Geçersiz Kılmalar + +`[plugins.overrides.]` altında belirli eklentiler için izinleri ve ortam değişkenlerini ayrıntılı olarak yapılandırın: + +- **`enabled`**: Bu eklentiyi etkinleştirir veya devre dışı bırakır (varsayılan: `true`). +- **`allow_unsigned`**: Bu eklentinin imzasız çalışıp çalışamayacağını geçersiz kılar (`true`/`false`). +- **`max_memory_mb`**: Özellikle bu eklenti için ayrılan MB cinsinden maksimum bellek sınırı. +- **`allowed_permissions`**: Bu eklenti için önceden onaylanmış izinler. +- **`blocked_permissions`**: Bu eklenti için açıkça engellenmiş izinler. +- **`loopback_only`**: Bu eklenti için geri döngü (loopback) ağ kısıtlamasını geçersiz kılar. +- **`environment`**: Doğrudan eklentinin WASI ortamına aktarılan özel ortam değişkenleri tablosu. diff --git a/docs/tr_TR/config/server-links.md b/docs/tr_TR/config/server-links.md new file mode 100644 index 0000000..fd5b2ec --- /dev/null +++ b/docs/tr_TR/config/server-links.md @@ -0,0 +1,50 @@ +# Sunucu Bağlantıları + +Minecraft istemcileri (1.21 sürümünden itibaren) duraklatma menüsünde ve oyun menülerinde tıklanabilir sunucu bağlantıları görüntüleyebilir. Standart ve özel sunucu bağlantılarını `pumpkin.toml` dosyasındaki `[server_links]` altında yapılandırabilirsiniz. + +## Yapılandırma + +:::code-group + +```toml [pumpkin.toml] +[server_links] +enabled = true +bug_report = "https://github.com/Pumpkin-MC/Pumpkin/issues" +support = "" +status = "" +feedback = "" +community = "" +website = "" +forums = "" +news = "" +announcements = "" + +[server_links.custom] +# "Store" = "https://store.example.com" +# "Discord" = "https://discord.gg/example" +``` + +::: + +### Yapılandırma Seçenekleri + +- **`enabled`**: Bağlanan istemcilere sunucu bağlantılarını iletmeyi etkinleştiren ana anahtar (varsayılan: `true`). +- **`bug_report`**: Sunucu veya proje hata takipçisine giden bağlantı (varsayılan: `"https://github.com/Pumpkin-MC/Pumpkin/issues"`). +- **`support`**: Teknik destek veya yardım masasına giden bağlantı. +- **`status`**: Sunucu çalışma durumu/kesinti sayfasına giden bağlantı. +- **`feedback`**: Oyuncu geri bildirim veya öneri sayfasına giden bağlantı. +- **`community`**: Topluluk forumları veya grubuna giden bağlantı. +- **`website`**: Resmi web sitesine giden bağlantı. +- **`forums`**: Tartışma forumlarına giden bağlantı. +- **`news`**: Sunucu blogu veya haber sayfasına giden bağlantı. +- **`announcements`**: Duyurular sayfasına giden bağlantı. + +### Özel Bağlantılar + +`[server_links.custom]` altında istemci menülerinde gösterilecek isteğe bağlı anahtar-değer bağlantılarını tanımlayın: + +```toml +[server_links.custom] +"Store" = "https://store.example.com" +"Discord" = "https://discord.gg/example" +``` diff --git a/docs/tr_TR/config/world.md b/docs/tr_TR/config/world.md new file mode 100644 index 0000000..752451d --- /dev/null +++ b/docs/tr_TR/config/world.md @@ -0,0 +1,41 @@ +# Dünya + +Pumpkin, `pumpkin.toml` dosyasındaki `[world]` altında dünya depolama formatlarının, otomatik kaydetme aralıklarının ve aydınlatma modlarının ayrıntılı olarak ayarlanmasına olanak tanır. + +## Yapılandırma + +:::code-group + +```toml [pumpkin.toml] +[world] +lighting = "default" +autosave_ticks = 6000 + +[world.chunk] +type = "anvil" +write_in_place = false + +[world.chunk.compression] +algorithm = "LZ4" +level = 6 +``` + +::: + +### Dünya Ayarları + +- **`lighting`**: Işık motorunun yayılım hesaplama modu. + - `"default"`: Standart Vanilla Minecraft ışık yayılımı. + - `"full"`: Gölgeler olmaksızın her yerde tam gökyüzü ışığı. + - `"dark"`: Her yerde tamamen karanlık aydınlatma (sıfır ışık). +- **`autosave_ticks`**: Dünyanın otomatik kaydedilmesi arasındaki sunucu tick sayısı (varsayılan: `6000`, 20 TPS'de 5 dakikaya eşdeğerdir). Bunun `0` olarak ayarlanması dünyanın otomatik kaydedilmesini devre dışı bırakır. + +### Chunk Depolama Ayarları + +- **`type`**: Kullanılacak chunk depolama formatı: + - `"anvil"`: Standart Minecraft Anvil bölge dosyası formatı (`.mca`). + - `"linear"`: Daha küçük disk alanı için hızlı sıkıştırma kullanan Linear bölge depolama formatı. + - `"pump"`: Pumpkin'ın yerel optimize edilmiş dünya formatı. +- **`write_in_place`**: Chunk'ların boş alanları yeniden tahsis etmek yerine mevcut bölge dosyaları içinde doğrudan yerinde (in-place) yeniden yazılıp yazılmayacağı (varsayılan: `false`). +- **`compression.algorithm`**: Chunk verileri için kullanılan sıkıştırma algoritması (`"LZ4"`, `"ZLib"`, `"GZip"`, `"Custom"`). +- **`compression.level`**: Chunk depolaması için sıkıştırma düzeyi (varsayılan: `6`). diff --git a/docs/tr_TR/developer/architecture.md b/docs/tr_TR/developer/architecture.md new file mode 100644 index 0000000..19c26b1 --- /dev/null +++ b/docs/tr_TR/developer/architecture.md @@ -0,0 +1,139 @@ +# Pumpkin Architecture & Crates + +Pumpkin is organized as a modular Cargo workspace composed of specialized crates. Each crate has distinct responsibilities, strictly defined boundaries, and minimal cross-dependencies. + +This architecture enables high parallel compilation speeds, isolated testing, zero-cost data sharing, and clean separation between network protocols, game logic, and world management. + +--- + +## Workspace Crate Overview + +### Workspace Architecture at a Glance + +| Layer | Crates | Responsibilities | +| :--- | :--- | :--- | +| **Core Runtime** | `pumpkin` | Game loop, world ticking, player sessions, entity management, and event loops | +| **Networking & Protocols** | `pumpkin-protocol`, `pumpkin-auth`, `pumpkin-nbt` | Java/Bedrock packet serialization, Yggdrasil & Xbox Live authentication, SNBT | +| **World & Gameplay** | `pumpkin-world`, `pumpkin-inventory`, `pumpkin-command` | Region I/O (Anvil/Linear), terrain generation, block ticking, containers, Brigadier tree | +| **Extensibility** | `pumpkin-plugin-runtime`, `pumpkin-host-bindings`, `pumpkin-plugin-api`, `pumpkin-plugin-wit` | WebAssembly plugin execution via Wasmtime, WIT host APIs, native and WASM loaders | +| **Foundations** | `pumpkin-data`, `pumpkin-config`, `pumpkin-util` | Code-generated registries, typed configuration files, mathematical vectors, permissions | + +--- + +## Detailed Crate Breakdown + +### 1. `pumpkin` (Core Server) +- **Path**: `crates/pumpkin` +- **Purpose**: The main server binary and centralized orchestration engine. +- **Responsibilities**: + - Initializes configuration, logging (`tracing`), and telemetry. + - Spawns Tokio network listeners for Minecraft: Java Edition, Bedrock Edition, RCON, Query, and LAN broadcast. + - Manages the server tick loop (`Server::tick`), coordinating world updates, player actions, entity movements, and chunk broadcasting. + - Dispatches incoming client packets through `crates/pumpkin/src/net/java/` and `net/bedrock/`. + - Integrates the plugin manager, firing event handlers before and after packet processing. + +### 2. `pumpkin-protocol` (Network Protocol) +- **Path**: `crates/pumpkin-protocol` +- **Purpose**: High-speed network wire format serialization and deserialization for Minecraft Java and Bedrock editions. +- **Responsibilities**: + - Implements the `ClientPacket` (serialization) and `ServerPacket` (deserialization) traits. + - Handles protocol primitives: `VarInt`, `VarLong`, UUIDs, NBT compounds, and string codecs. + - Implements packets across all states: `Handshake`, `Status`, `Login`, `Config`, and `Play`. + - Provides packet compression (ZLib/Fast compression) and packet encryption (AES-128/CFB8). + - Encapsulates RCON and GameSpy4 Query protocols. + +### 3. `pumpkin-world` (World & Chunks) +- **Path**: `crates/pumpkin-world` +- **Purpose**: World loading, saving, terrain generation, and chunk management. +- **Responsibilities**: + - Supports multiple chunk storage formats: + - **Vanilla Anvil (`.mca`)**: Reading and writing standard region files. + - **Linear (`.linear`)**: High-efficiency zstd-compressed region format saving 50–95% disk space. + - **Slime (`.slime`)**: Single-file world format. + - **Pump**: Native Pumpkin chunk storage format. + - Implements world generation pipelines: Perlin noise heightmaps, 3D noise routers, biome climate parameters, placed features, carvers, and superflat terrain. + - Manages block lighting and sky lighting propagation. + - Coordinates chunk loading, unloading, and streaming via cylindrical chunk iterators. + +### 4. `pumpkin-data` (Minecraft Datasets & Registries) +- **Path**: `crates/pumpkin-data` +- **Purpose**: Compile-time static registries and data tables generated directly from vanilla Minecraft assets and reports. +- **Responsibilities**: + - Contains complete ID mappings, properties, and definitions for blocks, items, biomes, entities, sounds, particles, enchantments, and recipes. + - Packet ID lookups across multiple Minecraft protocol versions. + - Zero-cost lookup tables generated by `tools/pumpkin-codegen`. + +### 5. `pumpkin-config` (Configuration Management) +- **Path**: `crates/pumpkin-config` +- **Purpose**: TOML configuration parsing, serialization, and validation. +- **Responsibilities**: + - Loads and writes `pumpkin.toml`. + - Configures server ports, compression thresholds, proxy modes, resource packs, logging levels, world settings, and Bedrock integration. + - Provides safe defaults and schema validations. + +### 6. `pumpkin-inventory` (Inventory & Containers) +- **Path**: `crates/pumpkin-inventory` +- **Purpose**: Server-side container window handlers and inventory mechanics. +- **Responsibilities**: + - Manages player inventory, chest containers, crafting tables, furnaces, and villager trading screens. + - Validates client slot clicks, drag operations, item stacking, and item splitting. + - Coordinates window packet synchronization to ensure client inventory fidelity. + +### 7. `pumpkin-command` (Command Dispatcher) +- **Path**: `crates/pumpkin-command` +- **Purpose**: Brigadier-inspired hierarchical command tree dispatcher and parser. +- **Responsibilities**: + - Constructs modular command trees with typed arguments (`Integer`, `Float`, `Entity`, `BlockPos`, etc.). + - Executes permission-checked commands. + - Provides real-time autocompletion suggestions sent to connected clients. + +### 8. `pumpkin-auth` (Authentication & Session Verification) +- **Path**: `crates/pumpkin-auth` +- **Purpose**: Cryptographic authentication and player identity verification. +- **Responsibilities**: + - Validates client sessions with Mojang's Yggdrasil authentication servers. + - Handles RSA keypair generation and shared secret exchange during the Login state. + - Supports offline-mode UUID generation and custom authentication endpoints. + - Implements proxy forwarding protocols: [BungeeCord](https://github.com/SpigotMC/BungeeCord), [BungeeGuard](https://github.com/lucko/BungeeGuard), and [Velocity](https://github.com/PaperMC/Velocity) modern forwarding with HMAC-SHA256 signatures. + +### 9. `pumpkin-nbt` (Named Binary Tag Codec) +- **Path**: `crates/pumpkin-nbt` +- **Purpose**: High-throughput binary NBT parser and serializer. +- **Responsibilities**: + - Parses and encodes all standard NBT tag types (`Byte`, `Short`, `Int`, `Long`, `Float`, `Double`, `ByteArray`, `String`, `List`, `Compound`, `IntArray`, `LongArray`). + - Supports compression layers: Gzip, Zlib, and uncompressed Network NBT. + +### 10. `pumpkin-plugin-*` (Extensibility Infrastructure) +- **Paths**: + - `crates/pumpkin-plugin-api`: The Rust developer SDK for compiling native plugins. + - `crates/pumpkin-plugin-runtime`: Wasmtime-powered WebAssembly execution host. + - `crates/pumpkin-plugin-wit`: WebAssembly Interface Type (WIT) specifications. + - `crates/pumpkin-host-bindings`: Auto-generated host bindings connecting Wasmtime and the server core. +- **Purpose**: Enables safe, polyglot plugin execution (Rust, Python, C#, C, Go, Kotlin) with sandboxed memory and near-native performance. + +### 11. `pumpkin-util` & `pumpkin-macros` (Foundations) +- **Paths**: `crates/pumpkin-util`, `crates/pumpkin-macros`, `crates/pumpkin-api-macros` +- **Purpose**: Foundational types, shared algorithms, and procedural macros. +- **Responsibilities**: + - Mathematical utilities: `Vector2`, `Vector3`, `BoundingBox`, and angle conversions. + - Text and chat formatting: `TextComponent` and JSON component serialization. + - Procedural macros such as `#[java_packet(...)]` that bind packet structs to protocol IDs and packet traits. + +### 12. `tools/` (Developer Tooling) +- **`tools/pumpkin-codegen`**: Automated code generator that parses Minecraft version jars, data reports, and asset archives to emit Rust source files into `pumpkin-data`. +- **`tools/pumpkin-fuzzer`**: Fuzz testing suites for network packet parsers, NBT readers, and region file decoders to identify potential crash vectors or memory anomalies. + +--- + +## Concurrency & Threading Model + +| Component | Runtime / Pool | Primary Workload | Communication Mechanism | +| :--- | :--- | :--- | :--- | +| **Network & Socket I/O** | Tokio Async Runtime | TCP packet reads, Bedrock UDP packets, framing, session handshakes | Async tasks (`tokio::spawn`), non-blocking reactors | +| **Heavy Compute** | Rayon Thread Pool | Terrain generation, noise routers, light propagation, chunk compression | Worker threads via crossbeam / Rayon iterators | +| **Inter-Thread Bridge** | Tokio Channels | Dispatching completed chunks and encoded packets back to network tasks | Non-blocking `tokio::sync::mpsc` channels | + +Pumpkin strictly adheres to this separation of concerns: +- **I/O Bound**: Managed on Tokio asynchronous tasks. +- **CPU Bound**: Managed on Rayon thread pools. +- **Communication**: Coordinated via cross-thread channels (`tokio::sync::mpsc`) without ever blocking the Tokio reactor loop. diff --git a/docs/tr_TR/developer/blocks/adding-blocks.md b/docs/tr_TR/developer/blocks/adding-blocks.md new file mode 100644 index 0000000..0770ed8 --- /dev/null +++ b/docs/tr_TR/developer/blocks/adding-blocks.md @@ -0,0 +1,182 @@ +# Adding a Block Behavior + +This guide walks through implementing interactive behavior for a block in Pumpkin. + +--- + +## When Is a Behavior Needed? + +- **Non-Interactive Blocks** (e.g., stone, dirt, terracotta): Do **not** require any custom Rust code. Their breaking speeds, hardness, and placement properties are automatically derived from `pumpkin-data`. +- **Interactive or Functional Blocks** (e.g., doors, chests, levers, crops, campfires, TNT): Require an implementation of the `BlockBehaviour` trait to respond to player clicks, redstone signals, physics steps, or timer ticks. + +--- + +## Step 1: Create the Behavior Struct + +Create a new file under `crates/pumpkin/src/block/blocks//.rs` (or directly in `crates/pumpkin/src/block/blocks/`). + +Use the `#[pumpkin_block(...)]` macro to associate your behavior struct with one or more vanilla block identifiers: + +```rust +use std::sync::Arc; +use pumpkin_data::Block; +use pumpkin_data::block_properties::{DoorLikeProperties, Facing}; +use pumpkin_macros::pumpkin_block; +use pumpkin_util::math::position::BlockPos; +use crate::block::registry::BlockActionResult; +use crate::block::{ + BlockBehaviour, NormalUseArgs, OnNeighborUpdateArgs, PlacedArgs, UseWithItemArgs, +}; +use crate::world::World; + +/// Associates this behavior struct with one or multiple block IDs. +#[pumpkin_block([ + "minecraft:oak_door", + "minecraft:iron_door", + "minecraft:spruce_door", + "minecraft:birch_door" +])] +pub struct DoorBlock; + +impl BlockBehaviour for DoorBlock { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state_id = args.world.get_block_state(args.location).await; + + // Iron doors require redstone power and cannot be opened manually by hand + if args.block.id == pumpkin_data::Block::IRON_DOOR.id { + return BlockActionResult::Pass; + } + + // Toggle open state + let is_open = DoorLikeProperties::open(state_id); + let new_state = DoorLikeProperties::with_open(state_id, !is_open); + args.world.set_block_state(args.location, new_state).await; + + BlockActionResult::Consume + } + + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + // Handle neighbor redstone signals or support block destruction + } +} +``` + +--- + +## Step 2: `BlockBehaviour` Hook Reference + +The `BlockBehaviour` trait exposes lifecycle hooks modeled on vanilla mechanics: + +### Player & Item Interaction Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `normal_use` | `(&self, args: NormalUseArgs<'_>) -> BlockActionResult` | Triggered when right-clicking the block with an empty hand. | +| `use_with_item` | `(&self, args: UseWithItemArgs<'_>) -> BlockActionResult` | Triggered when right-clicking the block while holding an item. | +| `get_screen_handler_factory` | `(&self, args: GetScreenHandlerFactoryArgs<'_>) -> Option>` | Returns an inventory container factory to open a UI screen (e.g., Chests, Crafting Tables). | + +### World Lifecycle & Physics Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `placed` | `(&self, args: PlacedArgs<'_>)` | Triggered immediately after the block is placed in the world. | +| `broken` | `(&self, args: BrokenArgs<'_>)` | Triggered when broken by a player or game mechanics. | +| `on_entity_collision` | `(&self, args: OnEntityCollisionArgs<'_>)` | Triggered when an entity walks inside, steps on, or falls into the block. | +| `on_neighbor_update` | `(&self, args: OnNeighborUpdateArgs<'_>)` | Triggered when an adjacent block state updates or is destroyed. | +| `on_projectile_hit` | `(&self, args: OnProjectileHitArgs<'_>)` | Triggered when an arrow, snowball, or projectile strikes the block. | + +### Redstone & Power Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `emits_redstone_power` | `(&self, state_id: BlockStateId) -> bool` | Declares whether the block emits redstone power to neighbors. | +| `get_weak_redstone_power` | `(&self, ...) -> u8` | Returns emitted weak power level (0–15). | +| `get_strong_redstone_power` | `(&self, ...) -> u8` | Returns emitted strong direct power level (0–15). | + +### Foliage & Bonemeal Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `is_valid_bonemeal_target` | `(&self, args: BonemealArgs<'_>) -> bool` | Checks if bone meal can be applied (e.g., crop not fully grown). | +| `is_bonemeal_success` | `(&self, args: BonemealArgs<'_>) -> bool` | Evaluates random chance roll for bone meal success. | +| `perform_bonemeal` | `(&self, args: BonemealArgs<'_>)` | Advances growth stages or spawns adjacent flora. | + +--- + +## Step 3: Modifying Block States + +All block properties are strongly typed in `pumpkin_data::block_properties`: + +```rust +use pumpkin_data::block_properties::{BedLikeProperties, Part}; + +// 1. Read typed properties from the numeric BlockStateId +let part = BedLikeProperties::part(state_id); // Part::Head or Part::Foot +let is_occupied = BedLikeProperties::occupied(state_id); + +// 2. Generate a new BlockStateId with updated properties +let updated_state = BedLikeProperties::with_occupied(state_id, true); + +// 3. Commit the updated state to the world +world.set_block_state(pos, updated_state).await; +``` + +--- + +## Step 4: Adding a Block Entity (Tile Entity) + +If your block needs to store persistent custom state (inventories, custom text, timers): + +1. Create a block entity struct in `crates/pumpkin/src/block/entities/`: + ```rust + use pumpkin_util::math::position::BlockPos; + use crate::block::entities::BlockEntity; + + pub struct CustomBlockEntity { + pub pos: BlockPos, + pub custom_counter: u32, + } + + impl BlockEntity for CustomBlockEntity { + fn to_nbt(&self) -> pumpkin_nbt::compound::NbtCompound { + let mut compound = pumpkin_nbt::compound::NbtCompound::new(); + compound.put_int("Counter", self.custom_counter as i32); + compound + } + + fn from_nbt(&mut self, tag: &pumpkin_nbt::compound::NbtCompound) { + if let Some(val) = tag.get_int("Counter") { + self.custom_counter = val as u32; + } + } + + fn tick(&mut self, world: &std::sync::Arc, pos: &BlockPos) { + // Periodic logic per game tick + } + } + ``` + +2. Link the block entity in your `BlockBehaviour`: + ```rust + impl BlockBehaviour for CustomBlock { + fn create_block_entity(&self, pos: BlockPos) -> Option>> { + Some(Arc::new(Mutex::new(CustomBlockEntity::new(pos)))) + } + } + ``` + +--- + +## Step 5: Registering the Block + +1. Export your module in `crates/pumpkin/src/block/blocks/mod.rs`: + ```rust + pub mod doors; + ``` + +2. Register the behavior struct in `crates/pumpkin/src/block/registry.rs`: + ```rust + manager.register(DoorBlock); + ``` + +At startup, `manager.register` automatically reads the block IDs declared in `#[pumpkin_block(...)]` and routes interactions to your implementation. diff --git a/docs/tr_TR/developer/blocks/index.md b/docs/tr_TR/developer/blocks/index.md new file mode 100644 index 0000000..525a200 --- /dev/null +++ b/docs/tr_TR/developer/blocks/index.md @@ -0,0 +1,104 @@ +# Block Architecture & State System + +Blocks form the static and dynamic foundation of Minecraft worlds. In Pumpkin, block handling is cleanly bifurcated into two distinct layers: + +1. **Static Data Layer ([`pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data))**: Compile-time generated metadata including numeric block IDs, block state IDs, hardness, blast resistance, light emission, opacity, collision shapes, and block state properties generated by [`pumpkin-codegen`](/developer/codegen). +2. **Dynamic Behavior Layer ([`pumpkin::block`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/block))**: Server-side behavioral hooks—such as opening containers, toggling redstone signals, growing crops, breaking triggers, and collision effects—handled via the `BlockBehaviour` trait. + +--- + +## Static Blocks vs. Interactive Behaviors + +Because Minecraft contains thousands of block state combinations, Pumpkin avoids allocating runtime behavior structs for simple, non-interactive blocks: + +| Category | Description | Examples | Runtime Behavior Required? | +| :--- | :--- | :--- | :--- | +| **Static Blocks** | Rigid solid voxels without interactive logic or custom physics | `stone`, `dirt`, `cobblestone`, `bedrock`, `obsidian` | No — Placement, breaking times, and tool requirements are handled entirely by `pumpkin-data`. | +| **Interactive Blocks** | Blocks triggered by right-clicks or tool actions | `door`, `trapdoor`, `lever`, `button`, `crafting_table` | Yes — Implements `BlockBehaviour::normal_use` or `use_with_item`. | +| **Collision Blocks** | Blocks that modify entity movement or apply forces | `honey_block`, `slime_block`, `soul_sand`, `powder_snow` | Yes — Implements `on_entity_collision` or `on_landed_upon`. | +| **Redstone Components** | Power sources, conductors, and logic gates | `redstone_wire`, `repeater`, `comparator`, `observer` | Yes — Implements `emits_redstone_power` and `on_neighbor_update`. | +| **Block Entities** | Blocks with persistent, serialized custom states | `chest`, `furnace`, `sign`, `decorated_pot`, `spawner` | Yes — Implements `create_block_entity` and links a `BlockEntity`. | + +--- + +## Block IDs, States, and Properties + +Minecraft distinguishes between a **Block** type and a specific **Block State**: + +### 1. `BlockId` and `BlockStateId` + +- **`BlockId`**: An identifier representing a base block type (e.g., `minecraft:oak_door`). +- **`BlockStateId`**: A 16-bit integer representing a unique combination of block properties (e.g., `minecraft:oak_door[facing=north,half=lower,hinge=left,open=false,powered=false]`). + +In Pumpkin, block data is looked up from precomputed lookup tables in `pumpkin-data`: + +```rust +use pumpkin_data::Block; +use pumpkin_data::BlockStateId; + +// Access static properties directly from generated data +let door_block = Block::OAK_DOOR; +let default_state_id: BlockStateId = door_block.default_state_id; + +let hardness = door_block.hardness; +let blast_resistance = door_block.blast_resistance; +let is_air = door_block.is_air; +``` + +### 2. Generated State Property Helpers + +Rather than parsing raw strings or bitmasks manually, [`pumpkin-codegen`](/developer/codegen) generates strongly typed property helpers in `pumpkin_data::block_properties`: + +```rust +use pumpkin_data::block_properties::{DoorLikeProperties, DoubleBlockHalf, Facing}; + +// Read typed properties from a numeric BlockStateId +let is_open = DoorLikeProperties::open(state_id); +let half = DoorLikeProperties::half(state_id); +let facing = DoorLikeProperties::facing(state_id); + +// Generate a new BlockStateId with mutated properties +let toggled_state_id = DoorLikeProperties::with_open(state_id, !is_open); +``` + +--- + +## Physics, Bounding Boxes & Raycasting + +Pumpkin calculates collision and raycasting bounding boxes directly using pre-baked shapes in `pumpkin-data`: + +- **Solid Collisions**: During physics ticks, an entity's axis-aligned bounding box (AABB) is tested against surrounding block collision voxels. +- **Raycasting**: When a player targets a block to place or break, client raycasts are validated against the server-side block outline shapes. +- **Custom Movement Hooks**: Blocks like slime blocks or honey blocks override vertical rebound or horizontal velocity: + - `bounce_entity_after_fall`: Dampens fall damage and multiplies rebound velocity. + - `stop_vertical_movement_after_fall`: Zeros fall velocity upon contact. + +--- + +## Block Entities (Tile Entities) + +When a block must store arbitrary dynamic data that cannot fit into standard block state bits (such as container inventories, custom sign text, brewing timers, or mob spawner delays), it uses a **Block Entity**: + +| Component | Responsibility | Implementation File | +| :--- | :--- | :--- | +| `BlockEntity` Trait | Defines NBT serialization (`to_nbt`, `from_nbt`) and tick execution | [`crates/pumpkin/src/block/entities/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/block/entities) | +| `create_block_entity` | Hook in `BlockBehaviour` called when placing the block | Attached to relevant block behaviors | +| Chunk Entity Storage | Manages active block entities and coordinates within chunk sections | [`crates/pumpkin-world`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-world) | + +--- + +## The Block Registry & Dispatch System + +Block interaction events are routed through the central `BlockRegistry` in [`crates/pumpkin/src/block/registry.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/block/registry.rs): + +1. **Registration**: At server initialization, all behavior structs are registered into the `BlockManager`. +2. **Mapping**: The manager maps numeric `BlockId` keys to thread-safe behavior instances (`Arc`). +3. **Dispatch**: When the network layer receives block action packets (such as `CUseItemOn` or block dig packets), the server looks up the behavior in $O(1)$ time and invokes the relevant hook. + +--- + +## Next Steps + +To implement a new interactive block behavior, continue to the practical guide: + +- **[Adding a Block](/developer/blocks/adding-blocks)**: Complete walk-through of declaring attributes, implementing interaction hooks, creating block entities, and registering behaviors. diff --git a/docs/tr_TR/developer/codegen.md b/docs/tr_TR/developer/codegen.md new file mode 100644 index 0000000..3908d04 --- /dev/null +++ b/docs/tr_TR/developer/codegen.md @@ -0,0 +1,66 @@ +# Code Generation (`pumpkin-codegen`) + +Minecraft is a massive game with thousands of block states, items, packet IDs, sound events, recipes, and biome parameters. These registries frequently change between Minecraft updates. + +To maintain 100% fidelity without manually maintaining tens of thousands of error-prone constants, Pumpkin utilizes an automated code generation tool: [`tools/pumpkin-codegen`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/tools/pumpkin-codegen). + +--- + +## Purpose & Workflow + +`pumpkin-codegen` parses official Minecraft server data generator reports, asset files, and version specifications, and outputs zero-cost, strongly-typed Rust files directly into [`crates/pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data). + +The pipeline operates in three distinct stages: +1. **Ingestion**: Reads raw JSON assets and vanilla server reports from `assets/` and `assets/datapacks/`. +2. **Transformation**: Normalizes states, calculates bitsets, processes ViaVersion remappings, and optimizes lookup tables. +3. **Emission**: Emits compiled Rust constants and structs into [`crates/pumpkin-data/src/generated`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data/src/generated). + +--- + +## Generated Datasets + +`pumpkin-codegen` generates the following components in `pumpkin-data`: + +| Category | Description | Generated Source | +| :--- | :--- | :--- | +| **Packets** | Serverbound and clientbound packet IDs for all protocol states across supported Minecraft versions. | `packet.rs` | +| **Blocks** | Block definitions, default states, state properties (facing, powered, waterlogged, etc.), and block collision boxes. | `block.rs` | +| **Items** | Item definitions, stack sizes, durability, data components, food properties, and equipment slots. | `item.rs` | +| **Registries** | Dynamic registries for biomes, dimensions, chat types, damage types, trim materials, and banner patterns. | `registry.rs` | +| **Biomes & Noise** | Climate parameters, noise router configurations, 3D noise settings, and surface rules. | `biome.rs`, `noise_router.rs` | +| **Entities** | Entity types, default bounding boxes, attributes, and tracked entity metadata schemas. | `entity_type.rs`, `tracked_data.rs` | +| **Recipes & Crafting** | Shaped, shapeless, smelting, blasting, smoking, and stonecutting recipes. | `recipes.rs` | +| **Tags** | Block tags, item tags, fluid tags, and entity type tags (e.g., `#minecraft:mineable/pickaxe`). | `tag.rs` | +| **Enchantments & Potions** | Enchantment definitions, potion effects, and brewing stand recipes. | `enchantments.rs`, `potion.rs` | +| **Translations** | Official localization keys for chat messages and item names. | `translations.rs` | +| **Plugin WIT** | WebAssembly Component Model WIT files defining plugin interfaces. | `wit/` | + +--- + +## Running Code Generation + +To run the code generator locally: + +```bash +# Navigate to the workspace root +cd Pumpkin + +# Run the codegen tool +cargo run -p pumpkin-codegen +``` + +### When Should You Run Codegen? + +- **Upgrading to a New Minecraft Version**: When porting Pumpkin to a new minor or major Minecraft release. +- **Adding Protocol Mappings**: When adding support for an older or newer client protocol version. +- **Updating WIT Definitions**: When adding new functions or types to the WebAssembly plugin API. + +--- + +## Data Sources + +`pumpkin-codegen` reads data from several primary sources: + +1. **Minecraft Data Generator Reports**: Generated by running `java -DbundlerMainClass=net.minecraft.data.Main -jar server.jar --reports`. +2. **Minecraft Client Assets**: Extracted from client `.jar` files for translation keys, model shapes, and sound mappings. +3. **Minecraft Protocol Wiki**: Cross-referenced with protocol specifications for packet IDs. diff --git a/docs/tr_TR/developer/commands/adding-commands.md b/docs/tr_TR/developer/commands/adding-commands.md new file mode 100644 index 0000000..eda739a --- /dev/null +++ b/docs/tr_TR/developer/commands/adding-commands.md @@ -0,0 +1,246 @@ +# Adding a Command + +This guide explains how to implement and register a new command in the Pumpkin server codebase. + +Commands in Pumpkin are implemented in the `crates/pumpkin/src/command/commands/` directory and registered into the central [`CommandDispatcher`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/dispatcher.rs) during server initialization. + +--- + +## Anatomy of a Pumpkin Command + +A typical command file contains: +1. **Metadata Constants**: Command description, permission node identifier, and argument names. +2. **Permission Registration**: Declaring default OP requirements in the server's [`PermissionRegistry`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-util/src/permission.rs). +3. **Command Executors**: Structs implementing the [`CommandExecutor`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/mod.rs) trait for each terminal node or subcommand. +4. **Registration Function**: Constructing the argument tree using fluent builders (`command`, `literal`, `argument`). + +--- + +## Step-by-Step Implementation: Creating `/heal` + +Let's implement a complete `/heal` command that can heal either the executing player or a set of target players: + +``` +/heal -> Heals the executing sender +/heal -> Heals target player(s) +/heal -> Heals target player(s) by a specified amount +``` + +### 1. Create the File + +Create `crates/pumpkin/src/command/commands/heal.rs`: + +```rust +use std::sync::Arc; + +use crate::command::argument_builder::{ArgumentBuilder, argument, command}; +use crate::command::argument_types::core::float::FloatArgumentType; +use crate::command::argument_types::entity::EntityArgumentType; +use crate::command::context::command_context::CommandContext; +use crate::command::errors::error_types::CommandErrorType; +use crate::command::node::dispatcher::CommandDispatcher; +use crate::command::node::{CommandExecutor, CommandExecutorResult}; +use crate::entity::EntityBase; +use pumpkin_data::translation; +use pumpkin_util::PermissionLvl; +use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry}; +use pumpkin_util::text::TextComponent; + +const DESCRIPTION: &str = "Restores health to target entities."; +const PERMISSION: &str = "pumpkin:command.heal"; + +const ARG_TARGETS: &str = "targets"; +const ARG_AMOUNT: &str = "amount"; + +const ERROR_NOT_PLAYER: CommandErrorType<0> = CommandErrorType::new( + translation::java::PERMISSIONS_REQUIRES_PLAYER, + translation::java::PERMISSIONS_REQUIRES_PLAYER, +); +``` + +### 2. Implement the Executors + +Command execution logic is encapsulated in structs implementing [`CommandExecutor`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/mod.rs): + +```rust +/// Heals the executing player to full health. +struct SelfHealExecutor; + +impl CommandExecutor for SelfHealExecutor { + fn execute(&self, context: &CommandContext) -> CommandExecutorResult { + let player = context + .source + .output + .as_player() + .ok_or_else(|| ERROR_NOT_PLAYER.create_without_context())?; + + // Restore player health + player.living_entity.set_health(20.0); + + context.source.send_feedback( + TextComponent::text("Fully restored your health!"), + true, // Broadcast to operators if configured + ); + + // Return number of affected entities + Ok(1) + } +} + +/// Heals specified target players. +struct TargetsHealExecutor { + has_amount: bool, +} + +impl CommandExecutor for TargetsHealExecutor { + fn execute(&self, context: &CommandContext) -> CommandExecutorResult { + let targets = EntityArgumentType::get_players(context, ARG_TARGETS)?; + + let amount = if self.has_amount { + FloatArgumentType::get(context, ARG_AMOUNT)? + } else { + 20.0 + }; + + let count = targets.len(); + for target in &targets { + let current = target.living_entity.health.load(); + target.living_entity.set_health((current + amount).min(20.0)); + } + + let msg = if count == 1 { + TextComponent::text(format!("Healed {} by {} HP", targets[0].gameprofile.name, amount)) + } else { + TextComponent::text(format!("Healed {} players by {} HP", count, amount)) + }; + + context.source.send_feedback(msg, true); + + Ok(count as i32) + } +} +``` + +### 3. Build the Command Tree + +Use the fluent builder functions `command`, `literal`, and `argument` to assemble the syntax tree and register the permission: + +```rust +pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) { + // 1. Register permission node with default requirement (OP level 2) + registry.register_permission_or_panic(Permission::new( + PERMISSION, + DESCRIPTION, + PermissionDefault::Op(PermissionLvl::Two), + )); + + // 2. Assemble command tree + dispatcher.register( + command("heal", DESCRIPTION) + .requires(PERMISSION) + .executes(SelfHealExecutor) + .then( + argument(ARG_TARGETS, EntityArgumentType::Players) + .executes(TargetsHealExecutor { has_amount: false }) + .then( + argument(ARG_AMOUNT, FloatArgumentType::new(1.0, 100.0)) + .executes(TargetsHealExecutor { has_amount: true }), + ), + ), + ); +} +``` + +--- + +## Command Structure Patterns + +### Adding Subcommand Literals +Use `literal("subcommand_name")` to create keyword branches: + +```rust +use crate::command::argument_builder::{argument, command, literal}; + +dispatcher.register( + command("weather", "Set the world weather") + .requires("minecraft:command.weather") + .then(literal("clear").executes(ClearWeatherExecutor)) + .then(literal("rain").executes(RainWeatherExecutor)) + .then(literal("thunder").executes(ThunderWeatherExecutor)) +); +``` + +### Cross-Platform Translation Feedback +Pumpkin servers serve both Java and Bedrock Edition players simultaneously. Whenever possible, use vanilla translation keys with `TextComponent::translate_cross` so messages render localized on the client: + +```rust +use pumpkin_data::translation; +use pumpkin_util::text::TextComponent; + +let msg = TextComponent::translate_cross( + translation::java::COMMANDS_KILL_SUCCESS_SINGLE, + translation::bedrock::COMMANDS_KILL_SUCCESSFUL, + [target.get_display_name()], +); + +context.source.send_feedback(msg, true); +``` + +### Error Handling in Executors +Executors must return `CommandExecutorResult`, which resolves to `Result`. If validation fails, return an error cleanly without panicking: + +```rust +use crate::command::errors::error_types::CommandErrorType; + +const ERROR_INVALID_VALUE: CommandErrorType<0> = CommandErrorType::new( + "Amount must be greater than zero.", + "Amount must be greater than zero.", +); + +if amount <= 0.0 { + return Err(ERROR_INVALID_VALUE.create_without_context()); +} +``` + +--- + +## Registering in `default_dispatcher` + +Once your command is implemented, register it in `crates/pumpkin/src/command/commands/mod.rs`: + +1. **Declare the Module**: + ```rust + // In crates/pumpkin/src/command/commands/mod.rs + mod heal; + ``` + +2. **Invoke `register` in `default_dispatcher`**: + ```rust + pub fn default_dispatcher( + permission_manager: &PermissionManager, + commands_config: &CommandsConfig, + ) -> CommandDispatcher { + let registry = &permission_manager.registry; + let mut dispatcher = CommandDispatcher::new(); + + // ... existing registrations ... + heal::register(&mut dispatcher, registry); + + dispatcher + } + ``` + +--- + +## Best Practices Checklist + +- [ ] **No Panics or Unwraps**: Use `?` or return descriptive `CommandSyntaxError`s when arguments are invalid. +- [ ] **Permission Nodes**: Prefix permission strings with appropriate namespaces (`minecraft:command.` for vanilla commands, `pumpkin:command.` for server-specific commands). +- [ ] **Return Success Counts**: The returned `i32` represents the command's exit code or number of affected objects for redstone command blocks and `/execute store result`. +- [ ] **Respect GameRules**: When notifying players other than the sender, check if `send_command_feedback` is enabled: + ```rust + let server = context.source.server(); + if server.level_info.load().game_rules.send_command_feedback { + target.send_system_message(&feedback); + } + ``` diff --git a/docs/tr_TR/developer/commands/arguments-and-suggestions.md b/docs/tr_TR/developer/commands/arguments-and-suggestions.md new file mode 100644 index 0000000..6794ae1 --- /dev/null +++ b/docs/tr_TR/developer/commands/arguments-and-suggestions.md @@ -0,0 +1,159 @@ +# Argument Types & Suggestions + +Pumpkin provides a comprehensive suite of type-safe command arguments through `pumpkin-command` and the main `pumpkin` server crate. Arguments deserialize raw token streams into structured Rust types, validate ranges, and furnish tab-completion suggestions to clients. + +--- + +## Standard Argument Types + +Argument parsers implement the [`ArgumentType`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/argument_types/argument_type.rs) trait. The following built-in types are available for command builders: + +### 1. Primitive & Scalar Types + +| Argument Type | Import Path | Deserialized Type | Description | +| :--- | :--- | :--- | :--- | +| **`IntegerArgumentType`** | `...::core::integer` | `i32` | Standard 32-bit integer with optional `[min, max]` bounds. | +| **`FloatArgumentType`** | `...::core::float` | `f32` | 32-bit floating point number with bounds. | +| **`DoubleArgumentType`** | `...::core::double` | `f64` | 64-bit floating point number with bounds. | +| **`LongArgumentType`** | `...::core::long` | `i64` | 64-bit integer with bounds. | +| **`BoolArgumentType`** | `...::core::bool` | `bool` | Boolean value (`true` or `false`). | +| **`StringArgumentType`** | `...::core::string` | `String` | String tokens based on quoting rules. | + +#### String Argument Behaviors +- **`StringArgumentType::Word`**: Parses a single word without whitespace. +- **`StringArgumentType::Quotable`**: Parses either a single word or a quoted phrase (`"hello world"`). +- **`StringArgumentType::Greedy`**: Consumes all remaining characters in the input stream (ideal for chat/broadcast commands like `/say` and `/me`). + +--- + +## Coordinate Argument Types + +Minecraft coordinates support absolute positions, relative offsets (`~`), and local caret directions (`^`): + +```rust +use crate::command::argument_builder::argument; +use crate::command::argument_types::coordinates::block_pos::BlockPosArgumentType; +use crate::command::argument_types::coordinates::vec3::Vec3ArgumentType; + +// Block position (integer BlockPos) +argument("pos", BlockPosArgumentType) + +// Precise entity coordinates (f64 Vector3) +argument("location", Vec3ArgumentType) +``` + +### Retrieving Parsed Coordinates +Inside an executor, coordinate arguments resolve relative offsets against the sender's current position and orientation in the [`CommandSource`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/command/context/command_source.rs): + +```rust +// Extracts centered f64 coordinates (handling ~ and ^ relative to sender) +let pos: Vector3 = Vec3ArgumentType::get_coordinates(context, "location")?; + +// Extracts integer block position +let block_pos: BlockPos = BlockPosArgumentType::get_block_pos(context, "pos")?; +``` + +--- + +## Entity Selectors (`EntityArgumentType`) + +Entity selectors allow players and command blocks to target entities using player names, UUIDs, or vanilla target selectors (`@p`, `@r`, `@a`, `@e`, `@s`). + +In Pumpkin, entity selectors are accessed via [`EntityArgumentType`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/command/argument_types/entity.rs): + +```rust +pub enum EntityArgumentType { + /// Matches exactly one player + Player, + /// Matches zero or more players + Players, + /// Matches exactly one entity + Entity, + /// Matches zero or more entities + Entities, +} +``` + +### Resolution in Executors + +```rust +// 1. Target single player +let player: Arc = EntityArgumentType::get_player(context, "target")?; + +// 2. Target multiple players +let players: Vec> = EntityArgumentType::get_players(context, "targets")?; + +// 3. Target single living entity or mob +let entity: Arc = EntityArgumentType::get_entity(context, "target")?; + +// 4. Target multiple entities +let entities: Vec> = EntityArgumentType::get_entities(context, "targets")?; +``` + +### Supported Selector Syntaxes +Pumpkin's entity selector parser (`crates/pumpkin/src/command/argument_types/entity_selector/`) parses and validates: +- `@p`: Nearest player relative to execution coordinates. +- `@r`: Random player. +- `@a`: All online players. +- `@e`: All entities in loaded world chunks. +- `@s`: Executing entity (`context.source.entity`). +- **Bracket Arguments**: Filter queries like `@e[type=zombie,distance=..10,limit=5,sort=nearest]`. + +--- + +## Registry & Game Mechanic Types + +Pumpkin provides specialized argument parsers for Minecraft data types: + +- **`GameModeArgumentType`**: Resolves `survival`, `creative`, `adventure`, `spectator` or numeric IDs `0`–`3`. +- **`BlockArgumentType`**: Parses block identifiers and block states (e.g., `minecraft:chest[facing=north]`). +- **`ItemArgumentType`**: Parses item identifiers and optional data components (e.g., `minecraft:diamond_sword`). +- **`NbtArgumentType`**: Parses SNBT (Stringified NBT) tags into Pumpkin's NBT representation. +- **`TimeArgumentType`**: Parses durations with suffixes (e.g., `10s`, `20t`, `1d`). + +--- + +## Suggestion Providers & Tab Completion + +Arguments can supply dynamic tab-completion candidates to connected players using the [`SuggestionProvider`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/suggestion/mod.rs) trait: + +```rust +use crate::command::context::command_context::CommandContext; +use crate::command::suggestion::provider::SuggestionProvider; +use pumpkin_command::suggestion::suggestions::{Suggestions, SuggestionsBuilder}; + +struct CustomSuggestionProvider; + +impl SuggestionProvider for CustomSuggestionProvider { + fn suggest( + &self, + context: &CommandContext, + mut builder: SuggestionsBuilder, + ) -> Suggestions { + // Retrieve already typed token prefix + let remaining = builder.remaining(); + + let candidates = ["alpha", "bravo", "charlie"]; + for candidate in candidates { + if candidate.starts_with(remaining) { + builder.suggest(candidate); + } + } + + builder.build() + } +} +``` + +### Attaching Suggestions to Arguments +Use the `.suggests(...)` method on `RequiredArgumentBuilder`: + +```rust +argument("code", StringArgumentType::Word) + .suggests(CustomSuggestionProvider) + .executes(MyExecutor) +``` + +### Client Completion Protocol +- **Java Edition**: When a player presses `Tab`, the client transmits a `ServerboundCommandSuggestion` packet. Pumpkin matches the cursor position within the `CommandDispatcher`, executes the target node's `SuggestionProvider`, and responds with `ClientboundCommandSuggestions` containing completion ranges and tooltip tooltips. +- **Bedrock Edition**: Suggestion options for literals and enums are serialized ahead of time in the `CAvailableCommands` packet as dynamic enum value tables. diff --git a/docs/tr_TR/developer/commands/execution-and-context.md b/docs/tr_TR/developer/commands/execution-and-context.md new file mode 100644 index 0000000..6f5f044 --- /dev/null +++ b/docs/tr_TR/developer/commands/execution-and-context.md @@ -0,0 +1,118 @@ +# Execution & Context + +In Pumpkin, command execution separates the **physical origin** of a command from its **runtime context**. This design enables complex command routing, dimension shifts, conditional execution, and vanilla-compliant `/execute` chaining. + +--- + +## `CommandSender` vs. `CommandSource` + +Understanding the distinction between `CommandSender` and `CommandSource` is fundamental to developing commands in Pumpkin: + +| Attribute | `CommandSender` | `CommandSource` | +| :--- | :--- | :--- | +| **Concept** | The physical origin and network destination | The mutable runtime execution environment | +| **Mutability** | Immutable throughout command execution | Cloned and modified across `/execute` branches | +| **Key Fields** | Terminal socket, connection handle, RCON buffer | World, position coordinates, rotation, entity, silent flag | +| **Creation** | Instantiated on network connection or terminal input | Constructed via `sender.into_source(&server)` | + +### 1. `CommandSender` (Physical Caller) +Represents the entity or socket that initiated the command and receives its output: + +```rust +pub enum CommandSender { + Console, + Rcon(Arc>>), + Player(Arc), + CommandBlock(Arc, Arc), + Dummy, +} +``` + +- **Output Target**: Determines where messages formatted with `send_message` are routed (e.g., standard out, RCON buffer, player packet, or block entity NBT). +- **Hard Permissions**: Holds fixed base permissions (e.g., Console has level 4 bypass; Command Blocks have level 2). + +### 2. `CommandSource` (Execution Environment) +Represents the mutable runtime state during command evaluation: + +```rust +pub struct CommandSource { + pub output: CommandSender, + pub world: Option>, + pub entity: Option>, + pub position: Vector3, + pub rotation: Vector2, + pub name: String, + pub display_name: TextComponent, + pub server: Option>, + pub silent: bool, + pub command_result_taker: ResultValueTaker, + pub entity_anchor: EntityAnchor, +} +``` + +A sender converts into a `CommandSource` via `sender.into_source(&server)`. + +--- + +## The `/execute` Command & Context Redirection + +The `/execute` command manipulates `CommandSource` properties before passing control to a downstream command. + +### Branching Modifiers +- **`as `**: Changes `context.source.entity` to target entities without altering position or rotation. +- **`at `**: Updates `context.source.world`, `position`, and `rotation` to match the target entity. +- **`positioned `**: Modifies `context.source.position` to new coordinates. +- **`in `**: Changes `context.source.world` to the specified dimension (Overworld, Nether, The End). +- **`anchored (feet|eyes)`**: Adjusts coordinate offsets for line-of-sight and facing checks. + +### Context Forking & Chaining +When `/execute` matches multiple targets (e.g., `/execute as @e run ...`), `pumpkin-command` uses **forking**: +1. The dispatcher forks the execution context once per matched target. +2. The child command executes independently for each cloned `CommandSource`. +3. The cumulative success count is aggregated across all executions. + +--- + +## Result Values & Success Counts + +Vanilla commands communicate results back through integer exit codes and return values. + +### The Return Value Callback +Executors return `CommandExecutorResult` (`Result`). The returned integer signifies: +- **0**: Failure or zero entities affected. +- **> 0**: Number of entities affected, blocks changed, or items cleared. + +For Command Blocks, this value is persisted into the block entity's `SuccessCount`: + +```rust +pub fn set_success_count(&self, count: u32) { + if let Self::CommandBlock(c, _) = self { + c.success_count.store(count, Ordering::SeqCst); + } +} +``` + +### `/execute store` +When chained with `/execute store (result|success) ...`, Pumpkin intercepts the return value via `ReturnValueCallback` and writes it to scoreboards, bossbars, or entity NBT tags. + +--- + +## Feedback & GameRules Integration + +Sending output to the command sender is governed by server configuration and world game rules: + +```rust +context.source.send_feedback( + TextComponent::text("Command executed successfully!"), + true, // Broadcast to server operators +); +``` + +### Output Rules Matrix + +| Condition | Feedback Behavior | +| :--- | :--- | +| `silent == true` | Output is suppressed completely. | +| `send_command_feedback == false` | Player senders do not receive success feedback (errors are still displayed). | +| `command_block_output == false` | Command block execution output is not broadcast to online operators. | +| `broadcast_console_to_ops == true` | Server console and RCON commands emit a copy of their feedback to all online OP level 2+ players. | diff --git a/docs/tr_TR/developer/commands/index.md b/docs/tr_TR/developer/commands/index.md new file mode 100644 index 0000000..3b31eb9 --- /dev/null +++ b/docs/tr_TR/developer/commands/index.md @@ -0,0 +1,142 @@ +# Commands Architecture & Dispatcher + +Pumpkin features a high-performance, pure-Rust implementation of the **Brigadier** command tree specification through the dedicated `pumpkin-command` crate. It coordinates command parsing, requirement resolution, type-safe argument deserialization, execution branching, and dual-edition client tree synchronization for both **Minecraft: Java Edition** and **Minecraft: Bedrock Edition**. + +### Command Processing Pipeline + +Every command executed in Pumpkin flows through the following discrete stages: + +1. **Input Normalization**: Strips single leading slashes while preserving plugin multi-slash conventions (such as `//set`). +2. **Availability Check**: Rejects commands disabled via server configuration with standard `unknown command` syntax errors. +3. **Permission Evaluation**: Validates requirements (`PermissionLvl` or custom registry nodes) against the executing sender. +4. **Tokenization & Parsing**: `StringReader` recursively walks the `Tree`, matching literal keywords and parsing typed argument parameters. +5. **Context Dispatch**: Invokes the matched `CommandExecutor` callback with a typed `CommandContext`. +6. **Feedback & Result Consumption**: Formats cross-platform translation components, notifies observers, and writes exit codes back to the caller. + +--- + +## The `pumpkin-command` Crate + +Pumpkin decouples command parsing and tree representation from the main server runtime into the modular `pumpkin-command` crate (`crates/pumpkin-command`). + +Key components of `pumpkin-command` include: +- **`Tree`**: Contiguous vector-backed storage of attached command nodes (`AttachedNode`). +- **`CommandDispatcher`**: The central registry and dispatch engine. It processes raw command strings, parses tokens with `StringReader`, traverses the command tree, and executes target callbacks. +- **`ArgumentBuilder`**: Fluent builder API (`command`, `literal`, `argument`) modeled after Mojang's Brigadier library. +- **`CommandSource`**: Generic execution state representing the current position, rotation, dimension, entity, and feedback output stream. + +--- + +## The Command Node Hierarchy + +Commands in Pumpkin are modeled as directed graphs of nodes. To optimize memory allocations and avoid circular reference issues during building, the system separates nodes into **detached** and **attached** states: + +### 1. Detached Nodes (`DetachedNode`) +Constructed using fluent builders (`CommandArgumentBuilder`, `LiteralArgumentBuilder`, `RequiredArgumentBuilder`) before registration: +- **`CommandDetachedNode`**: Represents a root command entrypoint with an identifier and a description. +- **`LiteralDetachedNode`**: Represents a literal keyword in a subcommand branch (e.g., `set` in `/gamemode creative`). +- **`ArgumentDetachedNode`**: Represents a typed parameter parsed by an `ArgumentType` (e.g., coordinates, entity selectors, integer ranges). + +### 2. Attached Nodes (`AttachedNode`) +Once registered into the `CommandDispatcher`, detached trees are flattened into an indexed `Tree` structure: +```rust +pub enum AttachedNode { + Root(RootAttachedNode), + Literal(LiteralAttachedNode), + Argument(ArgumentAttachedNode), + Command(CommandAttachedNode), +} +``` +Each attached node has: +- **`NodeId`**: A zero-cost typed index (`NonZero`) into the contiguous node arena. +- **`children`**: Fast lookup map (`FxHashMap`) for literals and child arguments. +- **`requirements`**: Predicates determining whether a sender has permission to view or execute the node. +- **`command`**: Optional `CommandExecutor` callback invoked when parsing terminates on this node. +- **`redirect`**: Optional `Redirection` pointer to loop back or redirect to another sub-tree (used heavily by `/execute`). + +--- + +## Dispatch & Execution Pipeline + +When a command arrives at the server, `CommandDispatcher::handle_command` processes it through several stages: + +```rust +pub fn handle_command<'a>(&'a self, source: &S, mut input: &'a str) { + // 1. Strip single leading slash (preserving double-slash commands like //set) + if let Some(sliced) = input.strip_prefix('/') { + let first_token = input.split_whitespace().next().unwrap_or(""); + let sliced_token = sliced.split_whitespace().next().unwrap_or(""); + if !self.has_command(first_token) + && (self.has_command(sliced_token) || !first_token.starts_with("//")) + { + input = sliced; + } + } + + // 2. Check disabled commands from configuration + if self.is_disabled(Self::command_name(input)) { + let reader = StringReader::new(input); + Self::send_error_to_source(source, DISPATCHER_UNKNOWN_COMMAND.create(&reader), input); + return; + } + + // 3. Parse and execute + let output = self.execute_input(input, source); + if let Err(error) = output { + Self::send_error_to_source(source, error, input); + } +} +``` + +### Syntax Error Formatting +When parsing encounters invalid syntax, unexpected tokens, or out-of-range arguments, Pumpkin constructs a `CommandSyntaxError`. If context is available, the dispatcher formats a visual pointer highlighting exactly where the parser failed: + +``` +Unknown or incomplete command, see below for error +/gamemode creatve<--[HERE] +``` + +In the Java Edition client, this error text is annotated with a `ClickEvent::SuggestCommand`, allowing the player to click the error in chat to copy the failed command into their input box for quick editing. + +--- + +## Dual-Edition Network Synchronization + +Unlike vanilla servers which only serialize command graphs for Java Edition, Pumpkin dynamically synchronizes the registered command tree across both Java and Bedrock clients: + +### 1. Java Edition: `CCommands` Packet +When a player joins or their permission level updates (e.g., via `/op` or `/deop`), Pumpkin builds and sends a `CCommands` packet (`pumpkin_protocol::java::client::play::CCommands`): +- Converts `AttachedNode`s into a flat array of `ProtoNode` entries. +- Filters out disabled commands and nodes where `requirements.evaluate(source)` returns `false`. +- Serializes argument parsers (`brigadier:string`, `brigadier:integer`, `minecraft:entity`, etc.) and client-side suggestion identifiers. + +### 2. Bedrock Edition: `CAvailableCommands` Packet +Bedrock Edition uses an overload-and-enum format rather than Brigadier's recursive graph. In `client_suggestions.rs`, Pumpkin traverses the `AttachedNode` tree to synthesize: +- **`CommandData`**: Root command name, description, permission flags, and aliases. +- **`OverloadData`**: Flattened permutations of argument chains leading to executable endpoints. +- **`ParamData`**: Bedrock parameter types, bitflags (`ARG_FLAG_VALID`, `ARG_FLAG_ENUM`), and dynamically collected enum value tables. + +--- + +## Universal Execution Targets + +Pumpkin commands can be executed across diverse senders: + +| Sender | Source Representation | Output Destination | Default Permission | +| :--- | :--- | :--- | :--- | +| **Player** | `CommandSender::Player(Arc)` | Client system chat packet | Loaded from permission level / permissions file | +| **Server Console** | `CommandSender::Console` | Standard stdout (`tracing::info!`) | Level 4 (Full administrative bypass) | +| **RCON Console** | `CommandSender::Rcon(Arc>>)` | Network response packet payload | Level 4 | +| **Command Block** | `CommandSender::CommandBlock(Arc, Arc)` | NBT `LastOutput` field | Level 2 | +| **Datapack Function** | `CommandSender` inherited from caller | Inherited | Inherited | +| **WASM Plugin** | Invoked through WIT `execute_command` host call | Captured via `ResultDeferrer` | Configurable | + +--- + +## Command Configuration & Disabling + +Pumpkin allows server administrators to disable individual commands via `config/commands.toml` (`CommandsConfig`). When a command is disabled: +1. It is omitted from the `CCommands` packet for Java clients. +2. It is omitted from the `CAvailableCommands` packet for Bedrock clients. +3. Tab completion ignores the command entirely. +4. Any attempt to invoke the command returns `DISPATCHER_UNKNOWN_COMMAND`. diff --git a/docs/tr_TR/developer/entities/ai-and-mobs.md b/docs/tr_TR/developer/entities/ai-and-mobs.md new file mode 100644 index 0000000..bb1652f --- /dev/null +++ b/docs/tr_TR/developer/entities/ai-and-mobs.md @@ -0,0 +1,111 @@ +# Mob AI & Goal System + +Pumpkin implements a modular, priority-driven Artificial Intelligence (AI) goal system for computer-controlled mobs, located in [`crates/pumpkin/src/entity/ai/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity/ai) and [`crates/pumpkin/src/entity/mob/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity/mob). + +--- + +## The Goal System Architecture + +Mob behaviors are decomposed into independent modular tasks called **Goals**. Each mob possesses a **GoalSelector** that maintains an ordered collection of goals evaluated on every server tick. + +### Goal Priority Hierarchy + +Goals are assigned an integer priority (where lower numbers represent higher precedence). During each tick, higher-priority actions preempt lower-priority background tasks: + +| Priority | Goal Type | Evaluation Condition | Behavior | +| :--- | :--- | :--- | :--- | +| **1 (Highest)** | `SwimGoal` | Entity is submerged in water | Jump repeatedly to swim to the water surface and avoid drowning. | +| **2** | `MeleeAttackGoal` | Valid hostile target in sight/range | Navigate towards target entity and execute melee attacks. | +| **3** | `WanderAroundGoal` | No active combat target | Pick a random nearby navigable voxel and walk to it. | +| **4 (Lowest)** | `LookAtEntityGoal` | Player or mob within observation radius | Rotate head and body pitch/yaw towards the target entity. | + +### Execution Flow + +1. **Evaluation**: On every game tick, `GoalSelector::tick()` iterates through registered goals in order of priority. +2. **Activation**: If an inactive goal satisfies `can_start()`, it is activated via `start()`. +3. **Execution**: Active goals run their per-tick logic in `tick()`. +4. **Preemption & Termination**: If `should_continue()` returns `false`, or a mutually exclusive goal with higher priority activates, `stop()` is called and the goal resets. + +### The `Goal` Lifecycle + +Every AI behavior implements the `Goal` trait: + +```rust +pub trait Goal: Send + Sync { + /// Evaluates preconditions to determine if this goal can activate. + fn can_start(&self) -> bool; + + /// Called once when the goal transitions from inactive to active. + fn start(&mut self) {} + + /// Evaluates whether the goal should continue executing on subsequent ticks. + fn should_continue(&self) -> bool { + self.can_start() + } + + /// Executes the continuous behavior on each server tick. + fn tick(&mut self) {} + + /// Called when the goal completes, fails, or is preempted by a higher priority goal. + fn stop(&mut self) {} +} +``` + +--- + +## Standard Built-in Goals + +Pumpkin provides standard vanilla AI behaviors in `crates/pumpkin/src/entity/ai/`: + +| Goal | Description | Target Mobs | +| :--- | :--- | :--- | +| **`SwimGoal`** | Jumps repeatedly while submerged in water to prevent drowning. | Almost all land mobs | +| **`MeleeAttackGoal`** | Navigates towards an attack target and executes a strike when within reach. | Zombies, Spiders, Iron Golems | +| **`RangedAttackGoal`** | Stays at range and shoots projectiles (arrows, snowballs). | Skeletons, Pillagers, Blazes | +| **`FleeSunGoal`** | Seeks shade or water during daytime to prevent combustion. | Zombies, Skeletons | +| **`PanicGoal`** | Sprints in random directions when taking damage. | Cows, Pigs, Sheep, Chickens | +| **`WanderAroundGoal`** | Selects random accessible coordinates nearby when idle. | Passive & neutral mobs | +| **`LookAtEntityGoal`** | Rotates head to face nearby players within sensory range. | Villagers, Mobs | + +--- + +## Navigation & Pathfinding + +Mobs navigate the block world using Pumpkin's 3D grid pathfinder: + +1. **Path Calculation**: Resolves a path from the mob's origin to target coordinates using an A* algorithm optimized for Minecraft block geometries. +2. **Obstacle Avoidance**: + - Avoids hazardous blocks (lava, fire, sweet berry bushes). + - Prevents walking off ledges higher than the mob's fall threshold. + - Computes jumping steps over 1-block obstacles. +3. **Waypoint Steering**: The navigation engine updates the mob's velocity vectors per tick to smoothly guide the entity along waypoints. + +--- + +## Entity Attributes System + +Mob capabilities and stats are governed by the **Attributes** system in [`crates/pumpkin/src/entity/attributes.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/entity/attributes.rs): + +```rust +use pumpkin_data::entity::attribute::EntityAttribute; + +// Common vanilla attributes +EntityAttribute::GENERIC_MAX_HEALTH; // Base hit points (e.g. 20.0 for Zombies) +EntityAttribute::GENERIC_MOVEMENT_SPEED; // Base walking velocity (e.g. 0.23) +EntityAttribute::GENERIC_ATTACK_DAMAGE; // Melee attack damage (e.g. 3.0) +EntityAttribute::GENERIC_FOLLOW_RANGE; // Sensory detection range in blocks (e.g. 35.0) +EntityAttribute::GENERIC_KNOCKBACK_RESISTANCE; // Resistance against knockback (0.0 to 1.0) +``` + +### Modifiers +Attributes support dynamic modifiers with additive or multiplicative calculations (e.g., speed bonuses from Swiftness potions or armor attribute boosts). + +--- + +## Ageable & Passive Entities + +Passive animals (cows, sheep, pigs) implement the `Ageable` trait: + +- **Baby State**: Renders with smaller collision boxes and oversized heads. +- **Growing Age**: A negative tick timer counting upward to `0`. When it reaches `0`, the entity matures into an adult. +- **Breeding Cooldown**: Positive tick timer tracking cooldowns between successful matings. diff --git a/docs/tr_TR/developer/entities/index.md b/docs/tr_TR/developer/entities/index.md new file mode 100644 index 0000000..53623ee --- /dev/null +++ b/docs/tr_TR/developer/entities/index.md @@ -0,0 +1,77 @@ +# Entity Architecture & Hierarchy + +Entities in Minecraft represent all dynamic, living and non-living objects that exist within a world—ranging from dropped item stacks and arrows to zombies, villagers, and players. + +In Pumpkin, entity systems are implemented in [`crates/pumpkin/src/entity/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity). The architecture leverages object-oriented trait composition on top of concurrency-safe, cache-friendly data structures. + +--- + +## Entity Trait Hierarchy + +Pumpkin structures entities into an inheritance hierarchy using Rust traits: + +| Trait / Type | Parent Trait | Core Capabilities & State | +| :--- | :--- | :--- | +| `Entity` | *(None / Base Struct)* | Entity ID, UUID, atomic position and velocity vectors, bounding box, pose | +| `EntityBase` | *(Root Trait)* | Dynamic runtime downcasting (`get_living_entity`, etc.), world tick, damage, teleportation | +| `LivingEntity` | `EntityBase` | Health, absorption, potion effects, equipment slots, attribute containers | +| `Mob` | `LivingEntity` | AI goal selector, navigation, pathfinding, target tracking, death loot | +| `Player` | `LivingEntity` | Network client session, inventory, crafting screens, hunger, advancements | + +### 1. `Entity` (Base Struct) +Contains fundamental physics, geometry, and spatial state common to all entities: +- **`entity_id`**: Fast 32-bit unique integer identifier used across network packets. +- **`uuid`**: 128-bit persistent UUID. +- **`position` & `velocity`**: Thread-safe vectors (`AtomicCell>`) allowing concurrent reading without lock contention. +- **`bounding_box`**: 3D axis-aligned bounding box (AABB) used for collision resolution and raycast targeting. +- **`pose`**: Current pose (`Standing`, `Crouching`, `Sleeping`, `FallFlying`, etc.). + +### 2. `EntityBase` (Foundation Trait) +The root dynamic trait implemented by every entity in the server: +- Supports safe runtime downcasting via helper methods (`get_living_entity()`, `get_mob()`, `get_player()`). +- Provides hooks for world interaction: `tick()`, `damage()`, `teleport()`, `on_lightning_strike()`, `is_immune_to_explosion()`. + +### 3. `LivingEntity` (Biological Trait) +Extends `EntityBase` for entities with health, combat, and animation states: +- Manages health bars, absorption, invulnerability ticks, and fall damage. +- Tracks active potion effects (`crates/pumpkin/src/entity/effect/`). +- Handles equipment slots (`MainHand`, `OffHand`, `Helmet`, `Chestplate`, `Leggings`, `Boots`). +- Encapsulates attribute containers (max health, movement speed, knockback resistance). + +### 4. `Mob` (AI Trait) +Extends `LivingEntity` for computer-controlled mobs: +- Manages AI goal selectors (attacking, wandering, fleeing, looking around). +- Controls navigation and pathfinding over blocks. +- Spawns configured loot tables upon death. + +### 5. `Player` (Human Client) +Represents a human player connected through a game client: +- Links to network socket handlers (`JavaClient` or `BedrockClient`). +- Manages container screen handlers, ender chests, player inventory, and crafting. +- Tracks hunger, saturation, exhaustion, experience levels, and advancement criteria. + +--- + +## Physics & Movement Simulation + +On every server tick, Pumpkin simulates entity physics through a deterministic sequence: + +1. **Apply Velocity & Gravity**: Evaluates current velocity and adds downward gravitational acceleration unless `has_gravity()` is false or the entity is flying. +2. **Resolve Block Collisions**: Checks the entity's 3D axis-aligned bounding box (`bounding_box`) against solid block voxels in the surrounding chunk; velocity along colliding axes is dampened or zeroed. +3. **Apply Fluid Drag & Buoyancy**: Evaluates whether the entity intersects water or lava fluids, applying drag coefficients and buoyant upward force. +4. **Synchronize & Broadcast**: Persists the updated coordinates in atomic position cells and broadcasts relative delta movement packets to observing players. +5. **Void Death**: Non-player entities falling below $Y = -128$ are automatically despawned. + +--- + +## Combat & Damage System + +Damage calculations reside in [`crates/pumpkin/src/entity/combat.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/entity/combat.rs): + +1. **Damage Types**: Damage is categorized by `DamageType` (e.g., `GENERIC`, `PLAYER_ATTACK`, `FALL`, `FIRE`, `MAGIC`, `EXPLOSION`, `LIGHTNING_BOLT`). +2. **Damage Scaling**: + - Armor defense points mitigate incoming physical damage according to the vanilla formula: + $$\text{damage} = \text{damage} \times \left(1 - \frac{\min(20, \max(\text{armor} / 5, \text{armor} - \frac{\text{damage}}{2 + \text{toughness} / 4}))}{25}\right)$$ + - Resistance status effects and Protection enchantments apply secondary reductions. +3. **Knockback**: When damage is dealt, a directional impulse vector is added to the victim's velocity, modulated by their knockback resistance attribute. +4. **Hurt Animations**: Triggers hurt sounds and broadcasts red-tint flash metadata to all observers. diff --git a/docs/tr_TR/developer/entities/spawning-and-tracking.md b/docs/tr_TR/developer/entities/spawning-and-tracking.md new file mode 100644 index 0000000..82a3d7b --- /dev/null +++ b/docs/tr_TR/developer/entities/spawning-and-tracking.md @@ -0,0 +1,95 @@ +# Spawning & Network Tracking + +To maintain high tick rates with hundreds of active entities, Pumpkin utilizes a chunk-based entity tracking and streaming system. + +Entities are held in chunk-level collections inside [`crates/pumpkin-world`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-world) and synchronized across clients via edition-specific network packets. + +--- + +## The Spawning Pipeline + +Spawning an entity involves creating an instance, allocating its network IDs, registering it with the world, and broadcasting spawn packets to chunk observers: + +1. **Construct Entity Instance**: Instantiate the specific entity struct (such as `ZombieEntity`, `ArrowEntity`, or `ItemEntity`) initialized with its starting world and spatial coordinates. +2. **Allocate Unique Network Identifiers**: Assign an atomic 32-bit `entity_id` for network protocol packets and a 128-bit persistent UUID. +3. **Register in World & Chunk Map**: Insert the entity into the world's entity storage and index it within its corresponding chunk coordinate entry. +4. **Query Chunk Observers**: Look up all connected players within tracking range whose view distance includes the containing chunk. +5. **Broadcast Edition-Specific Spawn Packets**: Transmit the relevant spawn and metadata packets tailored to each observing client's edition (Java or Bedrock). + +### Spawning via the World API + +```rust +use std::sync::Arc; +use pumpkin_data::entity::EntityType; +use pumpkin_util::math::vector3::Vector3; +use crate::entity::Entity; +use crate::entity::mob::zombie::ZombieEntity; + +// 1. Create base entity with target world and position +let position = Vector3::new(100.5, 64.0, -200.5); +let base_entity = Entity::new(world.clone(), position, &EntityType::ZOMBIE); + +// 2. Wrap in specific entity implementation +let zombie = Arc::new(ZombieEntity::new(base_entity)); + +// 3. Register into the world +world.spawn_entity(zombie); +``` + +--- + +## Network Packet Synchronization + +When a player enters a chunk containing entities—or when an entity moves into an observed chunk—Pumpkin dispatches spawn packets tailored to the client's edition: + +### Java Edition Spawning +1. **`CSpawnEntity`**: Declares the entity ID, UUID, entity type, position, and initial pitch/yaw. +2. **`CSetEntityMetadata`**: Sends initial visual states (custom name tags, burning flags, baby/adult state). +3. **`CEntityVelocity`**: Synchronizes initial momentum. +4. **`CUpdateEntityRot` & `CHeadRot`**: Synchronizes head orientation independently from body yaw. + +### Bedrock Edition Spawning +1. **`CAddActor`**: Declares the runtime ID, actor type identifier, position, rotation, and embedded `SyncedActorDataList`. +2. **`CSetActorMotion`**: Sets initial velocity vector. + +--- + +## Movement & Position Synchronization + +Transmitting absolute 64-bit coordinates every tick would congest network bandwidth. Instead, Pumpkin dynamically switches between compact relative delta updates and authoritative full synchronization based on movement distance: + +| Metric / Scenario | Threshold | Java Protocol Packet | Bedrock Protocol Packet | Bandwidth Impact | +| :--- | :--- | :--- | :--- | :--- | +| **Incremental Movement** | $\Delta < 8$ blocks per tick | `CUpdateEntityPos` / `CUpdateEntityPosRot` | `CMoveActorDelta` | Minimal (fixed-point byte deltas) | +| **Teleport / Long Distance** | $\Delta \ge 8$ blocks or chunk warp | `CTeleportEntity` / `CEntityPositionSync` | `CMoveActorAbsolute` | Full 64-bit float coordinate payload | +| **Rotation Only** | Position unchanged | `CUpdateEntityRot` & `CHeadRot` | `CMoveActorDelta` (rot flags only) | Minimal (byte angles) | + +### 1. Relative Delta Movement +If the entity moved less than 8 blocks in a single tick, Pumpkin sends delta packets using fixed-point encoded increments: +- **Java**: `CUpdateEntityPos` (XYZ delta), `CUpdateEntityRot` (yaw/pitch delta), or `CUpdateEntityPosRot` (both). +- **Bedrock**: `CMoveActorDelta` with bitflags indicating which coordinates changed (`MOVE_ACTOR_DELTA_FLAG_HAS_X`, `HAS_Y`, `HAS_Z`). + +### 2. Full Position Synchronization +When an entity teleports, crosses chunk boundaries, or accumulates floating-point drift: +- **Java**: Sends `CEntityPositionSync` containing absolute coordinates, velocities, and on-ground flags. +- **Bedrock**: Sends `CMoveActorAbsolute`. + +--- + +## Passenger & Vehicle Mounting + +When an entity mounts another entity (e.g., a player mounting a horse, boat, or minecart): + +- **Java Edition**: Sends `CSetPassengers` containing the vehicle's `VarInt` entity ID and an array of passenger entity IDs. +- **Bedrock Edition**: Sends `CSetActorLink` defining the rider and vehicle runtime IDs and link type (`Rider`, `Passenger`). + +--- + +## Despawning & Entity Removal + +When an entity dies, is removed from the world, or steps out of a player's tracking radius: + +- **Java Edition**: Sends `CRemoveEntities` with an array of entity IDs. +- **Bedrock Edition**: Sends `CRemoveActor` with the entity's runtime ID. + +The client immediately frees its local rendering and audio memory for the removed entity. diff --git a/docs/tr_TR/developer/extractor.md b/docs/tr_TR/developer/extractor.md new file mode 100644 index 0000000..2b09f8f --- /dev/null +++ b/docs/tr_TR/developer/extractor.md @@ -0,0 +1,144 @@ +# Vanilla Data Extractor + +The [Pumpkin Extractor](https://github.com/Pumpkin-MC/Extractor) is a dedicated Fabric mod written in Kotlin that introspects a running vanilla Minecraft server or client to dump authoritative game registries and runtime metadata into structured JSON files. + +These JSON dumps are consumed by [`pumpkin-codegen`](/developer/codegen) to generate strongly-typed Rust code in `pumpkin-data` and `pumpkin-world`. + +--- + +## Datapacks vs. Extractor: Our Data Philosophy + +Before creating a new extractor, it is crucial to understand Pumpkin's data hierarchy: + +> [!IMPORTANT] +> **We preferably use vanilla datapacks whenever possible.** +> If vanilla Minecraft already exposes a registry or system through official datapack JSON files, we consume the datapack directly rather than writing custom extractor code. + +### When to Use Datapacks vs. When to Use the Extractor + +| Source | When to Use | Examples | +| :--- | :--- | :--- | +| **Vanilla Datapacks** *(Preferred)* | Any data officially serialized as JSON by Mojang in `data/minecraft/`. | Recipes, tags (`blocks`, `items`, `fluids`), biomes, dimension types, damage types, trim materials, worldgen noise parameters. | +| **Fabric Extractor** *(Only when necessary)* | Game data **hardcoded in Java bytecode** that cannot be exported from raw datapack files. | Block collision shapes, AABB bounding boxes, block hardness, packet ID-to-state mappings, entity tracked data IDs, fuel burn durations, composter chances, and flower pot conversion tables. | + +--- + +## Extractor Mod Architecture + +The Extractor runs on top of the Fabric loader using official Fabric Yarn mappings: + +- **Mod Entry Point (`src/main/kotlin/de/snowii/extractor/Extractor.kt`)**: Registers with Fabric's `ServerLifecycleEvents.SERVER_STARTED` event. When the headless server finishes booting, it invokes all registered extractors and dumps files to `run/pumpkin_extractor_output/`. +- **Extractor Interface**: Every data extractor implements the `Extractor.Extractor` interface: + ```kotlin + interface Extractor { + fun fileName(): String + + @Throws(Exception::class) + fun extract(server: MinecraftServer): JsonElement + + // Returns true if output belongs in tests/ rather than the root output folder + fun isTest(): Boolean = false + } + ``` +- **Output Directory**: JSON files are saved to `pumpkin_extractor_output/` encoded in UTF-8 without HTML escaping. + +--- + +## How to Add a New Extractor + +Follow this step-by-step workflow when you need to extract new hardcoded vanilla mechanics: + +### Step 1: Verify the Data Cannot Be Sourced from Datapacks +Inspect the official Minecraft client/server JAR or Mojang's data reports: +- Check `data/minecraft/` for existing registries, tags, or JSON definitions. +- If the data is present in the datapack, add it directly to `Pumpkin/assets/datapacks/` instead of writing an extractor. + +### Step 2: Create the Extractor Class +If the data is hardcoded in Java methods, create a new Kotlin class in: +```text +src/main/kotlin/de/snowii/extractor/extractors/.kt +``` + +Implement the `Extractor.Extractor` interface: + +```kotlin +package de.snowii.extractor.extractors + +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import de.snowii.extractor.Extractor +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.server.MinecraftServer +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack + +class Fuels : Extractor.Extractor { + override fun fileName(): String { + return "fuels.json" + } + + override fun extract(server: MinecraftServer): JsonElement { + val fuelsJson = JsonObject() + + // Query vanilla runtime methods + server.fuelValues().fuelItems().forEach { fuel -> + val itemId = BuiltInRegistries.ITEM.getKey(fuel).toString() + val burnDuration = server.fuelValues().burnDuration(ItemStack(fuel)) + fuelsJson.add(itemId, JsonPrimitive(burnDuration)) + } + + return fuelsJson + } +} +``` + +### Step 3: Register the Extractor +Open `src/main/kotlin/de/snowii/extractor/Extractor.kt` and add an instance of your class to the `extractors` array: + +```kotlin +val extractors = arrayOf( + // ... existing extractors ... + Blocks(), + Items(), + Entities(), + Fuels(), // [!code ++] +) +``` + +### Step 4: Run the Extractor +Execute the headless server task in the Extractor repository: + +```bash +./gradlew runServer +``` + +Once the extraction finishes, locate your new JSON file in: +```text +run/pumpkin_extractor_output/.json +``` + +### Step 5: Integrate with `pumpkin-codegen` +1. Move the generated JSON file into Pumpkin: + ```bash + cp run/pumpkin_extractor_output/fuels.json ../Pumpkin/assets/ + ``` +2. In the `Pumpkin` repository, open `tools/pumpkin-codegen`: + - Create a parser for `.json`. + - Write a code generation template to produce the corresponding Rust structs or static lookup tables in `crates/pumpkin-data`. +3. Run code generation: + ```bash + cargo run -p pumpkin-codegen + ``` +4. Verify that `pumpkin-data` compiles and passes all unit tests: + ```bash + cargo test -p pumpkin-data + ``` + +--- + +## Best Practices for Extractor Contributions + +- **Sort Keys Deterministically**: When generating JSON objects or lists, sort entries by numeric registry ID or alphabetically by identifier (`minecraft:stone`, `minecraft:dirt`). This prevents noisy git diffs when re-running extraction across updates. +- **Prefer Standard Types**: Use vanilla identifiers (`net.minecraft.resources.Identifier`) as string keys instead of arbitrary integers where possible. +- **Access Wideners**: If private vanilla fields or methods need to be accessed, declare an access widener in `src/main/resources/extractor.accesswidener` rather than resorting to brittle runtime reflection. diff --git a/docs/tr_TR/developer/items/adding-items.md b/docs/tr_TR/developer/items/adding-items.md new file mode 100644 index 0000000..6335d14 --- /dev/null +++ b/docs/tr_TR/developer/items/adding-items.md @@ -0,0 +1,121 @@ +# Adding an Item Behavior + +This guide details how to implement custom interactive mechanics for an item in Pumpkin. + +--- + +## When Is an Item Behavior Needed? + +- **Passive Items** (e.g., diamonds, gold ingots, sticks): Do **not** require custom Rust code. Their stack sizes, tool categories, and crafting recipes are handled automatically by `pumpkin-data` and `pumpkin-inventory`. +- **Interactive Items** (e.g., snowballs, ender pearls, hoes, bows, potions): Require an implementation of `ItemBehaviour` to handle right-click events, cooldowns, animations, and entity spawns. + +--- + +## Step 1: Define the Item Struct & Metadata + +Create a new file under `crates/pumpkin/src/item/items/.rs` (e.g., `items/snowball.rs`): + +```rust +use std::sync::Arc; +use pumpkin_data::entity::EntityType; +use pumpkin_data::item::Item; +use pumpkin_data::sound::{Sound, SoundCategory}; +use crate::entity::Entity; +use crate::entity::player::Player; +use crate::entity::projectile::snowball::SnowballEntity; +use crate::item::{ItemBehaviour, ItemMetadata}; + +pub struct SnowBallItem; + +// 1. Declare the associated numeric Item ID(s) +impl ItemMetadata for SnowBallItem { + fn ids() -> Box<[u16]> { + [Item::SNOWBALL.id].into() + } +} + +// 2. Implement the interaction behavior +impl ItemBehaviour for SnowBallItem { + fn normal_use(&self, _item: &Item, player: &Player) { + let position = player.position(); + let world = player.world(); + + // Play throw sound effect + world.play_sound( + Sound::EntitySnowballThrow, + SoundCategory::Neutral, + &position, + ); + + // Construct and launch the projectile entity + let entity = Entity::new(world.clone(), position, &EntityType::SNOWBALL); + let snowball = SnowballEntity::new_shot(entity, player.get_entity()); + let (yaw, pitch) = player.rotation(); + snowball.thrown.set_velocity_from(pitch, yaw, 0.0, 1.5, 1.0); + world.spawn_entity(Arc::new(snowball)); + + // Safely decrement held item unless in Creative mode + let mut held = player.inventory.held_item(); + held.decrement_unless_creative(player.gamemode.load(), 1); + player.inventory.set_held_item(held); + } +} +``` + +--- + +## Step 2: `ItemBehaviour` Hook Reference + +The `ItemBehaviour` trait provides comprehensive hooks for different interaction models: + +### Instant Single-Use Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `normal_use` | `(&self, item: &Item, player: &Player)` | Triggered on right-clicking into open air. | +| `normal_use_with_rotation` | `(&self, item: &Item, player: &Player, yaw: f32, pitch: f32)` | Provides client-reported view angles for precise raycasting and aiming. | +| `use_on_block` | `(&self, item: &mut ItemStack, player: &Player, location: BlockPos, face: BlockDirection, cursor_pos: Vector3, block: &Block, server: &Server) -> BlockActionResult` | Triggered when right-clicking a block face (e.g., stripping logs with an axe, tilling farmland with a hoe). | +| `use_on_entity` | `(&self, item: &mut ItemStack, player: &Player, entity: Arc)` | Triggered when right-clicking another living entity (e.g., shearing sheep, nametags). | + +### Channeled & Timed Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `get_use_duration` | `(&self) -> i32` | Duration in game ticks required to complete the action (e.g., `32` ticks for food, `72000` ticks for bows). | +| `on_use_tick` | `(&self, stack: &ItemStack, player: &Player, remaining_use_ticks: i32)` | Executed on each tick while right-click is held. | +| `on_stopped_using` | `(&self, stack: &ItemStack, player: &Player)` | Triggered when the player releases right-click (e.g., firing a charged bow). | + +--- + +## Step 3: Durability & Cooldowns + +### Decrementing Stacks Safely +Always use `decrement_unless_creative` to ensure survival players consume items while creative players retain them: + +```rust +item_stack.decrement_unless_creative(player.gamemode.load(), 1); +``` + +### Applying Cooldowns +To prevent action spamming (e.g., Ender Pearls, Chorus Fruit): + +```rust +// Start a 20-tick (1-second) cooldown on Ender Pearls +player.start_cooldown(Item::ENDER_PEARL.registry_key.to_string(), 20); +``` + +--- + +## Step 4: Registering the Item + +1. Export your module in `crates/pumpkin/src/item/items/mod.rs`: + ```rust + pub mod snowball; + ``` + +2. Register the struct in `crates/pumpkin/src/item/registry.rs`: + ```rust + items.register(SnowBallItem); + ``` + +When the server initializes, `items.register` maps the IDs declared in `ItemMetadata::ids()` to your behavior instance. diff --git a/docs/tr_TR/developer/items/index.md b/docs/tr_TR/developer/items/index.md new file mode 100644 index 0000000..70f9f9b --- /dev/null +++ b/docs/tr_TR/developer/items/index.md @@ -0,0 +1,71 @@ +# Item Architecture & Data Components + +Items represent portable game objects held in player inventories, hotbars, armor slots, and world containers. In Pumpkin, item handling is divided into two primary subsystems: + +1. **Static Data Layer ([`pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data))**: Compile-time generated metadata including item IDs, max stack sizes, max durability, tool tier properties, food nutrition, and data components generated by [`pumpkin-codegen`](/developer/codegen). +2. **Dynamic Behavior Layer ([`pumpkin::item`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/item))**: Interactive right-click actions, projectile launching, eating durations, bow charging, and block interactions handled via the `ItemBehaviour` trait. + +--- + +## Static Items vs. Actionable Items + +| Item Type | Description | Examples | Runtime Behavior Struct Needed? | +| :--- | :--- | :--- | :--- | +| **Passive Items** | Items without active right-click effects | `diamond`, `stick`, `iron_ingot`, `feather` | No — Stacking, moving, crafting, and dropping are handled automatically by `pumpkin-inventory`. | +| **Standard Block Items** | Items corresponding directly to placeable blocks | `stone`, `oak_planks`, `glass` | No — Placement logic queries the target block's data. | +| **Projectiles & Throwables** | Items spawned into physical entities when thrown | `snowball`, `egg`, `ender_pearl`, `splash_potion` | Yes — Implements `ItemBehaviour::normal_use`. | +| **Channeled / Charged Items** | Items with use durations or charging animations | `bow`, `crossbow`, `spyglass`, `trident` | Yes — Implements `get_use_duration`, `on_use_tick`, `on_stopped_using`. | +| **Tools with Block Interactions**| Items modifying existing blocks in the world | `hoe` (tilling), `axe` (stripping), `shovel` (pathmaking) | Yes — Implements `ItemBehaviour::use_on_block`. | + +--- + +## The `ItemStack` Structure + +An `ItemStack` represents an instance of an item in an inventory container. It encapsulates: + +- **`item`**: Reference to the static [`Item`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-data/src/item.rs) definition. +- **`item_count`**: Current stack size ($1 \le \text{count} \le \text{max\_stack\_size}$). +- **Data Components**: Dynamic properties attached to the item stack (custom names, lore, damage values, enchantments, potion effects). + +```rust +use pumpkin_data::item::Item; +use pumpkin_data::item_stack::ItemStack; + +// Create a new item stack +let mut stack = ItemStack::new(1, Item::DIAMOND_SWORD); + +// Safely decrement stack count, respecting creative mode +stack.decrement_unless_creative(player.gamemode.load(), 1); +``` + +--- + +## Data Components System + +Modern Minecraft represents item properties via modular **Data Components**: + +| Component | Responsibility | Description | +| :--- | :--- | :--- | +| `FoodImpl` | Nutrition & Saturation | Controls hunger points restored and saturation ratio. | +| `ConsumableImpl` | Consumption Mechanics | Controls eating duration, sound effects, and animation types. | +| `Damageable` | Durability | Tracks current damage points and breaks when max durability is reached. | +| `UseCooldown` | Cooldown Group | Prevents item re-use for a specified duration in game ticks. | +| `Enchantments` | Enchantment Levels | Key-value mapping of applied enchantments and levels. | + +--- + +## The Item Registry & Dispatch Architecture + +Item interactions are managed by the `ItemRegistry` in [`crates/pumpkin/src/item/registry.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/item/registry.rs): + +1. **Registration**: At server startup, all `ItemBehaviour` implementations are registered along with their target numeric item IDs. +2. **Interaction Routing**: When a client sends use packets (`CUseItem`, `CUseItemOn`), Pumpkin evaluates cooldowns and routes execution to the registered `ItemBehaviour`. +3. **Cooldown System**: Players track active cooldowns keyed by cooldown group strings. If an item is on cooldown, interactions are canceled before calling the behavior. + +--- + +## Next Steps + +To implement interactive mechanics for a new item, proceed to the practical guide: + +- **[Adding an Item](/developer/items/adding-items)**: Step-by-step instructions for implementing `ItemMetadata`, `ItemBehaviour`, managing cooldowns, and registering items. diff --git a/docs/tr_TR/developer/networking/bedrock/adding-packets.md b/docs/tr_TR/developer/networking/bedrock/adding-packets.md new file mode 100644 index 0000000..f68b888 --- /dev/null +++ b/docs/tr_TR/developer/networking/bedrock/adding-packets.md @@ -0,0 +1,182 @@ +# Adding a Bedrock Edition Packet + +This guide explains how to implement, encode, decode, and handle a new Minecraft: Bedrock Edition packet in Pumpkin. + +--- + +## Naming Conventions + +- **Clientbound packets** (Server → Client) must start with **`C`** (e.g., `CChunkRadiusUpdated`, `CContainerOpen`). +- **Serverbound packets** (Client → Server) must start with **`S`** (e.g., `SRequestChunkRadius`, `SPlayerAction`). + +--- + +## Step 1: Identifying the Packet ID + +In the Bedrock protocol, packet IDs are fixed integer constants assigned by Mojang. Verify the numeric ID from the official protocol documentation or network packet traces. + +For example: +- `Disconnect` packet ID: `5` +- `RequestChunkRadius` packet ID: `69` + +--- + +## Step 2: Implementing a Clientbound Packet + +Clientbound packets implement the `PacketWrite` trait from `pumpkin-protocol::serial`. + +### 1. Create the Packet File +Place the file in `crates/pumpkin-protocol/src/bedrock/client/.rs`: + +```rust +use std::io::{Error, Write}; +use pumpkin_macros::packet; +use crate::{codec::var_int::VarInt, serial::PacketWrite}; + +#[packet(5)] +pub struct CDisconnect { + pub reason: VarInt, + pub skip_message: bool, + pub message: String, + pub filtered_message: String, +} + +impl CDisconnect { + #[must_use] + pub const fn new(reason: i32, message: String) -> Self { + Self { + reason: VarInt(reason), + skip_message: message.is_empty(), + message, + filtered_message: String::new(), + } + } +} + +impl PacketWrite for CDisconnect { + fn write(&self, writer: &mut W) -> Result<(), Error> { + self.reason.write(writer)?; + self.skip_message.write(writer)?; + if !self.skip_message { + self.message.write(writer)?; + self.filtered_message.write(writer)?; + } + Ok(()) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/bedrock/client/mod.rs`: + +```rust +pub mod disconnect; +pub use disconnect::*; +``` + +--- + +## Step 3: Implementing a Serverbound Packet + +Serverbound packets implement the `PacketRead` trait. Pumpkin provides a procedural derive macro `#[derive(PacketRead)]` for automatic deserialization when fields implement `PacketRead`. + +### 1. Create the Packet File +Place the file in `crates/pumpkin-protocol/src/bedrock/server/.rs`: + +```rust +use pumpkin_macros::packet; +use crate::{codec::var_int::VarInt, serial::PacketRead}; + +#[derive(PacketRead, Debug)] +#[packet(69)] +pub struct SRequestChunkRadius { + pub chunk_radius: VarInt, + pub max_chunk_radius: u8, +} +``` + +If a packet requires custom parsing logic, implement `PacketRead` manually: + +```rust +use std::io::{Error, Read}; +use crate::serial::PacketRead; + +impl PacketRead for SMyCustomPacket { + fn read(reader: &mut R) -> Result { + let chunk_radius = VarInt::read(reader)?; + let max_chunk_radius = u8::read(reader)?; + Ok(Self { chunk_radius, max_chunk_radius }) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/bedrock/server/mod.rs`: + +```rust +pub mod request_chunk_radius; +pub use request_chunk_radius::*; +``` + +--- + +## Step 4: Dispatching Incoming Packets in `pumpkin` + +Incoming packets are dispatched in `crates/pumpkin/src/net/bedrock/mod.rs`: + +```rust +// Inside BedrockClient::handle_game_packet +match packet.id { + SRequestChunkRadius::PACKET_ID => { + let packet = SRequestChunkRadius::read(reader)?; + self.handle_request_chunk_radius(player, &packet); + } + _ => {} +} +``` + +--- + +## Step 5: Implementing the Handler Function + +Implement the handler logic in `crates/pumpkin/src/net/bedrock/play/.rs`: + +```rust +use super::*; +use pumpkin_protocol::bedrock::server::request_chunk_radius::SRequestChunkRadius; + +impl BedrockClient { + pub fn handle_request_chunk_radius(&self, player: &Arc, packet: &SRequestChunkRadius) { + let requested_radius = packet.chunk_radius.0; + + // Clamp view distance within server configuration limits + let clamped = requested_radius.clamp(2, 16); + + // Notify client that view radius was accepted + self.try_enqueue_client_packet(&CChunkRadiusUpdated { + chunk_radius: VarInt(clamped), + }); + } +} +``` + +Remember to register the new module in `crates/pumpkin/src/net/bedrock/play/mod.rs`: + +```rust +pub mod request_chunk_radius; +``` + +--- + +## Step 6: Sending Outgoing Packets + +To send a Bedrock packet to a connected client: + +```rust +// Enqueues the packet into the client's outgoing batch queue +self.try_enqueue_client_packet(&CChunkRadiusUpdated { + chunk_radius: VarInt(view_distance), +}); +``` + +Batched packets are automatically compressed, framed with `0xfe`, and transmitted over the RakNet/NetherNet socket. diff --git a/docs/tr_TR/developer/networking/bedrock/authentication.md b/docs/tr_TR/developer/networking/bedrock/authentication.md new file mode 100644 index 0000000..1571a73 --- /dev/null +++ b/docs/tr_TR/developer/networking/bedrock/authentication.md @@ -0,0 +1,61 @@ +# Bedrock Edition Authentication + +Minecraft: Bedrock Edition implements a certificate chain authentication protocol based on JSON Web Tokens (JWT) cryptographically anchored to Microsoft / Xbox Live root public keys. + +Authentication handling is powered by the [`pumpkin-auth`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-auth) crate and processed during the Bedrock login handshake in `crates/pumpkin/src/net/bedrock/login/`. + +--- + +## The Handshake Flow + +When a Bedrock client initiates connection, it sends an `SLogin` packet containing the client's protocol version, an **Identity Chain**, and **Client Data**: + +1. **Token Acquisition**: The Bedrock client signs into Xbox Live, obtaining a chained JSON Web Token (JWT) certificate signed by Microsoft's CA. +2. **Login Transmission**: The client packages this identity chain alongside a separate `ClientData` JWT into `SLogin`. +3. **Chain Validation**: Pumpkin walks the certificate chain, verifying each token's signature against Mojang's public root keys and extracting the player's Xbox User ID (XUID), Gamertag, and ephemeral session public key. +4. **Hardware Verification**: Pumpkin verifies the `ClientData` payload using the client's ephemeral public key, extracting geometry, skins, and device OS telemetry. +5. **Session Authorization**: Once validated, Pumpkin emits `CPlayStatus` (Login Success) and progresses to resource pack negotiation. + +--- + +## Token Payloads + +### 1. Identity Chain (`Chain`) +An array of JWT tokens forming an unbroken chain of trust: +- **Root Token**: Cryptographically signed by Mojang's official public root certificate authority. +- **Intermediate Tokens**: Issued by Xbox Live services, verifying the player's Xbox Live User ID (**XUID**), gamertag, and generating an ephemeral ECDSA public key for the session. +- **Claims**: Includes `extraData` with `displayName`, `identity` (UUID), `XUID`, and `titleId`. + +### 2. Client Data (`ClientData`) +A separate JWT signed with the client's ephemeral private key corresponding to the public key in the identity chain: +- **Hardware & Environment**: `DeviceOS`, `DeviceModel`, `CurrentInputMode`, `DefaultInputMode`, `ClientRandomId`. +- **Game Version**: `GameVersion` string. +- **Player Cosmetics**: Full skin texture bitmaps, cape data, geometry names, and persona definitions. + +--- + +## Verification in `pumpkin-auth` + +Pumpkin verifies token signatures using **NIST P-384 ECDSA** cryptography: + +```rust +// In crates/pumpkin/src/net/bedrock/login/mod.rs +match AuthPayload::verify(&jwt_payload) { + Ok(profile) => { + // Successfully verified against Mojang public keys + info!("Authenticated Bedrock player: {} (XUID: {})", profile.name, profile.xuid); + } + Err(LoginError::ChainValidationFailed(err)) => { + warn!("Bedrock JWT chain validation failed: {err}"); + self.try_kick(DisconnectReason::Kicked, "Authentication failed".into()); + } + Err(LoginError::SelfSignedNotAllowed) => { + warn!("Rejected unauthenticated self-signed Bedrock client"); + self.try_kick(DisconnectReason::Kicked, "Server enforces Xbox Live authentication".into()); + } +} +``` + +### Online vs. Offline Mode +- **Online Mode** (`online_mode = true` in `pumpkin.toml`): The chain must successfully validate against Mojang's public keys. Self-signed or unsigned tokens are immediately rejected. +- **Offline Mode** (`online_mode = false`): Pumpkin accepts self-signed client chains, generating offline UUIDs while extracting cosmetic skins and device metadata. diff --git a/docs/tr_TR/developer/networking/bedrock/nethernet.md b/docs/tr_TR/developer/networking/bedrock/nethernet.md new file mode 100644 index 0000000..88c92f0 --- /dev/null +++ b/docs/tr_TR/developer/networking/bedrock/nethernet.md @@ -0,0 +1,61 @@ +# NetherNet WebRTC Transport + +**NetherNet** is Mojang's modern, WebRTC-based transport protocol for Minecraft: Bedrock Edition. Designed for high-resilience peer-to-peer and relayed communication, NetherNet enables seamless cross-platform multiplayer (mobile, console, desktop) without requiring server operators or players to configure router port-forwarding. + +Pumpkin provides a native, zero-dependency NetherNet implementation in [`crates/pumpkin/src/net/bedrock/nethernet.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/bedrock/nethernet.rs). + +--- + +## Architectural Layers + +| Layer | Implementation | Responsibility | +| :--- | :--- | :--- | +| **HTTP Signaling** | Axum HTTP Router (`ice_router.rs`) | Handles SDP Offer (`POST /offer`), SDP Answer (`GET /answer`), and ICE Candidates (`POST /candidate`) | +| **NAT Traversal** | ICE Agent | Coordinates STUN/TURN binding discovery and hole punching | +| **Transport Security** | DTLS Handshake | Generates ephemeral session keys and secures the WebRTC transport using ECDSA P-384 keys | +| **Data Delivery** | WebRTC DataChannels | Unordered/ordered datagram channels streaming standard Bedrock game batches to the dispatcher | + +--- + +## Core Components in Pumpkin + +Pumpkin's NetherNet subsystem is divided into four distinct modules: + +### 1. Cryptographic Identity (`nethernet-key.der`) +- Pumpkin generates and loads a **NIST P-384 ECDSA private key** (`nethernet-key.der`). +- Used to cryptographically sign session descriptions (SDP offers and answers), authenticating the server's identity to connecting clients. +- Encoded using standard PKCS#8 DER formatting. + +### 2. HTTP Signaling Router (`ice_router.rs`) +Signaling is handled over HTTP via an [Axum](https://github.com/tokio-rs/axum) router: + +- **`POST /offer`**: The client submits an SDP offer describing its WebRTC codecs, transport profiles, and network interfaces. +- **`GET /answer`**: Pumpkin generates a corresponding local SDP answer containing its negotiated capabilities. +- **`POST /candidate`**: Both endpoints exchange trickle ICE candidates to discover the fastest direct network path (host, server-reflexive, or relay). + +### 3. Local Network Discovery (`discovery.rs`) +- Listens and broadcasts availability on the local subnet via UDP multicast. +- Allows Bedrock clients on the same Wi-Fi or LAN to discover and connect to the Pumpkin server automatically without entering an IP address. + +### 4. DataChannel Packet Streaming +- Once the WebRTC peer connection transitions to the `Connected` state, an ordered, reliable **WebRTC DataChannel** is opened. +- Bedrock batch packets (`0xfe`) flow bidirectionally through the DataChannel directly into Pumpkin's `incoming_game_packet_send` channel. + +--- + +## Configuration in `pumpkin.toml` + +NetherNet is configured under `[networking.bedrock.nethernet]` in `pumpkin.toml`: + +```toml +[networking.bedrock] +enabled = true +address = "0.0.0.0:19132" + +[networking.bedrock.nethernet] +enabled = true +signaling_address = "0.0.0.0:19133" +ice_servers = [ + "stun:stun.l.google.com:19302" +] +``` diff --git a/docs/tr_TR/developer/networking/bedrock/overview.md b/docs/tr_TR/developer/networking/bedrock/overview.md new file mode 100644 index 0000000..ae0d615 --- /dev/null +++ b/docs/tr_TR/developer/networking/bedrock/overview.md @@ -0,0 +1,80 @@ +# Bedrock Edition Networking Overview + +Pumpkin features native support for **Minecraft: Bedrock Edition** clients (iOS, Android, Windows, Xbox, PlayStation, Switch). + +Bedrock protocol structures and wire codecs reside in `pumpkin-protocol::bedrock`, while connection management and gameplay handlers reside in `pumpkin::net::bedrock`. + +--- + +## Transport Layers: RakNet & NetherNet + +Unlike Java Edition, which communicates exclusively over TCP streams, Bedrock Edition operates over UDP using two primary transport layers: + +1. **RakNet (Port 19132)**: + - A reliable, sequenced UDP networking library originally developed by Jenkins Software. + - Provides connection handshakes, packet acknowledgment (ACK/NACK), MTU negotiation, and packet splitting for payloads larger than the network MTU. +2. **NetherNet**: + - Modern peer-to-peer and relayed transport protocol used by modern Bedrock versions. + - Integrated in Pumpkin via `crates/pumpkin/src/net/bedrock/nethernet.rs`. + +--- + +## Packet Framing & Batching (`0xfe`) + +Bedrock does not send individual game packets over the network directly. Instead, multiple game packets are bundled into a single **batch packet**: + +1. **Header Byte**: Every Bedrock game packet batch starts with `0xfe`. +2. **Compression**: The payload following `0xfe` is compressed using **Deflate** (or ZLib) above a negotiated threshold. +3. **Sub-packet Framing**: When decompressed, the batch contains one or more sequential game packets, each prefixed by its length encoded as a `VarUInt`. + +In Pumpkin, batch decoding and assembly are managed by `BedrockBatchDecoder` and `BedrockBatchEncoder` in `crates/pumpkin-protocol/src/bedrock/`. + +--- + +## Connection Lifecycle & State Progression + +Bedrock does not have explicit protocol states like Java Edition. Instead, it follows a strict sequence of packets: + +| Step | Packet Exchanged | Purpose | +| :--- | :--- | :--- | +| **1. Network Settings** | `SRequestNetworkSettings` -> `CNetworkSettings` | Negotiates compression algorithm (Deflate/Snappy) and payload threshold | +| **2. Authentication** | `SLogin` -> `CPlayStatus` | Transmits JWT identity chain and client hardware data; verifies Xbox Live tokens | +| **3. Resource Negotiation** | `CResourcePacksInfo` -> `SResourcePackClientResponse` | Negotiates server resource packs and download behavior | +| **4. World Initialization** | `CStartGame` + `CItemRegistry` + `CCreativeContent` | Streams world spawn position, dimension settings, runtime player entity ID, and item palettes | +| **5. Ready to Spawn** | `SSetLocalPlayerAsInitialized` -> `CPlayStatus` | Signals client loading completion and transitions to active in-game play | + +--- + +## Packet Dispatching in `pumpkin` + +Inside `crates/pumpkin/src/net/bedrock/mod.rs`, decompressed sub-packets are dispatched based on their numeric packet ID: + +```rust +match packet.id { + SPlayerAuthInput::PACKET_ID => { + let packet = SPlayerAuthInput::read(reader)?; + self.handle_player_auth_input(player, packet, server); + } + SRequestChunkRadius::PACKET_ID => { + let packet = SRequestChunkRadius::read(reader)?; + self.handle_request_chunk_radius(player, &packet); + } + SInventoryTransaction::PACKET_ID => { + let packet = SInventoryTransaction::read(reader)?; + self.handle_inventory_action(player, packet); + } + _ => { + trace!("Unhandled Bedrock packet ID: {}", packet.id); + } +} +``` + +--- + +## Authentication & NetherNet + +- **JWT Authentication**: Bedrock verifies player identities via certificate chains signed by Microsoft / Xbox Live root authorities. +- **NetherNet WebRTC**: High-performance peer-to-peer transport using WebRTC DataChannels, ICE NAT traversal, and P-384 ECDSA keys. + +For complete implementation details, see **[Bedrock Authentication & NetherNet](/developer/networking/bedrock/authentication)**. + diff --git a/docs/tr_TR/developer/networking/index.md b/docs/tr_TR/developer/networking/index.md new file mode 100644 index 0000000..0685c45 --- /dev/null +++ b/docs/tr_TR/developer/networking/index.md @@ -0,0 +1,49 @@ +# Networking Overview + +Pumpkin is built with a high-performance, asynchronous networking stack capable of servicing both **Minecraft: Java Edition** and **Minecraft: Bedrock Edition** clients simultaneously. + +All protocol definitions, serialization traits, and wire-level codecs are isolated in the [`pumpkin-protocol`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-protocol) crate, while client connection management, session state machines, and gameplay packet dispatching are managed in [`pumpkin/src/net`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/net). + +--- + +## Supported Editions + +Pumpkin separates network implementations by edition due to their fundamentally distinct transport layers, packet encodings, and state models: + +| Feature | Minecraft: Java Edition | Minecraft: Bedrock Edition | +| :--- | :--- | :--- | +| **Transport Layer** | TCP socket streams | UDP via RakNet & NetherNet | +| **Framing** | VarInt length-prefixed frames | Batched packets encapsulated in `0xfe` headers | +| **Encryption** | AES-128 / CFB8 stream cipher | Cryptographic session handshake | +| **Compression** | Per-packet ZLib (above configurable threshold) | Per-batch Deflate / ZLib compression | +| **State Model** | Explicit states: Handshake, Status, Login, Config, Play | Packet-driven state progression | +| **Protocol Crate** | `pumpkin-protocol::java` | `pumpkin-protocol::bedrock` | +| **Server Net Module** | `pumpkin::net::java` | `pumpkin::net::bedrock` | + +--- + +## Networking Architecture + +The networking pipeline routes incoming connections through specialized transport and codec layers before delegating to the unified server game loop: + +1. **Transport Listeners**: Independent listeners bind to their respective ports—Tokio TCP for Java Edition (`:25565`) and UDP/RakNet/NetherNet for Bedrock Edition (`:19132`). +2. **Session Abstraction**: Each accepted connection spawns an isolated session task (`JavaClient` or `BedrockClient`) managing wire framing and encryption. +3. **Codec Deserialization**: Wire bytes are deserialized into typed packet structs via `pumpkin-protocol::java` (`ClientPacket`/`ServerPacket`) and `pumpkin-protocol::bedrock` (`PacketWrite`/`PacketRead`). +4. **Unified Gameplay Dispatch**: Decoded packets are dispatched into the unified game loop, updating player entity states and streaming world chunks. + +--- + +## Navigating the Networking Docs + +Explore the documentation for each edition: + +- **[Entity Metadata & Synced Data](/developer/networking/metadata)**: Synchronization of visual entity states (DataWatchers in Java, Actor Data in Bedrock). +- **[Java Edition](/developer/networking/java/overview)**: + - **[Architecture & Lifecycle](/developer/networking/java/overview)**: State machine, `JavaClient` vs. `Player`, and multi-version negotiation. + - **[How to Add a Packet](/developer/networking/java/adding-packets)**: Guide to defining, serializing, and dispatching Java Edition packets. + - **[Authentication](/developer/networking/java/authentication)**: Mojang Yggdrasil validation, GameProfiles, and proxy forwarding. +- **[Bedrock Edition](/developer/networking/bedrock/overview)**: + - **[Architecture & Transports](/developer/networking/bedrock/overview)**: RakNet and NetherNet integration, batching, and session handling. + - **[How to Add a Packet](/developer/networking/bedrock/adding-packets)**: Guide to defining and handling Bedrock Edition packets. + - **[Authentication & NetherNet](/developer/networking/bedrock/authentication)**: JWT certificate chain verification, WebRTC DataChannels, P-384 keys, and signaling. +- **[RCON](/developer/networking/rcon)**: Implementation of Valve's Remote Console protocol for administrative management. diff --git a/docs/tr_TR/developer/networking/java/adding-packets.md b/docs/tr_TR/developer/networking/java/adding-packets.md new file mode 100644 index 0000000..1800d06 --- /dev/null +++ b/docs/tr_TR/developer/networking/java/adding-packets.md @@ -0,0 +1,205 @@ +# Adding a Java Edition Packet + +This guide walks you through implementing, serializing, dispatching, and sending a new Java Edition packet in Pumpkin. + +--- + +## Naming Conventions + +To keep packet definitions uniform across the codebase, follow these naming conventions: + +- **Clientbound packets** (Server → Client) must start with **`C`** (e.g., `CBlockUpdate`, `CPlayDisconnect`). +- **Serverbound packets** (Client → Server) must start with **`S`** (e.g., `SChatCommand`, `SPlayerPosition`). +- If a packet exists in multiple states, prefix the state name before the packet action (e.g., `CLoginDisconnect`, `CConfigDisconnect`, and `CPlayDisconnect`). + +--- + +## Step 1: Find the Packet ID in `pumpkin-data` + +Packet IDs across all supported Minecraft versions are maintained in `pumpkin-data`. Import the corresponding constant: + +```rust +// Clientbound Play state disconnect +use pumpkin_data::packet::clientbound::play::DISCONNECT; + +// Serverbound Play state player position +use pumpkin_data::packet::serverbound::play::MOVE_PLAYER_POS; +``` + +--- + +## Step 2: Implementing a Clientbound Packet + +Clientbound packets are sent from Pumpkin to the Minecraft client. They implement the `ClientPacket` trait from `pumpkin-protocol`. + +### 1. Create the Struct File +Place the file in `crates/pumpkin-protocol/src/java/client//.rs` (e.g., `play/disconnect.rs`): + +```rust +use std::io::Write; +use pumpkin_data::packet::clientbound::play::DISCONNECT; +use pumpkin_macros::java_packet; +use pumpkin_protocol::ClientPacket; +use pumpkin_protocol::ser::{NetworkWriteExt, WritingError}; +use pumpkin_util::text::TextComponent; +use pumpkin_util::version::JavaMinecraftVersion; + +/// Displays a disconnect screen with a message to the player in Play state. +#[java_packet(DISCONNECT)] +pub struct CPlayDisconnect<'a> { + pub reason: &'a TextComponent, +} + +impl<'a> CPlayDisconnect<'a> { + #[must_use] + pub const fn new(reason: &'a TextComponent) -> Self { + Self { reason } + } +} + +impl ClientPacket for CPlayDisconnect<'_> { + fn write_packet_data( + &self, + mut write: impl Write, + version: &JavaMinecraftVersion, + ) -> Result<(), WritingError> { + // Use NetworkWriteExt methods to serialize fields + write.write_component(self.reason, version) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/java/client//mod.rs`: + +```rust +mod disconnect; +pub use disconnect::*; +``` + +--- + +## Step 3: Implementing a Serverbound Packet + +Serverbound packets are received by Pumpkin from the game client. They implement the `ServerPacket` trait from `pumpkin-protocol`. + +### 1. Create the Struct File +Place the file in `crates/pumpkin-protocol/src/java/server//.rs` (e.g., `play/player_position.rs`): + +```rust +use pumpkin_data::packet::serverbound::play::MOVE_PLAYER_POS; +use pumpkin_macros::java_packet; +use pumpkin_protocol::ServerPacket; +use pumpkin_protocol::ser::{NetworkReadExt, ReadingError}; +use pumpkin_util::math::vector3::Vector3; +use pumpkin_util::version::JavaMinecraftVersion; + +#[java_packet(MOVE_PLAYER_POS)] +pub struct SPlayerPosition { + pub position: Vector3, + pub collision: u8, +} + +impl<'a> ServerPacket<'a> for SPlayerPosition { + fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result { + let x = bytebuf.get_f64_be()?; + let y = bytebuf.get_f64_be()?; + + // Handle version-specific fields if necessary + if *version <= JavaMinecraftVersion::V_1_7_6 { + let _stance = bytebuf.get_f64_be()?; + } + + let z = bytebuf.get_f64_be()?; + let collision = bytebuf.get_u8()?; + + Ok(Self { + position: Vector3::new(x, y, z), + collision, + }) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/java/server//mod.rs`: + +```rust +mod player_position; +pub use player_position::*; +``` + +--- + +## Step 4: Network Codec Extension Helpers + +`NetworkWriteExt` and `NetworkReadExt` provide helper methods for standard Minecraft types: + +| Data Type | Reading Method | Writing Method | +| :--- | :--- | :--- | +| **`VarInt`** | `bytebuf.get_var_int()?` | `write.write_var_int(val)?` | +| **`VarLong`** | `bytebuf.get_var_long()?` | `write.write_var_long(val)?` | +| **`String`** | `bytebuf.get_string()?` | `write.write_string(val)?` | +| **`UUID`** | `bytebuf.get_uuid()?` | `write.write_uuid(&val)?` | +| **`TextComponent`** | `bytebuf.get_component()?` | `write.write_component(val, version)?` | +| **`Position`** | `bytebuf.get_block_pos()?` | `write.write_block_pos(val)?` | +| **`NBT`** | `bytebuf.get_nbt()?` | `write.write_nbt(&val)?` | + +--- + +## Step 5: Handling Incoming Packets in `pumpkin` + +Incoming serverbound packets must be handled in the appropriate state handler in `crates/pumpkin/src/net/java/`. + +For packets in the **Play state**, open `crates/pumpkin/src/net/java/mod.rs` and update `handle_play_packet`: + +```rust +// In crates/pumpkin/src/net/java/mod.rs +pub fn handle_play_packet( + &self, + player: &Arc, + server: &Arc, + packet: &RawPacket, +) -> Result<(), Box> { + let version = self.version.load(); + let mut payload = &packet.payload[..]; + + match packet.id { + // Match against version-specific packet ID + id if id == SPlayerPosition::to_id(version) => { + let packet = SPlayerPosition::read(&mut payload, &version)?; + self.handle_player_position(player, &packet); + } + _ => {} + } + Ok(()) +} +``` + +Then implement the handler function: + +```rust +impl JavaClient { + pub fn handle_player_position(&self, player: &Arc, packet: &SPlayerPosition) { + // Validate coordinates, update player position, check collisions + player.set_position(packet.position); + } +} +``` + +--- + +## Step 6: Sending Outgoing Packets + +To send a clientbound packet to a player: + +```rust +// 1. Send directly to a player's connection +player.client.send_packet(&CPlayDisconnect::new(&reason)); + +// 2. Broadcast to all players watching a chunk +world.broadcast_packet_except( + &[player.gameprofile.id], + &CBlockUpdate::new(block_pos, block_id), +).await; +``` diff --git a/docs/tr_TR/developer/networking/java/authentication.md b/docs/tr_TR/developer/networking/java/authentication.md new file mode 100644 index 0000000..861da51 --- /dev/null +++ b/docs/tr_TR/developer/networking/java/authentication.md @@ -0,0 +1,83 @@ +# Java Edition Authentication + +Authentication verifies the identity of players connecting to Pumpkin to prevent username spoofing and account impersonation. + +Pumpkin handles authentication in the [`pumpkin-auth`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-auth) crate and orchestrates it during the `Login` state in `crates/pumpkin/src/net/java/login/`. + +--- + +## Why Authentication Matters + +Offline (non-authenticated) accounts generate player UUIDs strictly based on their offline username string (`UUID::new_v3(...)`). Without verification, any user can join using the username of an operator or administrator and gain privileged access. + +In online mode (`online_mode = true` in `pumpkin.toml`), Pumpkin cryptographically verifies with Mojang's session servers that the connecting client owns the requested account. + +--- + +## How Yggdrasil Authentication Works + +The authentication exchange between the client, Pumpkin, and Mojang's Yggdrasil servers follows these steps: + +1. **Initiation**: The client sends `SLoginStart` declaring its desired username and optional offline UUID. +2. **Key Exchange**: Pumpkin replies with `CEncryptionBegin`, providing a random 4-byte verify token, the server ID string, and Pumpkin's 1024-bit RSA public key. +3. **Session Join**: The client generates a random 16-byte shared secret (`AES-128`), computes a SHA-1 server hash, and calls Mojang's session server (`POST /session/minecraft/join`) using its authenticated Microsoft access token. +4. **Verification**: The client sends `SEncryptionResponse` containing the RSA-encrypted shared secret and encrypted verify token. Pumpkin decrypts the token with its RSA private key, recalculates the SHA-1 digest, and queries Mojang (`GET /session/minecraft/hasJoined`). +5. **Profile Retrieval**: Mojang validates that the client and server hashes match, returning the authenticated player's UUID, username, and signed skin/cape texture properties (`GameProfile`). +6. **Encryption Activation**: Pumpkin activates full-stream symmetric encryption (`AES-128/CFB8`) and completes the login sequence by sending `CLoginSuccess`. + +--- + +## Game Profile Structure + +When Mojang's `hasJoined` endpoint validates a session, it returns a `GameProfile` containing the player's authenticated UUID, username, and signed texture properties: + +```rust +pub struct GameProfile { + pub id: Uuid, + pub name: String, + pub properties: Vec, + pub profile_actions: Option>, +} + +pub struct Property { + pub name: String, + pub value: String, // Base64 encoded texture data + pub signature: Option, // Cryptographic signature verifying authenticity +} +``` + +Pumpkin verifies that skin and cape signatures are cryptographically signed with Mojang's public key to prevent clients from injecting malicious payloads or unauthorized textures. + +--- + +## Custom Authentication Endpoints + +For private networks or enterprise environments running custom Yggdrasil-compatible authentication servers, endpoints can be customized in `pumpkin.toml`: + +```toml +[networking.java.authentication] +enabled = true +fallbacks = [] +connect_timeout = 5000 +read_timeout = 5000 +prevent_proxy_connections = false + +[networking.java.authentication.player_profile] +allow_banned_players = false +allowed_actions = ["FORCED_NAME_CHANGE", "USING_BANNED_SKIN"] + +[networking.java.authentication.textures] +enabled = true +allowed_url_schemes = ["http", "https"] +allowed_url_domains = [".minecraft.net", ".mojang.com"] +``` + +--- + +## Proxy Authentication Forwarding + +When running Pumpkin behind proxies, client connections terminate at the proxy. Pumpkin supports the three primary proxy forwarding standards: + +- **Velocity Modern Forwarding**: Validates player data forwarded with HMAC-SHA256 secret tokens (`[networking.proxy.velocity]`). +- **BungeeGuard**: Token-based authentication header validation for BungeeCord setups. +- **Legacy BungeeCord**: Handshake forwarding appending IP, UUID, and properties to the server host string. diff --git a/docs/tr_TR/developer/networking/java/overview.md b/docs/tr_TR/developer/networking/java/overview.md new file mode 100644 index 0000000..e79753c --- /dev/null +++ b/docs/tr_TR/developer/networking/java/overview.md @@ -0,0 +1,74 @@ +# Java Edition Networking Overview + +The Java Edition network stack handles standard Minecraft PC clients connecting over TCP. Wire-level structures and serialization logic reside in `pumpkin-protocol::java`, while connection management and gameplay packet handlers reside in `pumpkin::net::java`. + +--- + +## The Five Protocol States + +The Minecraft Java protocol operates as an explicit finite state machine. Every connection begins in the `Handshake` state and transitions according to client requests and server responses: + +| State | Transition Trigger | Purpose & Next State | +| :--- | :--- | :--- | +| **Handshake** | Client initiates connection | Inspects protocol version and transitions to `Status`, `Login`, or `Transfer` | +| **Status** | `NextState = 1` | Streams MOTD, active player counts, favicon, and ping response | +| **Login** | `NextState = 2` | Authenticates account (Yggdrasil), negotiates encryption (AES-128), transitions to `Config` | +| **Transfer** | `NextState = 3` | Modern proxy / server-transfer negotiation | +| **Config** | Authentication success | Exchanges dynamic registries, data tags, feature flags, and resource packs | +| **Play** | Configuration acknowledged | Primary bidirectional gameplay loop (chunks, movement, combat, chat) | + +1. **Handshake (`0x00`)**: Always the first packet sent by the client. It declares the client's protocol version, server address, port, and desired next state (`Status`, `Login`, or `Transfer`). +2. **Status**: Lightweight query state. The client requests a server list ping, and Pumpkin responds with the server version, player counts, favicon, and MOTD. +3. **Login**: Authenticates the player. If `online_mode` is enabled, Pumpkin performs an encryption handshake (AES-128/CFB8) and verifies the player's account with Mojang's Yggdrasil session servers. +4. **Config**: Introduced in modern Minecraft (1.20.2+). The server sends feature flags, known registries, resource packs, and custom server links before the player joins the physical world. +5. **Play**: The primary gameplay state. Used for all in-game actions including block breaking, entity movement, combat, chat, and chunk streaming. + +--- + +## Connection Representation: `JavaClient` vs. `Player` + +To keep memory usage minimal and prevent unnecessary allocations, Pumpkin cleanly separates pre-play connections from active players: + +### `JavaClient` +- **Scope**: Exists across `Handshake`, `Status`, `Login`, and `Config` states. +- **Location**: `crates/pumpkin/src/net/java/mod.rs`. +- **Characteristics**: + - Contains connection metadata: client protocol version, remote address, encryption/compression state. + - Has no world coordinates, no physics, no inventory, and no living entity. + - Very low memory footprint. + +### `Player` +- **Scope**: Exists solely during the `Play` state. +- **Location**: `crates/pumpkin/src/entity/player/`. +- **Characteristics**: + - Implements `EntityBase`, representing a living entity inside a `World` and `Level`. + - Holds inventory screens, health, hunger, velocities, and chunk view subscription queues. + - Contains an `Arc` reference for network transmission. + +--- + +## Multi-Version Support (`JavaMinecraftVersion`) + +Pumpkin supports multiple Minecraft client versions. Protocol differences are handled cleanly via `pumpkin_util::version::JavaMinecraftVersion`: + +- Packet IDs may differ across Minecraft versions. The `#[java_packet(ID)]` macro works with `pumpkin-data` to resolve the correct ID for the target version. +- When reading or writing packets, methods accept a `&JavaMinecraftVersion` reference to conditionally include or omit fields that changed in specific Minecraft updates. + +```rust +if *version <= JavaMinecraftVersion::V_1_7_6 { + // Read legacy stance coordinate present in early Minecraft versions + let _stance = bytebuf.get_f64_be()?; +} +``` + +--- + +## The Packet Pipeline + +Incoming Java packets pass through a structured pipeline: + +1. **Length Framing**: `tokio_util::codec` decodes VarInt frame lengths. +2. **Decompression & Decryption**: If enabled, the raw buffer is decrypted via AES/CFB8 and decompressed via ZLib. +3. **Plugin Event Interception**: A `PacketReceivedEvent` is dispatched to the plugin manager. Plugins can inspect or cancel the packet before server processing. +4. **ID Dispatching**: The packet ID is matched against version-specific constants in `crates/pumpkin/src/net/java/mod.rs` (or the respective state module). +5. **Handling**: The packet struct is deserialized and processed by state-specific handler functions. diff --git a/docs/tr_TR/developer/networking/metadata.md b/docs/tr_TR/developer/networking/metadata.md new file mode 100644 index 0000000..9def4ae --- /dev/null +++ b/docs/tr_TR/developer/networking/metadata.md @@ -0,0 +1,123 @@ +# Entity Metadata & Synced Data + +Minecraft synchronizes persistent entity visual states, animation flags, poses, and properties via **Entity Metadata** (historically called *DataWatchers* in Java Edition and *Actor Data* in Bedrock Edition). + +Instead of transmitting full state packets whenever an entity crouches, catches fire, or takes damage, the server broadcasts compact metadata updates to nearby observers. + +--- + +## Edition Comparison + +| Feature | Minecraft: Java Edition | Minecraft: Bedrock Edition | +| :--- | :--- | :--- | +| **Packet Name** | `CSetEntityMetadata` (`SET_ENTITY_DATA`) | `CSetActorData` (Packet ID `39`) | +| **Storage Structure** | `TrackedData` list terminating with `0xFF` | `SyncedActorDataList` (`HashMap`) | +| **Key Type** | Index (`u8`) mapped to typed `MetaDataType` | Enum keys (`entity_data_key::*`) | +| **Flags** | Bitfield byte (fire, crouching, sprinting, glowing) | Bitfield int64 (`entity_data_flag::*`) | +| **Dynamic Properties** | N/A (baked into tracked indices) | `PropertySyncData` (client/server sync) | +| **Crate Location** | `crates/pumpkin-protocol/src/java/client/play/entity_metadata.rs` | `crates/pumpkin-protocol/src/bedrock/client/set_actor_data.rs` | + +--- + +## 1. Java Edition: `CSetEntityMetadata` + +In Java Edition, entity metadata consists of numbered index entries. Each entry defines an index, a `MetaDataType` ID, and the encoded value. The list terminates with a single `0xFF` byte. + +### Key Index Hierarchy + +Entity metadata follows an object-oriented inheritance model: + +1. **Entity (Base)**: + - `0`: State bitfield (flags: bit `0` = on fire, bit `1` = crouching, bit `3` = sprinting, bit `5` = invisible, bit `6` = glowing). + - `1`: Air supply (`VarInt`). + - `2`: Custom name (optional `TextComponent`). + - `3`: Custom name visible (`bool`). + - `4`: Silent (`bool`). + - `5`: No gravity (`bool`). + - `6`: Pose (`Standing`, `FallFlying`, `Sleeping`, `Swimming`, `SpinAttack`, `Crouching`, `Dying`). +2. **LivingEntity (extends Entity)**: + - `8`: Hand states (using item, active hand). + - `9`: Health (`f32`). + - `10`: Potion effect ambient color. +3. **Player (extends LivingEntity)**: + - Additional hearts, skin customization bitmask, main hand indicator. + +### Sending Metadata Updates in Pumpkin + +Pumpkin encodes entity metadata in `pumpkin-protocol`: + +```rust +use pumpkin_protocol::VarInt; +use pumpkin_protocol::java::client::play::CSetEntityMetadata; + +// Construct metadata update for entity with ID 42 +let packet = CSetEntityMetadata::new( + VarInt(entity.entity_id), + serialized_metadata_bytes, +); + +// Broadcast to chunk observers +world.broadcast_packet_all(&packet).await; +``` + +--- + +## 2. Bedrock Edition: `CSetActorData` + +Bedrock Edition uses `CSetActorData` (`packet(39)`), storing properties in a dictionary mapping `u32` keys to strongly-typed `MetadataValue` variants. + +### Key & Flag Constants + +Bedrock exposes flags as a 64-bit integer bitfield under `entity_data_key::FLAGS`: + +- `entity_data_flag::ON_FIRE` (`0`): Entity is burning. +- `entity_data_flag::SNEAKING` (`1`): Player is sneaking. +- `entity_data_flag::RIDING` (`2`): Entity is mounted on a vehicle. +- `entity_data_flag::SPRINTING` (`3`): Player is sprinting. +- `entity_data_flag::USING_ITEM` (`4`): Player is actively charging a bow, eating food, or using a shield. +- `entity_data_flag::INVISIBLE` (`5`): Entity is invisible. +- `entity_data_flag::GLOWING` (`26`): Entity has the glowing effect outline. + +### Constructing and Updating Actor Data in Pumpkin + +In `crates/pumpkin/src/entity/living.rs`: + +```rust +use pumpkin_protocol::bedrock::client::set_actor_data::{ + CSetActorData, MetadataValue, PropertySyncData, SyncedActorDataList, + entity_data_flag, entity_data_key, +}; + +let mut actor_data = SyncedActorDataList::new(); + +// Set entity flags (e.g. actively using an item) +let mut flags: i64 = 0; +if is_using_item { + flags |= 1i64 << entity_data_flag::USING_ITEM; +} +actor_data.set(entity_data_key::FLAGS, MetadataValue::Int64(flags)); + +// Set air supply +actor_data.set(entity_data_key::AIR_SUPPLY, MetadataValue::Short(air as i16)); + +// Set custom name tag +actor_data.set(entity_data_key::NAME, MetadataValue::String(name)); + +let packet = CSetActorData { + target_runtime_id: VarULong(entity_id), + actor_data, + synced_properties: PropertySyncData::default(), + tick: VarULong(current_tick), +}; + +client.try_enqueue_client_packet(&packet); +``` + +--- + +## Multi-Version Compatibility + +Because Mojang frequently shifts metadata indices between Minecraft releases, `pumpkin-protocol` leverages translation tables from `pumpkin-data`: + +- For Java Edition, `pumpkin_data::meta_data_type::MetaDataType` remaps indices dynamically based on the client's `JavaMinecraftVersion`. +- For Bedrock Edition, `SyncedActorDataList` validates keys against the active Bedrock protocol version to prevent client crashes. diff --git a/docs/tr_TR/developer/networking/proxy/bungeecord.md b/docs/tr_TR/developer/networking/proxy/bungeecord.md new file mode 100644 index 0000000..cb06ff5 --- /dev/null +++ b/docs/tr_TR/developer/networking/proxy/bungeecord.md @@ -0,0 +1,73 @@ +# BungeeCord Forwarding + +Pumpkin provides native support for BungeeCord and Waterfall proxy networks, including **BungeeGuard** authentication token verification. + +Implementation is located in [`crates/pumpkin/src/net/proxy/bungeecord.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/bungeecord.rs). + +--- + +## How BungeeCord Forwarding Operates + +Unlike modern protocols that use custom login channels, BungeeCord operates by encoding client metadata directly into the `server_address` field of the initial `SHandshake` packet. + +The string uses null character (`\0`) delimiters: + +```text +[original_host]\0[player_ip:port]\0[player_uuid]\0[properties_json] +``` + +1. **`original_host`**: The domain name the player originally typed into their client. +2. **`player_ip:port`**: The genuine IP address and ephemeral source port of the client. +3. **`player_uuid`**: The player's Mojang UUID (32 hex characters without hyphens or 36 characters with hyphens). +4. **`properties_json`**: A JSON array containing skin textures, cape textures, and Mojang cryptographic signatures. + +--- + +## The Security Risk: Proxy Bypasses + +Because BungeeCord's handshake string is unauthenticated plain text, an attacker could connect directly to an exposed backend port, inject an arbitrary UUID and operator username, and gain full server control. + +Traditionally, this required strict Linux firewall rules (`iptables` / `ufw`) to restrict port access exclusively to the proxy IP. + +--- + +## BungeeGuard Protection + +To eliminate proxy bypasses without relying solely on network firewalls, Pumpkin integrates **BungeeGuard** token verification: + +1. The BungeeCord proxy injects a shared secret into the player's profile properties under the key `bungeeguard-token`. +2. When Pumpkin processes the handshake in `bungeecord_login`: + - It checks whether `secret` is configured in `pumpkin.toml`. + - It inspects the forwarded properties for `bungeeguard-token`. + - If the token matches, Pumpkin **strips the secret property** from the player's profile (so other plugins cannot read it) and completes the login. + - If the token is missing or incorrect, Pumpkin rejects the connection with `BungeeCordError::MissingToken` or `BungeeCordError::InvalidToken`. + +```rust +// In crates/pumpkin/src/net/proxy/bungeecord.rs +if !secret.is_empty() { + let Some(token_index) = properties.iter().position(|p| p.name == BUNGEEGUARD_TOKEN_PROPERTY) else { + return Err(BungeeCordError::MissingToken); + }; + + if properties[token_index].value != secret { + return Err(BungeeCordError::InvalidToken); + } + properties.remove(token_index); +} +``` + +--- + +## Configuration in `pumpkin.toml` + +To enable BungeeCord forwarding with BungeeGuard: + +```toml +[networking.proxy] +enabled = true + +[networking.proxy.bungeecord] +enabled = true +# Optional: Configure the BungeeGuard shared secret to block direct connections +secret = "YOUR_BUNGEEGUARD_SECRET_HERE" +``` diff --git a/docs/tr_TR/developer/networking/proxy/index.md b/docs/tr_TR/developer/networking/proxy/index.md new file mode 100644 index 0000000..300c89e --- /dev/null +++ b/docs/tr_TR/developer/networking/proxy/index.md @@ -0,0 +1,46 @@ +# Proxy Forwarding Overview + +In multi-server Minecraft networks, players connect through a front-facing reverse proxy (such as Velocity, BungeeCord, or Vine) that routes connections between lobbies and sub-servers without requiring players to reconnect. + +Because backend servers receive TCP connections directly from the proxy machine rather than the player, the server must be informed of the player's genuine **IP address**, **UUID**, **username**, and **skin properties**. + +Pumpkin provides native support for all three major proxy forwarding protocols in [`crates/pumpkin/src/net/proxy/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/net/proxy). + +--- + +## The Proxy Forwarding Problem + +Without secure proxy forwarding: +1. **IP Loss**: Every connected player appears with the proxy's loopback or internal IP (breaking IP bans and geo-location). +2. **UUID Spoofing & Bypasses**: Malicious users could connect directly to the backend server's port with any spoofed username or operator identity. + +To prevent proxy bypasses, Pumpkin validates cryptographic secrets, tokens, or digital signatures sent by the proxy. + +--- + +## Comparison of Supported Protocols + +| Protocol | Protocol Transport | Security / Cryptography | Replay Protection | Crate Module | +| :--- | :--- | :--- | :--- | :--- | +| **[BungeeCord](/developer/networking/proxy/bungeecord)** | `SHandshake` server address string | Optional token in profile properties (`bungeeguard-token`) | None | `pumpkin::net::proxy::bungeecord` | +| **[Velocity](/developer/networking/proxy/velocity)** | Login plugin message (`velocity:player_info`) | Symmetric HMAC-SHA256 signature | None | `pumpkin::net::proxy::velocity` | +| **[Vine](/developer/networking/proxy/vine)** | Login plugin message (`vine:player_info`) | Asymmetric **Ed25519** digital signatures | 16-byte nonce challenge + 30s timestamp window | `pumpkin::net::proxy::vine` | + +--- + +## Lifecycle Architecture + +1. **Player Authentication**: The player connects to the proxy (BungeeCord, Velocity, or Vine) and authenticates against Mojang's session servers. +2. **Backend Connection**: The proxy connects to Pumpkin over TCP and sends `SHandshake`. +3. **Data Forwarding**: + - **BungeeCord Mode**: The proxy injects the player's remote IP, UUID, and properties directly into the `SHandshake` hostname string (separated by null bytes `\0`). + - **Modern Mode (Velocity / Vine)**: Pumpkin sends `CLoginPluginRequest` on a dedicated channel (`velocity:player_info` or `vine:player_info`). The proxy signs the player profile using HMAC-SHA256 (Velocity) or Ed25519 (Vine) and replies with `SLoginPluginResponse`. +4. **Validation & Session Initialization**: Pumpkin validates the signature, binds the real IP and GameProfile to the `JavaClient`, and sends `CLoginSuccess`. + +--- + +## Proxy Documentation + +- **[BungeeCord](/developer/networking/proxy/bungeecord)**: Legacy host string injection and optional BungeeGuard token validation. +- **[Velocity Modern Forwarding](/developer/networking/proxy/velocity)**: HMAC-SHA256 signed player info during the Login state. +- **[Vine Modern Forwarding](/developer/networking/proxy/vine)**: Next-generation proxy forwarding with Ed25519 signatures and replay attack prevention. diff --git a/docs/tr_TR/developer/networking/proxy/velocity.md b/docs/tr_TR/developer/networking/proxy/velocity.md new file mode 100644 index 0000000..b0333e7 --- /dev/null +++ b/docs/tr_TR/developer/networking/proxy/velocity.md @@ -0,0 +1,88 @@ +# Velocity Modern Forwarding + +[Velocity](https://papermc.io/software/velocity) is a high-performance proxy created by PaperMC. It uses modern plugin messaging during the `Login` state to securely transmit player data with **HMAC-SHA256** message authentication codes. + +Pumpkin implements Velocity modern forwarding in [`crates/pumpkin/src/net/proxy/velocity.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/velocity.rs). + +--- + +## How Velocity Forwarding Operates + +Unlike BungeeCord, which injects unauthenticated data into the handshake string, Velocity operates during the **Login** state over a dedicated plugin channel: + +1. **Handshake & Login**: The player authenticates with Velocity. Velocity initiates a standard TCP connection to Pumpkin and transmits `SHandshake` and `SLoginStart`. +2. **Channel Query**: Recognizing that Velocity modern forwarding is enabled, Pumpkin pauses the login sequence and dispatches a `CLoginPluginRequest` on channel `velocity:player_info`. +3. **Payload Generation**: Velocity bundles the player's remote address, UUID, username, and skin textures. It calculates a 32-byte HMAC-SHA256 signature across this payload using the shared secret. +4. **Signature Verification**: Velocity sends `SLoginPluginResponse`. Pumpkin validates the HMAC-SHA256 digest against its configured secret. If valid, Pumpkin overwrites the connection's network address and GameProfile and transitions to `Config` / `Play`. + +--- + +## Wire Payload Structure + +The payload in `SLoginPluginResponse` is structured as follows: + +```text +┌─────────────────────────────────────────────────────────────┐ +│ 32-byte HMAC-SHA256 Signature │ +├─────────────────────────────────────────────────────────────┤ +│ Forwarding Protocol Version (VarInt: 1 - 4) │ +├─────────────────────────────────────────────────────────────┤ +│ Client Remote IP Address (String / IPv4 / IPv6) │ +├─────────────────────────────────────────────────────────────┤ +│ Player UUID (128-bit / UUID) │ +├─────────────────────────────────────────────────────────────┤ +│ Player Username (String) │ +├─────────────────────────────────────────────────────────────┤ +│ Profile Properties Count (VarInt) │ +│ ├─ Property Name (String) │ +│ ├─ Property Value (String) │ +│ └─ Optional Signature (String) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Cryptographic Validation in Pumpkin + +Inside `crates/pumpkin/src/net/proxy/velocity.rs`: + +```rust +use hmac::{Hmac, KeyInit, Mac}; +use sha2::Sha256; + +type HmacSha256 = Hmac; + +// Extract signature and payload +let (signature, data_to_verify) = data.split_at(32); + +// Initialize HMAC with secret from pumpkin.toml +let mut mac = HmacSha256::new_from_slice(secret.as_bytes()) + .map_err(|_| VelocityError::FailedVerifyIntegrity)?; + +mac.update(data_to_verify); + +// Constant-time verification prevents timing attacks +mac.verify_slice(signature) + .map_err(|_| VelocityError::FailedVerifyIntegrity)?; +``` + +If the signature does not match (e.g., if an attacker attempts to connect directly to the backend without knowing the secret), Pumpkin drops the connection immediately. + +--- + +## Configuration in `pumpkin.toml` + +1. Open `velocity.toml` on your Velocity proxy and set: + ```toml + player-info-forwarding-mode = "modern" + forwarding-secret-file = "forwarding.secret" + ``` +2. Copy the generated secret string from `forwarding.secret` into `pumpkin.toml`: + ```toml + [networking.proxy] + enabled = true + + [networking.proxy.velocity] + enabled = true + secret = "YOUR_VELOCITY_FORWARDING_SECRET_HERE" + ``` diff --git a/docs/tr_TR/developer/networking/proxy/vine.md b/docs/tr_TR/developer/networking/proxy/vine.md new file mode 100644 index 0000000..fed2a8a --- /dev/null +++ b/docs/tr_TR/developer/networking/proxy/vine.md @@ -0,0 +1,112 @@ +# Vine Modern Forwarding + +**Vine** is a next-generation, high-performance Minecraft proxy written in Rust. Designed for ultra-low latency, multi-threading, and enterprise-grade security, Vine introduces modern cryptographic forwarding based on **Ed25519 asymmetric signatures** and **replay attack protection**. + +Pumpkin provides native support for Vine forwarding in [`crates/pumpkin/src/net/proxy/vine.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/vine.rs). + +--- + +## Why Vine Forwarding? + +While Velocity's HMAC-SHA256 forwarding represents an improvement over legacy BungeeCord, it relies on a **symmetric shared secret**. If one backend server in a network is compromised, an attacker obtains the shared secret and can forge authenticated player logins across the entire server cluster. + +Vine solves this with: +1. **Asymmetric Public-Key Cryptography (Ed25519)**: The proxy signs player data using its private key. Backend servers only store the proxy's **Public Key** (`public_key`). Even if a backend server is compromised, the attacker cannot forge identities to other servers. +2. **Replay Attack Protection (16-byte Challenge Nonce)**: Pumpkin generates a cryptographic random 16-byte nonce for each connection. The proxy must sign and return this nonce. Replaying a captured login packet is impossible. +3. **Timestamp Drift Window**: Transmits a Unix timestamp that must be within ±30 seconds of the backend's system clock (`MAX_TIMESTAMP_DRIFT_SECS = 30`). + +--- + +## The Handshake Sequence + +1. **Connection**: The player connects and authenticates with Vine. Vine opens a backend connection to Pumpkin and sends `SHandshake` and `SLoginStart`. +2. **Challenge Query**: Pumpkin generates a cryptographically random 16-byte nonce and sends `CLoginPluginRequest` on the `vine:player_info` channel. +3. **Asymmetric Signing**: Vine serializes the version, timestamp, the returned 16-byte nonce, the client IP address, and the player GameProfile. It signs this byte stream with its Ed25519 private key. +4. **Tri-fold Validation**: Vine replies with `SLoginPluginResponse`. Pumpkin verifies: + - **Clock Skew**: The embedded timestamp is within ±30 seconds of UTC time. + - **Nonce Check**: The signed nonce exactly matches the 16-byte challenge generated in step 2. + - **Ed25519 Signature**: The signature is cryptographically valid against the configured `public_key`. +5. **Session Success**: If all three checks pass, Pumpkin applies the verified identity and emits `CLoginSuccess`. + +--- + +## Wire Payload Structure + +The `SLoginPluginResponse` data layout: + +```text +┌─────────────────────────────────────────────────────────────┐ +│ 64-byte Ed25519 Digital Signature │ +├─────────────────────────────────────────────────────────────┤ +│ Forwarding Protocol Version (i32: 1) │ +├─────────────────────────────────────────────────────────────┤ +│ Unix Timestamp in Seconds (i64) │ +├─────────────────────────────────────────────────────────────┤ +│ 16-byte Challenge Nonce │ +├─────────────────────────────────────────────────────────────┤ +│ Client Remote IP Address (String / IPv4 / IPv6) │ +├─────────────────────────────────────────────────────────────┤ +│ Player UUID (128-bit / UUID) │ +├─────────────────────────────────────────────────────────────┤ +│ Player Username (String) │ +├─────────────────────────────────────────────────────────────┤ +│ Profile Properties Count (VarInt) │ +│ ├─ Property Name (String) │ +│ ├─ Property Value (String) │ +│ └─ Optional Signature (String) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Verification in Pumpkin + +In `crates/pumpkin/src/net/proxy/vine.rs`: + +```rust +use ed25519_dalek::{Signature, Verifier, VerifyingKey}; + +// 1. Validate payload length (64-byte signature + 25-byte headers minimum) +if data.len() < 89 { + return Err(VineError::DataTooShort); +} + +// 2. Extract signature and signed payload +let (sig_bytes, signed_data) = data.split_at(64); +let signature = Signature::from_slice(sig_bytes) + .map_err(|_| VineError::InvalidSignature)?; + +// 3. Verify timestamp skew (30 second maximum drift) +let skew = (server_now - timestamp).abs(); +if skew > MAX_TIMESTAMP_DRIFT_SECS { + return Err(VineError::TimestampExpired(skew, MAX_TIMESTAMP_DRIFT_SECS)); +} + +// 4. Verify challenge nonce matches connection state +if received_nonce != expected_nonce { + return Err(VineError::ChallengeMismatch); +} + +// 5. Verify Ed25519 signature with proxy's public key +verifying_key.verify(signed_data, &signature) + .map_err(|_| VineError::InvalidSignature)?; +``` + +--- + +## Configuration in `pumpkin.toml` + +Configure Vine forwarding in `pumpkin.toml`: + +```toml +[networking.proxy] +enabled = true + +[networking.proxy.vine] +enabled = true +# 64-character hex-encoded Ed25519 public key of the Vine proxy +public_key = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + +# Alternatively, if secret is supplied, the public key is derived automatically +secret = "" +``` diff --git a/docs/tr_TR/developer/networking/query.md b/docs/tr_TR/developer/networking/query.md new file mode 100644 index 0000000..7d97a9c --- /dev/null +++ b/docs/tr_TR/developer/networking/query.md @@ -0,0 +1,97 @@ +# GameSpy4 Query Protocol + +Pumpkin includes native support for the **GameSpy4 Query** protocol over UDP. External monitoring systems, Discord bots, and server listing platforms use Query to poll real-time player counts, MOTD, server versions, and active player lists with minimal network overhead. + +Query wire encoding is implemented in [`crates/pumpkin-protocol/src/query.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-protocol/src/query.rs), and the asynchronous UDP listener is implemented in [`crates/pumpkin/src/net/query.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/query.rs). + +--- + +## Why Query? (UDP vs. Status Ping) + +While standard Minecraft clients ping servers via the TCP `Status` state (SLP), Query operates over stateless **UDP**: + +- **No TCP Handshake**: Avoids TCP connection overhead and connection state tables. +- **Detailed Server State**: Returns comprehensive key-value metadata and a full list of connected player usernames (not available via basic TCP ping). +- **Amplification Attack Protection**: Enforces cryptographic challenge tokens so malicious actors cannot abuse the UDP port for denial-of-service reflection attacks. + +--- + +## The Handshake & Challenge Token Flow + +To prevent UDP spoofing and reflection DDoS attacks, Query requires a two-step handshake: + +1. **Handshake Challenge**: The client transmits `SHandshake` (`0xFEFD`, type `0x09`, and a 32-bit session ID). Pumpkin generates a cryptographic 32-bit integer challenge token tied to the client's `SocketAddr` and responds with `CHandshake`. +2. **Status Query**: The client sends `SStatusRequest` containing the received challenge token: + - **Basic Status** (no padding): Returns MOTD, game type (`SMP`), map name, online player count, and max player capacity. + - **Full Status** (4-byte padding): Returns detailed server metadata key-value pairs (`hostname`, `gametype`, `game_id`, `version`, `plugins`, `map`, `numplayers`, `maxplayers`, `hostport`, `hostip`) followed by an array of online player usernames. + +### Challenge Token Invalidation +In `crates/pumpkin/src/net/query.rs`, challenge tokens are stored in a thread-safe map (`Arc>>`). A background Tokio timer purges all challenge tokens every 30 seconds to prevent replay abuse: + +```rust +// Tokens are strictly bound to the client's IP and port +let valid_challenge_tokens = Arc::new(RwLock::new(HashMap::new())); + +// Background expiration loop +tokio::spawn(async move { + let mut interval = time::interval(Duration::from_secs(30)); + loop { + interval.tick().await; + valid_challenge_tokens.write().await.clear(); + } +}); +``` + +--- + +## Packet Formats + +Every incoming Query packet starts with the 2-byte magic header `0xFEFD` followed by a 1-byte packet type: + +| Type Byte | Name | Direction | Description | +| :--- | :--- | :--- | :--- | +| `0x09` | Handshake Request | Client → Server | Client asks for a challenge token. | +| `0x09` | Handshake Response | Server → Client | Returns the numeric token encoded as an ASCII string. | +| `0x00` | Stat Request | Client → Server | Requests basic or full status with challenge token. | +| `0x00` | Basic Stat Response | Server → Client | 11-byte short summary. | +| `0x00` | Full Stat Response | Server → Client | Key-value dictionary + null-separated player list. | + +--- + +## Response Payloads + +### 1. Basic Status (`CBasicStatus`) +Contains essential server fields: +- **`motd`**: Server message of the day (stripped of color codes). +- **`gametype`**: Fixed string `"SMP"`. +- **`map`**: Active world/level name (e.g., `"world"`). +- **`numplayers`**: Online player count. +- **`maxplayers`**: Maximum configured player capacity. +- **`hostport`**: Port number (`u16` Little Endian). +- **`hostip`**: Server IP string. + +### 2. Full Status (`CFullStatus`) +Returns two null-terminated sections: + +1. **Key-Value Metadata Map**: + - `hostname`: Server description / MOTD. + - `gametype`: `"SMP"`. + - `game_id`: `"MINECRAFT"`. + - `version`: Server protocol / Minecraft version string. + - `plugins`: Installed server software and plugin names. + - `map`: Level name. + - `numplayers` / `maxplayers`: Capacity stats. + - `hostport` / `hostip`: Network bindings. +2. **Player List**: + - An array of null-terminated UTF-8 strings containing the usernames of all currently connected players. + - Concludes with a terminal null byte (`\0`). + +--- + +## Configuration in `pumpkin.toml` + +```toml +[networking.query] +enabled = true +address = "0.0.0.0:25565" +``` diff --git a/docs/tr_TR/developer/plugins/index.md b/docs/tr_TR/developer/plugins/index.md new file mode 100644 index 0000000..8cc993f --- /dev/null +++ b/docs/tr_TR/developer/plugins/index.md @@ -0,0 +1,56 @@ +# Plugin Engine Architecture + +Pumpkin features a modular, multi-runtime plugin engine designed for performance, safety, and cross-language interoperability. + +Unlike traditional Minecraft servers that rely on a single JVM class loader, Pumpkin isolates plugin execution through dedicated loaders supporting both **native high-performance Rust binaries** and **sandboxed WebAssembly components** compiled from Rust, Python, C#, C, Go, or Kotlin. + +--- + +## Architectural Overview + +### Architectural Breakdown + +| Subsystem | Primary Crate | Responsibility | +| :--- | :--- | :--- | +| **Plugin Manager** | `crates/pumpkin/src/plugin` | Lifecycle orchestration (load, enable, disable), event dispatching, and loader registration | +| **Native Loader** | `crates/pumpkin/src/plugin/loader/native` | Direct dynamic library loading (`.so`, `.dll`, `.dylib`) for zero-overhead Rust extensions | +| **WASM Loader** | `crates/pumpkin/src/plugin/loader/wasm` | Wasmtime execution engine for sandboxed components compiled from Rust, Python, Go, C#, C, and Kotlin | +| **Interface Specifications** | `crates/pumpkin-plugin-wit` | WebAssembly Interface Types (WIT) defining typed functions, types, and host imports | +| **Security & Verification** | `crates/pumpkin/src/plugin/loader/wasm/sign` | Cryptographic signature verification via W3C wasmsign2 / Ed25519 public keys | + +--- + +## The Dual Execution Model + +| Feature | Native Plugins | WebAssembly (Wasmtime) Plugins | +| :--- | :--- | :--- | +| **File Extension** | `.so` (Linux), `.dll` (Windows), `.dylib` (macOS) | `.wasm` (Component Model) | +| **Languages** | Rust (compiled dynamically) | Rust, Python, C#, C, Go, Kotlin | +| **Performance** | Native CPU execution (zero overhead) | JIT-compiled with near-native speed | +| **Sandboxing** | None (runs in host address space) | Memory-isolated sandboxed execution | +| **Security & Signing** | OS-level permissions | Cryptographic W3C Wasm-Sign verification | +| **Hot Unloading** | Limited (OS shared library constraints) | Fully supported memory reclamation | + +--- + +## Event Lifecycle & Dispatching + +Plugins interact with Pumpkin primarily through events: + +1. **Event Dispatch**: + - `server.plugin_manager.fire_blocking(&server, &mut event)`: Dispatches events synchronously on the executing thread (e.g., cancelling player block placement or modifying packet payloads). + - Asynchronous events run on non-blocking Tokio tasks. +2. **Cancellable Events**: + - Handlers can set `event.cancelled = true` to abort the default game logic (such as cancelling player movement, interactions, or packet processing). + +--- + +## Plugin Engine Documentation + +Explore the internal mechanics of Pumpkin's plugin system: + +- **[Adding a Plugin Loader](/developer/plugins/loaders)**: Step-by-step guide on implementing the `PluginLoader` trait for new scripting languages and runtimes. +- **[WASM Signing & Verification](/developer/plugins/wasm-signing)**: Detailed specification of Ed25519 cryptographic signing, W3C `wasmsign2` custom sections, and marketplace license verification. + +> [!NOTE] +> Looking to develop third-party plugins rather than working on Pumpkin's internal plugin engine? Check out the **[Plugin Development Guide](/plugin-dev/introduction)**. diff --git a/docs/tr_TR/developer/plugins/loaders.md b/docs/tr_TR/developer/plugins/loaders.md new file mode 100644 index 0000000..7e0f070 --- /dev/null +++ b/docs/tr_TR/developer/plugins/loaders.md @@ -0,0 +1,161 @@ +# Adding a Plugin Loader + +Pumpkin's plugin architecture is runtime-agnostic. The server does not assume all plugins run in WebAssembly or native shared libraries; instead, all execution runtimes implement the unified [`PluginLoader`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/plugin/loader/mod.rs) trait. + +This guide explains how `PluginLoader` operates and demonstrates how to implement a custom loader for new script runtimes or virtual machines (such as Lua, JavaScript/Boa, or GraalVM). + +--- + +## The `PluginLoader` Trait + +All plugin loaders implement the `PluginLoader` trait located in `crates/pumpkin/src/plugin/loader/mod.rs`: + +```rust +use std::{any::Any, path::Path, pin::Pin, sync::Arc}; +use crate::plugin::{PluginMetadata, api::Plugin}; + +pub type PluginLoadFuture<'a> = Pin< + Box< + dyn Future< + Output = Result< + (Arc, PluginMetadata, Box), + LoaderError, + >, + > + Send + + 'a, + >, +>; + +pub type PluginUnloadFuture<'a> = + Pin> + Send + 'a>>; + +pub trait PluginLoader: Send + Sync { + /// Determines whether this loader handles the target file. + fn can_load(&self, path: &Path) -> bool; + + /// Loads, parses, and initializes a plugin instance asynchronously. + fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a>; + + /// Unloads the plugin and reclaims associated resources. + fn unload(&self, data: Box) -> PluginUnloadFuture<'_>; + + /// Indicates whether the runtime supports safe unloading without memory leaks. + fn can_unload(&self) -> bool; +} +``` + +--- + +## Existing Loaders in Pumpkin + +Pumpkin currently provides two official loaders: + +1. **`NativePluginLoader` (`loader/native.rs`)**: + - Matches dynamic library files (`.so` on Linux, `.dll` on Windows, `.dylib` on macOS). + - Dynamically links the library using [`libloading`](https://docs.rs/libloading/). + - Locates and invokes the extern entrypoint function (`pumpkin_plugin_init`). + - `can_unload()` returns `false` due to OS-level shared library unloading hazards. +2. **`WasmPluginLoader` (`loader/wasm/mod.rs`)**: + - Matches `.wasm` files. + - Verifies cryptographic signatures (see [WASM Signing](/developer/plugins/wasm-signing)). + - JIT-compiles WebAssembly components using [Wasmtime](https://wasmtime.dev/). + - Links host WIT exports (`pumpkin-host-bindings`). + - `can_unload()` returns `true` because Wasmtime safely drops guest memory. + +--- + +## Step-by-Step: Adding a Custom Plugin Loader + +Here is how to create a custom loader (e.g., for a scripting runtime): + +### Step 1: Define the Loader Struct +Create a new file in `crates/pumpkin/src/plugin/loader/my_loader.rs`: + +```rust +use std::{any::Any, path::Path, sync::Arc}; +use crate::plugin::loader::{LoaderError, PluginLoadFuture, PluginLoader, PluginUnloadFuture}; +use crate::plugin::{PluginMetadata, api::Plugin}; + +pub struct MyCustomLoader; + +impl MyCustomLoader { + #[must_use] + pub fn new() -> Self { + Self + } +} +``` + +### Step 2: Implement File Recognition (`can_load`) +Check the file extension or inspect header bytes: + +```rust +impl PluginLoader for MyCustomLoader { + fn can_load(&self, path: &Path) -> bool { + path.extension() + .and_then(|ext| ext.to_str()) + .is_some_and(|ext| ext.eq_ignore_ascii_case("script")) + } +``` + +### Step 3: Implement Asynchronous Loading (`load`) +The `load` method reads the script, initializes the VM, registers server host callbacks, and returns a tuple containing: +1. `Arc`: The generic plugin handle. +2. `PluginMetadata`: Name, author, version, description. +3. `Box`: Loader-specific context (e.g., VM state) retained for unloading. + +```rust + fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a> { + Box::pin(async move { + let script_content = tokio::fs::read_to_string(path).await + .map_err(|e| LoaderError::InitializationFailed(e.to_string()))?; + + // 1. Parse plugin metadata from header or accompanying manifest + let metadata = PluginMetadata { + name: "ExampleScript".into(), + version: "1.0.0".into(), + authors: vec!["Author".into()], + description: Some("Custom script plugin".into()), + }; + + // 2. Initialize runtime instance + let plugin_instance = Arc::new(MyScriptPluginWrapper::new(script_content)); + let runtime_state: Box = Box::new(()); + + Ok((plugin_instance, metadata, runtime_state)) + }) + } +``` + +### Step 4: Implement Clean Unloading (`unload` & `can_unload`) +If your runtime can cleanly terminate without leaking resources: + +```rust + fn can_unload(&self) -> bool { + true + } + + fn unload(&self, data: Box) -> PluginUnloadFuture<'_> { + Box::pin(async move { + // Drop runtime state and disconnect event listeners + drop(data); + Ok(()) + }) + } +} +``` + +### Step 5: Register the Loader in `PluginManager` + +Open `crates/pumpkin/src/plugin/mod.rs` and register your loader in `PluginManager::new()`: + +```rust +// In crates/pumpkin/src/plugin/mod.rs +let mut loaders: Vec> = Vec::new(); + +loaders.push(Box::new(NativePluginLoader::new())); +loaders.push(Box::new(WasmPluginLoader::new())); +loaders.push(Box::new(MyCustomLoader::new())); // Add custom loader +``` + +During server startup, Pumpkin iterates through all registered loaders in sequence when scanning the `plugins/` folder, dispatching each file to the first loader whose `can_load` returns `true`. diff --git a/docs/tr_TR/developer/plugins/wasm-signing.md b/docs/tr_TR/developer/plugins/wasm-signing.md new file mode 100644 index 0000000..40a36eb --- /dev/null +++ b/docs/tr_TR/developer/plugins/wasm-signing.md @@ -0,0 +1,86 @@ +# WASM Plugin Signing & Verification + +Pumpkin implements a cryptographic signing and license verification system for WebAssembly plugins. This ensures plugin integrity, authenticates author identity, and protects verified or marketplace plugins against unauthorized tampering. + +The signing subsystem is implemented in [`crates/pumpkin/src/plugin/loader/wasm/wasm_host/signature.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/plugin/loader/wasm/wasm_host/signature.rs) based on the **W3C `wasmsign2`** specification using custom WebAssembly sections. + +--- + +## Why Sign WebAssembly Plugins? + +- **Tamper Protection**: Ensures that compiled bytecode has not been injected with malicious logic after compilation. +- **Author Identity**: Authenticates that a plugin was built and released by a verified developer. +- **Marketplace Licensing**: Validates license keys and entitlements for plugins distributed through the official [Pumpkin Marketplace](https://market.pumpkinmc.org). + +--- + +## Custom Sections in the WASM Binary + +Pumpkin embeds signing and verification metadata directly into two standard WebAssembly custom sections: +- **`pumpkin.metadata`**: Stores developer, publication, and license claims in JSON format. +- **`wasm_signature`**: Stores the cryptographic signature envelope compliant with the W3C `wasmsign2` standard. + +### 1. `pumpkin.metadata` Section +Contains a JSON string holding publication and license claims: + +```json +{ + "marketplace_url": "https://market.pumpkinmc.org", + "plugin_id": 1024, + "plugin_name": "EconomyPlus", + "version": "1.2.0", + "dev_id": 42, + "dev_name": "DevName", + "is_paid": true, + "user_id": 1001, + "license_key": "XXXX-XXXX-XXXX-XXXX", + "issued_at": "2026-09-14T05:00:00Z" +} +``` + +### 2. `wasm_signature` Section +Follows the W3C `wasmsign2` envelope structure: + +```json +{ + "version": 1, + "algorithm": "Ed25519", + "public_key_hex": "4a7b...", + "signature_hex": "8e1c..." +} +``` + +--- + +## The Signing Pipeline + +The signing process guarantees integrity by hashing and signing the clean WebAssembly bytecode combined with the metadata claims: + +### Signing Algorithm Steps +1. **Sanitization (`strip_pumpkin_sections`)**: Uses [`wasmparser`](https://docs.rs/wasmparser/) to strip any preexisting `pumpkin.metadata` or `wasm_signature` custom sections to avoid recursion. +2. **Payload Construction**: + ```rust + let mut sign_payload = Vec::new(); + sign_payload.extend_from_slice(&clean_wasm); + sign_payload.extend_from_slice(&metadata_json); + ``` +3. **Ed25519 Signature**: The payload is signed using [`ed25519-dalek`](https://docs.rs/ed25519-dalek/) with the developer's 32-byte secret key. +4. **Encoding**: Uses [`wasm-encoder`](https://docs.rs/wasm-encoder/) to append the `pumpkin.metadata` and `wasm_signature` custom sections to the clean binary. + +--- + +## The Verification Pipeline + +When Pumpkin boots and loads a WebAssembly plugin, the verification sequence executes before the plugin is instantiated: + +1. **Custom Section Extraction**: The `WasmPluginLoader` scans the binary for `pumpkin.metadata` and `wasm_signature`. +2. **Key Retrieval**: + - If the plugin is marked as marketplace-verified, Pumpkin verifies the public key against the Marketplace Public Key Endpoint (`https://market.pumpkinmc.org/api/v1/rest/public-key`). + - If signed by an independent developer, the key is matched against trusted developer keys configured in `pumpkin.toml`. +3. **Bytecode Verification**: + - Pumpkin strips the custom sections from the loaded binary to recover the original `clean_wasm`. + - Reconstructs the payload `(clean_wasm + metadata_json)`. + - Verifies the signature using `verifying_key.verify(&payload, &signature)`. +4. **License & Integrity Validation**: + - If verification succeeds, Wasmtime instantiates the plugin safely. + - If verification fails or the signature is invalid, loading is aborted with an error, protecting the server from running compromised code. diff --git a/docs/tr_TR/developer/porting-versions.md b/docs/tr_TR/developer/porting-versions.md new file mode 100644 index 0000000..0a5014a --- /dev/null +++ b/docs/tr_TR/developer/porting-versions.md @@ -0,0 +1,287 @@ +# Porting to Newer Minecraft Versions + +Upgrading Pumpkin to support a newer Minecraft release—whether a minor patch (e.g., `1.21.4` to `1.21.5`) or a major update (e.g., `1.21` to `26.1`/`26.2`)—is a structured, multi-step process. + +> [!IMPORTANT] +> **The Extractor mod must be ported first!** +> Pumpkin cannot be ported in isolation. The primary source for Pumpkin's assets (`blocks.json`, `items.json`, `entities.json`, `packets.json`, `tracked_data.json`, `synced_registries.json`, etc.) is our dedicated Fabric mod: [Pumpkin-MC/Extractor](https://github.com/Pumpkin-MC/Extractor). You **must first port and run the Extractor on the target Minecraft version** before running `pumpkin-codegen` or updating server crates. + +### Porting Lifecycle at a Glance + +| Stage | Responsibility | Primary Crates / Tools | +| :--- | :--- | :--- | +| **1. Port Extractor** | Extract vanilla block states, entities, items, packets, and registries | `Pumpkin-MC/Extractor` (Fabric mod) | +| **2. Update Datapacks** | Ingest new data packs, tags, recipes, and Bedrock mappings | `assets/datapacks/`, `assets/bedrock` | +| **3. Version Declarations** | Register protocol version and ViaVersion/ViaBackwards remap nodes | `pumpkin-util`, `tools/pumpkin-codegen` | +| **4. Code Generation** | Regenerate zero-cost Rust data tables and registries | `pumpkin-codegen` -> `pumpkin-data` | +| **5. Compiler Fixes** | Update packet layouts, codecs, and data components | `pumpkin-protocol`, `pumpkin-inventory` | +| **6. Gameplay Logic** | Implement new block behaviors, item mechanics, and mob AI | `crates/pumpkin/src/block`, `item`, `entity` | +| **7. Multi-Version QA** | Validate backward compatibility, test suites, and client joins | `cargo test`, `pumpkin-gametest` | + +--- + +## Phase 1: Port the Extractor Mod First (`Pumpkin-MC/Extractor`) + +The [Extractor](https://github.com/Pumpkin-MC/Extractor) is a Kotlin-based Fabric mod that boots an actual Minecraft client or server instance to introspect live game registries and extract data into the exact JSON formats Pumpkin expects. + +### 1. Clone or Update the Extractor Repository +```bash +git clone https://github.com/Pumpkin-MC/Extractor +cd Extractor +``` + +### 2. Update `gradle.properties` +Bump the dependency versions to match the target Minecraft version, Yarn mappings, and Fabric ecosystem: + +```properties +minecraft_version=26.2 +yarn_mappings=26.2+build.1:v2 +loader_version=0.16.10 +fabric_version=0.115.0+26.2 +``` + +### 3. Resolve Extractor Compilation Errors +Minecraft and Fabric internal APIs often change between releases. Check and fix any broken extractors in `src/main/kotlin/de/snowii/extractor/extractors/`: +- **`Blocks.kt`**: Extracts block states, hardness, resistance, luminance, and collision box offsets. +- **`Items.kt`**: Extracts item IDs, max stack sizes, and default components. +- **`Entities.kt`**: Extracts entity IDs, dimensions (width, height), and eye heights. +- **`Packets.kt`**: Extracts packet IDs for handshake, status, login, configuration, and play states. +- **`SyncedRegistries.kt`**: Extracts dynamic registries synchronized to clients. +- **`TrackedData.kt`**: Extracts entity tracked data indices and serializers. +- **`MultiNoise.kt` & `Biome.kt`**: Extracts world generation biome trees and noise parameters. + +### 4. Run the Extractor +Execute the server runner (or client runner for translation/screen dumps): + +```bash +./gradlew runServer +``` + +Once the extraction finishes, all dumped JSON files will be located in `run/pumpkin_extractor_output/`. + +### 5. Replace the Old Asset Files in Pumpkin +Copy the newly generated JSON files from `run/pumpkin_extractor_output/` and use them to **replace the old asset files** in Pumpkin: +- Replace the core game assets in `Pumpkin/assets/` (`blocks.json`, `items.json`, `entities.json`, `packets.json`, `properties.json`, `synced_registries.json`, `tracked_data.json`, `en_us.json`, etc.). +- Replace the world generation test files in `Pumpkin/crates/pumpkin-world/assets/`. + +--- + +## Phase 2: Update Assets & Datapacks + +Once the raw extractor assets are in place, update the versioned datapacks and supplemental files in Pumpkin: + +### 1. Add the Versioned Datapack +Extract the internal vanilla data pack from the new Minecraft server JAR and place it under `assets/datapacks//`: + +``` +assets/datapacks// +└── data/ + ├── minecraft/ + │ ├── advancement/ + │ ├── chat_type/ + │ ├── damage_type/ + │ ├── dimension_type/ + │ ├── enchantment/ + │ ├── item_modifier/ + │ ├── loot_table/ + │ ├── recipe/ + │ ├── tags/ + │ └── worldgen/ + └── c/ + └── tags/ (Conventional / Common Tags) +``` + +### 2. Update Versioned Assets +Update or generate the corresponding versioned metadata files: +- `assets/registry/_synced_registries.json`: Dynamic registries streamed to the client during the `Configuration` network state. +- `assets/tags/_tags.json`: Tag collections. +- `assets/tracked_data/_tracked_data.json`: Entity metadata indices. + +### 3. Update Bedrock Mappings +If porting Bedrock Edition support simultaneously, update the files in `assets/bedrock/` using the latest mappings from [GeyserMC](https://github.com/GeyserMC/Geyser) and Bedrock Dedicated Server (BDS). + +### 4. Decompile Sources for Reverse Engineering (Optional but Recommended) +For complex changes—such as worldgen noise router formulas and custom block collision shapes—decompile the matching vanilla JAR into `minecraft-sources-/` (using Vineflower or Fabric Loom with official Mojang mappings) for reference. + +--- + +## Phase 3: Register Version Constants & ViaVersion Mappings + +Pumpkin supports multi-version clients through automated protocol remapping powered by **ViaBackwards** / **ViaRewind** mapping tables. + +### 1. Declare Version in `pumpkin-util` +Open `crates/pumpkin-util/src/version.rs` and add the new version to [`JavaMinecraftVersion`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-util/src/version.rs): + +```rust +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum JavaMinecraftVersion { + // ... existing versions ... + V_1_21_4, + V_1_21_11, + V_26_2, // New target version +} + +impl JavaMinecraftVersion { + pub const fn protocol_version(&self) -> i32 { + match self { + // ... + Self::V_26_2 => 770, // Protocol version number + } + } + + pub const fn version_name(&self) -> &'static str { + match self { + // ... + Self::V_26_2 => "26.2", + } + } +} +``` + +### 2. Declare Version in `pumpkin-codegen` +Open `tools/pumpkin-codegen/src/version.rs` and add the identical enum variant to the codegen AST. + +### 3. Add ViaVersion / ViaBackwards Mappings +To enable clients on older versions to connect to the updated server (e.g., a `1.21.4` client connecting to a `26.2` server): +1. Obtain the NBT mapping file from the [ViaBackwards repository](https://github.com/ViaVersion/ViaBackwards): + `mappings-to.nbt` +2. Save it to: + `assets/viabackwards/data/mappings-to.nbt` +3. In `tools/pumpkin-codegen/src/remap/mod.rs`, link the new node into the `remap_nodes!` macro: + +```rust +let node_26_2 = $crate::remap::MappingNode { + version: $crate::version::JavaMinecraftVersion::V_26_2, + value: "../../assets/viabackwards/data/mappings-26.2to26.1.nbt", + child: Some(&node_26_1), +}; +$remapper.process(&node_26_2) +``` + +4. Check `tools/pumpkin-codegen/src/remap/` to ensure specific remap builders (`block_state.rs`, `item_id.rs`, `entity_id.rs`, `sound_id.rs`, `argument_type.rs`) correctly process any new remapping structures introduced by ViaVersion. + +--- + +## Phase 4: Run Code Generation (`pumpkin-codegen`) + +With all extractor assets, datapacks, and mapping nodes in place, execute the codegen tool from the workspace root: + +```bash +cargo run -p pumpkin-codegen +``` + +### What Gets Regenerated? +`pumpkin-codegen` generates zero-cost Rust data tables into `crates/pumpkin-data/src/generated/`: +- **`block.rs`**: All block IDs, properties, state masks, and collision shapes. +- **`item.rs`**: Items, default components, and tool tiers. +- **`entity_type.rs` & `tracked_data.rs`**: Entity type IDs, default bounding boxes, and metadata serializers. +- **`packet.rs`**: Serverbound and clientbound packet ID constants across protocol states. +- **`recipes.rs`**: All crafting, cooking, and stonecutting recipes parsed from the new datapack. +- **`tag.rs`**: Tag lookups (`#minecraft:mineable/axe`, `#c:iron_ingots`). +- **`remap/*.rs`**: State remap lookup tables (`block_state_remap.rs`, `item_id_remap.rs`, `entity_id_remap.rs`, `sound_id_remap.rs`). + +--- + +## Phase 5: Resolve Workspace Compilation Errors + +After regenerating `pumpkin-data`, the Rust compiler will flag breaking changes across the workspace. Run: + +```bash +cargo check --workspace +``` + +### Common Compiler Breakages & Fixes: + +#### 1. Packet ID & Layout Changes (`pumpkin-protocol`) +Mojang frequently adds, moves, or alters packet IDs and payloads: +- Update `pumpkin-protocol/src/java/client/` and `pumpkin-protocol/src/java/server/`. +- Inspect packets like `LoginSuccess`, `PlayLogin`, `Respawn`, and `PlayerSpawnData` for newly added fields (e.g., new game mode flags, dimension attributes, or teleportation IDs). +- For Bedrock Edition, check `StartGame`, `NetworkItem`, and container transaction packets in `pumpkin-protocol/src/bedrock/`. + +#### 2. Data Component Changes +If items switched from older NBT structures to new Data Components (or if components were renamed/restructured): +- Update `crates/pumpkin-data/src/data_component.rs`. +- Update serialization and deserialization in `pumpkin-inventory`. + +#### 3. Registry Synchronization +During the `Configuration` phase, servers send dynamic registry contents to Java clients. If new registries were added to vanilla Minecraft (e.g., trim materials, banner patterns, instrument sounds): +- Verify `crates/pumpkin-data/src/generated/registry.rs`. +- Ensure `pumpkin/src/net/java/config.rs` streams all required registry codecs to the client. + +--- + +## Phase 6: Implement Logic for New Blocks, Items & Entities + +Once the server compiles, any newly introduced gameplay content must be implemented. + +### 1. New Blocks +1. Locate new block identifiers in `pumpkin-data::Block`. +2. Create a behavior module under `crates/pumpkin/src/block/blocks/.rs` implementing [`BlockBehaviour`](/developer/blocks/adding-blocks): + ```rust + pub struct NewBlockBehaviour; + + impl BlockBehaviour for NewBlockBehaviour { + fn on_use(&self, block: &Block, player: &Arc, location: BlockPos, world: &Arc) { + // Interaction logic + } + } + ``` +3. Register the behavior in the block registry in `crates/pumpkin/src/block/mod.rs`. + +### 2. New Items +1. Locate new item identifiers in `pumpkin-data::Item`. +2. If the item has unique right-click or use behavior (e.g., weapons, projectiles, instruments), create an item behavior in `crates/pumpkin/src/item/items/.rs` implementing [`ItemBehaviour`](/developer/items/adding-items). +3. If the item places entities (e.g., spawn eggs, boats, minecarts), configure entity spawning logic in `pumpkin/src/item/items/spawn_egg.rs`. +4. Register the behavior in `crates/pumpkin/src/item/mod.rs`. + +### 3. New Entities +1. Check `pumpkin-data::entity_type::EntityType` for new entity IDs. +2. Create an entity struct in `crates/pumpkin/src/entity/mob/` or `living/` extending [`LivingEntity`](/developer/entities/). +3. Implement `TrackedData` fields to match vanilla client expectations (e.g., poses, animations, glowing states). +4. Assign AI goals and navigation tasks using the [Mob AI & Goals system](/developer/entities/ai-and-mobs). +5. Add spawn packet construction in `crates/pumpkin/src/entity/spawning.rs`. + +--- + +## Phase 7: Multi-Version & ViaVersion Verification + +Ensure that older clients can still connect seamlessly: + +1. **Verify Generated Remap Tables**: + Check `crates/pumpkin-data/src/generated/block_state_remap.rs` and `item_id_remap.rs`. Ensure that novel block states fall back gracefully to compatible equivalents on older client versions (e.g., new wood variants mapping to oak or spruce). +2. **Packet Translation**: + Verify that packet writers in `pumpkin-protocol` apply `remap` functions when serializing data for a connection whose negotiated `protocol_version` is older than the server's native version. + +--- + +## Phase 8: Testing & Verification Checklist + +Before opening a Pull Request for a version port, complete the following QA checklist: + +- [ ] **Automated Tests**: + ```bash + cargo test --workspace + ``` +- [ ] **Game Test Suite**: + ```bash + cargo test -p pumpkin-gametest + ``` +- [ ] **Strict Linter Rules**: + ```bash + cargo clippy --workspace --all-targets -- -D warnings + ``` +- [ ] **Spellcheck**: + ```bash + typos + ``` +- [ ] **In-Game Java Client Test**: + - Connect with the native new Minecraft client version. + - Test login, world loading, chunk generation, and player movement. + - Place and break new blocks; verify collision boxes and redstone reactivity. + - Use and equip new items; test attack speed, durability, and animations. + - Spawn and fight new entities; test animations, hurt sounds, and drops. +- [ ] **In-Game Older Version Client Test**: + - Connect with an older supported client version (e.g., 1.21.x). + - Verify that blocks, items, and entities translate without causing client crashes or desyncs. +- [ ] **In-Game Bedrock Client Test**: + - Verify that Bedrock clients can authenticate, join, load world chunks, and view custom items. diff --git a/docs/tr_TR/plugin-dev/c/first-command.md b/docs/tr_TR/plugin-dev/c/first-command.md new file mode 100644 index 0000000..3ac3ebd --- /dev/null +++ b/docs/tr_TR/plugin-dev/c/first-command.md @@ -0,0 +1,90 @@ +# Creating Your First Command + +Writing commands in C for Pumpkin is low-overhead, fast, and uses C WIT bindings. + +--- + +## 1. Quick Example + +Here is a complete C plugin file (`main.c`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```c [main.c] +#include +#include "pumpkin.h" + +void plugin_on_load(pumpkin_context_t ctx) { + // 1. Register permission node + pumpkin_permission_t perm = { + .node = "my_c_plugin:hello", + .description = "Allows executing the /hello command", + .default_access = PERMISSION_DEFAULT_ALLOW + }; + pumpkin_register_permission(ctx, &perm); + + // 2. Build and register command tree + const char* names[] = {"hello", "hi"}; + pumpkin_command_t cmd = pumpkin_command_new(names, 2, "Greets the player"); + + pumpkin_register_command(ctx, cmd, "my_c_plugin:hello"); +} + +int32_t plugin_handle_command(uint32_t command_id, pumpkin_sender_t sender, pumpkin_server_t server) { + pumpkin_sender_send_message(sender, "Hello from C Plugin!"); + return 1; +} + +pumpkin_metadata_t plugin_get_metadata() { + pumpkin_metadata_t meta = { + .name = "my_c_plugin", + .version = "0.1.0", + .description = "My first Pumpkin C plugin" + }; + return meta; +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Struct +Populate a `pumpkin_permission_t` struct and call `pumpkin_register_permission`: +```c +pumpkin_permission_t perm = { + .node = "my_c_plugin:hello", + .description = "Allows executing the /hello command", + .default_access = PERMISSION_DEFAULT_ALLOW +}; +pumpkin_register_permission(ctx, &perm); +``` + +### Step 2: Build the Command Tree +Create the command tree by passing name array and description: +```c +const char* names[] = {"hello", "hi"}; +pumpkin_command_t cmd = pumpkin_command_new(names, 2, "Greets the player"); +``` + +### Step 3: Register Command & Handle Execution +Pass the command handle to `pumpkin_register_command` and handle responses inside `plugin_handle_command`: +```c +pumpkin_register_command(ctx, cmd, "my_c_plugin:hello"); +``` + +::: + +--- + +## Next Steps + +- Check out the [C Quick Start Guide](./quick-start) to set up your compiler toolchain. diff --git a/docs/tr_TR/plugin-dev/csharp/first-command.md b/docs/tr_TR/plugin-dev/csharp/first-command.md new file mode 100644 index 0000000..c2a34ca --- /dev/null +++ b/docs/tr_TR/plugin-dev/csharp/first-command.md @@ -0,0 +1,110 @@ +# Creating Your First Command + +Registering custom commands in Pumpkin using C# provides a strongly-typed, clean structure. + +--- + +## 1. Quick Example + +Here is a complete C# plugin file (`MyPlugin.cs`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```csharp [MyPlugin.cs] +using PluginWorld; +using PluginWorld.wit.Exports.pumpkin.plugin.v0_1_0; +using PluginWorld.wit.Imports.pumpkin.plugin.v0_1_0; + +namespace MyPumpkinPlugin; + +public class MyPlugin : IPluginWorldExports, IMetadataExports +{ + public static void InitPlugin() { } + + public static void OnLoad(IContextImports.Context context) + { + // 1. Register permission node + var perm = new IPermissionImports.Permission( + "my_csharp_plugin:hello", + "Allows executing the /hello command", + IPermissionImports.PermissionDefault.Allow, + [] + ); + IContextImports.RegisterPermission(context, perm); + + // 2. Build and register command tree + var command = ICommandImports.Command.New(["hello", "hi"], "Greets the player"); + IContextImports.RegisterCommand(context, command, "my_csharp_plugin:hello"); + } + + public static void OnUnload(IContextImports.Context context) { } + + public static IEventImports.Event HandleEvent(uint eventId, IServerImports.Server server, IEventImports.Event @event) + { + return @event; + } + + public static int HandleCommand(uint commandId, ICommandImports.CommandSender sender, IServerImports.Server server, ICommandImports.ConsumedArgs args) + { + ICommandImports.SendMessage(sender, "Hello from C# Plugin!"); + return 1; + } + + public static void HandleTask(uint handlerId, IServerImports.Server server) { } + + public static IMetadataExports.PluginMetadata Metadata() + { + return new IMetadataExports.PluginMetadata( + "my_csharp_plugin", + "0.1.0", + "My first Pumpkin C# plugin", + ["Developer"], + [] + ); + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Node +Construct an `IPermissionImports.Permission` record and register it via `IContextImports.RegisterPermission`: +```csharp +var perm = new IPermissionImports.Permission( + "my_csharp_plugin:hello", + "Allows executing the /hello command", + IPermissionImports.PermissionDefault.Allow, + [] +); +IContextImports.RegisterPermission(context, perm); +``` + +### Step 2: Build the Command Tree +Instantiate your command with primary names and description: +```csharp +var command = ICommandImports.Command.New(["hello", "hi"], "Greets the player"); +``` + +### Step 3: Register and Handle +Register the command with `IContextImports.RegisterCommand` and process incoming executions inside `HandleCommand`: +```csharp +IContextImports.RegisterCommand(context, command, "my_csharp_plugin:hello"); +``` + +::: + +--- + +## Next Steps + +- Follow the [C# Quick Start Guide](./quick-start) to configure `.csproj` and build WebAssembly outputs. diff --git a/docs/tr_TR/plugin-dev/d/quick-start.md b/docs/tr_TR/plugin-dev/d/quick-start.md new file mode 100644 index 0000000..e9383a9 --- /dev/null +++ b/docs/tr_TR/plugin-dev/d/quick-start.md @@ -0,0 +1,130 @@ +# Hızlı Başlangıç + +Bu kılavuz, [D programlama dilini](https://dlang.org/) kullanarak Pumpkin sunucu eklentileri yazmaya başlamanıza yardımcı olacaktır. + +Pumpkin için D eklentileri, resmi [`pumpkin-api-d`](https://github.com/Pumpkin-MC/pumpkin-api-d) bağlamaları kullanılarak WebAssembly (Wasm) bileşenleri olarak derlenir. + +--- + +## Ön Koşullar + +Başlamadan önce aşağıdaki araçların kurulu olduğundan emin olun: + +- **[LDC](https://github.com/ldc-developers/ldc)**: Sürüm 1.43 veya üzeri (LLVM tabanlı D derleyicisi). +- **`addon-wasi`**: LDC için WASI hedef paketi. +- **[DUB](https://dub.pm/)**: D paket ve derleme aracı (genellikle LDC ile birlikte gelir). + +--- + +## 1. Projeyi Kurma + +Eklenti projeniz için yeni bir dizin oluşturun ve bir `dub.json` yapılandırma dosyası ekleyin: + +```bash +mkdir my-d-plugin +cd my-d-plugin +``` + +Proje kökünde `dub.json` dosyasını oluşturun: + +```json [dub.json] +{ + "name": "my-d-plugin", + "description": "D ile geliştirilmiş minimal bir Pumpkin eklentisi.", + "license": "proprietary", + "authors": ["Adınız"], + "copyright": "Copyright © 2026, Adınız", + + "dependencies": { + "pumpkin-api-d": "~>0.1.0" + }, + + "targetType": "executable", + "dflags": ["-Xcc=-mexec-model=reactor"], + "buildTypes": { + "debug": { + "buildOptions": ["debugMode", "debugInfo"] + }, + "release": { + "buildOptions": ["optimize", "inline"], + "dflags": ["-L-S"] + } + } +} +``` + +> [!NOTE] +> `-Xcc=-mexec-model=reactor` bayrağı, eklentinin bir CLI yürütülebilir programı yerine giriş noktaları dışa aktaran bir bileşen (reaktör) olarak bağlanması için WebAssembly bağlayıcısı tarafından zorunlu kılınmıştır. + +--- + +## 2. Eklentiyi Yazma + +`source` dizinini oluşturun ve kodunuzu `source/app.d` içine ekleyin: + +```d [source/app.d] +module plugin; + +import wit.pumpkin.plugin.plugin; +import wit.common; +import pumpkin.register; + +struct MyPlugin { + @GetMetadata PluginMetadata getMetadata() + { + immutable(WitString)[$] authors = [ + "Adınız".witList + ]; + + return (immutable PluginMetadata( + name: "my-d-plugin".witList, + version_: "0.1.0".witList, + authors: authors.witList, + description: "D dilinde örnek bir eklenti.".witList, + dependencies: WitList!WitString(), + permissions: WitList!WitString() + )).witClone; + } + + @OnLoad + Result!(void, WitString) onLoad(Context ctx) + { + scope (exit) + ctx.witDrop; // Gelen kaynaklar kullanıldıktan sonra serbest bırakılmalıdır + + log(Level.info, "D eklentisinden merhaba!".witList); + return ok!WitString; + } +} + +mixin RegisterPlugin!MyPlugin; +``` + +--- + +## 3. Eklentiyi Derleme + +Eklentinizi `wasm32-wasip2` mimarisini hedefleyen bir WebAssembly bileşeni olarak derleyin: + +```bash +dub build -b release -a wasm32-wasip2 +``` + +Bu komut proje kök dizininizde `my-d-plugin.wasm` dosyasını oluşturacaktır. + +--- + +## 4. Eklentiyi Çalıştırma + +1. Derlenen `.wasm` dosyasını Pumpkin sunucunuzun `plugins/` dizinine kopyalayın: + ```bash + cp my-d-plugin.wasm /yol/pumpkin/plugins/ + ``` +2. Pumpkin sunucusunu başlatın veya yeniden başlatın: + ```bash + ./pumpkin + ``` +3. Eklentinin yüklendiğini doğrulamak için sunucu günlüklerini kontrol edin: + ```text + [INFO] D eklentisinden merhaba! + ``` diff --git a/docs/tr_TR/plugin-dev/go/basic-logic.md b/docs/tr_TR/plugin-dev/go/basic-logic.md new file mode 100644 index 0000000..7e0e4a6 --- /dev/null +++ b/docs/tr_TR/plugin-dev/go/basic-logic.md @@ -0,0 +1,60 @@ +# Writing the basic logic + +## Plugin Entry Point + +In Go, your plugin must implement the `api.Plugin` interface and register itself in the `init()` function. You also need to import `wit_exports` for WebAssembly compatibility. + +:::code-group + +```go [main.go] +package main + +import ( + "github.com/Pumpkin-MC/pumpkin-api-go/api" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_context" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_logging" + _ "github.com/Pumpkin-MC/pumpkin-api-go/pkg/wit_exports" // Required for WASM exports +) + +type MyPlugin struct { + api.DefaultPlugin +} + +func (p *MyPlugin) Metadata() api.Metadata { + return api.Metadata{ + Name: "my-go-plugin", + Version: "0.1.0", + Authors: []string{"you"}, + } +} + +func (p *MyPlugin) OnLoad(ctx *pumpkin_plugin_context.Context) { + pumpkin_plugin_logging.Log(pumpkin_plugin_logging.LevelInfo(), "Go plugin loaded!") +} + +func init() { + api.RegisterPlugin(&MyPlugin{}) +} + +func main() {} +``` + +::: + +## Compiling the plugin + +To compile your plugin to WebAssembly, you must use **TinyGo**. The standard Go compiler does not yet support the specific WASI targets required by Pumpkin in the same way. + +Run the following command in your project folder: + +```bash +tinygo build -o my_plugin.wasm -target=wasi main.go +``` + +This will generate a `my_plugin.wasm` file that can be loaded by Pumpkin. + +## Testing the plugin + +Installing a plugin is as simple as putting the plugin binary (`.wasm`) into the `plugins/` folder of your Pumpkin server! + +When you start up the server and run the `/plugins` command, you should see your plugin listed. diff --git a/docs/tr_TR/plugin-dev/go/first-command.md b/docs/tr_TR/plugin-dev/go/first-command.md new file mode 100644 index 0000000..f20ffdd --- /dev/null +++ b/docs/tr_TR/plugin-dev/go/first-command.md @@ -0,0 +1,103 @@ +# Creating Your First Command + +Registering commands in Go with Pumpkin is built on standard Go idiom using TinyGo WASM compilation. + +--- + +## 1. Quick Example + +Here is a complete Go plugin file (`main.go`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```go [main.go] +package main + +import ( + "github.com/Pumpkin-MC/pumpkin-api-go/api" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_command" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_context" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_permission" + _ "github.com/Pumpkin-MC/pumpkin-api-go/pkg/wit_exports" +) + +type MyPlugin struct { + api.DefaultPlugin +} + +func (p *MyPlugin) Metadata() api.Metadata { + return api.Metadata{ + Name: "my_go_plugin", + Version: "0.1.0", + Authors: []string{"Developer"}, + Description: "My first Pumpkin Go plugin", + } +} + +func (p *MyPlugin) OnLoad(ctx *pumpkin_plugin_context.Context) { + // 1. Register permission node + ctx.RegisterPermission(pumpkin_plugin_permission.Permission{ + Node: "my_go_plugin:hello", + Description: "Allows executing the /hello command", + Default: pumpkin_plugin_permission.PermissionDefaultAllow(), + }) + + // 2. Build and register command tree + cmd := pumpkin_plugin_command.New([]string{"hello", "hi"}, "Greets the player") + ctx.RegisterCommand(cmd, "my_go_plugin:hello") +} + +func (p *MyPlugin) HandleCommand(cmdID uint32, sender pumpkin_plugin_command.CommandSender) int32 { + sender.SendMessage("Hello from Go Plugin!") + return 1 +} + +func init() { + api.RegisterPlugin(&MyPlugin{}) +} + +func main() {} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Register Permission +Call `ctx.RegisterPermission` inside `OnLoad`: +```go +ctx.RegisterPermission(pumpkin_plugin_permission.Permission{ + Node: "my_go_plugin:hello", + Description: "Allows executing the /hello command", + Default: pumpkin_plugin_permission.PermissionDefaultAllow(), +}) +``` + +### Step 2: Build the Command Tree +Instantiate the command tree with `pumpkin_plugin_command.New`: +```go +cmd := pumpkin_plugin_command.New([]string{"hello", "hi"}, "Greets the player") +``` + +### Step 3: Register & Execute +Register the command with `ctx.RegisterCommand` and handle execution in `HandleCommand`: +```go +ctx.RegisterCommand(cmd, "my_go_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Follow the [Go Quick Start Guide](./quick-start) to set up TinyGo compilation. +- Learn more about [Basic Logic](./basic-logic). diff --git a/docs/tr_TR/plugin-dev/go/quick-start.md b/docs/tr_TR/plugin-dev/go/quick-start.md new file mode 100644 index 0000000..90bacb4 --- /dev/null +++ b/docs/tr_TR/plugin-dev/go/quick-start.md @@ -0,0 +1,35 @@ +# Creating a new project + +Pumpkin plugins use the [Go](https://go.dev/) programming language and are compiled to WebAssembly. + +## Prerequisites + +Before we can start, make sure you have the following installed: +- [Go](https://go.dev/doc/install) (latest version recommended) +- [TinyGo](https://tinygo.org/getting-started/install/) (required for WASM compilation) + +## Initializing a new module + +First, create a new directory for your project and initialize a Go module: + +```bash +mkdir hello-pumpkin +cd hello-pumpkin +go mod init github.com/yourname/hello-pumpkin +``` + +Next, add the Pumpkin Go API as a dependency: + +```bash +go get github.com/Pumpkin-MC/pumpkin-api-go +``` + +## Project Structure + +Your project should have at least a `main.go` file. The structure should look like this: + +```text +├── go.mod +├── go.sum +└── main.go +``` diff --git a/docs/tr_TR/plugin-dev/introduction.md b/docs/tr_TR/plugin-dev/introduction.md index d7d39ee..6111ec6 100644 --- a/docs/tr_TR/plugin-dev/introduction.md +++ b/docs/tr_TR/plugin-dev/introduction.md @@ -1,4 +1,4 @@ -# Pumpkin Eklenti Geliştirme +# Pumpkin Eklenti Geliştirme ::: warning Pumpkin Eklenti API'si hâlâ çok erken bir geliştirme aşamasındadır ve her an değişebilir. @@ -15,3 +15,5 @@ Pumpkin Eklenti API'si birçok yerde Spigot/Bukkit eklenti API'sinden ilham alı * [C# API](https://github.com/Pumpkin-MC/pumpkin-api-cs) * [Go API](https://github.com/Pumpkin-MC/pumpkin-api-go) * [C API](https://github.com/Pumpkin-MC/pumpkin-api-c) +* [Kotlin API](https://github.com/Pumpkin-MC/pumpkin-api-kt) +* [D API](https://github.com/Pumpkin-MC/pumpkin-api-d) diff --git a/docs/tr_TR/plugin-dev/kotlin/first-command.md b/docs/tr_TR/plugin-dev/kotlin/first-command.md new file mode 100644 index 0000000..12831f5 --- /dev/null +++ b/docs/tr_TR/plugin-dev/kotlin/first-command.md @@ -0,0 +1,94 @@ +# Creating Your First Command + +Registering custom commands in Pumpkin using Kotlin is concise, idiomatic, and strongly typed. + +--- + +## 1. Quick Example + +Here is a complete Kotlin plugin (`Plugin.kt`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```kotlin [src/wasmWasiMain/kotlin/plugin/Plugin.kt] +package plugin + +import pumpkin.plugin.context.Context +import pumpkin.plugin.command.Command +import pumpkin.plugin.permission.Permission +import pumpkin.plugin.permission.PermissionDefault + +class MyPlugin { + fun onLoad(ctx: Context) { + // 1. Register permission node + ctx.registerPermission( + Permission( + node = "my_kotlin_plugin:hello", + description = "Allows executing the /hello command", + default = PermissionDefault.ALLOW, + children = emptyList() + ) + ) + + // 2. Build and register command tree + val cmd = Command( + names = listOf("hello", "hi"), + description = "Greets the player" + ) + ctx.registerCommand(cmd, "my_kotlin_plugin:hello") + } + + fun handleCommand(sender: pumpkin.plugin.command.CommandSender): Int { + sender.sendMessage("Hello from Kotlin Plugin!") + return 1 + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Node +Construct a `Permission` object and pass it to `ctx.registerPermission`: +```kotlin +ctx.registerPermission( + Permission( + node = "my_kotlin_plugin:hello", + description = "Allows executing the /hello command", + default = PermissionDefault.ALLOW, + children = emptyList() + ) +) +``` + +### Step 2: Build the Command Tree +Instantiate `Command` with primary names and aliases: +```kotlin +val cmd = Command( + names = listOf("hello", "hi"), + description = "Greets the player" +) +``` + +### Step 3: Register with Context +Register the command with `ctx.registerCommand` and handle calls inside `handleCommand`: +```kotlin +ctx.registerCommand(cmd, "my_kotlin_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Follow the [Kotlin Quick Start Guide](./quick-start) to configure your `Makefile` and Kotlin/Wasm build tools. diff --git a/docs/tr_TR/plugin-dev/kotlin/quick-start.md b/docs/tr_TR/plugin-dev/kotlin/quick-start.md new file mode 100644 index 0000000..acefd9c --- /dev/null +++ b/docs/tr_TR/plugin-dev/kotlin/quick-start.md @@ -0,0 +1,81 @@ +# Quick Start + +This guide will help you get started with writing Pumpkin server plugins using Kotlin. + +:::warning +Until the Kotlin + Wasm component toolchain matures more, there will be some oddity and inconvenience. + +Bugs are also expected. +::: + +## Prerequisites + +Before you start, ensure you have the following installed: +- JDK 17 or later + - To run Gradle 9.4 +- [Rust](https://rust-lang.org/) + - This is required as a key component (wit-bindgen) is written and Rust must be built from a particular Kotlin-enabled fork. + - You only need a default Rust install for your host platform. NOT for any WebAssembly targets +- [wasm-tools](https://github.com/bytecodealliance/wasm-tools) + - To bundle the Wasm Kotlin produces into a component +- Make (e.g. [GNU Make](https://www.gnu.org/software/make/)) + - To run the convenience `Makefile`. You may opt to do without and perform the contained steps manually. + +## Setting up the project + +Unlike most of the other APIs packages available, [pumpkin-api-kt](https://github.com/Pumpkin-MC/pumpkin-api-kt) is a TEMPLATE, not e.g. a Maven package. + +To start off, clone the template (and rename it as you please): +```sh +git clone --recurse-submodules https://github.com/Pumpkin-MC/pumpkin-api-kt +mv pumpkin-api-kt my_kotlin_plugin +cd my_kotlin_plugin +``` + +Next, we'll want to update the `wit` submodule so we are using the latest version of the Pumpkin plugin API. + +```sh +cd wit +git pull origin master +cd .. +``` + +Finally, rename the Gradle project. Change the `rootProject.name` in `settings.gradle.kts`, AND the `PROJECT_NAME` in `Makefile`. They must both match. Whatever you name the project, is going to be the filename of the Wasm produced. + +## Creating your first plugin + +As part of the template, a basic plugin is implemented in `src/wasmWasiMain/kotlin/plugin/Plugin.kt`. + +Feel free to modify the metadata at the bottom. However, before changing anything else, it's recommended that you build the plugin (see next section) first so that the bindings are generated and IDE completion works for `pumpkin`. + +## Building the plugin + +To build your plugin into a WebAssembly component: + +```sh +make +``` + +The compiled `.wasm` file will be located in `build`. You can place this file in the `plugins` folder of your Pumpkin server. + +Note that doing this the first time might take awhile, while it builds `wit-bindgen` from Rust source. + +Running `make` will check for `wit-bindgen` updates each time. You may want to run instead `make componentify` after the initial setup to avoid this. + +## Troubleshooting + +### Linker errors +If you start getting errors like +``` +main ThreadId(01) pumpkin::plugin: Failed to load plugin from +"./plugins/my_plugin.wasm": Wasm plugin initialization error: plugin failed +to load with error: component imports instance 'pumpkin:plugin/gui@0.1.0', but +a matching implementation was not found in the linker +``` +or other "linker" errors when loading your plugin into Pumpkin, update the `wit` submodule. + +```sh +cd wit +git pull origin master +cd .. +``` diff --git a/docs/tr_TR/plugin-dev/migrating-from-bukkit/commands.md b/docs/tr_TR/plugin-dev/migrating-from-bukkit/commands.md new file mode 100644 index 0000000..1200e99 --- /dev/null +++ b/docs/tr_TR/plugin-dev/migrating-from-bukkit/commands.md @@ -0,0 +1,171 @@ +# Bukkit'ten Komutları Taşıma + +Bukkit'te komutlar `plugin.yml` dosyasında tanımlanır ve `CommandExecutor` sınıfları ya da Brigadier kütüphanesi aracılığıyla işlenir. + +Pumpkin'da komutlar **Brigadier esintili bir komut ağacı** kullanılarak oluşturulur ve `context.register_command` üzerinden programatik olarak kaydedilir. Hiçbir YAML yapılandırma dosyası kullanılmaz. + +--- + +## Temel Farklar + +| Özellik | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Bildirim (Tanımlama)** | `plugin.yml` dosyasında `commands:` altında bildirilir | Kod içinde `Command::new(...)` kullanılarak oluşturulur | +| **İzinler** | `plugin.yml` içinde bağlanır veya `player.hasPermission()` ile denetlenir | `register_command()` sırasında açık bir izin düğümü bağlanır | +| **Sekme Tamamlama (Tab Completion)** | `TabCompleter` içinde manuel dize eşleştirme | İstemci tarafında otomatik ağaç tabanlı sekme tamamlama | +| **Alt Komutlar** | Manuel `if (args[0].equalsIgnoreCase("..."))` kontrolü | İçe içe yerleştirilmiş `Command` ağaç düğümleri | + +--- + +## Kod Karşılaştırması: Basit Komut + +Bukkit ve Pumpkin'da `/feed` komutunun nasıl kaydedildiğini karşılaştırın: + +### 1. Bukkit Uygulaması (Java) + +```java [FeedCommand.java] +// 1. Registered in plugin.yml: +// commands: +// feed: +// description: Feeds the player +// permission: myplugin.feed + +public class FeedCommand implements CommandExecutor { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("Only players can execute this command!"); + return true; + } + Player player = (Player) sender; + if (!player.hasPermission("myplugin.feed")) { + player.sendMessage("No permission!"); + return true; + } + player.setFoodLevel(20); + player.sendMessage("Your hunger has been satisfied!"); + return true; + } +} +``` + +### 2. Pumpkin Uygulamaları + +::: code-group + +```rust [Rust] +use pumpkin_plugin_api::{ + command::{CommandHandler, CommandSender, ConsumedArgs, CommandError, Command}, + permission::{Permission, PermissionDefault}, + Context, Plugin, PluginMetadata, Server, +}; + +struct FeedExecutor; + +impl CommandHandler for FeedExecutor { + fn handle(&self, sender: CommandSender, _server: Server, _args: ConsumedArgs) -> Result { + sender.send_message("Your hunger has been satisfied!"); + Ok(1) + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { MyPlugin } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "feed_plugin".into(), + version: "1.0.0".into(), + authors: vec!["Developer".into()], + description: "Feed command plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Register permission node (Replaces plugin.yml permission) + context.register_permission(&Permission { + node: "feed_plugin:feed".to_string(), + description: "Allows executing /feed".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), + })?; + + // 2. Build command tree + let command = Command::new(&["feed".to_string(), "eat".to_string()], "Feeds the player") + .execute(FeedExecutor); + + // 3. Register with context + context.register_command(command, "feed_plugin:feed")?; + + Ok(()) + } +} +``` + +```python [Python] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + permission, command, context, server +) + +class FeedExecutor: + def handle(self, sender: command.CommandSender, srv: server.Server, args: command.ConsumedArgs) -> int: + sender.send_message("Your hunger has been satisfied!") + return 1 + +class FeedPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata(name="feed_plugin", version="1.0.0", authors=["Dev"], description="Feed command") + + def on_load(self, ctx: context.Context) -> None: + ctx.register_permission(permission.Permission( + node="feed_plugin:feed", + description="Allows executing /feed", + default=permission.PermissionDefault.ALLOW, + children=[] + )) + + cmd = command.Command(["feed", "eat"], "Feeds the player") + cmd.execute(FeedExecutor()) + ctx.register_command(cmd, "feed_plugin:feed") + +register_plugin(FeedPlugin) +``` + +```kotlin [Kotlin] +package plugin + +import pumpkin.plugin.context.Context +import pumpkin.plugin.command.Command +import pumpkin.plugin.command.CommandSender +import pumpkin.plugin.permission.Permission +import pumpkin.plugin.permission.PermissionDefault + +class FeedPlugin { + fun onLoad(ctx: Context) { + ctx.registerPermission( + Permission( + node = "feed_plugin:feed", + description = "Allows executing /feed", + default = PermissionDefault.ALLOW, + children = emptyList() + ) + ) + + val cmd = Command( + names = listOf("feed", "eat"), + description = "Feeds the player" + ) + ctx.registerCommand(cmd, "feed_plugin:feed") + } + + fun handleCommand(sender: CommandSender): Int { + sender.sendMessage("Your hunger has been satisfied!") + return 1 + } +} +``` + +::: diff --git a/docs/tr_TR/plugin-dev/migrating-from-bukkit/configuration.md b/docs/tr_TR/plugin-dev/migrating-from-bukkit/configuration.md new file mode 100644 index 0000000..4458961 --- /dev/null +++ b/docs/tr_TR/plugin-dev/migrating-from-bukkit/configuration.md @@ -0,0 +1,69 @@ +# Yapılandırma ve Veri Depolamayı Taşıma + +Bukkit eklentileri büyük ölçüde `config.yml`, `YamlConfiguration` ve Bukkit yapılandırma serileştirme sistemine dayanır. + +Pumpkin'da ise WebAssembly eklentileri; yapılandırma dosyalarının **TOML**, **JSON** veya **YAML** gibi standart formatlar kullanılarak yerel olarak işlenebildiği güvenli bir yalıtılmış ortamda (sandbox) çalışır. + +--- + +## Temel Farklar + +| Özellik | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Yapılandırma Dosyası Formatı** | `config.yml` (YAML formatı) | Esnek: TOML, JSON veya özel dosyalar | +| **Varsayılan Yapılandırma Oluşturma** | `saveDefaultConfig()` | Eklenti dizininden veya gömülü WASM varlığından yapılandırmayı okuma | +| **Veri Ayrıştırma (Parsing)** | `getConfig().getString(...)` | Yerel serde / JSON / TOML seri durumdan çıkarma (deserialization) | +| **Dosya G/Ç Yalıtımı (Sandboxing)** | Herhangi bir konuma doğrudan dosya sistemi erişimi | WASM eklentisine tanınan yalıtılmış dizin erişimi | + +--- + +## Kod Karşılaştırması: Yapılandırma Yükleme + +### 1. Bukkit Uygulaması (Java) + +```java [ConfigExample.java] +public class ConfigExample extends JavaPlugin { + @Override + public void onEnable() { + saveDefaultConfig(); // Saves config.yml from jar resources + FileConfiguration config = getConfig(); + + String welcomeMsg = config.getString("welcome-message", "Default welcome!"); + int maxItems = config.getInt("max-items", 10); + getLogger().info("Loaded message: " + welcomeMsg); + } +} +``` + +### 2. Pumpkin Uygulaması (Rust) + +Pumpkin'da, temiz TOML yapılandırma dosyalarını ayrıştırmak için `serde::Deserialize` türetmesi yapabilirsiniz: + +```rust [config.rs] +use serde::Deserialize; +use std::fs; + +#[derive(Deserialize, Debug)] +pub struct PluginConfig { + pub welcome_message: String, + pub max_items: i32, +} + +impl Default for PluginConfig { + fn default() -> Self { + Self { + welcome_message: "Default welcome!".to_string(), + max_items: 10, + } + } +} + +pub fn load_config() -> PluginConfig { + let config_path = "plugins/my_plugin/config.toml"; + if let Ok(content) = fs::read_to_string(config_path) { + toml::from_str(&content).unwrap_or_default() + } else { + PluginConfig::default() + } +} +``` diff --git a/docs/tr_TR/plugin-dev/migrating-from-bukkit/events.md b/docs/tr_TR/plugin-dev/migrating-from-bukkit/events.md new file mode 100644 index 0000000..2c24ca0 --- /dev/null +++ b/docs/tr_TR/plugin-dev/migrating-from-bukkit/events.md @@ -0,0 +1,117 @@ +# Bukkit'ten Olayları Taşıma + +Bukkit'te olaylar, `Listener` arayüzünü uygulayan sınıflar oluşturularak ve metotlara `@EventHandler` ek açıklaması (annotation) eklenerek işlenir. + +Pumpkin'da olay işleyicileri (event handlers), `context.register_event_handler(...)` aracılığıyla açıkça kaydedilen yapılar (structs) veya nesnelerdir. Pumpkin ayrıca sunucu eşzamanlılığını maksimize etmek için **Bloklayan (Blocking)** ve **Bloklamayan (Non-blocking)** olaylar arasında ayrım yapar. + +--- + +## Temel Farklar + +| Özellik | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Dinleyici Bildirimi** | Metotlar üzerinde `@EventHandler` ek açıklaması | `EventHandler` trait / arayüz uygulaması | +| **Kayıt** | `pm.registerEvents(listener, plugin)` | `context.register_event_handler(handler, priority, is_blocking)` | +| **Yürütme Modeli** | Ana iş parçacığında yürütme (`EventPriority`) | **Bloklayan** (sıralı/iptal edilebilir) ve **Bloklamayan** (eşzamanlı) arasında seçim | +| **İptal Etme** | `event.setCancelled(true)` | Bloklayan olaylarda `event.cancel()` | + +--- + +## Pumpkin'da Bloklayan ve Bloklamayan Olaylar + +Tüm olay işleyicilerinin ana sunucu iş parçacığında sıralı olarak çalıştığı Bukkit'in aksine Pumpkin, bir olay işleyicisinin **bloklayan** olup olmadığını belirlemenize olanak tanır: + +```rust +// Registration signature in Rust: +context.register_event_handler(handler, priority, is_blocking)?; +``` + +- **Bloklayan (`is_blocking = true`)**: Sıralı öncelik sırasına göre yürütülür. Olay verilerini değiştirebilir (ör. giriş mesajını düzenleme) veya olayı iptal edebilir. +- **Bloklamayan (`is_blocking = false`)**: Çalışan iş parçacıkları (worker threads) arasında eşzamanlı olarak yürütülür. İptal işleminin gerekmediği günlük kaydı (logging), metrikler veya harici bildirimler için idealdir. + +--- + +## Kod Karşılaştırması: Oyuncu Giriş Olayı + +### 1. Bukkit Uygulaması (Java) + +```java [JoinListener.java] +public class JoinListener implements Listener { + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + event.setJoinMessage("Welcome " + player.getName() + " to the server!"); + } +} + +// In JavaPlugin: +// getServer().getPluginManager().registerEvents(new JoinListener(), this); +``` + +### 2. Pumpkin Uygulamaları + +::: code-group + +```rust [Rust] +use pumpkin_plugin_api::{ + Context, Plugin, PluginMetadata, Server, + events::{EventData, EventHandler, EventPriority, PlayerJoinEvent}, + text::TextComponent, +}; + +struct JoinHandler; + +impl EventHandler for JoinHandler { + fn handle<'a>( + &'a self, + _server: Server, + mut event: EventData, + ) -> EventData { + event.join_message = TextComponent::text("Welcome to the Pumpkin server!"); + event + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { MyPlugin } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "join_plugin".into(), + version: "1.0.0".into(), + authors: vec!["Developer".into()], + description: "Join event handler".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Register blocking event handler with Normal priority + context.register_event_handler(JoinHandler, EventPriority::Normal, true)?; + Ok(()) + } +} +``` + +```python [Python] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + event, server, context +) + +class MyPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata(name="join_plugin", version="1.0.0", authors=["Dev"], description="Join event") + + def on_load(self, ctx: context.Context) -> None: + self.register_event(ctx, event.EventType.PLAYER_JOIN_EVENT, self.on_player_join) + + def on_player_join(self, srv: server.Server, evt: event.PlayerJoinEventData) -> event.PlayerJoinEventData: + print(f"Player joined!") + return evt + +register_plugin(MyPlugin) +``` + +::: diff --git a/docs/tr_TR/plugin-dev/migrating-from-bukkit/index.md b/docs/tr_TR/plugin-dev/migrating-from-bukkit/index.md new file mode 100644 index 0000000..3730fed --- /dev/null +++ b/docs/tr_TR/plugin-dev/migrating-from-bukkit/index.md @@ -0,0 +1,27 @@ +# Geçiş Genel Bakış ve Mimarisi + +**Bukkit / Spigot / Paper** eklenti geliştirmeden Pumpkin'a geçiş yapmak; Java merkezli nesne yönelimli bir modelden derlenmiş, çok dilli bir **WebAssembly (WASM)** bileşen modeline geçişi ifade eder. + +--- + +Aşağıdaki tablo, geleneksel Bukkit/Paper paradigmasını Pumpkin'ın modern WebAssembly mimarisiyle karşılaştırmaktadır: + +| Kavram | Bukkit / Spigot / Paper | Pumpkin | +| :--- | :--- | :--- | +| **Dil Desteği** | Java / Kotlin / Scala (JVM) | Rust, Python, Kotlin, C#, Go, C | +| **İkili Dosya Çıktısı** | `.jar` Java Arşivi | `.wasm` WebAssembly Bileşeni | +| **Eklenti Tanımlayıcısı** | `plugin.yml` dosyası | Programatik `PluginMetadata` yapısı (struct) | +| **Yaşam Döngüsü Kancaları** | `onEnable()` / `onDisable()` | `on_load(context)` / `on_unload(context)` | +| **Güvenlik ve Yalıtım** | Kısıtlamasız JVM Yansıması (Reflection) | Yalıtılmış (sandboxed) WASM yetenek modeli | +| **Eşzamanlılık** | Tek iş parçacıklı tick döngüsü (`BukkitScheduler`) | Async çalışma zamanı ile çok iş parçacıklı yerel yürütüm | + +--- + +## Ayrıntılı Geçiş Konuları + +Başlıca eklenti alt sistemlerini taşımaya yönelik özel kılavuzları inceleyin: + +- [Komutları Taşıma](./commands) — `getCommand().setExecutor()` ve `plugin.yml` yapısından Brigadier komut ağaçlarına geçiş. +- [Olayları Taşıma](./events) — `@EventHandler` ve `Listener` arayüzlerini Pumpkin'ın bloklayan ve bloklamayan olay sistemiyle değiştirme. +- [Envanterleri ve GUI'leri Taşıma](./inventories) — `Bukkit.createInventory()` yaklaşımından Pumpkin konteyner ve pencere işleyicilerine geçiş. +- [Yapılandırma ve Verileri Taşıma](./configuration) — `getConfig()` / `config.yml` yapısını yerel TOML, JSON veya özel depolama ile değiştirme. diff --git a/docs/tr_TR/plugin-dev/migrating-from-bukkit/inventories.md b/docs/tr_TR/plugin-dev/migrating-from-bukkit/inventories.md new file mode 100644 index 0000000..49ecca2 --- /dev/null +++ b/docs/tr_TR/plugin-dev/migrating-from-bukkit/inventories.md @@ -0,0 +1,72 @@ +# Bukkit'ten Envanterleri ve GUI'leri Taşıma + +Envanter GUI'leri (sandık menüleri, mağaza arayüzleri veya özel oyuncu pencereleri gibi) oluşturmak, Minecraft eklenti geliştirmede yaygın bir görevdir. + +Bukkit'te özel GUI'ler `Bukkit.createInventory()` kullanılarak oluşturulur ve `InventoryClickEvent` aracılığıyla işlenir. Pumpkin'da ise envanter pencereleri **Pencere API'leri (Window APIs)** ve konteyner tıklama paketleri aracılığıyla yönetilir. + +--- + +## Temel Farklar + +| Özellik | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **Envanter Oluşturma** | `Bukkit.createInventory(owner, size, title)` | Pencere konteyner tanımları (`WindowType`) | +| **Pencereleri Açma** | `player.openInventory(inv)` | `player.open_window(window)` | +| **Tıklamaları Yakalama** | `InventoryClickEvent` işleme | Pencere tıklama geri çağırma (callback) işleyicileri | +| **Eşya Verisi** | `ItemMeta` içeren `ItemStack` | Bileşenler / NBT içeren `ItemStack` | + +--- + +## Kod Karşılaştırması: Özel Menü GUI'si Açma + +### 1. Bukkit Uygulaması (Java) + +```java [MenuGUI.java] +public class MenuGUI implements Listener { + + public void openMenu(Player player) { + Inventory inv = Bukkit.createInventory(null, 9, Component.text("Custom Menu")); + + ItemStack item = new ItemStack(Material.DIAMOND); + ItemMeta meta = item.getItemMeta(); + meta.displayName(Component.text("Click Me!")); + item.setItemMeta(meta); + + inv.setItem(4, item); + player.openInventory(inv); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (event.getView().getTitle().equals("Custom Menu")) { + event.setCancelled(true); + if (event.getSlot() == 4) { + event.getWhoClicked().sendMessage("Diamond clicked!"); + } + } + } +} +``` + +### 2. Pumpkin Uygulaması (Rust) + +```rust [gui.rs] +use pumpkin_plugin_api::{ + player::Player, + item::ItemStack, + window::{Window, WindowType}, + text::TextComponent, +}; + +pub fn open_custom_menu(player: &Player) { + // 1. Create a 9-slot chest window + let mut window = Window::new(WindowType::Generic9x1, TextComponent::text("Custom Menu")); + + // 2. Set item in slot 4 + let item = ItemStack::new("minecraft:diamond", 1); + window.set_item(4, Some(item)); + + // 3. Open window for player + player.open_window(window); +} +``` diff --git a/docs/tr_TR/plugin-dev/python/first-command.md b/docs/tr_TR/plugin-dev/python/first-command.md new file mode 100644 index 0000000..00058f4 --- /dev/null +++ b/docs/tr_TR/plugin-dev/python/first-command.md @@ -0,0 +1,95 @@ +# Creating Your First Command + +Registering and handling custom commands in Pumpkin with Python is designed to be straightforward and expressive. + +--- + +## 1. Quick Example + +Here is a complete, minimal Python plugin (`main.py`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```python [main.py] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + permission, command, context, server +) + +class HelloCommand: + def handle(self, sender: command.CommandSender, srv: server.Server, args: command.ConsumedArgs) -> int: + sender.send_message("Hello from Python Plugin!") + return 1 + +class MyPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata( + name="my_python_plugin", + version="0.1.0", + authors=["Developer"], + description="My first Pumpkin Python plugin" + ) + + def on_load(self, ctx: context.Context) -> None: + # 1. Register permission node + ctx.register_permission(permission.Permission( + node="my_python_plugin:hello", + description="Allows executing the /hello command", + default=permission.PermissionDefault.ALLOW, + children=[] + )) + + # 2. Build the command tree with aliases & executor + cmd = command.Command(["hello", "hi"], "Greets the player") + cmd.execute(HelloCommand()) + + # 3. Register the command + ctx.register_command(cmd, "my_python_plugin:hello") + +register_plugin(MyPlugin) +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission +Commands in Pumpkin require a permission node. Register it with `permission.PermissionDefault.ALLOW` to allow execution by default: +```python +ctx.register_permission(permission.Permission( + node="my_python_plugin:hello", + description="Allows executing the /hello command", + default=permission.PermissionDefault.ALLOW, + children=[] +)) +``` + +### Step 2: Build the Command Tree +Create a `command.Command` instance with primary names and aliases, then attach your handler with `.execute(...)`: +```python +cmd = command.Command(["hello", "hi"], "Greets the player") +cmd.execute(HelloCommand()) +``` + +### Step 3: Register with Context +Pass the command tree and permission node string to `ctx.register_command`: +```python +ctx.register_command(cmd, "my_python_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Check out [Events](./events) to handle player actions and server events in Python. +- Learn more about [Basic Logic](./basic-logic). diff --git a/docs/tr_TR/plugin-dev/rust/command/first-command.md b/docs/tr_TR/plugin-dev/rust/command/first-command.md new file mode 100644 index 0000000..9840346 --- /dev/null +++ b/docs/tr_TR/plugin-dev/rust/command/first-command.md @@ -0,0 +1,115 @@ +# Creating Your First Command + +Registering and handling custom commands in Pumpkin is designed to be developer-friendly, fast, and structured like Mojang's Brigadier system. + +--- + +## 1. Quick Example + +Here is a complete, minimal Rust plugin that registers a `/hello` command with permission checks and sends a response back to the player. + +```rust [src/lib.rs] +use pumpkin_plugin_api::{ + command::{CommandHandler, CommandSender, ConsumedArgs, CommandError, Command}, + permission::{Permission, PermissionDefault}, + Context, Plugin, PluginMetadata, Server, +}; + +struct HelloExecutor; + +impl CommandHandler for HelloExecutor { + fn handle( + &self, + sender: CommandSender, + _server: Server, + _args: ConsumedArgs, + ) -> Result { + sender.send_message("Hello from Pumpkin!"); + Ok(1) + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { + MyPlugin + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "my_plugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Developer".into()], + description: "My first Pumpkin plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Register permission node + context.register_permission(&Permission { + node: "my_plugin:hello".to_string(), + description: "Allows executing the /hello command".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), + })?; + + // 2. Build the command tree with aliases & executor + let names = ["hello".to_string(), "hi".to_string()]; + let command = Command::new(&names, "Greets the player").execute(HelloExecutor); + + // 3. Register the command + context.register_command(command, "my_plugin:hello")?; + + Ok(()) + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission +Commands in Pumpkin require a permission node. Using `PermissionDefault::Allow` allows all players to execute it by default: +```rust +context.register_permission(&Permission { + node: "my_plugin:hello".to_string(), + description: "Allows executing the /hello command".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), +})?; +``` + +### Step 2: Build the Command Tree +Use `Command::new` to define primary names and aliases, then attach your `CommandHandler` struct with `.execute(...)`: +```rust +let names = ["hello".to_string(), "hi".to_string()]; +let command = Command::new(&names, "Greets the player") + .execute(HelloExecutor); +``` + +### Step 3: Register with Context +Pass the command tree and the permission node string to `context.register_command`: +```rust +context.register_command(command, "my_plugin:hello")?; +``` + +::: + +--- + +## Next Steps + +- Explore complex subcommands and argument parsing in the [Rock-Paper-Scissors Tutorial](./rock-paper-scissors). +- Learn how to handle inventory and GUI interactions in plugin logic. diff --git a/docs/tr_TR/plugin-dev/rust/command/inventories.md b/docs/tr_TR/plugin-dev/rust/command/inventories.md deleted file mode 100644 index 70179bb..0000000 --- a/docs/tr_TR/plugin-dev/rust/command/inventories.md +++ /dev/null @@ -1,347 +0,0 @@ -# Özel bir envanteri açma - -## Genel Bakış - -Pumpkin'deki envanter sistemi, öğe depolama ve yönetimi için esnek bir yol sunar. Bu rehber, özel envanterlerin nasıl oluşturulacağını ve uygulanacağını açıklar. - -## İçindekiler - -- [Özel bir envanteri açma](#özel-bir-envanteri-açma) - - [Genel Bakış](#genel-bakış) - - [İçindekiler](#içindekiler) - - [Temel Envanter Uygulaması](#temel-envanter-uygulaması) - - [Gerekli Trait'ler](#gerekli-traitler) - - [Inventory Trait](#inventory-trait) - - [Clearable Trait](#clearable-trait) - - [Screen Handler'lar](#screen-handlerlar) - - [Genel Screen Handler'ları Kullanma](#genel-screen-handlerları-kullanma) - - [Özel Screen Handler'lar Oluşturma](#özel-screen-handlerlar-oluşturma) - - [En İyi Uygulamalar](#en-iyi-uygulamalar) - - [Örnekler](#örnekler) - - [Temel Envanter Kullanımı](#temel-envanter-kullanımı) - -## Temel Envanter Uygulaması - -`BasicInventory` struct'ı, 27 slotlu standard bir envanter uygulaması sunar. İşte kendi envanterinizi nasıl uygulayacağınız: - -```rust -use pumpkin_world::{ - inventory::{ - split_stack, {Clearable, Inventory}, - }, - item::ItemStack, -}; - -#[derive(Debug)] -pub struct BasicInventory { - pub items: [Arc>; 27], -} -``` - -## Gerekli Trait'ler - -### Inventory Trait - -`Inventory` trait'i, tüm envanterlerin uygulaması gereken temel işlevselliği tanımlar: - -```rust -impl Inventory for BasicInventory { - // Get the total number of slots in the inventory - fn size(&self) -> usize { - self.items.len() - } - - // Check if the inventory is completely empty - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - for slot in self.items.iter() { - if !slot.lock().await.is_empty() { - return false; - } - } - - true - }) - } - - // Get a reference to an item stack in a specific slot - fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc>> { - Box::pin(async move { self.items[slot].clone() }) - } - - // Remove and return the entire stack from a slot - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut removed = ItemStack::EMPTY.clone(); - let mut guard = self.items[slot].lock().await; - std::mem::swap(&mut removed, &mut *guard); - removed - }) - } - - // Remove a specific amount of items from a stack - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { split_stack(&self.items, slot, amount).await }) - } - - // Set the contents of a specific slot - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - *self.items[slot].lock().await = stack; - }) - } -} -``` - -### Clearable Trait - -`Clearable` trait'i, bir envanteri boşaltmak için işlevsellik sağlar. Bu, `Inventory` trait'i için uygulanması gerekir: - -```rust -impl Clearable for YourInventory { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - for item in self.items.iter() { - *item.lock().await = ItemStack::EMPTY.clone(); - } - }) - } -} -``` - -## Screen Handler'lar - -Screen handler'lar, envanterler için kullanıcı arayüzünü oluşturmak ve yönetmek için kullanılır. Öğelerin slotlar arasında nasıl taşınacağını ve envanterin oyuncunun envanteriyle nasıl etkileşime gireceğini tanımlarlar. - -### Genel Screen Handler'ları Kullanma - -Pumpkin, yaygın envanter düzenleri için genel bir screen handler sağlar. İşte nasıl kullanacağınız: - -```rust -use pumpkin_inventory::generic_container_screen_handler::create_generic_9x3; -use pumpkin_inventory::player::player_inventory::PlayerInventory; -use pumpkin_inventory::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerFactory}; - -struct MyScreenFactory { - inventory: Arc, -} - -impl ScreenHandlerFactory for MyScreenFactory { - fn create_screen_handler( - &self, - sync_id: u8, - player_inventory: &Arc, - _player: &dyn InventoryPlayer, - ) -> Option>> { - Some(Arc::new(Mutex::new(create_generic_9x3( - sync_id, - player_inventory, - self.inventory.clone(), - )))) - } - - fn get_display_name(&self) -> TextComponent { - TextComponent::translate("container.barrel", vec![]) - } -} - -// Create a screen factory -let factory = MyScreenFactory { - inventory: inventory.clone(), -}; - -// Open the inventory for a player -player.open_handled_screen(factory).await; - -// The screen will automatically handle: -// - Item movement between slots -// - Quick move functionality -// - Player inventory interaction -// - Screen closing -``` - -### Özel Screen Handler'lar Oluşturma - -`ScreenHandler` trait'ini uygulayarak özel screen handler'lar oluşturabilirsiniz. Bu size daha fazla esneklik sağlar ve sadece envanter olmak dışında amaçlar için ekranlar oluşturmanıza olanak tanır. - -Özel bir screen handler oluşturmak için, `Slot` trait'ini uygulayan yeni bir struct oluşturabilirsiniz. `NormalSlot`, Pumpkin ile gelen ve yalnızca bir envanterin indeksine karşılık gelen, kısıtlaması olmayan bir slottur. - -TODO: Özel slot örneği - -Genel screen handler'ın kaynak kodundan bir örnek: - -```rust -use std::{any::Any, sync::Arc}; - - -use pumpkin_data::screen::WindowType; -use pumpkin_world::{inventory::Inventory, item::ItemStack}; - -use pumpkin_world::{ - player::player_inventory::PlayerInventory, - screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour}, - slot::NormalSlot, -}; - -pub fn create_generic_9x3( - sync_id: u8, - player_inventory: &Arc, - inventory: Arc, -) -> GenericContainerScreenHandler { - GenericContainerScreenHandler::new( - WindowType::Generic9x3, - sync_id, - player_inventory, - inventory, - 3, - ) -} - -pub struct GenericContainerScreenHandler { - pub inventory: Arc, - pub rows: u8, - behaviour: ScreenHandlerBehaviour, -} - -impl GenericContainerScreenHandler { - fn new( - screen_type: WindowType, - sync_id: u8, - player_inventory: &Arc, - inventory: Arc, - rows: u8, - ) -> Self { - let mut handler = Self { - inventory, - rows, - behaviour: ScreenHandlerBehaviour::new(sync_id, Some(screen_type)), - }; - - handler.add_inventory_slots(); - let player_inventory: Arc = player_inventory.clone(); - handler.add_player_slots(&player_inventory); - - handler - } - - fn add_inventory_slots(&mut self) { - for i in 0..self.rows { - for j in 0..9 { - self.add_slot(Arc::new(NormalSlot::new( - self.inventory.clone(), - (j + i * 9) as usize, - ))); - } - } - } -} - -impl ScreenHandler for GenericContainerScreenHandler { - fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.default_on_closed(player).await; - self.inventory.on_close().await; - }) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn get_behaviour(&self) -> &ScreenHandlerBehaviour { - &self.behaviour - } - - fn get_behaviour_mut(&mut self) -> &mut ScreenHandlerBehaviour { - &mut self.behaviour - } - - fn quick_move<'a>( - &'a mut self, - _player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ItemStackFuture<'a> { - Box::pin(async move { - let mut stack_left = ItemStack::EMPTY.clone(); - // Assuming bounds check passed for slot_index by caller or within quick_move spec - let slot = self.get_behaviour().slots[slot_index as usize].clone(); - - if slot.has_stack().await { - let slot_stack_lock = slot.get_stack().await; - let slot_stack_guard = slot_stack_lock.lock().await; - stack_left = slot_stack_guard.clone(); - // Release the guard before calling insert_item which needs its own lock - drop(slot_stack_guard); - - // Re-acquire lock for insert_item (which expects &mut ItemStack) - let mut slot_stack_mut = slot_stack_lock.lock().await; - - if slot_index < (self.rows * 9) as i32 { - // Move from inventory to player area (end) - if !self - .insert_item( - &mut slot_stack_mut, - (self.rows * 9).into(), - self.get_behaviour().slots.len() as i32, - true, - ) - .await - { - return ItemStack::EMPTY.clone(); - } - } else if !self - .insert_item(&mut slot_stack_mut, 0, (self.rows * 9).into(), false) - .await - { - // Move from player area to inventory (start) - return ItemStack::EMPTY.clone(); - } - - // Check the resulting state of the slot stack after insert_item - if slot_stack_mut.is_empty() { - drop(slot_stack_mut); // Release lock - slot.set_stack(ItemStack::EMPTY.clone()).await; - } else { - drop(slot_stack_mut); // Release lock - slot.mark_dirty().await; - } - } - - stack_left - }) - } -} -``` - -## En İyi Uygulamalar - -1. **İş Parçacığı Güvenliği** - - Slot sınırı kontrolü için doğru hata yönetimi uygulayın - - Deadlock'ları önlemek için `inventory.set_stack()` veya `slot.get_cloned_stack()` kullanmadan önce stack kilitlerini bırakmayı unutmayın - -2. **Envanter Yönetimi** - - Slotları temizlemek veya boş envanterleri başlatmak için `ItemStack::EMPTY` sabitini kullanın - -3. **Screen Handler Uygulaması** - - Eşya kaybını önlemek için envanter kapanışını doğru şekilde yönetin - - Slot sayısının o pencere türündeki slot sayısıyla uyuştuğundan emin olun - -## Örnekler - -### Temel Envanter Kullanımı - -```rust -// Create a new inventory -let inventory = BasicInventory { - items: [(); 27].map(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), -}; - -// Add items to a slot -inventory.set_stack(0, ItemStack::new(1, &Items::OAK_LOG)).await; - -// Remove items from a slot -let removed = inventory.remove_stack(0).await; - -// Check if inventory is empty -let is_empty = inventory.is_empty().await; -``` diff --git a/docs/tr_TR/plugin-dev/rust/join-event.md b/docs/tr_TR/plugin-dev/rust/events.md similarity index 100% rename from docs/tr_TR/plugin-dev/rust/join-event.md rename to docs/tr_TR/plugin-dev/rust/events.md diff --git a/docs/tr_TR/plugin-dev/rust/introduction.md b/docs/tr_TR/plugin-dev/rust/introduction.md deleted file mode 100644 index 75b963d..0000000 --- a/docs/tr_TR/plugin-dev/rust/introduction.md +++ /dev/null @@ -1,11 +0,0 @@ -# Örnek Bir Eklenti Yazma - -Bu eğitim, yeni bir eklenti oluşturmanız için size rehberlik eder. - -Bu eğitim, Rust ve komut satırı konusunda biraz deneyiminiz olduğunu varsayar. - -Bu eğitimde anlatılan konular: - -- Katılma mesajını değiştirme -- Taş-kağıt-makas komutu oluşturma -- Eklenti verilerini eklentinin dizinine kaydetme (yakında) diff --git a/docs/tr_TR/plugin-dev/rust/plugin-utils.md b/docs/tr_TR/plugin-dev/rust/plugin-utils.md new file mode 100644 index 0000000..3adfbbe --- /dev/null +++ b/docs/tr_TR/plugin-dev/rust/plugin-utils.md @@ -0,0 +1,348 @@ +# Licensing & Updates (`pumpkin-plugin-utils`) + +`pumpkin-plugin-utils` is an official utility crate for Pumpkin Rust plugins. It provides built-in tools for **licensing checks**, **cryptographic signature verification**, and **update checks** against the [Pumpkin Marketplace](https://market.pumpkinmc.org). + +## Features + +- **Offline Ed25519 Signature Verification**: Verifies plugin integrity and embedded marketplace metadata in `< 1ms` on startup without blocking the server. +- **Global Metadata Caching**: Call `init(&context)` once in your plugin's `on_load` lifecycle method to cache verified metadata globally. +- **Online License Verification**: Query the marketplace `/api/v1/rest/check-license` endpoint with automatic 7-day lease caching (`license_lease.json`). +- **Offline Grace Period**: Resilient offline lease evaluation ensures your plugin keeps working during marketplace maintenance or network outages. +- **Zero-Argument Update Checking**: Easily check if newer plugin releases are available on the marketplace. +- **Dynamic Public Key Resolution**: Resolves the marketplace public key dynamically via host WIT imports or HTTPS fallback without hardcoding keys. + +--- + +## Adding the Dependency + +Add `pumpkin-plugin-utils` alongside `pumpkin-plugin-api` in your plugin's `Cargo.toml`: + +:::code-group + +```toml [Cargo.toml] +[package] +name = "my-plugin" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +pumpkin-plugin-api = "0.1.0-dev+26.2-26.45" +pumpkin-plugin-utils = "0.1.0-dev+26.2-26.45" +tracing = "0.1" +``` + +::: + +--- + +## Initialization + +Before performing license or update checks, initialize the utility crate inside your plugin's `on_load` method using the plugin's `Context`: + +```rust +use pumpkin_plugin_api::{Context, Plugin, PluginMetadata}; +use tracing::{info, warn}; + +struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { + Self + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "MyPlugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Author".into()], + description: "Plugin with license and update checks".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Initialize pumpkin-plugin-utils (verifies signature & caches metadata) + let metadata = pumpkin_plugin_utils::init(&context) + .map_err(|e| format!("Initialization failed: {e}"))?; + + info!( + "Loaded plugin '{}' v{} (Dev: {})", + metadata.plugin_name, metadata.version, metadata.dev_name + ); + + Ok(()) + } +} +``` + +::: info What happens during `init`? +1. Locates the plugin's `.wasm` binary in the server directory. +2. Extracts the `pumpkin.metadata` and `wasm_signature` custom sections. +3. Cryptographically verifies the Ed25519 signature over the WASM binary and metadata. +4. Caches the verified `PumpkinMetadata` globally for subsequent calls. +::: + +--- + +## Checking for Updates + +`pumpkin-plugin-utils` provides both zero-argument automatic checking and custom manual checking. + +### 1. Automatic Update Check (Recommended) + +Once initialized, call `pumpkin_plugin_utils::check_for_updates()` without arguments. It automatically uses the cached plugin name, current version, and marketplace endpoint: + +```rust +use tracing::{info, warn}; + +fn check_updates() { + match pumpkin_plugin_utils::check_for_updates() { + Ok(update) => { + if update.update_available { + info!( + "A new update is available: {}!", + update.latest_version.as_deref().unwrap_or("unknown") + ); + } else { + info!("Plugin is up to date."); + } + } + Err(err) => { + warn!("Failed to check for updates: {err}"); + } + } +} +``` + +### 2. Manual Update Check (`UpdateChecker`) + +If you want to manually specify the plugin name, version, or a custom marketplace URL, use `UpdateChecker`: + +```rust +use pumpkin_plugin_utils::UpdateChecker; + +let checker = UpdateChecker::new(); +let response = checker.check_for_updates( + "my-plugin", + "1.0.0", + "https://market.pumpkinmc.org", +)?; + +if response.update_available { + println!("Update found: {:?}", response.latest_version); +} +``` + +### Update Response Structure + +The update check returns a `CheckUpdateResponse`: + +```rust +pub struct CheckUpdateResponse { + /// Whether a newer stable release exists on the marketplace. + pub update_available: bool, + /// The latest stable version string, if available. + pub latest_version: Option, +} +``` + +--- + +## Checking and Verifying Licenses + +`pumpkin-plugin-utils` supports online verification against the marketplace API as well as offline lease management with configurable grace periods. + +### 1. Online License Check + +To verify a license against the marketplace (`GET /api/v1/rest/check-license`): + +```rust +// Pass `None` to use the embedded license key from metadata +let response = pumpkin_plugin_utils::check_license_online(None) + .map_err(|e| format!("License verification failed: {e}"))?; + +if response.valid { + info!("License is valid (status: {})", response.status); +} else { + warn!("License is invalid: {}", response.status); + return Err(format!("Invalid license status: {}", response.status).into()); +} +``` + +#### Providing a Custom License Key Override +If your plugin allows server owners to configure a license key in a `config.toml` file, pass `Some(&key)`: + +```rust +let config_key = "LIC-CUSTOM-KEY-12345"; +let response = pumpkin_plugin_utils::check_license_online(Some(config_key))?; +``` + +::: tip 7-Day Lease Caching +Successful online checks automatically save a verified lease file (`license_lease.json`) into the plugin's data folder with a 7-day expiration. +::: + +### 2. Evaluating License with Grace Period + +To keep paid plugins functional when the server is offline or the marketplace is unreachable, use `evaluate_license`: + +```rust +use pumpkin_plugin_utils::LicenseStatus; +use tracing::{info, warn}; + +// Specify grace period duration in days (e.g., 7 days) +let grace_period_days = 7; + +match pumpkin_plugin_utils::evaluate_license(grace_period_days) { + LicenseStatus::Valid(meta) => { + info!("License valid for buyer (User ID: {})", meta.user_id); + } + LicenseStatus::GracePeriod { metadata, days_remaining, reason } => { + warn!( + "Running in offline grace period ({} days remaining). Reason: {}", + days_remaining, reason + ); + } + LicenseStatus::Invalid(reason) => { + return Err(format!("Plugin license invalid or tampered: {reason}").into()); + } + LicenseStatus::Unsigned => { + info!("Plugin is running in unsigned / development mode."); + } +} +``` + +### `LicenseStatus` Enum + +| Variant | Description | +| :--- | :--- | +| `LicenseStatus::Valid(PumpkinMetadata)` | License is valid, verified, or the plugin is free / open-source. | +| `LicenseStatus::GracePeriod { metadata, days_remaining, reason }` | Operating offline with a previously validated lease that is within the allowable grace period. | +| `LicenseStatus::Invalid(String)` | Signature is invalid, expired, revoked, or metadata was tampered with. | +| `LicenseStatus::Unsigned` | Binary does not contain marketplace metadata/signature (useful during local development). | + +--- + +## Accessing Plugin Metadata + +You can access the verified plugin metadata anywhere in your codebase using `get_metadata` or `metadata()`: + +```rust +if let Some(meta) = pumpkin_plugin_utils::get_metadata() { + println!("Plugin Name: {}", meta.plugin_name); + println!("Plugin Version: {}", meta.version); + println!("Is Paid: {}", meta.is_paid); + println!("License Key: {:?}", meta.license_key); + println!("Buyer User ID: {}", meta.user_id); + println!("Developer: {} (ID: {})", meta.dev_name, meta.dev_id); + println!("Marketplace URL: {}", meta.marketplace_url); + println!("Issued At: {}", meta.issued_at); +} +``` + +### `PumpkinMetadata` Fields + +| Field | Type | Description | +| :--- | :--- | :--- | +| `plugin_name` | `String` | Canonical plugin name on the marketplace. | +| `version` | `String` | SemVer version string of the plugin binary. | +| `plugin_id` | `i64` | Unique plugin identifier on the marketplace. | +| `dev_id` | `i64` | Developer account ID. | +| `dev_name` | `String` | Developer display name. | +| `is_paid` | `bool` | `true` if this is a premium/commercial plugin. | +| `user_id` | `i64` | Buyer / licensee user ID (`0` for free plugins). | +| `license_key` | `Option` | Unique license key issued to the licensee. | +| `marketplace_url` | `String` | Base marketplace URL (`https://market.pumpkinmc.org`). | +| `issued_at` | `String` | ISO-8601 timestamp when the binary/license was issued. | + +--- + +## Complete Example + +Here is a full plugin implementation combining initialization, online licensing checks, offline grace periods, and update checking: + +:::code-group + +```rust:line-numbers [src/lib.rs] +use pumpkin_plugin_api::{Context, Plugin, PluginMetadata}; +use pumpkin_plugin_utils::LicenseStatus; +use tracing::{error, info, warn}; + +struct CommercialPlugin; + +impl Plugin for CommercialPlugin { + fn new() -> Self { + Self + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "CommercialPlugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Developer".into()], + description: "A commercial Pumpkin plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Initialize plugin-utils (verifies Ed25519 signature & caches metadata) + let meta = pumpkin_plugin_utils::init(&context).map_err(|err| { + error!("Signature verification failed: {err}"); + format!("Security verification error: {err}") + })?; + + info!("Loaded {} v{}", meta.plugin_name, meta.version); + + // 2. Validate license (offline evaluation + online check) + match pumpkin_plugin_utils::evaluate_license(7) { + LicenseStatus::Valid(_) => { + info!("Local license status: Valid"); + } + LicenseStatus::GracePeriod { days_remaining, .. } => { + warn!("Local license in grace period: {days_remaining} days remaining"); + } + LicenseStatus::Unsigned => { + info!("Running in development mode (unsigned binary)"); + } + LicenseStatus::Invalid(reason) => { + error!("Invalid license: {reason}"); + return Err(format!("License verification failed: {reason}").into()); + } + } + + // 3. Online license check if paid + if meta.is_paid { + if let Ok(check) = pumpkin_plugin_utils::check_license_online(None) { + if !check.valid { + return Err(format!("Online license rejected: {}", check.status).into()); + } + info!("Online license check confirmed: {}", check.status); + } else { + warn!("Online license verification unreachable, falling back to local lease"); + } + } + + // 4. Check for updates + if let Ok(update) = pumpkin_plugin_utils::check_for_updates() { + if update.update_available { + info!( + "A new version is available on the marketplace: {}", + update.latest_version.as_deref().unwrap_or("unknown") + ); + } + } + + Ok(()) + } + + fn on_unload(&mut self, _context: Context) -> pumpkin_plugin_api::Result<()> { + info!("CommercialPlugin unloaded."); + Ok(()) + } +} + +pumpkin_plugin_api::register_plugin!(CommercialPlugin); +``` + +::: diff --git a/docs/tr_TR/troubleshooting/common_issues.md b/docs/tr_TR/troubleshooting/common_issues.md index 67a7234..3f69437 100644 --- a/docs/tr_TR/troubleshooting/common_issues.md +++ b/docs/tr_TR/troubleshooting/common_issues.md @@ -1,4 +1,4 @@ -# Yaygın Sorunlar +# Yaygın Sorunlar 1. ## Kullanıcı adı doğrulanamadı @@ -7,3 +7,14 @@ **Neden:** Bu, kimlik doğrulama ile ve genellikle `prevent_proxy_connections` ayarıyla ilgilidir. **Çözüm:** `pumpkin.toml` dosyasında `prevent_proxy_connections` değerini devre dışı bırakın + +2. ## 0.0.0.0 kullanılarak sunucuya bağlanılamıyor + + **Sorun:** Sunucuyu kendi bilgisayarında çalıştıran oyuncular, `0.0.0.0` veya `0.0.0.0:25565` girdiklerinde bağlanamıyor (örneğin "Connection refused" hatası alıyor). + + **Neden:** `pumpkin.toml` dosyasında `0.0.0.0`, Pumpkin'in tüm yerel ağ arayüzlerini dinlemesini sağlar. Bu, sunucu için bir dinleme (bind) adresidir; Minecraft istemcisinin bağlanabileceği geçerli bir hedef adres değildir. + + **Çözüm:** + - **Aynı bilgisayardan** bağlanıyorsanız Minecraft istemcisinde `localhost` veya `127.0.0.1` yazın. + - **Aynı yerel ağdaki (LAN) başka bir cihazdan** bağlanıyorsanız sunucu bilgisayarının yerel IP adresini (örn. `192.168.x.x`) kullanın. + - **İnternet üzerinden** bağlanıyorsanız diğer oyuncular genel (public) IP adresinizi veya alan adınızı kullanmalıdır (modeminizden bağlantı noktası yönlendirmesi - port forwarding yapılmış olmalıdır). diff --git a/docs/zh_cn/admin/introduction.md b/docs/zh_cn/admin/introduction.md new file mode 100644 index 0000000..fd4fb41 --- /dev/null +++ b/docs/zh_cn/admin/introduction.md @@ -0,0 +1,25 @@ +# 管理员概述 + +欢迎来到面向 Pumpkin 服务器管理员的**管理员指南**。Pumpkin 是一款使用 Rust 编写的高性能、多线程 Minecraft 服务器,旨在以极低的资源消耗支持海量并发玩家。 + +--- + +## 服务器管理员核心特性 + +- **极致多线程**:专为现代多核处理器从底层设计打造。 +- **WASM 插件架构**:安全、沙箱化的插件执行环境,支持使用 Rust、Python、Kotlin、C#、Go 或 C 开发,无 JVM 开销。 +- **原生跨代理支持**:开箱即用支持 Velocity、BungeeCord 及现代代理转发。 +- **基于 TOML 的配置**:清晰、人类可读的配置文件,位于 `pumpkin.toml` 及各特定功能的 TOML 配置文件中。 + +--- + +## 管理员指南与主题 + +探索以下管理指南: + +- [从 Bukkit / Paper / Spigot 服务器迁移](./migrating-from-bukkit) — 服务器管理、插件、世界存储和性能方面的核心差异。 +- [服务器配置](../config/introduction) — `pumpkin.toml` 设置的详细分解说明。 +- [代理设置](../config/proxy) — 配置 BungeeCord 与 Velocity 玩家转发。 +- [命令与权限](../config/commands) — 管理游戏内管理员命令与权限。 +- [身份验证](../config/authentication) — 正版在线模式与离线模式以及 Yggdrasil 设置。 +- [故障排除与常见问题](../troubleshooting/common_issues) — 解决端口绑定、内存以及插件加载问题。 diff --git a/docs/zh_cn/admin/migrating-from-bukkit.md b/docs/zh_cn/admin/migrating-from-bukkit.md new file mode 100644 index 0000000..df0e5a6 --- /dev/null +++ b/docs/zh_cn/admin/migrating-from-bukkit.md @@ -0,0 +1,126 @@ +# 从 Bukkit / Spigot / Paper 迁移至 Pumpkin + +本指南涵盖将服务器从 Bukkit、Spigot 或 Paper 迁移至 Pumpkin 时的架构与配置差异。 + +--- + +## 1. 架构差异 + +| 方面 | Bukkit / Spigot / Paper | Pumpkin | +| :--- | :--- | :--- | +| **运行时环境** | Java 虚拟机(需要 Java 17/21+) | 原生可执行文件(Rust 编译,无需 Java) | +| **启动命令** | `java [flags] -jar server.jar` | `./pumpkin` | +| **Tick 循环** | 带有异步工作分配的单线程 Tick 循环 | 跨 CPU 核心的多线程 Tick 执行 | +| **插件格式** | 面向 Bukkit API 的 Java 字节码(`.jar`) | WebAssembly(`.wasm`),或通过 PatchBukkit 支持 `.jar` | +| **插件开发语言** | Java、Kotlin、Scala | Rust、Python、Kotlin、C#、Go、C | +| **配置文件** | `server.properties`、`paper.yml`、`spigot.yml` | `pumpkin.toml`(TOML 格式) | +| **内存管理** | JVM 垃圾回收(Garbage Collection) | 原生操作系统内存分配 | + +--- + +## 2. 插件与扩展 + +### WebAssembly 插件 +Pumpkin 的主要插件格式为 WebAssembly(`.wasm`): +- **隔离性**:插件在沙箱化的 WebAssembly 运行时中运行。 +- **语言支持**:开发者可以使用 Rust、Python、Go、C#、C 或 Kotlin 将插件编译为 WASM。 +- **移植插件**:关于将自定义 Bukkit 插件移植到 Pumpkin,请参见[开发者迁移指南](../plugin-dev/migrating-from-bukkit/index)。 + +### Bukkit 插件兼容性:PatchBukkit +对于依赖现有 Bukkit/Spigot `.jar` 插件的服务器,**PatchBukkit** 项目提供了兼容层: +- **架构**:PatchBukkit 在 Pumpkin 内部嵌入了一个 JVM 并重新实现了部分 Bukkit API,通过 JNI 和 FFI 与 Pumpkin 桥接。 +- **插件目录**:兼容的 `.jar` 插件放置在 `patchbukkit/patchbukkit-plugins/` 目录中。 +- **现状与限制**:PatchBukkit 仍在积极开发中。标准 Bukkit API 调用受支持;严重依赖 NMS(`net.minecraft.server`)内部类、CraftBukkit 反射或字节码操作的插件可能无法正常工作。 +- **Java 依赖**:使用 PatchBukkit 需要在宿主系统上安装 Java 运行时(JRE/JDK)以运行内嵌的 JVM。 + +--- + +## 3. 配置与属性 + +Pumpkin 使用 TOML 配置文件替代 `server.properties` 和 YAML: + +| Paper / Spigot(`server.properties`) | Pumpkin(`pumpkin.toml`) | +| :--- | :--- | +| `server-port=25565` | `server_address = "0.0.0.0:25565"` | +| `motd=...` | `motd = "A Pumpkin Server"` | +| `max-players=20` | `max_players = 20` | +| `online-mode=true` | `online_mode = true` | +| `view-distance=10` | `view_distance = 10` | +| `simulation-distance=8` | `simulation_distance = 8` | + +--- + +## 4. 世界与数据存储 + +- **格式兼容性**:Pumpkin 支持标准的 Anvil 区块格式(`.mca` 文件)。 +- **目录结构**:从现有的 Paper/Spigot 复制世界时,请确保维度目录(`world`、`world_nether`、`world_the_end`)与 Pumpkin 的目录布局相符。 +- **需要最新的世界与数据包格式**:至少在目前阶段,Pumpkin 仅支持对应目标 Minecraft 版本的最新世界格式和数据包格式。运行时不会自动转换旧版世界格式或旧版数据包。 + - **在客户端中升级世界**:如果要迁移较旧的世界,请在导入 Pumpkin 前先进行升级。您可以直接在官方 Minecraft 客户端中更新世界:使用匹配的最新版本打开客户端,进入**单人游戏**,选择该世界,点击**编辑**并选择**优化世界**。或者,您也可以在匹配最新版本的原版/Paper 服务器中启动并保存一次该世界。 + - **数据包**:确保 `world/datapacks` 中的所有自定义数据包均已更新到目标版本所要求的格式版本。 + +--- + +## 5. 启动与 JVM 标志 + +### 无需安装 Java +纯净的 Pumpkin 是一个独立的自包含原生可执行文件。运行该服务器不需要在系统或容器中安装 Java 运行时(JRE/JDK)。 + +### 启动命令对比 +Paper 和 Spigot 服务器通常使用带有堆内存分配和垃圾回收参数的 JVM 启动脚本(如 Aikar's Flags): + +:::code-group + +```bash [Paper (JVM)] +# 带有 Aikar's Flags 的典型 Paper 启动命令: +java -Xms10G -Xmx10G \ + -XX:+UseG1GC \ + -XX:+ParallelRefProcEnabled \ + -XX:MaxGCPauseMillis=200 \ + -XX:+UnlockExperimentalVMOptions \ + -XX:+DisableExplicitGC \ + -XX:+AlwaysPreTouch \ + -XX:G1NewSizePercent=30 \ + -XX:G1MaxNewSizePercent=40 \ + -XX:G1HeapRegionSize=8M \ + -XX:G1ReservePercent=20 \ + -XX:G1HeapWastePercent=5 \ + -XX:G1MixedGCCountTarget=4 \ + -XX:InitiatingHeapOccupancyPercent=15 \ + -XX:G1MixedGCLiveThresholdPercent=90 \ + -XX:G1RSetUpdatingPauseTimePercent=5 \ + -XX:SurvivorRatio=32 \ + -XX:+PerfDisableSharedMem \ + -XX:MaxTenuringThreshold=1 \ + -Dusing.aikars.flags=https://mcflags.emc.gs \ + -Daikars.new.flags=true \ + -jar paper.jar --nogui +``` + +```bash [Pumpkin (原生)] +# Linux / macOS: +./pumpkin + +# Windows: +./pumpkin.exe + +# Docker: +docker run -p 25565:25565 -v ./data:/data pumpkinmc/pumpkin:latest +``` + +::: + +### 内存机制差异 +- **无垃圾回收器**:Pumpkin 由 Rust 编写,在编译期使用确定性的内存管理(RAII)。不存在运行时的垃圾回收器,因此无需调优 GC 停顿。 +- **无需固定堆内存**:原生程序中不存在 `-Xms` 和 `-Xmx` 标志。内存根据区块和实体的加载动态向操作系统申请,卸载后立即释放。 +- **JVM 标志不适用**:用于调优垃圾回收策略、分代大小或 JVM 内部机制的参数均不适用,且无法传递给程序。 + +--- + +## 管理员检查清单 + +1. [ ] 备份现有服务器数据和世界文件,并确保世界与数据包已升级到最新格式(例如通过客户端的“优化世界”)。 +2. [ ] 将 `server.properties` 中的设置映射至 `pumpkin.toml`。 +3. [ ] 确定所需插件并寻找 WebAssembly(`.wasm`)替代,或使用 [PatchBukkit](#bukkit-插件兼容性patchbukkit) 进行测试。 +4. [ ] 若使用代理网络,在 `pumpkin.toml` 中配置代理设置(`Velocity` / `BungeeCord`)。 +5. [ ] 更新启动脚本以直接运行 `./pumpkin`,无需传入 JVM 参数。 +6. [ ] 启动服务器并检查连接和日志。 diff --git a/docs/zh_cn/config/bedrock.md b/docs/zh_cn/config/bedrock.md new file mode 100644 index 0000000..8bf59cf --- /dev/null +++ b/docs/zh_cn/config/bedrock.md @@ -0,0 +1,50 @@ +# 基岩版与 NetherNet + +Pumpkin 原生支持 Minecraft 基岩版客户端,包括 NetherNet WebRTC/ICE 传输层以及用户名自定义。 + +## 配置 + +:::code-group + +```toml [pumpkin.toml] +[networking.bedrock] +enabled = true +online_mode = true +max_players = 1000 +view_distance = 16 +simulation_distance = 10 +motd = "A blazingly fast Pumpkin server!" +username_prefix = "" +replace_username_spaces = true +chunk_caching = true + +[networking.bedrock.nethernet] +enabled = true +address = "0.0.0.0:19132" +identity_key = "nethernet-key.der" +stun_servers = [] +``` + +::: + +### 基岩版选项 + +- **`enabled`**:是否接受来自基岩版客户端的连接(默认:`true`)。 +- **`online_mode`**:是否对基岩版玩家强制执行 Xbox Live 身份验证(默认:`true`)。 +- **`max_players`**:基岩版最大并发玩家数量(设置为 `0` 表示不限制,默认:`1000`)。 +- **`view_distance`**:发送给基岩版客户端的最大区块视距(默认:`16`)。 +- **`simulation_distance`**:基岩版玩家的最大 Tick 模拟距离(默认:`10`)。 +- **`motd`**:在基岩版服务器列表中显示的每日消息(MOTD)。 +- **`username_prefix`**:添加到基岩版玩家代号(Gamertag)前面的可选前缀(例如 `"."` 或 `"*"`),防止在跨平台联机服务器中与 Java 版账户发生名称冲突(默认:`""`)。 +- **`replace_username_spaces`**:是否自动将基岩版玩家代号中的空格替换为下划线 `_`,以确保在 Minecraft 斜杠命令中可以正常引用玩家名称(默认:`true`)。 +- **`chunk_caching`**:是否启用客户端区块 Blob 缓存以节省网络带宽(默认:`true`)。 + +### NetherNet 传输设置 + +NetherNet 是 Minecraft 基岩版的现代 WebRTC/ICE 网络传输协议: + +- **`enabled`**:是否允许客户端使用 NetherNet 连接(默认:`true`)。 +- **`address`**:用于 TCP 信令和 UDP ICE 多路复用的网络套接字地址(默认:`"0.0.0.0:19132"`)。 +- **`external_ip`**:当服务器托管在 NAT 之后时对外通告的可选公网 IP 地址。 +- **`identity_key`**:PKCS#8 P-384 身份私钥文件的路径,在重启时保留该文件以实现客户端的首次信任(TOFU)验证(默认:`"nethernet-key.der"`)。 +- **`stun_servers`**:用于 ICE NAT 穿透的 STUN 服务器 URL 可选列表。 diff --git a/docs/zh_cn/config/chat.md b/docs/zh_cn/config/chat.md new file mode 100644 index 0000000..0b58d24 --- /dev/null +++ b/docs/zh_cn/config/chat.md @@ -0,0 +1,39 @@ +# 聊天与反垃圾信息 + +Pumpkin 在 `pumpkin.toml` 的 `[chat]` 配置项下提供了可自定义的游戏内聊天格式以及内置的反垃圾信息(Anti-Spam)保护。 + +## 配置 + +:::code-group + +```toml [pumpkin.toml] +[chat] +format = "<{DISPLAYNAME}> {MESSAGE}" + +[chat.anti_spam] +enabled = true +spam_threshold = 200 +message_cost = 20 +decay_per_tick = 1 +ops_bypass = true +``` + +::: + +### 聊天设置 + +- **`format`**:聊天消息格式字符串(默认:`"<{DISPLAYNAME}> {MESSAGE}"`)。 + - `{DISPLAYNAME}`:玩家显示名称。 + - `{MESSAGE}`:聊天消息文本。 + > [!NOTE] + > 当启用了安全聊天举报(`allow_chat_reports = true`)时,自定义聊天格式将不会生效。 + +### 反垃圾信息保护 + +Pumpkin 实现了漏桶算法垃圾信息计数器,以防止刷屏玩家和机器人泛洪攻击服务器: + +- **`enabled`**:是否启用反垃圾信息保护(默认:`true`)。 +- **`spam_threshold`**:踢出玩家的垃圾信息计数器阈值(以 Tick 为单位,默认:`200` Tick)。 +- **`message_cost`**:每发送一条聊天消息或命令时,玩家垃圾信息计数器增加的数值(默认:`20` Tick)。 +- **`decay_per_tick`**:服务器每 Tick 从玩家垃圾信息计数器中衰减扣除的数值(默认:`1` Tick)。 +- **`ops_bypass`**:服务器管理员(OP)是否绕过反垃圾信息检查(默认:`true`)。 diff --git a/docs/zh_cn/config/packet-limiter.md b/docs/zh_cn/config/packet-limiter.md new file mode 100644 index 0000000..a253466 --- /dev/null +++ b/docs/zh_cn/config/packet-limiter.md @@ -0,0 +1,30 @@ +# 数据包限制器 + +Pumpkin 内置了数据包速率限制器,以保护服务器免受数据包泛洪、刷包攻击以及客户端漏洞利用的影响。可以针对 Java 版和基岩版客户端分别独立配置数据包速率限制。 + +## 配置 + +:::code-group + +```toml [pumpkin.toml] +[networking.java.packet_limiter] +enabled = true +max_packet_rate = 500.0 +burst_capacity = 500.0 +kick_message = "Kicked for spamming packets" + +[networking.bedrock.packet_limiter] +enabled = true +max_packet_rate = 500.0 +burst_capacity = 500.0 +kick_message = "Kicked for spamming packets" +``` + +::: + +### 配置选项 + +- **`enabled`**:是否为该客户端版本启用数据包速率限制器(默认:`true`)。 +- **`max_packet_rate`**:每个客户端连接每秒允许接收的最大数据包数量(默认:`500.0`)。设置为 `<= 0.0` 可在不禁用限制器模块的情况下取消速率限制。 +- **`burst_capacity`**:用于应对客户端数据包短时突发的令牌桶突发容量(默认:`500.0`)。 +- **`kick_message`**:当连接超出数据包限制时向玩家显示的断开连接消息(默认:`"Kicked for spamming packets"`)。 diff --git a/docs/zh_cn/config/player-data.md b/docs/zh_cn/config/player-data.md new file mode 100644 index 0000000..684d39c --- /dev/null +++ b/docs/zh_cn/config/player-data.md @@ -0,0 +1,41 @@ +# 玩家数据与游戏机制 + +用于保存持久化玩家数据、进度、配方同步以及趣味节日特性的设置均在 `pumpkin.toml` 中配置。 + +## 配置 + +:::code-group + +```toml [pumpkin.toml] +[player_data] +save_player_data = true +save_player_cron_interval = 300 + +[advancement] +save_advancements = true + +[recipe] +send_recipes = true + +[fun] +april_fools = true +``` + +::: + +### 玩家数据 + +- **`save_player_data`**:是否将持久化玩家数据(物品栏、位置、生命值)保存到磁盘(默认:`true`)。 +- **`save_player_cron_interval`**:自动定期保存在线玩家数据的时间间隔,以秒为单位(默认:`300` 秒 / 5 分钟)。 + +### 进度 + +- **`save_advancements`**:是否追踪玩家进度并将进度保存到磁盘(默认:`true`)。 + +### 配方 + +- **`send_recipes`**:是否将合成与烧炼配方同步给已连接的客户端,从而启用客户端配方书(默认:`true`)。 + +### 趣味特性 + +- **`april_fools`**:是否启用有趣的愚人节季节性彩蛋与特性(默认:`true`)。 diff --git a/docs/zh_cn/config/plugins.md b/docs/zh_cn/config/plugins.md new file mode 100644 index 0000000..c9d19c7 --- /dev/null +++ b/docs/zh_cn/config/plugins.md @@ -0,0 +1,59 @@ +# 插件 + +Pumpkin 提供了基于权能量化安全模型的 WebAssembly (WASM) 插件运行时。插件行为、签名验证及权限均在 `pumpkin.toml` 中的 `[plugins]` 下进行配置。 + +## 配置 + +:::code-group + +```toml [pumpkin.toml] +[plugins] +enabled = true +hot_reload = false +ask_permission_confirmation = true +allow_unsigned = true +allowed_permissions = [] +blocked_permissions = [] +inherit_env = false +loopback_only = false +verify_signatures = true + +# Optional per-plugin overrides +[plugins.overrides.my_plugin] +enabled = true +allow_unsigned = true +max_memory_mb = 128 +allowed_permissions = ["fs:read:data"] +blocked_permissions = ["network:outbound"] +loopback_only = true + +[plugins.overrides.my_plugin.environment] +API_KEY = "example_secret" +``` + +::: + +### 全局插件设置 + +- **`enabled`**:启用或禁用插件运行时的总开关(默认:`true`)。 +- **`hot_reload`**:监视 `plugins/` 目录并在运行时自动重新加载修改后的插件(默认:`false`)。 +- **`ask_permission_confirmation`**:当插件请求未经批准的功能/权限时,在服务器控制台中提示确认(默认:`true`)。 +- **`allow_unsigned`**:是否允许加载未签名的 WASM 插件(默认:`true`)。 +- **`allowed_permissions`**:为所有插件全局预先批准的权限列表,绕过控制台交互式提示(默认:`[]`)。 +- **`blocked_permissions`**:对所有插件全局拒绝的权限列表(默认:`[]`)。 +- **`inherit_env`**:宿主环境变量默认是否继承到插件的 WASI 沙箱中(默认:`false`)。 +- **`loopback_only`**:插件的出站网络连接是否限制为仅允许 `127.0.0.1` / 本地回环(localhost)(默认:`false`)。 +- **`max_memory_mb`**:(可选)每个插件实例的全局最大内存限制,单位为兆字节(MB)。 +- **`verify_signatures`**:Pumpkin 在加载 WASM 插件前是否验证其加密签名(默认:`true`)。 + +### 单个插件覆盖设置 + +在 `[plugins.overrides.]` 下针对特定插件微调权限与环境变量: + +- **`enabled`**:启用或禁用该特定插件(默认:`true`)。 +- **`allow_unsigned`**:覆盖该特定插件是否可以无签名运行(`true`/`false`)。 +- **`max_memory_mb`**:专为此插件分配的最大内存限制(单位:MB)。 +- **`allowed_permissions`**:为此插件预先批准的权限。 +- **`blocked_permissions`**:为此插件显式阻止的权限。 +- **`loopback_only`**:覆盖此插件的本地回环网络限制。 +- **`environment`**:直接传递到插件 WASI 环境中的自定义环境变量表。 diff --git a/docs/zh_cn/config/server-links.md b/docs/zh_cn/config/server-links.md new file mode 100644 index 0000000..b00e0e9 --- /dev/null +++ b/docs/zh_cn/config/server-links.md @@ -0,0 +1,50 @@ +# 服务器链接 + +Minecraft 客户端(自 1.21 起)可以在暂停菜单和游戏菜单中显示可点击的服务器链接。在 `pumpkin.toml` 的 `[server_links]` 下可以配置标准链接与自定义服务器链接。 + +## 配置 + +:::code-group + +```toml [pumpkin.toml] +[server_links] +enabled = true +bug_report = "https://github.com/Pumpkin-MC/Pumpkin/issues" +support = "" +status = "" +feedback = "" +community = "" +website = "" +forums = "" +news = "" +announcements = "" + +[server_links.custom] +# "Store" = "https://store.example.com" +# "Discord" = "https://discord.gg/example" +``` + +::: + +### 配置选项 + +- **`enabled`**:向连接的客户端广播服务器链接的总开关(默认:`true`)。 +- **`bug_report`**:服务器或项目的缺陷追踪器链接(默认:`"https://github.com/Pumpkin-MC/Pumpkin/issues"`)。 +- **`support`**:技术支持或帮助台链接。 +- **`status`**:服务器运行状态/监控页面链接。 +- **`feedback`**:玩家反馈或建议页面链接。 +- **`community`**:社区论坛或交流群链接。 +- **`website`**:官方网站链接。 +- **`forums`**:讨论论坛链接。 +- **`news`**:服务器博客或新闻页面链接。 +- **`announcements`**:公告页面链接。 + +### 自定义链接 + +在 `[server_links.custom]` 下可以定义在客户端菜单中显示的任意键值对链接: + +```toml +[server_links.custom] +"Store" = "https://store.example.com" +"Discord" = "https://discord.gg/example" +``` diff --git a/docs/zh_cn/config/world.md b/docs/zh_cn/config/world.md new file mode 100644 index 0000000..9b20688 --- /dev/null +++ b/docs/zh_cn/config/world.md @@ -0,0 +1,41 @@ +# 世界 + +Pumpkin 允许在 `pumpkin.toml` 的 `[world]` 配置项下微调世界存储格式、自动保存间隔以及光照模式。 + +## 配置 + +:::code-group + +```toml [pumpkin.toml] +[world] +lighting = "default" +autosave_ticks = 6000 + +[world.chunk] +type = "anvil" +write_in_place = false + +[world.chunk.compression] +algorithm = "LZ4" +level = 6 +``` + +::: + +### 世界设置 + +- **`lighting`**:光照引擎传播计算模式。 + - `"default"`:标准原版 Minecraft 光照传播。 + - `"full"`:全图最大天光,无阴影。 + - `"dark"`:全图完全黑暗(零光照)。 +- **`autosave_ticks`**:世界自动保存之间的服务器 Tick 数量(默认:`6000`,在 20 TPS 下相当于 5 分钟)。设置为 `0` 可禁用世界自动保存。 + +### 区块存储设置 + +- **`type`**:使用的区块存储格式: + - `"anvil"`:标准 Minecraft Anvil 区域文件格式(`.mca`)。 + - `"linear"`:Linear 区域存储格式,采用快速压缩算法以减少磁盘占用。 + - `"pump"`:Pumpkin 原生优化的世界格式。 +- **`write_in_place`**:是否在现有区域文件内就地重写区块,而不是重新分配空闲区块(默认:`false`)。 +- **`compression.algorithm`**:区块数据使用的压缩算法(`"LZ4"`、`"ZLib"`、`"GZip"`、`"Custom"`)。 +- **`compression.level`**:区块存储的压缩等级(默认:`6`)。 diff --git a/docs/zh_cn/developer/architecture.md b/docs/zh_cn/developer/architecture.md new file mode 100644 index 0000000..19c26b1 --- /dev/null +++ b/docs/zh_cn/developer/architecture.md @@ -0,0 +1,139 @@ +# Pumpkin Architecture & Crates + +Pumpkin is organized as a modular Cargo workspace composed of specialized crates. Each crate has distinct responsibilities, strictly defined boundaries, and minimal cross-dependencies. + +This architecture enables high parallel compilation speeds, isolated testing, zero-cost data sharing, and clean separation between network protocols, game logic, and world management. + +--- + +## Workspace Crate Overview + +### Workspace Architecture at a Glance + +| Layer | Crates | Responsibilities | +| :--- | :--- | :--- | +| **Core Runtime** | `pumpkin` | Game loop, world ticking, player sessions, entity management, and event loops | +| **Networking & Protocols** | `pumpkin-protocol`, `pumpkin-auth`, `pumpkin-nbt` | Java/Bedrock packet serialization, Yggdrasil & Xbox Live authentication, SNBT | +| **World & Gameplay** | `pumpkin-world`, `pumpkin-inventory`, `pumpkin-command` | Region I/O (Anvil/Linear), terrain generation, block ticking, containers, Brigadier tree | +| **Extensibility** | `pumpkin-plugin-runtime`, `pumpkin-host-bindings`, `pumpkin-plugin-api`, `pumpkin-plugin-wit` | WebAssembly plugin execution via Wasmtime, WIT host APIs, native and WASM loaders | +| **Foundations** | `pumpkin-data`, `pumpkin-config`, `pumpkin-util` | Code-generated registries, typed configuration files, mathematical vectors, permissions | + +--- + +## Detailed Crate Breakdown + +### 1. `pumpkin` (Core Server) +- **Path**: `crates/pumpkin` +- **Purpose**: The main server binary and centralized orchestration engine. +- **Responsibilities**: + - Initializes configuration, logging (`tracing`), and telemetry. + - Spawns Tokio network listeners for Minecraft: Java Edition, Bedrock Edition, RCON, Query, and LAN broadcast. + - Manages the server tick loop (`Server::tick`), coordinating world updates, player actions, entity movements, and chunk broadcasting. + - Dispatches incoming client packets through `crates/pumpkin/src/net/java/` and `net/bedrock/`. + - Integrates the plugin manager, firing event handlers before and after packet processing. + +### 2. `pumpkin-protocol` (Network Protocol) +- **Path**: `crates/pumpkin-protocol` +- **Purpose**: High-speed network wire format serialization and deserialization for Minecraft Java and Bedrock editions. +- **Responsibilities**: + - Implements the `ClientPacket` (serialization) and `ServerPacket` (deserialization) traits. + - Handles protocol primitives: `VarInt`, `VarLong`, UUIDs, NBT compounds, and string codecs. + - Implements packets across all states: `Handshake`, `Status`, `Login`, `Config`, and `Play`. + - Provides packet compression (ZLib/Fast compression) and packet encryption (AES-128/CFB8). + - Encapsulates RCON and GameSpy4 Query protocols. + +### 3. `pumpkin-world` (World & Chunks) +- **Path**: `crates/pumpkin-world` +- **Purpose**: World loading, saving, terrain generation, and chunk management. +- **Responsibilities**: + - Supports multiple chunk storage formats: + - **Vanilla Anvil (`.mca`)**: Reading and writing standard region files. + - **Linear (`.linear`)**: High-efficiency zstd-compressed region format saving 50–95% disk space. + - **Slime (`.slime`)**: Single-file world format. + - **Pump**: Native Pumpkin chunk storage format. + - Implements world generation pipelines: Perlin noise heightmaps, 3D noise routers, biome climate parameters, placed features, carvers, and superflat terrain. + - Manages block lighting and sky lighting propagation. + - Coordinates chunk loading, unloading, and streaming via cylindrical chunk iterators. + +### 4. `pumpkin-data` (Minecraft Datasets & Registries) +- **Path**: `crates/pumpkin-data` +- **Purpose**: Compile-time static registries and data tables generated directly from vanilla Minecraft assets and reports. +- **Responsibilities**: + - Contains complete ID mappings, properties, and definitions for blocks, items, biomes, entities, sounds, particles, enchantments, and recipes. + - Packet ID lookups across multiple Minecraft protocol versions. + - Zero-cost lookup tables generated by `tools/pumpkin-codegen`. + +### 5. `pumpkin-config` (Configuration Management) +- **Path**: `crates/pumpkin-config` +- **Purpose**: TOML configuration parsing, serialization, and validation. +- **Responsibilities**: + - Loads and writes `pumpkin.toml`. + - Configures server ports, compression thresholds, proxy modes, resource packs, logging levels, world settings, and Bedrock integration. + - Provides safe defaults and schema validations. + +### 6. `pumpkin-inventory` (Inventory & Containers) +- **Path**: `crates/pumpkin-inventory` +- **Purpose**: Server-side container window handlers and inventory mechanics. +- **Responsibilities**: + - Manages player inventory, chest containers, crafting tables, furnaces, and villager trading screens. + - Validates client slot clicks, drag operations, item stacking, and item splitting. + - Coordinates window packet synchronization to ensure client inventory fidelity. + +### 7. `pumpkin-command` (Command Dispatcher) +- **Path**: `crates/pumpkin-command` +- **Purpose**: Brigadier-inspired hierarchical command tree dispatcher and parser. +- **Responsibilities**: + - Constructs modular command trees with typed arguments (`Integer`, `Float`, `Entity`, `BlockPos`, etc.). + - Executes permission-checked commands. + - Provides real-time autocompletion suggestions sent to connected clients. + +### 8. `pumpkin-auth` (Authentication & Session Verification) +- **Path**: `crates/pumpkin-auth` +- **Purpose**: Cryptographic authentication and player identity verification. +- **Responsibilities**: + - Validates client sessions with Mojang's Yggdrasil authentication servers. + - Handles RSA keypair generation and shared secret exchange during the Login state. + - Supports offline-mode UUID generation and custom authentication endpoints. + - Implements proxy forwarding protocols: [BungeeCord](https://github.com/SpigotMC/BungeeCord), [BungeeGuard](https://github.com/lucko/BungeeGuard), and [Velocity](https://github.com/PaperMC/Velocity) modern forwarding with HMAC-SHA256 signatures. + +### 9. `pumpkin-nbt` (Named Binary Tag Codec) +- **Path**: `crates/pumpkin-nbt` +- **Purpose**: High-throughput binary NBT parser and serializer. +- **Responsibilities**: + - Parses and encodes all standard NBT tag types (`Byte`, `Short`, `Int`, `Long`, `Float`, `Double`, `ByteArray`, `String`, `List`, `Compound`, `IntArray`, `LongArray`). + - Supports compression layers: Gzip, Zlib, and uncompressed Network NBT. + +### 10. `pumpkin-plugin-*` (Extensibility Infrastructure) +- **Paths**: + - `crates/pumpkin-plugin-api`: The Rust developer SDK for compiling native plugins. + - `crates/pumpkin-plugin-runtime`: Wasmtime-powered WebAssembly execution host. + - `crates/pumpkin-plugin-wit`: WebAssembly Interface Type (WIT) specifications. + - `crates/pumpkin-host-bindings`: Auto-generated host bindings connecting Wasmtime and the server core. +- **Purpose**: Enables safe, polyglot plugin execution (Rust, Python, C#, C, Go, Kotlin) with sandboxed memory and near-native performance. + +### 11. `pumpkin-util` & `pumpkin-macros` (Foundations) +- **Paths**: `crates/pumpkin-util`, `crates/pumpkin-macros`, `crates/pumpkin-api-macros` +- **Purpose**: Foundational types, shared algorithms, and procedural macros. +- **Responsibilities**: + - Mathematical utilities: `Vector2`, `Vector3`, `BoundingBox`, and angle conversions. + - Text and chat formatting: `TextComponent` and JSON component serialization. + - Procedural macros such as `#[java_packet(...)]` that bind packet structs to protocol IDs and packet traits. + +### 12. `tools/` (Developer Tooling) +- **`tools/pumpkin-codegen`**: Automated code generator that parses Minecraft version jars, data reports, and asset archives to emit Rust source files into `pumpkin-data`. +- **`tools/pumpkin-fuzzer`**: Fuzz testing suites for network packet parsers, NBT readers, and region file decoders to identify potential crash vectors or memory anomalies. + +--- + +## Concurrency & Threading Model + +| Component | Runtime / Pool | Primary Workload | Communication Mechanism | +| :--- | :--- | :--- | :--- | +| **Network & Socket I/O** | Tokio Async Runtime | TCP packet reads, Bedrock UDP packets, framing, session handshakes | Async tasks (`tokio::spawn`), non-blocking reactors | +| **Heavy Compute** | Rayon Thread Pool | Terrain generation, noise routers, light propagation, chunk compression | Worker threads via crossbeam / Rayon iterators | +| **Inter-Thread Bridge** | Tokio Channels | Dispatching completed chunks and encoded packets back to network tasks | Non-blocking `tokio::sync::mpsc` channels | + +Pumpkin strictly adheres to this separation of concerns: +- **I/O Bound**: Managed on Tokio asynchronous tasks. +- **CPU Bound**: Managed on Rayon thread pools. +- **Communication**: Coordinated via cross-thread channels (`tokio::sync::mpsc`) without ever blocking the Tokio reactor loop. diff --git a/docs/zh_cn/developer/blocks/adding-blocks.md b/docs/zh_cn/developer/blocks/adding-blocks.md new file mode 100644 index 0000000..0770ed8 --- /dev/null +++ b/docs/zh_cn/developer/blocks/adding-blocks.md @@ -0,0 +1,182 @@ +# Adding a Block Behavior + +This guide walks through implementing interactive behavior for a block in Pumpkin. + +--- + +## When Is a Behavior Needed? + +- **Non-Interactive Blocks** (e.g., stone, dirt, terracotta): Do **not** require any custom Rust code. Their breaking speeds, hardness, and placement properties are automatically derived from `pumpkin-data`. +- **Interactive or Functional Blocks** (e.g., doors, chests, levers, crops, campfires, TNT): Require an implementation of the `BlockBehaviour` trait to respond to player clicks, redstone signals, physics steps, or timer ticks. + +--- + +## Step 1: Create the Behavior Struct + +Create a new file under `crates/pumpkin/src/block/blocks//.rs` (or directly in `crates/pumpkin/src/block/blocks/`). + +Use the `#[pumpkin_block(...)]` macro to associate your behavior struct with one or more vanilla block identifiers: + +```rust +use std::sync::Arc; +use pumpkin_data::Block; +use pumpkin_data::block_properties::{DoorLikeProperties, Facing}; +use pumpkin_macros::pumpkin_block; +use pumpkin_util::math::position::BlockPos; +use crate::block::registry::BlockActionResult; +use crate::block::{ + BlockBehaviour, NormalUseArgs, OnNeighborUpdateArgs, PlacedArgs, UseWithItemArgs, +}; +use crate::world::World; + +/// Associates this behavior struct with one or multiple block IDs. +#[pumpkin_block([ + "minecraft:oak_door", + "minecraft:iron_door", + "minecraft:spruce_door", + "minecraft:birch_door" +])] +pub struct DoorBlock; + +impl BlockBehaviour for DoorBlock { + fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { + let state_id = args.world.get_block_state(args.location).await; + + // Iron doors require redstone power and cannot be opened manually by hand + if args.block.id == pumpkin_data::Block::IRON_DOOR.id { + return BlockActionResult::Pass; + } + + // Toggle open state + let is_open = DoorLikeProperties::open(state_id); + let new_state = DoorLikeProperties::with_open(state_id, !is_open); + args.world.set_block_state(args.location, new_state).await; + + BlockActionResult::Consume + } + + fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { + // Handle neighbor redstone signals or support block destruction + } +} +``` + +--- + +## Step 2: `BlockBehaviour` Hook Reference + +The `BlockBehaviour` trait exposes lifecycle hooks modeled on vanilla mechanics: + +### Player & Item Interaction Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `normal_use` | `(&self, args: NormalUseArgs<'_>) -> BlockActionResult` | Triggered when right-clicking the block with an empty hand. | +| `use_with_item` | `(&self, args: UseWithItemArgs<'_>) -> BlockActionResult` | Triggered when right-clicking the block while holding an item. | +| `get_screen_handler_factory` | `(&self, args: GetScreenHandlerFactoryArgs<'_>) -> Option>` | Returns an inventory container factory to open a UI screen (e.g., Chests, Crafting Tables). | + +### World Lifecycle & Physics Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `placed` | `(&self, args: PlacedArgs<'_>)` | Triggered immediately after the block is placed in the world. | +| `broken` | `(&self, args: BrokenArgs<'_>)` | Triggered when broken by a player or game mechanics. | +| `on_entity_collision` | `(&self, args: OnEntityCollisionArgs<'_>)` | Triggered when an entity walks inside, steps on, or falls into the block. | +| `on_neighbor_update` | `(&self, args: OnNeighborUpdateArgs<'_>)` | Triggered when an adjacent block state updates or is destroyed. | +| `on_projectile_hit` | `(&self, args: OnProjectileHitArgs<'_>)` | Triggered when an arrow, snowball, or projectile strikes the block. | + +### Redstone & Power Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `emits_redstone_power` | `(&self, state_id: BlockStateId) -> bool` | Declares whether the block emits redstone power to neighbors. | +| `get_weak_redstone_power` | `(&self, ...) -> u8` | Returns emitted weak power level (0–15). | +| `get_strong_redstone_power` | `(&self, ...) -> u8` | Returns emitted strong direct power level (0–15). | + +### Foliage & Bonemeal Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `is_valid_bonemeal_target` | `(&self, args: BonemealArgs<'_>) -> bool` | Checks if bone meal can be applied (e.g., crop not fully grown). | +| `is_bonemeal_success` | `(&self, args: BonemealArgs<'_>) -> bool` | Evaluates random chance roll for bone meal success. | +| `perform_bonemeal` | `(&self, args: BonemealArgs<'_>)` | Advances growth stages or spawns adjacent flora. | + +--- + +## Step 3: Modifying Block States + +All block properties are strongly typed in `pumpkin_data::block_properties`: + +```rust +use pumpkin_data::block_properties::{BedLikeProperties, Part}; + +// 1. Read typed properties from the numeric BlockStateId +let part = BedLikeProperties::part(state_id); // Part::Head or Part::Foot +let is_occupied = BedLikeProperties::occupied(state_id); + +// 2. Generate a new BlockStateId with updated properties +let updated_state = BedLikeProperties::with_occupied(state_id, true); + +// 3. Commit the updated state to the world +world.set_block_state(pos, updated_state).await; +``` + +--- + +## Step 4: Adding a Block Entity (Tile Entity) + +If your block needs to store persistent custom state (inventories, custom text, timers): + +1. Create a block entity struct in `crates/pumpkin/src/block/entities/`: + ```rust + use pumpkin_util::math::position::BlockPos; + use crate::block::entities::BlockEntity; + + pub struct CustomBlockEntity { + pub pos: BlockPos, + pub custom_counter: u32, + } + + impl BlockEntity for CustomBlockEntity { + fn to_nbt(&self) -> pumpkin_nbt::compound::NbtCompound { + let mut compound = pumpkin_nbt::compound::NbtCompound::new(); + compound.put_int("Counter", self.custom_counter as i32); + compound + } + + fn from_nbt(&mut self, tag: &pumpkin_nbt::compound::NbtCompound) { + if let Some(val) = tag.get_int("Counter") { + self.custom_counter = val as u32; + } + } + + fn tick(&mut self, world: &std::sync::Arc, pos: &BlockPos) { + // Periodic logic per game tick + } + } + ``` + +2. Link the block entity in your `BlockBehaviour`: + ```rust + impl BlockBehaviour for CustomBlock { + fn create_block_entity(&self, pos: BlockPos) -> Option>> { + Some(Arc::new(Mutex::new(CustomBlockEntity::new(pos)))) + } + } + ``` + +--- + +## Step 5: Registering the Block + +1. Export your module in `crates/pumpkin/src/block/blocks/mod.rs`: + ```rust + pub mod doors; + ``` + +2. Register the behavior struct in `crates/pumpkin/src/block/registry.rs`: + ```rust + manager.register(DoorBlock); + ``` + +At startup, `manager.register` automatically reads the block IDs declared in `#[pumpkin_block(...)]` and routes interactions to your implementation. diff --git a/docs/zh_cn/developer/blocks/index.md b/docs/zh_cn/developer/blocks/index.md new file mode 100644 index 0000000..525a200 --- /dev/null +++ b/docs/zh_cn/developer/blocks/index.md @@ -0,0 +1,104 @@ +# Block Architecture & State System + +Blocks form the static and dynamic foundation of Minecraft worlds. In Pumpkin, block handling is cleanly bifurcated into two distinct layers: + +1. **Static Data Layer ([`pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data))**: Compile-time generated metadata including numeric block IDs, block state IDs, hardness, blast resistance, light emission, opacity, collision shapes, and block state properties generated by [`pumpkin-codegen`](/developer/codegen). +2. **Dynamic Behavior Layer ([`pumpkin::block`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/block))**: Server-side behavioral hooks—such as opening containers, toggling redstone signals, growing crops, breaking triggers, and collision effects—handled via the `BlockBehaviour` trait. + +--- + +## Static Blocks vs. Interactive Behaviors + +Because Minecraft contains thousands of block state combinations, Pumpkin avoids allocating runtime behavior structs for simple, non-interactive blocks: + +| Category | Description | Examples | Runtime Behavior Required? | +| :--- | :--- | :--- | :--- | +| **Static Blocks** | Rigid solid voxels without interactive logic or custom physics | `stone`, `dirt`, `cobblestone`, `bedrock`, `obsidian` | No — Placement, breaking times, and tool requirements are handled entirely by `pumpkin-data`. | +| **Interactive Blocks** | Blocks triggered by right-clicks or tool actions | `door`, `trapdoor`, `lever`, `button`, `crafting_table` | Yes — Implements `BlockBehaviour::normal_use` or `use_with_item`. | +| **Collision Blocks** | Blocks that modify entity movement or apply forces | `honey_block`, `slime_block`, `soul_sand`, `powder_snow` | Yes — Implements `on_entity_collision` or `on_landed_upon`. | +| **Redstone Components** | Power sources, conductors, and logic gates | `redstone_wire`, `repeater`, `comparator`, `observer` | Yes — Implements `emits_redstone_power` and `on_neighbor_update`. | +| **Block Entities** | Blocks with persistent, serialized custom states | `chest`, `furnace`, `sign`, `decorated_pot`, `spawner` | Yes — Implements `create_block_entity` and links a `BlockEntity`. | + +--- + +## Block IDs, States, and Properties + +Minecraft distinguishes between a **Block** type and a specific **Block State**: + +### 1. `BlockId` and `BlockStateId` + +- **`BlockId`**: An identifier representing a base block type (e.g., `minecraft:oak_door`). +- **`BlockStateId`**: A 16-bit integer representing a unique combination of block properties (e.g., `minecraft:oak_door[facing=north,half=lower,hinge=left,open=false,powered=false]`). + +In Pumpkin, block data is looked up from precomputed lookup tables in `pumpkin-data`: + +```rust +use pumpkin_data::Block; +use pumpkin_data::BlockStateId; + +// Access static properties directly from generated data +let door_block = Block::OAK_DOOR; +let default_state_id: BlockStateId = door_block.default_state_id; + +let hardness = door_block.hardness; +let blast_resistance = door_block.blast_resistance; +let is_air = door_block.is_air; +``` + +### 2. Generated State Property Helpers + +Rather than parsing raw strings or bitmasks manually, [`pumpkin-codegen`](/developer/codegen) generates strongly typed property helpers in `pumpkin_data::block_properties`: + +```rust +use pumpkin_data::block_properties::{DoorLikeProperties, DoubleBlockHalf, Facing}; + +// Read typed properties from a numeric BlockStateId +let is_open = DoorLikeProperties::open(state_id); +let half = DoorLikeProperties::half(state_id); +let facing = DoorLikeProperties::facing(state_id); + +// Generate a new BlockStateId with mutated properties +let toggled_state_id = DoorLikeProperties::with_open(state_id, !is_open); +``` + +--- + +## Physics, Bounding Boxes & Raycasting + +Pumpkin calculates collision and raycasting bounding boxes directly using pre-baked shapes in `pumpkin-data`: + +- **Solid Collisions**: During physics ticks, an entity's axis-aligned bounding box (AABB) is tested against surrounding block collision voxels. +- **Raycasting**: When a player targets a block to place or break, client raycasts are validated against the server-side block outline shapes. +- **Custom Movement Hooks**: Blocks like slime blocks or honey blocks override vertical rebound or horizontal velocity: + - `bounce_entity_after_fall`: Dampens fall damage and multiplies rebound velocity. + - `stop_vertical_movement_after_fall`: Zeros fall velocity upon contact. + +--- + +## Block Entities (Tile Entities) + +When a block must store arbitrary dynamic data that cannot fit into standard block state bits (such as container inventories, custom sign text, brewing timers, or mob spawner delays), it uses a **Block Entity**: + +| Component | Responsibility | Implementation File | +| :--- | :--- | :--- | +| `BlockEntity` Trait | Defines NBT serialization (`to_nbt`, `from_nbt`) and tick execution | [`crates/pumpkin/src/block/entities/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/block/entities) | +| `create_block_entity` | Hook in `BlockBehaviour` called when placing the block | Attached to relevant block behaviors | +| Chunk Entity Storage | Manages active block entities and coordinates within chunk sections | [`crates/pumpkin-world`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-world) | + +--- + +## The Block Registry & Dispatch System + +Block interaction events are routed through the central `BlockRegistry` in [`crates/pumpkin/src/block/registry.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/block/registry.rs): + +1. **Registration**: At server initialization, all behavior structs are registered into the `BlockManager`. +2. **Mapping**: The manager maps numeric `BlockId` keys to thread-safe behavior instances (`Arc`). +3. **Dispatch**: When the network layer receives block action packets (such as `CUseItemOn` or block dig packets), the server looks up the behavior in $O(1)$ time and invokes the relevant hook. + +--- + +## Next Steps + +To implement a new interactive block behavior, continue to the practical guide: + +- **[Adding a Block](/developer/blocks/adding-blocks)**: Complete walk-through of declaring attributes, implementing interaction hooks, creating block entities, and registering behaviors. diff --git a/docs/zh_cn/developer/codegen.md b/docs/zh_cn/developer/codegen.md new file mode 100644 index 0000000..3908d04 --- /dev/null +++ b/docs/zh_cn/developer/codegen.md @@ -0,0 +1,66 @@ +# Code Generation (`pumpkin-codegen`) + +Minecraft is a massive game with thousands of block states, items, packet IDs, sound events, recipes, and biome parameters. These registries frequently change between Minecraft updates. + +To maintain 100% fidelity without manually maintaining tens of thousands of error-prone constants, Pumpkin utilizes an automated code generation tool: [`tools/pumpkin-codegen`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/tools/pumpkin-codegen). + +--- + +## Purpose & Workflow + +`pumpkin-codegen` parses official Minecraft server data generator reports, asset files, and version specifications, and outputs zero-cost, strongly-typed Rust files directly into [`crates/pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data). + +The pipeline operates in three distinct stages: +1. **Ingestion**: Reads raw JSON assets and vanilla server reports from `assets/` and `assets/datapacks/`. +2. **Transformation**: Normalizes states, calculates bitsets, processes ViaVersion remappings, and optimizes lookup tables. +3. **Emission**: Emits compiled Rust constants and structs into [`crates/pumpkin-data/src/generated`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data/src/generated). + +--- + +## Generated Datasets + +`pumpkin-codegen` generates the following components in `pumpkin-data`: + +| Category | Description | Generated Source | +| :--- | :--- | :--- | +| **Packets** | Serverbound and clientbound packet IDs for all protocol states across supported Minecraft versions. | `packet.rs` | +| **Blocks** | Block definitions, default states, state properties (facing, powered, waterlogged, etc.), and block collision boxes. | `block.rs` | +| **Items** | Item definitions, stack sizes, durability, data components, food properties, and equipment slots. | `item.rs` | +| **Registries** | Dynamic registries for biomes, dimensions, chat types, damage types, trim materials, and banner patterns. | `registry.rs` | +| **Biomes & Noise** | Climate parameters, noise router configurations, 3D noise settings, and surface rules. | `biome.rs`, `noise_router.rs` | +| **Entities** | Entity types, default bounding boxes, attributes, and tracked entity metadata schemas. | `entity_type.rs`, `tracked_data.rs` | +| **Recipes & Crafting** | Shaped, shapeless, smelting, blasting, smoking, and stonecutting recipes. | `recipes.rs` | +| **Tags** | Block tags, item tags, fluid tags, and entity type tags (e.g., `#minecraft:mineable/pickaxe`). | `tag.rs` | +| **Enchantments & Potions** | Enchantment definitions, potion effects, and brewing stand recipes. | `enchantments.rs`, `potion.rs` | +| **Translations** | Official localization keys for chat messages and item names. | `translations.rs` | +| **Plugin WIT** | WebAssembly Component Model WIT files defining plugin interfaces. | `wit/` | + +--- + +## Running Code Generation + +To run the code generator locally: + +```bash +# Navigate to the workspace root +cd Pumpkin + +# Run the codegen tool +cargo run -p pumpkin-codegen +``` + +### When Should You Run Codegen? + +- **Upgrading to a New Minecraft Version**: When porting Pumpkin to a new minor or major Minecraft release. +- **Adding Protocol Mappings**: When adding support for an older or newer client protocol version. +- **Updating WIT Definitions**: When adding new functions or types to the WebAssembly plugin API. + +--- + +## Data Sources + +`pumpkin-codegen` reads data from several primary sources: + +1. **Minecraft Data Generator Reports**: Generated by running `java -DbundlerMainClass=net.minecraft.data.Main -jar server.jar --reports`. +2. **Minecraft Client Assets**: Extracted from client `.jar` files for translation keys, model shapes, and sound mappings. +3. **Minecraft Protocol Wiki**: Cross-referenced with protocol specifications for packet IDs. diff --git a/docs/zh_cn/developer/commands/adding-commands.md b/docs/zh_cn/developer/commands/adding-commands.md new file mode 100644 index 0000000..eda739a --- /dev/null +++ b/docs/zh_cn/developer/commands/adding-commands.md @@ -0,0 +1,246 @@ +# Adding a Command + +This guide explains how to implement and register a new command in the Pumpkin server codebase. + +Commands in Pumpkin are implemented in the `crates/pumpkin/src/command/commands/` directory and registered into the central [`CommandDispatcher`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/dispatcher.rs) during server initialization. + +--- + +## Anatomy of a Pumpkin Command + +A typical command file contains: +1. **Metadata Constants**: Command description, permission node identifier, and argument names. +2. **Permission Registration**: Declaring default OP requirements in the server's [`PermissionRegistry`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-util/src/permission.rs). +3. **Command Executors**: Structs implementing the [`CommandExecutor`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/mod.rs) trait for each terminal node or subcommand. +4. **Registration Function**: Constructing the argument tree using fluent builders (`command`, `literal`, `argument`). + +--- + +## Step-by-Step Implementation: Creating `/heal` + +Let's implement a complete `/heal` command that can heal either the executing player or a set of target players: + +``` +/heal -> Heals the executing sender +/heal -> Heals target player(s) +/heal -> Heals target player(s) by a specified amount +``` + +### 1. Create the File + +Create `crates/pumpkin/src/command/commands/heal.rs`: + +```rust +use std::sync::Arc; + +use crate::command::argument_builder::{ArgumentBuilder, argument, command}; +use crate::command::argument_types::core::float::FloatArgumentType; +use crate::command::argument_types::entity::EntityArgumentType; +use crate::command::context::command_context::CommandContext; +use crate::command::errors::error_types::CommandErrorType; +use crate::command::node::dispatcher::CommandDispatcher; +use crate::command::node::{CommandExecutor, CommandExecutorResult}; +use crate::entity::EntityBase; +use pumpkin_data::translation; +use pumpkin_util::PermissionLvl; +use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry}; +use pumpkin_util::text::TextComponent; + +const DESCRIPTION: &str = "Restores health to target entities."; +const PERMISSION: &str = "pumpkin:command.heal"; + +const ARG_TARGETS: &str = "targets"; +const ARG_AMOUNT: &str = "amount"; + +const ERROR_NOT_PLAYER: CommandErrorType<0> = CommandErrorType::new( + translation::java::PERMISSIONS_REQUIRES_PLAYER, + translation::java::PERMISSIONS_REQUIRES_PLAYER, +); +``` + +### 2. Implement the Executors + +Command execution logic is encapsulated in structs implementing [`CommandExecutor`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/node/mod.rs): + +```rust +/// Heals the executing player to full health. +struct SelfHealExecutor; + +impl CommandExecutor for SelfHealExecutor { + fn execute(&self, context: &CommandContext) -> CommandExecutorResult { + let player = context + .source + .output + .as_player() + .ok_or_else(|| ERROR_NOT_PLAYER.create_without_context())?; + + // Restore player health + player.living_entity.set_health(20.0); + + context.source.send_feedback( + TextComponent::text("Fully restored your health!"), + true, // Broadcast to operators if configured + ); + + // Return number of affected entities + Ok(1) + } +} + +/// Heals specified target players. +struct TargetsHealExecutor { + has_amount: bool, +} + +impl CommandExecutor for TargetsHealExecutor { + fn execute(&self, context: &CommandContext) -> CommandExecutorResult { + let targets = EntityArgumentType::get_players(context, ARG_TARGETS)?; + + let amount = if self.has_amount { + FloatArgumentType::get(context, ARG_AMOUNT)? + } else { + 20.0 + }; + + let count = targets.len(); + for target in &targets { + let current = target.living_entity.health.load(); + target.living_entity.set_health((current + amount).min(20.0)); + } + + let msg = if count == 1 { + TextComponent::text(format!("Healed {} by {} HP", targets[0].gameprofile.name, amount)) + } else { + TextComponent::text(format!("Healed {} players by {} HP", count, amount)) + }; + + context.source.send_feedback(msg, true); + + Ok(count as i32) + } +} +``` + +### 3. Build the Command Tree + +Use the fluent builder functions `command`, `literal`, and `argument` to assemble the syntax tree and register the permission: + +```rust +pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) { + // 1. Register permission node with default requirement (OP level 2) + registry.register_permission_or_panic(Permission::new( + PERMISSION, + DESCRIPTION, + PermissionDefault::Op(PermissionLvl::Two), + )); + + // 2. Assemble command tree + dispatcher.register( + command("heal", DESCRIPTION) + .requires(PERMISSION) + .executes(SelfHealExecutor) + .then( + argument(ARG_TARGETS, EntityArgumentType::Players) + .executes(TargetsHealExecutor { has_amount: false }) + .then( + argument(ARG_AMOUNT, FloatArgumentType::new(1.0, 100.0)) + .executes(TargetsHealExecutor { has_amount: true }), + ), + ), + ); +} +``` + +--- + +## Command Structure Patterns + +### Adding Subcommand Literals +Use `literal("subcommand_name")` to create keyword branches: + +```rust +use crate::command::argument_builder::{argument, command, literal}; + +dispatcher.register( + command("weather", "Set the world weather") + .requires("minecraft:command.weather") + .then(literal("clear").executes(ClearWeatherExecutor)) + .then(literal("rain").executes(RainWeatherExecutor)) + .then(literal("thunder").executes(ThunderWeatherExecutor)) +); +``` + +### Cross-Platform Translation Feedback +Pumpkin servers serve both Java and Bedrock Edition players simultaneously. Whenever possible, use vanilla translation keys with `TextComponent::translate_cross` so messages render localized on the client: + +```rust +use pumpkin_data::translation; +use pumpkin_util::text::TextComponent; + +let msg = TextComponent::translate_cross( + translation::java::COMMANDS_KILL_SUCCESS_SINGLE, + translation::bedrock::COMMANDS_KILL_SUCCESSFUL, + [target.get_display_name()], +); + +context.source.send_feedback(msg, true); +``` + +### Error Handling in Executors +Executors must return `CommandExecutorResult`, which resolves to `Result`. If validation fails, return an error cleanly without panicking: + +```rust +use crate::command::errors::error_types::CommandErrorType; + +const ERROR_INVALID_VALUE: CommandErrorType<0> = CommandErrorType::new( + "Amount must be greater than zero.", + "Amount must be greater than zero.", +); + +if amount <= 0.0 { + return Err(ERROR_INVALID_VALUE.create_without_context()); +} +``` + +--- + +## Registering in `default_dispatcher` + +Once your command is implemented, register it in `crates/pumpkin/src/command/commands/mod.rs`: + +1. **Declare the Module**: + ```rust + // In crates/pumpkin/src/command/commands/mod.rs + mod heal; + ``` + +2. **Invoke `register` in `default_dispatcher`**: + ```rust + pub fn default_dispatcher( + permission_manager: &PermissionManager, + commands_config: &CommandsConfig, + ) -> CommandDispatcher { + let registry = &permission_manager.registry; + let mut dispatcher = CommandDispatcher::new(); + + // ... existing registrations ... + heal::register(&mut dispatcher, registry); + + dispatcher + } + ``` + +--- + +## Best Practices Checklist + +- [ ] **No Panics or Unwraps**: Use `?` or return descriptive `CommandSyntaxError`s when arguments are invalid. +- [ ] **Permission Nodes**: Prefix permission strings with appropriate namespaces (`minecraft:command.` for vanilla commands, `pumpkin:command.` for server-specific commands). +- [ ] **Return Success Counts**: The returned `i32` represents the command's exit code or number of affected objects for redstone command blocks and `/execute store result`. +- [ ] **Respect GameRules**: When notifying players other than the sender, check if `send_command_feedback` is enabled: + ```rust + let server = context.source.server(); + if server.level_info.load().game_rules.send_command_feedback { + target.send_system_message(&feedback); + } + ``` diff --git a/docs/zh_cn/developer/commands/arguments-and-suggestions.md b/docs/zh_cn/developer/commands/arguments-and-suggestions.md new file mode 100644 index 0000000..6794ae1 --- /dev/null +++ b/docs/zh_cn/developer/commands/arguments-and-suggestions.md @@ -0,0 +1,159 @@ +# Argument Types & Suggestions + +Pumpkin provides a comprehensive suite of type-safe command arguments through `pumpkin-command` and the main `pumpkin` server crate. Arguments deserialize raw token streams into structured Rust types, validate ranges, and furnish tab-completion suggestions to clients. + +--- + +## Standard Argument Types + +Argument parsers implement the [`ArgumentType`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/argument_types/argument_type.rs) trait. The following built-in types are available for command builders: + +### 1. Primitive & Scalar Types + +| Argument Type | Import Path | Deserialized Type | Description | +| :--- | :--- | :--- | :--- | +| **`IntegerArgumentType`** | `...::core::integer` | `i32` | Standard 32-bit integer with optional `[min, max]` bounds. | +| **`FloatArgumentType`** | `...::core::float` | `f32` | 32-bit floating point number with bounds. | +| **`DoubleArgumentType`** | `...::core::double` | `f64` | 64-bit floating point number with bounds. | +| **`LongArgumentType`** | `...::core::long` | `i64` | 64-bit integer with bounds. | +| **`BoolArgumentType`** | `...::core::bool` | `bool` | Boolean value (`true` or `false`). | +| **`StringArgumentType`** | `...::core::string` | `String` | String tokens based on quoting rules. | + +#### String Argument Behaviors +- **`StringArgumentType::Word`**: Parses a single word without whitespace. +- **`StringArgumentType::Quotable`**: Parses either a single word or a quoted phrase (`"hello world"`). +- **`StringArgumentType::Greedy`**: Consumes all remaining characters in the input stream (ideal for chat/broadcast commands like `/say` and `/me`). + +--- + +## Coordinate Argument Types + +Minecraft coordinates support absolute positions, relative offsets (`~`), and local caret directions (`^`): + +```rust +use crate::command::argument_builder::argument; +use crate::command::argument_types::coordinates::block_pos::BlockPosArgumentType; +use crate::command::argument_types::coordinates::vec3::Vec3ArgumentType; + +// Block position (integer BlockPos) +argument("pos", BlockPosArgumentType) + +// Precise entity coordinates (f64 Vector3) +argument("location", Vec3ArgumentType) +``` + +### Retrieving Parsed Coordinates +Inside an executor, coordinate arguments resolve relative offsets against the sender's current position and orientation in the [`CommandSource`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/command/context/command_source.rs): + +```rust +// Extracts centered f64 coordinates (handling ~ and ^ relative to sender) +let pos: Vector3 = Vec3ArgumentType::get_coordinates(context, "location")?; + +// Extracts integer block position +let block_pos: BlockPos = BlockPosArgumentType::get_block_pos(context, "pos")?; +``` + +--- + +## Entity Selectors (`EntityArgumentType`) + +Entity selectors allow players and command blocks to target entities using player names, UUIDs, or vanilla target selectors (`@p`, `@r`, `@a`, `@e`, `@s`). + +In Pumpkin, entity selectors are accessed via [`EntityArgumentType`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/command/argument_types/entity.rs): + +```rust +pub enum EntityArgumentType { + /// Matches exactly one player + Player, + /// Matches zero or more players + Players, + /// Matches exactly one entity + Entity, + /// Matches zero or more entities + Entities, +} +``` + +### Resolution in Executors + +```rust +// 1. Target single player +let player: Arc = EntityArgumentType::get_player(context, "target")?; + +// 2. Target multiple players +let players: Vec> = EntityArgumentType::get_players(context, "targets")?; + +// 3. Target single living entity or mob +let entity: Arc = EntityArgumentType::get_entity(context, "target")?; + +// 4. Target multiple entities +let entities: Vec> = EntityArgumentType::get_entities(context, "targets")?; +``` + +### Supported Selector Syntaxes +Pumpkin's entity selector parser (`crates/pumpkin/src/command/argument_types/entity_selector/`) parses and validates: +- `@p`: Nearest player relative to execution coordinates. +- `@r`: Random player. +- `@a`: All online players. +- `@e`: All entities in loaded world chunks. +- `@s`: Executing entity (`context.source.entity`). +- **Bracket Arguments**: Filter queries like `@e[type=zombie,distance=..10,limit=5,sort=nearest]`. + +--- + +## Registry & Game Mechanic Types + +Pumpkin provides specialized argument parsers for Minecraft data types: + +- **`GameModeArgumentType`**: Resolves `survival`, `creative`, `adventure`, `spectator` or numeric IDs `0`–`3`. +- **`BlockArgumentType`**: Parses block identifiers and block states (e.g., `minecraft:chest[facing=north]`). +- **`ItemArgumentType`**: Parses item identifiers and optional data components (e.g., `minecraft:diamond_sword`). +- **`NbtArgumentType`**: Parses SNBT (Stringified NBT) tags into Pumpkin's NBT representation. +- **`TimeArgumentType`**: Parses durations with suffixes (e.g., `10s`, `20t`, `1d`). + +--- + +## Suggestion Providers & Tab Completion + +Arguments can supply dynamic tab-completion candidates to connected players using the [`SuggestionProvider`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-command/src/suggestion/mod.rs) trait: + +```rust +use crate::command::context::command_context::CommandContext; +use crate::command::suggestion::provider::SuggestionProvider; +use pumpkin_command::suggestion::suggestions::{Suggestions, SuggestionsBuilder}; + +struct CustomSuggestionProvider; + +impl SuggestionProvider for CustomSuggestionProvider { + fn suggest( + &self, + context: &CommandContext, + mut builder: SuggestionsBuilder, + ) -> Suggestions { + // Retrieve already typed token prefix + let remaining = builder.remaining(); + + let candidates = ["alpha", "bravo", "charlie"]; + for candidate in candidates { + if candidate.starts_with(remaining) { + builder.suggest(candidate); + } + } + + builder.build() + } +} +``` + +### Attaching Suggestions to Arguments +Use the `.suggests(...)` method on `RequiredArgumentBuilder`: + +```rust +argument("code", StringArgumentType::Word) + .suggests(CustomSuggestionProvider) + .executes(MyExecutor) +``` + +### Client Completion Protocol +- **Java Edition**: When a player presses `Tab`, the client transmits a `ServerboundCommandSuggestion` packet. Pumpkin matches the cursor position within the `CommandDispatcher`, executes the target node's `SuggestionProvider`, and responds with `ClientboundCommandSuggestions` containing completion ranges and tooltip tooltips. +- **Bedrock Edition**: Suggestion options for literals and enums are serialized ahead of time in the `CAvailableCommands` packet as dynamic enum value tables. diff --git a/docs/zh_cn/developer/commands/execution-and-context.md b/docs/zh_cn/developer/commands/execution-and-context.md new file mode 100644 index 0000000..6f5f044 --- /dev/null +++ b/docs/zh_cn/developer/commands/execution-and-context.md @@ -0,0 +1,118 @@ +# Execution & Context + +In Pumpkin, command execution separates the **physical origin** of a command from its **runtime context**. This design enables complex command routing, dimension shifts, conditional execution, and vanilla-compliant `/execute` chaining. + +--- + +## `CommandSender` vs. `CommandSource` + +Understanding the distinction between `CommandSender` and `CommandSource` is fundamental to developing commands in Pumpkin: + +| Attribute | `CommandSender` | `CommandSource` | +| :--- | :--- | :--- | +| **Concept** | The physical origin and network destination | The mutable runtime execution environment | +| **Mutability** | Immutable throughout command execution | Cloned and modified across `/execute` branches | +| **Key Fields** | Terminal socket, connection handle, RCON buffer | World, position coordinates, rotation, entity, silent flag | +| **Creation** | Instantiated on network connection or terminal input | Constructed via `sender.into_source(&server)` | + +### 1. `CommandSender` (Physical Caller) +Represents the entity or socket that initiated the command and receives its output: + +```rust +pub enum CommandSender { + Console, + Rcon(Arc>>), + Player(Arc), + CommandBlock(Arc, Arc), + Dummy, +} +``` + +- **Output Target**: Determines where messages formatted with `send_message` are routed (e.g., standard out, RCON buffer, player packet, or block entity NBT). +- **Hard Permissions**: Holds fixed base permissions (e.g., Console has level 4 bypass; Command Blocks have level 2). + +### 2. `CommandSource` (Execution Environment) +Represents the mutable runtime state during command evaluation: + +```rust +pub struct CommandSource { + pub output: CommandSender, + pub world: Option>, + pub entity: Option>, + pub position: Vector3, + pub rotation: Vector2, + pub name: String, + pub display_name: TextComponent, + pub server: Option>, + pub silent: bool, + pub command_result_taker: ResultValueTaker, + pub entity_anchor: EntityAnchor, +} +``` + +A sender converts into a `CommandSource` via `sender.into_source(&server)`. + +--- + +## The `/execute` Command & Context Redirection + +The `/execute` command manipulates `CommandSource` properties before passing control to a downstream command. + +### Branching Modifiers +- **`as `**: Changes `context.source.entity` to target entities without altering position or rotation. +- **`at `**: Updates `context.source.world`, `position`, and `rotation` to match the target entity. +- **`positioned `**: Modifies `context.source.position` to new coordinates. +- **`in `**: Changes `context.source.world` to the specified dimension (Overworld, Nether, The End). +- **`anchored (feet|eyes)`**: Adjusts coordinate offsets for line-of-sight and facing checks. + +### Context Forking & Chaining +When `/execute` matches multiple targets (e.g., `/execute as @e run ...`), `pumpkin-command` uses **forking**: +1. The dispatcher forks the execution context once per matched target. +2. The child command executes independently for each cloned `CommandSource`. +3. The cumulative success count is aggregated across all executions. + +--- + +## Result Values & Success Counts + +Vanilla commands communicate results back through integer exit codes and return values. + +### The Return Value Callback +Executors return `CommandExecutorResult` (`Result`). The returned integer signifies: +- **0**: Failure or zero entities affected. +- **> 0**: Number of entities affected, blocks changed, or items cleared. + +For Command Blocks, this value is persisted into the block entity's `SuccessCount`: + +```rust +pub fn set_success_count(&self, count: u32) { + if let Self::CommandBlock(c, _) = self { + c.success_count.store(count, Ordering::SeqCst); + } +} +``` + +### `/execute store` +When chained with `/execute store (result|success) ...`, Pumpkin intercepts the return value via `ReturnValueCallback` and writes it to scoreboards, bossbars, or entity NBT tags. + +--- + +## Feedback & GameRules Integration + +Sending output to the command sender is governed by server configuration and world game rules: + +```rust +context.source.send_feedback( + TextComponent::text("Command executed successfully!"), + true, // Broadcast to server operators +); +``` + +### Output Rules Matrix + +| Condition | Feedback Behavior | +| :--- | :--- | +| `silent == true` | Output is suppressed completely. | +| `send_command_feedback == false` | Player senders do not receive success feedback (errors are still displayed). | +| `command_block_output == false` | Command block execution output is not broadcast to online operators. | +| `broadcast_console_to_ops == true` | Server console and RCON commands emit a copy of their feedback to all online OP level 2+ players. | diff --git a/docs/zh_cn/developer/commands/index.md b/docs/zh_cn/developer/commands/index.md new file mode 100644 index 0000000..3b31eb9 --- /dev/null +++ b/docs/zh_cn/developer/commands/index.md @@ -0,0 +1,142 @@ +# Commands Architecture & Dispatcher + +Pumpkin features a high-performance, pure-Rust implementation of the **Brigadier** command tree specification through the dedicated `pumpkin-command` crate. It coordinates command parsing, requirement resolution, type-safe argument deserialization, execution branching, and dual-edition client tree synchronization for both **Minecraft: Java Edition** and **Minecraft: Bedrock Edition**. + +### Command Processing Pipeline + +Every command executed in Pumpkin flows through the following discrete stages: + +1. **Input Normalization**: Strips single leading slashes while preserving plugin multi-slash conventions (such as `//set`). +2. **Availability Check**: Rejects commands disabled via server configuration with standard `unknown command` syntax errors. +3. **Permission Evaluation**: Validates requirements (`PermissionLvl` or custom registry nodes) against the executing sender. +4. **Tokenization & Parsing**: `StringReader` recursively walks the `Tree`, matching literal keywords and parsing typed argument parameters. +5. **Context Dispatch**: Invokes the matched `CommandExecutor` callback with a typed `CommandContext`. +6. **Feedback & Result Consumption**: Formats cross-platform translation components, notifies observers, and writes exit codes back to the caller. + +--- + +## The `pumpkin-command` Crate + +Pumpkin decouples command parsing and tree representation from the main server runtime into the modular `pumpkin-command` crate (`crates/pumpkin-command`). + +Key components of `pumpkin-command` include: +- **`Tree`**: Contiguous vector-backed storage of attached command nodes (`AttachedNode`). +- **`CommandDispatcher`**: The central registry and dispatch engine. It processes raw command strings, parses tokens with `StringReader`, traverses the command tree, and executes target callbacks. +- **`ArgumentBuilder`**: Fluent builder API (`command`, `literal`, `argument`) modeled after Mojang's Brigadier library. +- **`CommandSource`**: Generic execution state representing the current position, rotation, dimension, entity, and feedback output stream. + +--- + +## The Command Node Hierarchy + +Commands in Pumpkin are modeled as directed graphs of nodes. To optimize memory allocations and avoid circular reference issues during building, the system separates nodes into **detached** and **attached** states: + +### 1. Detached Nodes (`DetachedNode`) +Constructed using fluent builders (`CommandArgumentBuilder`, `LiteralArgumentBuilder`, `RequiredArgumentBuilder`) before registration: +- **`CommandDetachedNode`**: Represents a root command entrypoint with an identifier and a description. +- **`LiteralDetachedNode`**: Represents a literal keyword in a subcommand branch (e.g., `set` in `/gamemode creative`). +- **`ArgumentDetachedNode`**: Represents a typed parameter parsed by an `ArgumentType` (e.g., coordinates, entity selectors, integer ranges). + +### 2. Attached Nodes (`AttachedNode`) +Once registered into the `CommandDispatcher`, detached trees are flattened into an indexed `Tree` structure: +```rust +pub enum AttachedNode { + Root(RootAttachedNode), + Literal(LiteralAttachedNode), + Argument(ArgumentAttachedNode), + Command(CommandAttachedNode), +} +``` +Each attached node has: +- **`NodeId`**: A zero-cost typed index (`NonZero`) into the contiguous node arena. +- **`children`**: Fast lookup map (`FxHashMap`) for literals and child arguments. +- **`requirements`**: Predicates determining whether a sender has permission to view or execute the node. +- **`command`**: Optional `CommandExecutor` callback invoked when parsing terminates on this node. +- **`redirect`**: Optional `Redirection` pointer to loop back or redirect to another sub-tree (used heavily by `/execute`). + +--- + +## Dispatch & Execution Pipeline + +When a command arrives at the server, `CommandDispatcher::handle_command` processes it through several stages: + +```rust +pub fn handle_command<'a>(&'a self, source: &S, mut input: &'a str) { + // 1. Strip single leading slash (preserving double-slash commands like //set) + if let Some(sliced) = input.strip_prefix('/') { + let first_token = input.split_whitespace().next().unwrap_or(""); + let sliced_token = sliced.split_whitespace().next().unwrap_or(""); + if !self.has_command(first_token) + && (self.has_command(sliced_token) || !first_token.starts_with("//")) + { + input = sliced; + } + } + + // 2. Check disabled commands from configuration + if self.is_disabled(Self::command_name(input)) { + let reader = StringReader::new(input); + Self::send_error_to_source(source, DISPATCHER_UNKNOWN_COMMAND.create(&reader), input); + return; + } + + // 3. Parse and execute + let output = self.execute_input(input, source); + if let Err(error) = output { + Self::send_error_to_source(source, error, input); + } +} +``` + +### Syntax Error Formatting +When parsing encounters invalid syntax, unexpected tokens, or out-of-range arguments, Pumpkin constructs a `CommandSyntaxError`. If context is available, the dispatcher formats a visual pointer highlighting exactly where the parser failed: + +``` +Unknown or incomplete command, see below for error +/gamemode creatve<--[HERE] +``` + +In the Java Edition client, this error text is annotated with a `ClickEvent::SuggestCommand`, allowing the player to click the error in chat to copy the failed command into their input box for quick editing. + +--- + +## Dual-Edition Network Synchronization + +Unlike vanilla servers which only serialize command graphs for Java Edition, Pumpkin dynamically synchronizes the registered command tree across both Java and Bedrock clients: + +### 1. Java Edition: `CCommands` Packet +When a player joins or their permission level updates (e.g., via `/op` or `/deop`), Pumpkin builds and sends a `CCommands` packet (`pumpkin_protocol::java::client::play::CCommands`): +- Converts `AttachedNode`s into a flat array of `ProtoNode` entries. +- Filters out disabled commands and nodes where `requirements.evaluate(source)` returns `false`. +- Serializes argument parsers (`brigadier:string`, `brigadier:integer`, `minecraft:entity`, etc.) and client-side suggestion identifiers. + +### 2. Bedrock Edition: `CAvailableCommands` Packet +Bedrock Edition uses an overload-and-enum format rather than Brigadier's recursive graph. In `client_suggestions.rs`, Pumpkin traverses the `AttachedNode` tree to synthesize: +- **`CommandData`**: Root command name, description, permission flags, and aliases. +- **`OverloadData`**: Flattened permutations of argument chains leading to executable endpoints. +- **`ParamData`**: Bedrock parameter types, bitflags (`ARG_FLAG_VALID`, `ARG_FLAG_ENUM`), and dynamically collected enum value tables. + +--- + +## Universal Execution Targets + +Pumpkin commands can be executed across diverse senders: + +| Sender | Source Representation | Output Destination | Default Permission | +| :--- | :--- | :--- | :--- | +| **Player** | `CommandSender::Player(Arc)` | Client system chat packet | Loaded from permission level / permissions file | +| **Server Console** | `CommandSender::Console` | Standard stdout (`tracing::info!`) | Level 4 (Full administrative bypass) | +| **RCON Console** | `CommandSender::Rcon(Arc>>)` | Network response packet payload | Level 4 | +| **Command Block** | `CommandSender::CommandBlock(Arc, Arc)` | NBT `LastOutput` field | Level 2 | +| **Datapack Function** | `CommandSender` inherited from caller | Inherited | Inherited | +| **WASM Plugin** | Invoked through WIT `execute_command` host call | Captured via `ResultDeferrer` | Configurable | + +--- + +## Command Configuration & Disabling + +Pumpkin allows server administrators to disable individual commands via `config/commands.toml` (`CommandsConfig`). When a command is disabled: +1. It is omitted from the `CCommands` packet for Java clients. +2. It is omitted from the `CAvailableCommands` packet for Bedrock clients. +3. Tab completion ignores the command entirely. +4. Any attempt to invoke the command returns `DISPATCHER_UNKNOWN_COMMAND`. diff --git a/docs/zh_cn/developer/entities/ai-and-mobs.md b/docs/zh_cn/developer/entities/ai-and-mobs.md new file mode 100644 index 0000000..bb1652f --- /dev/null +++ b/docs/zh_cn/developer/entities/ai-and-mobs.md @@ -0,0 +1,111 @@ +# Mob AI & Goal System + +Pumpkin implements a modular, priority-driven Artificial Intelligence (AI) goal system for computer-controlled mobs, located in [`crates/pumpkin/src/entity/ai/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity/ai) and [`crates/pumpkin/src/entity/mob/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity/mob). + +--- + +## The Goal System Architecture + +Mob behaviors are decomposed into independent modular tasks called **Goals**. Each mob possesses a **GoalSelector** that maintains an ordered collection of goals evaluated on every server tick. + +### Goal Priority Hierarchy + +Goals are assigned an integer priority (where lower numbers represent higher precedence). During each tick, higher-priority actions preempt lower-priority background tasks: + +| Priority | Goal Type | Evaluation Condition | Behavior | +| :--- | :--- | :--- | :--- | +| **1 (Highest)** | `SwimGoal` | Entity is submerged in water | Jump repeatedly to swim to the water surface and avoid drowning. | +| **2** | `MeleeAttackGoal` | Valid hostile target in sight/range | Navigate towards target entity and execute melee attacks. | +| **3** | `WanderAroundGoal` | No active combat target | Pick a random nearby navigable voxel and walk to it. | +| **4 (Lowest)** | `LookAtEntityGoal` | Player or mob within observation radius | Rotate head and body pitch/yaw towards the target entity. | + +### Execution Flow + +1. **Evaluation**: On every game tick, `GoalSelector::tick()` iterates through registered goals in order of priority. +2. **Activation**: If an inactive goal satisfies `can_start()`, it is activated via `start()`. +3. **Execution**: Active goals run their per-tick logic in `tick()`. +4. **Preemption & Termination**: If `should_continue()` returns `false`, or a mutually exclusive goal with higher priority activates, `stop()` is called and the goal resets. + +### The `Goal` Lifecycle + +Every AI behavior implements the `Goal` trait: + +```rust +pub trait Goal: Send + Sync { + /// Evaluates preconditions to determine if this goal can activate. + fn can_start(&self) -> bool; + + /// Called once when the goal transitions from inactive to active. + fn start(&mut self) {} + + /// Evaluates whether the goal should continue executing on subsequent ticks. + fn should_continue(&self) -> bool { + self.can_start() + } + + /// Executes the continuous behavior on each server tick. + fn tick(&mut self) {} + + /// Called when the goal completes, fails, or is preempted by a higher priority goal. + fn stop(&mut self) {} +} +``` + +--- + +## Standard Built-in Goals + +Pumpkin provides standard vanilla AI behaviors in `crates/pumpkin/src/entity/ai/`: + +| Goal | Description | Target Mobs | +| :--- | :--- | :--- | +| **`SwimGoal`** | Jumps repeatedly while submerged in water to prevent drowning. | Almost all land mobs | +| **`MeleeAttackGoal`** | Navigates towards an attack target and executes a strike when within reach. | Zombies, Spiders, Iron Golems | +| **`RangedAttackGoal`** | Stays at range and shoots projectiles (arrows, snowballs). | Skeletons, Pillagers, Blazes | +| **`FleeSunGoal`** | Seeks shade or water during daytime to prevent combustion. | Zombies, Skeletons | +| **`PanicGoal`** | Sprints in random directions when taking damage. | Cows, Pigs, Sheep, Chickens | +| **`WanderAroundGoal`** | Selects random accessible coordinates nearby when idle. | Passive & neutral mobs | +| **`LookAtEntityGoal`** | Rotates head to face nearby players within sensory range. | Villagers, Mobs | + +--- + +## Navigation & Pathfinding + +Mobs navigate the block world using Pumpkin's 3D grid pathfinder: + +1. **Path Calculation**: Resolves a path from the mob's origin to target coordinates using an A* algorithm optimized for Minecraft block geometries. +2. **Obstacle Avoidance**: + - Avoids hazardous blocks (lava, fire, sweet berry bushes). + - Prevents walking off ledges higher than the mob's fall threshold. + - Computes jumping steps over 1-block obstacles. +3. **Waypoint Steering**: The navigation engine updates the mob's velocity vectors per tick to smoothly guide the entity along waypoints. + +--- + +## Entity Attributes System + +Mob capabilities and stats are governed by the **Attributes** system in [`crates/pumpkin/src/entity/attributes.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/entity/attributes.rs): + +```rust +use pumpkin_data::entity::attribute::EntityAttribute; + +// Common vanilla attributes +EntityAttribute::GENERIC_MAX_HEALTH; // Base hit points (e.g. 20.0 for Zombies) +EntityAttribute::GENERIC_MOVEMENT_SPEED; // Base walking velocity (e.g. 0.23) +EntityAttribute::GENERIC_ATTACK_DAMAGE; // Melee attack damage (e.g. 3.0) +EntityAttribute::GENERIC_FOLLOW_RANGE; // Sensory detection range in blocks (e.g. 35.0) +EntityAttribute::GENERIC_KNOCKBACK_RESISTANCE; // Resistance against knockback (0.0 to 1.0) +``` + +### Modifiers +Attributes support dynamic modifiers with additive or multiplicative calculations (e.g., speed bonuses from Swiftness potions or armor attribute boosts). + +--- + +## Ageable & Passive Entities + +Passive animals (cows, sheep, pigs) implement the `Ageable` trait: + +- **Baby State**: Renders with smaller collision boxes and oversized heads. +- **Growing Age**: A negative tick timer counting upward to `0`. When it reaches `0`, the entity matures into an adult. +- **Breeding Cooldown**: Positive tick timer tracking cooldowns between successful matings. diff --git a/docs/zh_cn/developer/entities/index.md b/docs/zh_cn/developer/entities/index.md new file mode 100644 index 0000000..53623ee --- /dev/null +++ b/docs/zh_cn/developer/entities/index.md @@ -0,0 +1,77 @@ +# Entity Architecture & Hierarchy + +Entities in Minecraft represent all dynamic, living and non-living objects that exist within a world—ranging from dropped item stacks and arrows to zombies, villagers, and players. + +In Pumpkin, entity systems are implemented in [`crates/pumpkin/src/entity/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/entity). The architecture leverages object-oriented trait composition on top of concurrency-safe, cache-friendly data structures. + +--- + +## Entity Trait Hierarchy + +Pumpkin structures entities into an inheritance hierarchy using Rust traits: + +| Trait / Type | Parent Trait | Core Capabilities & State | +| :--- | :--- | :--- | +| `Entity` | *(None / Base Struct)* | Entity ID, UUID, atomic position and velocity vectors, bounding box, pose | +| `EntityBase` | *(Root Trait)* | Dynamic runtime downcasting (`get_living_entity`, etc.), world tick, damage, teleportation | +| `LivingEntity` | `EntityBase` | Health, absorption, potion effects, equipment slots, attribute containers | +| `Mob` | `LivingEntity` | AI goal selector, navigation, pathfinding, target tracking, death loot | +| `Player` | `LivingEntity` | Network client session, inventory, crafting screens, hunger, advancements | + +### 1. `Entity` (Base Struct) +Contains fundamental physics, geometry, and spatial state common to all entities: +- **`entity_id`**: Fast 32-bit unique integer identifier used across network packets. +- **`uuid`**: 128-bit persistent UUID. +- **`position` & `velocity`**: Thread-safe vectors (`AtomicCell>`) allowing concurrent reading without lock contention. +- **`bounding_box`**: 3D axis-aligned bounding box (AABB) used for collision resolution and raycast targeting. +- **`pose`**: Current pose (`Standing`, `Crouching`, `Sleeping`, `FallFlying`, etc.). + +### 2. `EntityBase` (Foundation Trait) +The root dynamic trait implemented by every entity in the server: +- Supports safe runtime downcasting via helper methods (`get_living_entity()`, `get_mob()`, `get_player()`). +- Provides hooks for world interaction: `tick()`, `damage()`, `teleport()`, `on_lightning_strike()`, `is_immune_to_explosion()`. + +### 3. `LivingEntity` (Biological Trait) +Extends `EntityBase` for entities with health, combat, and animation states: +- Manages health bars, absorption, invulnerability ticks, and fall damage. +- Tracks active potion effects (`crates/pumpkin/src/entity/effect/`). +- Handles equipment slots (`MainHand`, `OffHand`, `Helmet`, `Chestplate`, `Leggings`, `Boots`). +- Encapsulates attribute containers (max health, movement speed, knockback resistance). + +### 4. `Mob` (AI Trait) +Extends `LivingEntity` for computer-controlled mobs: +- Manages AI goal selectors (attacking, wandering, fleeing, looking around). +- Controls navigation and pathfinding over blocks. +- Spawns configured loot tables upon death. + +### 5. `Player` (Human Client) +Represents a human player connected through a game client: +- Links to network socket handlers (`JavaClient` or `BedrockClient`). +- Manages container screen handlers, ender chests, player inventory, and crafting. +- Tracks hunger, saturation, exhaustion, experience levels, and advancement criteria. + +--- + +## Physics & Movement Simulation + +On every server tick, Pumpkin simulates entity physics through a deterministic sequence: + +1. **Apply Velocity & Gravity**: Evaluates current velocity and adds downward gravitational acceleration unless `has_gravity()` is false or the entity is flying. +2. **Resolve Block Collisions**: Checks the entity's 3D axis-aligned bounding box (`bounding_box`) against solid block voxels in the surrounding chunk; velocity along colliding axes is dampened or zeroed. +3. **Apply Fluid Drag & Buoyancy**: Evaluates whether the entity intersects water or lava fluids, applying drag coefficients and buoyant upward force. +4. **Synchronize & Broadcast**: Persists the updated coordinates in atomic position cells and broadcasts relative delta movement packets to observing players. +5. **Void Death**: Non-player entities falling below $Y = -128$ are automatically despawned. + +--- + +## Combat & Damage System + +Damage calculations reside in [`crates/pumpkin/src/entity/combat.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/entity/combat.rs): + +1. **Damage Types**: Damage is categorized by `DamageType` (e.g., `GENERIC`, `PLAYER_ATTACK`, `FALL`, `FIRE`, `MAGIC`, `EXPLOSION`, `LIGHTNING_BOLT`). +2. **Damage Scaling**: + - Armor defense points mitigate incoming physical damage according to the vanilla formula: + $$\text{damage} = \text{damage} \times \left(1 - \frac{\min(20, \max(\text{armor} / 5, \text{armor} - \frac{\text{damage}}{2 + \text{toughness} / 4}))}{25}\right)$$ + - Resistance status effects and Protection enchantments apply secondary reductions. +3. **Knockback**: When damage is dealt, a directional impulse vector is added to the victim's velocity, modulated by their knockback resistance attribute. +4. **Hurt Animations**: Triggers hurt sounds and broadcasts red-tint flash metadata to all observers. diff --git a/docs/zh_cn/developer/entities/spawning-and-tracking.md b/docs/zh_cn/developer/entities/spawning-and-tracking.md new file mode 100644 index 0000000..82a3d7b --- /dev/null +++ b/docs/zh_cn/developer/entities/spawning-and-tracking.md @@ -0,0 +1,95 @@ +# Spawning & Network Tracking + +To maintain high tick rates with hundreds of active entities, Pumpkin utilizes a chunk-based entity tracking and streaming system. + +Entities are held in chunk-level collections inside [`crates/pumpkin-world`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-world) and synchronized across clients via edition-specific network packets. + +--- + +## The Spawning Pipeline + +Spawning an entity involves creating an instance, allocating its network IDs, registering it with the world, and broadcasting spawn packets to chunk observers: + +1. **Construct Entity Instance**: Instantiate the specific entity struct (such as `ZombieEntity`, `ArrowEntity`, or `ItemEntity`) initialized with its starting world and spatial coordinates. +2. **Allocate Unique Network Identifiers**: Assign an atomic 32-bit `entity_id` for network protocol packets and a 128-bit persistent UUID. +3. **Register in World & Chunk Map**: Insert the entity into the world's entity storage and index it within its corresponding chunk coordinate entry. +4. **Query Chunk Observers**: Look up all connected players within tracking range whose view distance includes the containing chunk. +5. **Broadcast Edition-Specific Spawn Packets**: Transmit the relevant spawn and metadata packets tailored to each observing client's edition (Java or Bedrock). + +### Spawning via the World API + +```rust +use std::sync::Arc; +use pumpkin_data::entity::EntityType; +use pumpkin_util::math::vector3::Vector3; +use crate::entity::Entity; +use crate::entity::mob::zombie::ZombieEntity; + +// 1. Create base entity with target world and position +let position = Vector3::new(100.5, 64.0, -200.5); +let base_entity = Entity::new(world.clone(), position, &EntityType::ZOMBIE); + +// 2. Wrap in specific entity implementation +let zombie = Arc::new(ZombieEntity::new(base_entity)); + +// 3. Register into the world +world.spawn_entity(zombie); +``` + +--- + +## Network Packet Synchronization + +When a player enters a chunk containing entities—or when an entity moves into an observed chunk—Pumpkin dispatches spawn packets tailored to the client's edition: + +### Java Edition Spawning +1. **`CSpawnEntity`**: Declares the entity ID, UUID, entity type, position, and initial pitch/yaw. +2. **`CSetEntityMetadata`**: Sends initial visual states (custom name tags, burning flags, baby/adult state). +3. **`CEntityVelocity`**: Synchronizes initial momentum. +4. **`CUpdateEntityRot` & `CHeadRot`**: Synchronizes head orientation independently from body yaw. + +### Bedrock Edition Spawning +1. **`CAddActor`**: Declares the runtime ID, actor type identifier, position, rotation, and embedded `SyncedActorDataList`. +2. **`CSetActorMotion`**: Sets initial velocity vector. + +--- + +## Movement & Position Synchronization + +Transmitting absolute 64-bit coordinates every tick would congest network bandwidth. Instead, Pumpkin dynamically switches between compact relative delta updates and authoritative full synchronization based on movement distance: + +| Metric / Scenario | Threshold | Java Protocol Packet | Bedrock Protocol Packet | Bandwidth Impact | +| :--- | :--- | :--- | :--- | :--- | +| **Incremental Movement** | $\Delta < 8$ blocks per tick | `CUpdateEntityPos` / `CUpdateEntityPosRot` | `CMoveActorDelta` | Minimal (fixed-point byte deltas) | +| **Teleport / Long Distance** | $\Delta \ge 8$ blocks or chunk warp | `CTeleportEntity` / `CEntityPositionSync` | `CMoveActorAbsolute` | Full 64-bit float coordinate payload | +| **Rotation Only** | Position unchanged | `CUpdateEntityRot` & `CHeadRot` | `CMoveActorDelta` (rot flags only) | Minimal (byte angles) | + +### 1. Relative Delta Movement +If the entity moved less than 8 blocks in a single tick, Pumpkin sends delta packets using fixed-point encoded increments: +- **Java**: `CUpdateEntityPos` (XYZ delta), `CUpdateEntityRot` (yaw/pitch delta), or `CUpdateEntityPosRot` (both). +- **Bedrock**: `CMoveActorDelta` with bitflags indicating which coordinates changed (`MOVE_ACTOR_DELTA_FLAG_HAS_X`, `HAS_Y`, `HAS_Z`). + +### 2. Full Position Synchronization +When an entity teleports, crosses chunk boundaries, or accumulates floating-point drift: +- **Java**: Sends `CEntityPositionSync` containing absolute coordinates, velocities, and on-ground flags. +- **Bedrock**: Sends `CMoveActorAbsolute`. + +--- + +## Passenger & Vehicle Mounting + +When an entity mounts another entity (e.g., a player mounting a horse, boat, or minecart): + +- **Java Edition**: Sends `CSetPassengers` containing the vehicle's `VarInt` entity ID and an array of passenger entity IDs. +- **Bedrock Edition**: Sends `CSetActorLink` defining the rider and vehicle runtime IDs and link type (`Rider`, `Passenger`). + +--- + +## Despawning & Entity Removal + +When an entity dies, is removed from the world, or steps out of a player's tracking radius: + +- **Java Edition**: Sends `CRemoveEntities` with an array of entity IDs. +- **Bedrock Edition**: Sends `CRemoveActor` with the entity's runtime ID. + +The client immediately frees its local rendering and audio memory for the removed entity. diff --git a/docs/zh_cn/developer/extractor.md b/docs/zh_cn/developer/extractor.md new file mode 100644 index 0000000..2b09f8f --- /dev/null +++ b/docs/zh_cn/developer/extractor.md @@ -0,0 +1,144 @@ +# Vanilla Data Extractor + +The [Pumpkin Extractor](https://github.com/Pumpkin-MC/Extractor) is a dedicated Fabric mod written in Kotlin that introspects a running vanilla Minecraft server or client to dump authoritative game registries and runtime metadata into structured JSON files. + +These JSON dumps are consumed by [`pumpkin-codegen`](/developer/codegen) to generate strongly-typed Rust code in `pumpkin-data` and `pumpkin-world`. + +--- + +## Datapacks vs. Extractor: Our Data Philosophy + +Before creating a new extractor, it is crucial to understand Pumpkin's data hierarchy: + +> [!IMPORTANT] +> **We preferably use vanilla datapacks whenever possible.** +> If vanilla Minecraft already exposes a registry or system through official datapack JSON files, we consume the datapack directly rather than writing custom extractor code. + +### When to Use Datapacks vs. When to Use the Extractor + +| Source | When to Use | Examples | +| :--- | :--- | :--- | +| **Vanilla Datapacks** *(Preferred)* | Any data officially serialized as JSON by Mojang in `data/minecraft/`. | Recipes, tags (`blocks`, `items`, `fluids`), biomes, dimension types, damage types, trim materials, worldgen noise parameters. | +| **Fabric Extractor** *(Only when necessary)* | Game data **hardcoded in Java bytecode** that cannot be exported from raw datapack files. | Block collision shapes, AABB bounding boxes, block hardness, packet ID-to-state mappings, entity tracked data IDs, fuel burn durations, composter chances, and flower pot conversion tables. | + +--- + +## Extractor Mod Architecture + +The Extractor runs on top of the Fabric loader using official Fabric Yarn mappings: + +- **Mod Entry Point (`src/main/kotlin/de/snowii/extractor/Extractor.kt`)**: Registers with Fabric's `ServerLifecycleEvents.SERVER_STARTED` event. When the headless server finishes booting, it invokes all registered extractors and dumps files to `run/pumpkin_extractor_output/`. +- **Extractor Interface**: Every data extractor implements the `Extractor.Extractor` interface: + ```kotlin + interface Extractor { + fun fileName(): String + + @Throws(Exception::class) + fun extract(server: MinecraftServer): JsonElement + + // Returns true if output belongs in tests/ rather than the root output folder + fun isTest(): Boolean = false + } + ``` +- **Output Directory**: JSON files are saved to `pumpkin_extractor_output/` encoded in UTF-8 without HTML escaping. + +--- + +## How to Add a New Extractor + +Follow this step-by-step workflow when you need to extract new hardcoded vanilla mechanics: + +### Step 1: Verify the Data Cannot Be Sourced from Datapacks +Inspect the official Minecraft client/server JAR or Mojang's data reports: +- Check `data/minecraft/` for existing registries, tags, or JSON definitions. +- If the data is present in the datapack, add it directly to `Pumpkin/assets/datapacks/` instead of writing an extractor. + +### Step 2: Create the Extractor Class +If the data is hardcoded in Java methods, create a new Kotlin class in: +```text +src/main/kotlin/de/snowii/extractor/extractors/.kt +``` + +Implement the `Extractor.Extractor` interface: + +```kotlin +package de.snowii.extractor.extractors + +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import de.snowii.extractor.Extractor +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.server.MinecraftServer +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack + +class Fuels : Extractor.Extractor { + override fun fileName(): String { + return "fuels.json" + } + + override fun extract(server: MinecraftServer): JsonElement { + val fuelsJson = JsonObject() + + // Query vanilla runtime methods + server.fuelValues().fuelItems().forEach { fuel -> + val itemId = BuiltInRegistries.ITEM.getKey(fuel).toString() + val burnDuration = server.fuelValues().burnDuration(ItemStack(fuel)) + fuelsJson.add(itemId, JsonPrimitive(burnDuration)) + } + + return fuelsJson + } +} +``` + +### Step 3: Register the Extractor +Open `src/main/kotlin/de/snowii/extractor/Extractor.kt` and add an instance of your class to the `extractors` array: + +```kotlin +val extractors = arrayOf( + // ... existing extractors ... + Blocks(), + Items(), + Entities(), + Fuels(), // [!code ++] +) +``` + +### Step 4: Run the Extractor +Execute the headless server task in the Extractor repository: + +```bash +./gradlew runServer +``` + +Once the extraction finishes, locate your new JSON file in: +```text +run/pumpkin_extractor_output/.json +``` + +### Step 5: Integrate with `pumpkin-codegen` +1. Move the generated JSON file into Pumpkin: + ```bash + cp run/pumpkin_extractor_output/fuels.json ../Pumpkin/assets/ + ``` +2. In the `Pumpkin` repository, open `tools/pumpkin-codegen`: + - Create a parser for `.json`. + - Write a code generation template to produce the corresponding Rust structs or static lookup tables in `crates/pumpkin-data`. +3. Run code generation: + ```bash + cargo run -p pumpkin-codegen + ``` +4. Verify that `pumpkin-data` compiles and passes all unit tests: + ```bash + cargo test -p pumpkin-data + ``` + +--- + +## Best Practices for Extractor Contributions + +- **Sort Keys Deterministically**: When generating JSON objects or lists, sort entries by numeric registry ID or alphabetically by identifier (`minecraft:stone`, `minecraft:dirt`). This prevents noisy git diffs when re-running extraction across updates. +- **Prefer Standard Types**: Use vanilla identifiers (`net.minecraft.resources.Identifier`) as string keys instead of arbitrary integers where possible. +- **Access Wideners**: If private vanilla fields or methods need to be accessed, declare an access widener in `src/main/resources/extractor.accesswidener` rather than resorting to brittle runtime reflection. diff --git a/docs/zh_cn/developer/items/adding-items.md b/docs/zh_cn/developer/items/adding-items.md new file mode 100644 index 0000000..6335d14 --- /dev/null +++ b/docs/zh_cn/developer/items/adding-items.md @@ -0,0 +1,121 @@ +# Adding an Item Behavior + +This guide details how to implement custom interactive mechanics for an item in Pumpkin. + +--- + +## When Is an Item Behavior Needed? + +- **Passive Items** (e.g., diamonds, gold ingots, sticks): Do **not** require custom Rust code. Their stack sizes, tool categories, and crafting recipes are handled automatically by `pumpkin-data` and `pumpkin-inventory`. +- **Interactive Items** (e.g., snowballs, ender pearls, hoes, bows, potions): Require an implementation of `ItemBehaviour` to handle right-click events, cooldowns, animations, and entity spawns. + +--- + +## Step 1: Define the Item Struct & Metadata + +Create a new file under `crates/pumpkin/src/item/items/.rs` (e.g., `items/snowball.rs`): + +```rust +use std::sync::Arc; +use pumpkin_data::entity::EntityType; +use pumpkin_data::item::Item; +use pumpkin_data::sound::{Sound, SoundCategory}; +use crate::entity::Entity; +use crate::entity::player::Player; +use crate::entity::projectile::snowball::SnowballEntity; +use crate::item::{ItemBehaviour, ItemMetadata}; + +pub struct SnowBallItem; + +// 1. Declare the associated numeric Item ID(s) +impl ItemMetadata for SnowBallItem { + fn ids() -> Box<[u16]> { + [Item::SNOWBALL.id].into() + } +} + +// 2. Implement the interaction behavior +impl ItemBehaviour for SnowBallItem { + fn normal_use(&self, _item: &Item, player: &Player) { + let position = player.position(); + let world = player.world(); + + // Play throw sound effect + world.play_sound( + Sound::EntitySnowballThrow, + SoundCategory::Neutral, + &position, + ); + + // Construct and launch the projectile entity + let entity = Entity::new(world.clone(), position, &EntityType::SNOWBALL); + let snowball = SnowballEntity::new_shot(entity, player.get_entity()); + let (yaw, pitch) = player.rotation(); + snowball.thrown.set_velocity_from(pitch, yaw, 0.0, 1.5, 1.0); + world.spawn_entity(Arc::new(snowball)); + + // Safely decrement held item unless in Creative mode + let mut held = player.inventory.held_item(); + held.decrement_unless_creative(player.gamemode.load(), 1); + player.inventory.set_held_item(held); + } +} +``` + +--- + +## Step 2: `ItemBehaviour` Hook Reference + +The `ItemBehaviour` trait provides comprehensive hooks for different interaction models: + +### Instant Single-Use Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `normal_use` | `(&self, item: &Item, player: &Player)` | Triggered on right-clicking into open air. | +| `normal_use_with_rotation` | `(&self, item: &Item, player: &Player, yaw: f32, pitch: f32)` | Provides client-reported view angles for precise raycasting and aiming. | +| `use_on_block` | `(&self, item: &mut ItemStack, player: &Player, location: BlockPos, face: BlockDirection, cursor_pos: Vector3, block: &Block, server: &Server) -> BlockActionResult` | Triggered when right-clicking a block face (e.g., stripping logs with an axe, tilling farmland with a hoe). | +| `use_on_entity` | `(&self, item: &mut ItemStack, player: &Player, entity: Arc)` | Triggered when right-clicking another living entity (e.g., shearing sheep, nametags). | + +### Channeled & Timed Hooks + +| Hook | Signature | Description | +| :--- | :--- | :--- | +| `get_use_duration` | `(&self) -> i32` | Duration in game ticks required to complete the action (e.g., `32` ticks for food, `72000` ticks for bows). | +| `on_use_tick` | `(&self, stack: &ItemStack, player: &Player, remaining_use_ticks: i32)` | Executed on each tick while right-click is held. | +| `on_stopped_using` | `(&self, stack: &ItemStack, player: &Player)` | Triggered when the player releases right-click (e.g., firing a charged bow). | + +--- + +## Step 3: Durability & Cooldowns + +### Decrementing Stacks Safely +Always use `decrement_unless_creative` to ensure survival players consume items while creative players retain them: + +```rust +item_stack.decrement_unless_creative(player.gamemode.load(), 1); +``` + +### Applying Cooldowns +To prevent action spamming (e.g., Ender Pearls, Chorus Fruit): + +```rust +// Start a 20-tick (1-second) cooldown on Ender Pearls +player.start_cooldown(Item::ENDER_PEARL.registry_key.to_string(), 20); +``` + +--- + +## Step 4: Registering the Item + +1. Export your module in `crates/pumpkin/src/item/items/mod.rs`: + ```rust + pub mod snowball; + ``` + +2. Register the struct in `crates/pumpkin/src/item/registry.rs`: + ```rust + items.register(SnowBallItem); + ``` + +When the server initializes, `items.register` maps the IDs declared in `ItemMetadata::ids()` to your behavior instance. diff --git a/docs/zh_cn/developer/items/index.md b/docs/zh_cn/developer/items/index.md new file mode 100644 index 0000000..70f9f9b --- /dev/null +++ b/docs/zh_cn/developer/items/index.md @@ -0,0 +1,71 @@ +# Item Architecture & Data Components + +Items represent portable game objects held in player inventories, hotbars, armor slots, and world containers. In Pumpkin, item handling is divided into two primary subsystems: + +1. **Static Data Layer ([`pumpkin-data`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-data))**: Compile-time generated metadata including item IDs, max stack sizes, max durability, tool tier properties, food nutrition, and data components generated by [`pumpkin-codegen`](/developer/codegen). +2. **Dynamic Behavior Layer ([`pumpkin::item`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/item))**: Interactive right-click actions, projectile launching, eating durations, bow charging, and block interactions handled via the `ItemBehaviour` trait. + +--- + +## Static Items vs. Actionable Items + +| Item Type | Description | Examples | Runtime Behavior Struct Needed? | +| :--- | :--- | :--- | :--- | +| **Passive Items** | Items without active right-click effects | `diamond`, `stick`, `iron_ingot`, `feather` | No — Stacking, moving, crafting, and dropping are handled automatically by `pumpkin-inventory`. | +| **Standard Block Items** | Items corresponding directly to placeable blocks | `stone`, `oak_planks`, `glass` | No — Placement logic queries the target block's data. | +| **Projectiles & Throwables** | Items spawned into physical entities when thrown | `snowball`, `egg`, `ender_pearl`, `splash_potion` | Yes — Implements `ItemBehaviour::normal_use`. | +| **Channeled / Charged Items** | Items with use durations or charging animations | `bow`, `crossbow`, `spyglass`, `trident` | Yes — Implements `get_use_duration`, `on_use_tick`, `on_stopped_using`. | +| **Tools with Block Interactions**| Items modifying existing blocks in the world | `hoe` (tilling), `axe` (stripping), `shovel` (pathmaking) | Yes — Implements `ItemBehaviour::use_on_block`. | + +--- + +## The `ItemStack` Structure + +An `ItemStack` represents an instance of an item in an inventory container. It encapsulates: + +- **`item`**: Reference to the static [`Item`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-data/src/item.rs) definition. +- **`item_count`**: Current stack size ($1 \le \text{count} \le \text{max\_stack\_size}$). +- **Data Components**: Dynamic properties attached to the item stack (custom names, lore, damage values, enchantments, potion effects). + +```rust +use pumpkin_data::item::Item; +use pumpkin_data::item_stack::ItemStack; + +// Create a new item stack +let mut stack = ItemStack::new(1, Item::DIAMOND_SWORD); + +// Safely decrement stack count, respecting creative mode +stack.decrement_unless_creative(player.gamemode.load(), 1); +``` + +--- + +## Data Components System + +Modern Minecraft represents item properties via modular **Data Components**: + +| Component | Responsibility | Description | +| :--- | :--- | :--- | +| `FoodImpl` | Nutrition & Saturation | Controls hunger points restored and saturation ratio. | +| `ConsumableImpl` | Consumption Mechanics | Controls eating duration, sound effects, and animation types. | +| `Damageable` | Durability | Tracks current damage points and breaks when max durability is reached. | +| `UseCooldown` | Cooldown Group | Prevents item re-use for a specified duration in game ticks. | +| `Enchantments` | Enchantment Levels | Key-value mapping of applied enchantments and levels. | + +--- + +## The Item Registry & Dispatch Architecture + +Item interactions are managed by the `ItemRegistry` in [`crates/pumpkin/src/item/registry.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/item/registry.rs): + +1. **Registration**: At server startup, all `ItemBehaviour` implementations are registered along with their target numeric item IDs. +2. **Interaction Routing**: When a client sends use packets (`CUseItem`, `CUseItemOn`), Pumpkin evaluates cooldowns and routes execution to the registered `ItemBehaviour`. +3. **Cooldown System**: Players track active cooldowns keyed by cooldown group strings. If an item is on cooldown, interactions are canceled before calling the behavior. + +--- + +## Next Steps + +To implement interactive mechanics for a new item, proceed to the practical guide: + +- **[Adding an Item](/developer/items/adding-items)**: Step-by-step instructions for implementing `ItemMetadata`, `ItemBehaviour`, managing cooldowns, and registering items. diff --git a/docs/zh_cn/developer/networking/bedrock/adding-packets.md b/docs/zh_cn/developer/networking/bedrock/adding-packets.md new file mode 100644 index 0000000..f68b888 --- /dev/null +++ b/docs/zh_cn/developer/networking/bedrock/adding-packets.md @@ -0,0 +1,182 @@ +# Adding a Bedrock Edition Packet + +This guide explains how to implement, encode, decode, and handle a new Minecraft: Bedrock Edition packet in Pumpkin. + +--- + +## Naming Conventions + +- **Clientbound packets** (Server → Client) must start with **`C`** (e.g., `CChunkRadiusUpdated`, `CContainerOpen`). +- **Serverbound packets** (Client → Server) must start with **`S`** (e.g., `SRequestChunkRadius`, `SPlayerAction`). + +--- + +## Step 1: Identifying the Packet ID + +In the Bedrock protocol, packet IDs are fixed integer constants assigned by Mojang. Verify the numeric ID from the official protocol documentation or network packet traces. + +For example: +- `Disconnect` packet ID: `5` +- `RequestChunkRadius` packet ID: `69` + +--- + +## Step 2: Implementing a Clientbound Packet + +Clientbound packets implement the `PacketWrite` trait from `pumpkin-protocol::serial`. + +### 1. Create the Packet File +Place the file in `crates/pumpkin-protocol/src/bedrock/client/.rs`: + +```rust +use std::io::{Error, Write}; +use pumpkin_macros::packet; +use crate::{codec::var_int::VarInt, serial::PacketWrite}; + +#[packet(5)] +pub struct CDisconnect { + pub reason: VarInt, + pub skip_message: bool, + pub message: String, + pub filtered_message: String, +} + +impl CDisconnect { + #[must_use] + pub const fn new(reason: i32, message: String) -> Self { + Self { + reason: VarInt(reason), + skip_message: message.is_empty(), + message, + filtered_message: String::new(), + } + } +} + +impl PacketWrite for CDisconnect { + fn write(&self, writer: &mut W) -> Result<(), Error> { + self.reason.write(writer)?; + self.skip_message.write(writer)?; + if !self.skip_message { + self.message.write(writer)?; + self.filtered_message.write(writer)?; + } + Ok(()) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/bedrock/client/mod.rs`: + +```rust +pub mod disconnect; +pub use disconnect::*; +``` + +--- + +## Step 3: Implementing a Serverbound Packet + +Serverbound packets implement the `PacketRead` trait. Pumpkin provides a procedural derive macro `#[derive(PacketRead)]` for automatic deserialization when fields implement `PacketRead`. + +### 1. Create the Packet File +Place the file in `crates/pumpkin-protocol/src/bedrock/server/.rs`: + +```rust +use pumpkin_macros::packet; +use crate::{codec::var_int::VarInt, serial::PacketRead}; + +#[derive(PacketRead, Debug)] +#[packet(69)] +pub struct SRequestChunkRadius { + pub chunk_radius: VarInt, + pub max_chunk_radius: u8, +} +``` + +If a packet requires custom parsing logic, implement `PacketRead` manually: + +```rust +use std::io::{Error, Read}; +use crate::serial::PacketRead; + +impl PacketRead for SMyCustomPacket { + fn read(reader: &mut R) -> Result { + let chunk_radius = VarInt::read(reader)?; + let max_chunk_radius = u8::read(reader)?; + Ok(Self { chunk_radius, max_chunk_radius }) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/bedrock/server/mod.rs`: + +```rust +pub mod request_chunk_radius; +pub use request_chunk_radius::*; +``` + +--- + +## Step 4: Dispatching Incoming Packets in `pumpkin` + +Incoming packets are dispatched in `crates/pumpkin/src/net/bedrock/mod.rs`: + +```rust +// Inside BedrockClient::handle_game_packet +match packet.id { + SRequestChunkRadius::PACKET_ID => { + let packet = SRequestChunkRadius::read(reader)?; + self.handle_request_chunk_radius(player, &packet); + } + _ => {} +} +``` + +--- + +## Step 5: Implementing the Handler Function + +Implement the handler logic in `crates/pumpkin/src/net/bedrock/play/.rs`: + +```rust +use super::*; +use pumpkin_protocol::bedrock::server::request_chunk_radius::SRequestChunkRadius; + +impl BedrockClient { + pub fn handle_request_chunk_radius(&self, player: &Arc, packet: &SRequestChunkRadius) { + let requested_radius = packet.chunk_radius.0; + + // Clamp view distance within server configuration limits + let clamped = requested_radius.clamp(2, 16); + + // Notify client that view radius was accepted + self.try_enqueue_client_packet(&CChunkRadiusUpdated { + chunk_radius: VarInt(clamped), + }); + } +} +``` + +Remember to register the new module in `crates/pumpkin/src/net/bedrock/play/mod.rs`: + +```rust +pub mod request_chunk_radius; +``` + +--- + +## Step 6: Sending Outgoing Packets + +To send a Bedrock packet to a connected client: + +```rust +// Enqueues the packet into the client's outgoing batch queue +self.try_enqueue_client_packet(&CChunkRadiusUpdated { + chunk_radius: VarInt(view_distance), +}); +``` + +Batched packets are automatically compressed, framed with `0xfe`, and transmitted over the RakNet/NetherNet socket. diff --git a/docs/zh_cn/developer/networking/bedrock/authentication.md b/docs/zh_cn/developer/networking/bedrock/authentication.md new file mode 100644 index 0000000..1571a73 --- /dev/null +++ b/docs/zh_cn/developer/networking/bedrock/authentication.md @@ -0,0 +1,61 @@ +# Bedrock Edition Authentication + +Minecraft: Bedrock Edition implements a certificate chain authentication protocol based on JSON Web Tokens (JWT) cryptographically anchored to Microsoft / Xbox Live root public keys. + +Authentication handling is powered by the [`pumpkin-auth`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-auth) crate and processed during the Bedrock login handshake in `crates/pumpkin/src/net/bedrock/login/`. + +--- + +## The Handshake Flow + +When a Bedrock client initiates connection, it sends an `SLogin` packet containing the client's protocol version, an **Identity Chain**, and **Client Data**: + +1. **Token Acquisition**: The Bedrock client signs into Xbox Live, obtaining a chained JSON Web Token (JWT) certificate signed by Microsoft's CA. +2. **Login Transmission**: The client packages this identity chain alongside a separate `ClientData` JWT into `SLogin`. +3. **Chain Validation**: Pumpkin walks the certificate chain, verifying each token's signature against Mojang's public root keys and extracting the player's Xbox User ID (XUID), Gamertag, and ephemeral session public key. +4. **Hardware Verification**: Pumpkin verifies the `ClientData` payload using the client's ephemeral public key, extracting geometry, skins, and device OS telemetry. +5. **Session Authorization**: Once validated, Pumpkin emits `CPlayStatus` (Login Success) and progresses to resource pack negotiation. + +--- + +## Token Payloads + +### 1. Identity Chain (`Chain`) +An array of JWT tokens forming an unbroken chain of trust: +- **Root Token**: Cryptographically signed by Mojang's official public root certificate authority. +- **Intermediate Tokens**: Issued by Xbox Live services, verifying the player's Xbox Live User ID (**XUID**), gamertag, and generating an ephemeral ECDSA public key for the session. +- **Claims**: Includes `extraData` with `displayName`, `identity` (UUID), `XUID`, and `titleId`. + +### 2. Client Data (`ClientData`) +A separate JWT signed with the client's ephemeral private key corresponding to the public key in the identity chain: +- **Hardware & Environment**: `DeviceOS`, `DeviceModel`, `CurrentInputMode`, `DefaultInputMode`, `ClientRandomId`. +- **Game Version**: `GameVersion` string. +- **Player Cosmetics**: Full skin texture bitmaps, cape data, geometry names, and persona definitions. + +--- + +## Verification in `pumpkin-auth` + +Pumpkin verifies token signatures using **NIST P-384 ECDSA** cryptography: + +```rust +// In crates/pumpkin/src/net/bedrock/login/mod.rs +match AuthPayload::verify(&jwt_payload) { + Ok(profile) => { + // Successfully verified against Mojang public keys + info!("Authenticated Bedrock player: {} (XUID: {})", profile.name, profile.xuid); + } + Err(LoginError::ChainValidationFailed(err)) => { + warn!("Bedrock JWT chain validation failed: {err}"); + self.try_kick(DisconnectReason::Kicked, "Authentication failed".into()); + } + Err(LoginError::SelfSignedNotAllowed) => { + warn!("Rejected unauthenticated self-signed Bedrock client"); + self.try_kick(DisconnectReason::Kicked, "Server enforces Xbox Live authentication".into()); + } +} +``` + +### Online vs. Offline Mode +- **Online Mode** (`online_mode = true` in `pumpkin.toml`): The chain must successfully validate against Mojang's public keys. Self-signed or unsigned tokens are immediately rejected. +- **Offline Mode** (`online_mode = false`): Pumpkin accepts self-signed client chains, generating offline UUIDs while extracting cosmetic skins and device metadata. diff --git a/docs/zh_cn/developer/networking/bedrock/nethernet.md b/docs/zh_cn/developer/networking/bedrock/nethernet.md new file mode 100644 index 0000000..88c92f0 --- /dev/null +++ b/docs/zh_cn/developer/networking/bedrock/nethernet.md @@ -0,0 +1,61 @@ +# NetherNet WebRTC Transport + +**NetherNet** is Mojang's modern, WebRTC-based transport protocol for Minecraft: Bedrock Edition. Designed for high-resilience peer-to-peer and relayed communication, NetherNet enables seamless cross-platform multiplayer (mobile, console, desktop) without requiring server operators or players to configure router port-forwarding. + +Pumpkin provides a native, zero-dependency NetherNet implementation in [`crates/pumpkin/src/net/bedrock/nethernet.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/bedrock/nethernet.rs). + +--- + +## Architectural Layers + +| Layer | Implementation | Responsibility | +| :--- | :--- | :--- | +| **HTTP Signaling** | Axum HTTP Router (`ice_router.rs`) | Handles SDP Offer (`POST /offer`), SDP Answer (`GET /answer`), and ICE Candidates (`POST /candidate`) | +| **NAT Traversal** | ICE Agent | Coordinates STUN/TURN binding discovery and hole punching | +| **Transport Security** | DTLS Handshake | Generates ephemeral session keys and secures the WebRTC transport using ECDSA P-384 keys | +| **Data Delivery** | WebRTC DataChannels | Unordered/ordered datagram channels streaming standard Bedrock game batches to the dispatcher | + +--- + +## Core Components in Pumpkin + +Pumpkin's NetherNet subsystem is divided into four distinct modules: + +### 1. Cryptographic Identity (`nethernet-key.der`) +- Pumpkin generates and loads a **NIST P-384 ECDSA private key** (`nethernet-key.der`). +- Used to cryptographically sign session descriptions (SDP offers and answers), authenticating the server's identity to connecting clients. +- Encoded using standard PKCS#8 DER formatting. + +### 2. HTTP Signaling Router (`ice_router.rs`) +Signaling is handled over HTTP via an [Axum](https://github.com/tokio-rs/axum) router: + +- **`POST /offer`**: The client submits an SDP offer describing its WebRTC codecs, transport profiles, and network interfaces. +- **`GET /answer`**: Pumpkin generates a corresponding local SDP answer containing its negotiated capabilities. +- **`POST /candidate`**: Both endpoints exchange trickle ICE candidates to discover the fastest direct network path (host, server-reflexive, or relay). + +### 3. Local Network Discovery (`discovery.rs`) +- Listens and broadcasts availability on the local subnet via UDP multicast. +- Allows Bedrock clients on the same Wi-Fi or LAN to discover and connect to the Pumpkin server automatically without entering an IP address. + +### 4. DataChannel Packet Streaming +- Once the WebRTC peer connection transitions to the `Connected` state, an ordered, reliable **WebRTC DataChannel** is opened. +- Bedrock batch packets (`0xfe`) flow bidirectionally through the DataChannel directly into Pumpkin's `incoming_game_packet_send` channel. + +--- + +## Configuration in `pumpkin.toml` + +NetherNet is configured under `[networking.bedrock.nethernet]` in `pumpkin.toml`: + +```toml +[networking.bedrock] +enabled = true +address = "0.0.0.0:19132" + +[networking.bedrock.nethernet] +enabled = true +signaling_address = "0.0.0.0:19133" +ice_servers = [ + "stun:stun.l.google.com:19302" +] +``` diff --git a/docs/zh_cn/developer/networking/bedrock/overview.md b/docs/zh_cn/developer/networking/bedrock/overview.md new file mode 100644 index 0000000..ae0d615 --- /dev/null +++ b/docs/zh_cn/developer/networking/bedrock/overview.md @@ -0,0 +1,80 @@ +# Bedrock Edition Networking Overview + +Pumpkin features native support for **Minecraft: Bedrock Edition** clients (iOS, Android, Windows, Xbox, PlayStation, Switch). + +Bedrock protocol structures and wire codecs reside in `pumpkin-protocol::bedrock`, while connection management and gameplay handlers reside in `pumpkin::net::bedrock`. + +--- + +## Transport Layers: RakNet & NetherNet + +Unlike Java Edition, which communicates exclusively over TCP streams, Bedrock Edition operates over UDP using two primary transport layers: + +1. **RakNet (Port 19132)**: + - A reliable, sequenced UDP networking library originally developed by Jenkins Software. + - Provides connection handshakes, packet acknowledgment (ACK/NACK), MTU negotiation, and packet splitting for payloads larger than the network MTU. +2. **NetherNet**: + - Modern peer-to-peer and relayed transport protocol used by modern Bedrock versions. + - Integrated in Pumpkin via `crates/pumpkin/src/net/bedrock/nethernet.rs`. + +--- + +## Packet Framing & Batching (`0xfe`) + +Bedrock does not send individual game packets over the network directly. Instead, multiple game packets are bundled into a single **batch packet**: + +1. **Header Byte**: Every Bedrock game packet batch starts with `0xfe`. +2. **Compression**: The payload following `0xfe` is compressed using **Deflate** (or ZLib) above a negotiated threshold. +3. **Sub-packet Framing**: When decompressed, the batch contains one or more sequential game packets, each prefixed by its length encoded as a `VarUInt`. + +In Pumpkin, batch decoding and assembly are managed by `BedrockBatchDecoder` and `BedrockBatchEncoder` in `crates/pumpkin-protocol/src/bedrock/`. + +--- + +## Connection Lifecycle & State Progression + +Bedrock does not have explicit protocol states like Java Edition. Instead, it follows a strict sequence of packets: + +| Step | Packet Exchanged | Purpose | +| :--- | :--- | :--- | +| **1. Network Settings** | `SRequestNetworkSettings` -> `CNetworkSettings` | Negotiates compression algorithm (Deflate/Snappy) and payload threshold | +| **2. Authentication** | `SLogin` -> `CPlayStatus` | Transmits JWT identity chain and client hardware data; verifies Xbox Live tokens | +| **3. Resource Negotiation** | `CResourcePacksInfo` -> `SResourcePackClientResponse` | Negotiates server resource packs and download behavior | +| **4. World Initialization** | `CStartGame` + `CItemRegistry` + `CCreativeContent` | Streams world spawn position, dimension settings, runtime player entity ID, and item palettes | +| **5. Ready to Spawn** | `SSetLocalPlayerAsInitialized` -> `CPlayStatus` | Signals client loading completion and transitions to active in-game play | + +--- + +## Packet Dispatching in `pumpkin` + +Inside `crates/pumpkin/src/net/bedrock/mod.rs`, decompressed sub-packets are dispatched based on their numeric packet ID: + +```rust +match packet.id { + SPlayerAuthInput::PACKET_ID => { + let packet = SPlayerAuthInput::read(reader)?; + self.handle_player_auth_input(player, packet, server); + } + SRequestChunkRadius::PACKET_ID => { + let packet = SRequestChunkRadius::read(reader)?; + self.handle_request_chunk_radius(player, &packet); + } + SInventoryTransaction::PACKET_ID => { + let packet = SInventoryTransaction::read(reader)?; + self.handle_inventory_action(player, packet); + } + _ => { + trace!("Unhandled Bedrock packet ID: {}", packet.id); + } +} +``` + +--- + +## Authentication & NetherNet + +- **JWT Authentication**: Bedrock verifies player identities via certificate chains signed by Microsoft / Xbox Live root authorities. +- **NetherNet WebRTC**: High-performance peer-to-peer transport using WebRTC DataChannels, ICE NAT traversal, and P-384 ECDSA keys. + +For complete implementation details, see **[Bedrock Authentication & NetherNet](/developer/networking/bedrock/authentication)**. + diff --git a/docs/zh_cn/developer/networking/index.md b/docs/zh_cn/developer/networking/index.md new file mode 100644 index 0000000..0685c45 --- /dev/null +++ b/docs/zh_cn/developer/networking/index.md @@ -0,0 +1,49 @@ +# Networking Overview + +Pumpkin is built with a high-performance, asynchronous networking stack capable of servicing both **Minecraft: Java Edition** and **Minecraft: Bedrock Edition** clients simultaneously. + +All protocol definitions, serialization traits, and wire-level codecs are isolated in the [`pumpkin-protocol`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-protocol) crate, while client connection management, session state machines, and gameplay packet dispatching are managed in [`pumpkin/src/net`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/net). + +--- + +## Supported Editions + +Pumpkin separates network implementations by edition due to their fundamentally distinct transport layers, packet encodings, and state models: + +| Feature | Minecraft: Java Edition | Minecraft: Bedrock Edition | +| :--- | :--- | :--- | +| **Transport Layer** | TCP socket streams | UDP via RakNet & NetherNet | +| **Framing** | VarInt length-prefixed frames | Batched packets encapsulated in `0xfe` headers | +| **Encryption** | AES-128 / CFB8 stream cipher | Cryptographic session handshake | +| **Compression** | Per-packet ZLib (above configurable threshold) | Per-batch Deflate / ZLib compression | +| **State Model** | Explicit states: Handshake, Status, Login, Config, Play | Packet-driven state progression | +| **Protocol Crate** | `pumpkin-protocol::java` | `pumpkin-protocol::bedrock` | +| **Server Net Module** | `pumpkin::net::java` | `pumpkin::net::bedrock` | + +--- + +## Networking Architecture + +The networking pipeline routes incoming connections through specialized transport and codec layers before delegating to the unified server game loop: + +1. **Transport Listeners**: Independent listeners bind to their respective ports—Tokio TCP for Java Edition (`:25565`) and UDP/RakNet/NetherNet for Bedrock Edition (`:19132`). +2. **Session Abstraction**: Each accepted connection spawns an isolated session task (`JavaClient` or `BedrockClient`) managing wire framing and encryption. +3. **Codec Deserialization**: Wire bytes are deserialized into typed packet structs via `pumpkin-protocol::java` (`ClientPacket`/`ServerPacket`) and `pumpkin-protocol::bedrock` (`PacketWrite`/`PacketRead`). +4. **Unified Gameplay Dispatch**: Decoded packets are dispatched into the unified game loop, updating player entity states and streaming world chunks. + +--- + +## Navigating the Networking Docs + +Explore the documentation for each edition: + +- **[Entity Metadata & Synced Data](/developer/networking/metadata)**: Synchronization of visual entity states (DataWatchers in Java, Actor Data in Bedrock). +- **[Java Edition](/developer/networking/java/overview)**: + - **[Architecture & Lifecycle](/developer/networking/java/overview)**: State machine, `JavaClient` vs. `Player`, and multi-version negotiation. + - **[How to Add a Packet](/developer/networking/java/adding-packets)**: Guide to defining, serializing, and dispatching Java Edition packets. + - **[Authentication](/developer/networking/java/authentication)**: Mojang Yggdrasil validation, GameProfiles, and proxy forwarding. +- **[Bedrock Edition](/developer/networking/bedrock/overview)**: + - **[Architecture & Transports](/developer/networking/bedrock/overview)**: RakNet and NetherNet integration, batching, and session handling. + - **[How to Add a Packet](/developer/networking/bedrock/adding-packets)**: Guide to defining and handling Bedrock Edition packets. + - **[Authentication & NetherNet](/developer/networking/bedrock/authentication)**: JWT certificate chain verification, WebRTC DataChannels, P-384 keys, and signaling. +- **[RCON](/developer/networking/rcon)**: Implementation of Valve's Remote Console protocol for administrative management. diff --git a/docs/zh_cn/developer/networking/java/adding-packets.md b/docs/zh_cn/developer/networking/java/adding-packets.md new file mode 100644 index 0000000..1800d06 --- /dev/null +++ b/docs/zh_cn/developer/networking/java/adding-packets.md @@ -0,0 +1,205 @@ +# Adding a Java Edition Packet + +This guide walks you through implementing, serializing, dispatching, and sending a new Java Edition packet in Pumpkin. + +--- + +## Naming Conventions + +To keep packet definitions uniform across the codebase, follow these naming conventions: + +- **Clientbound packets** (Server → Client) must start with **`C`** (e.g., `CBlockUpdate`, `CPlayDisconnect`). +- **Serverbound packets** (Client → Server) must start with **`S`** (e.g., `SChatCommand`, `SPlayerPosition`). +- If a packet exists in multiple states, prefix the state name before the packet action (e.g., `CLoginDisconnect`, `CConfigDisconnect`, and `CPlayDisconnect`). + +--- + +## Step 1: Find the Packet ID in `pumpkin-data` + +Packet IDs across all supported Minecraft versions are maintained in `pumpkin-data`. Import the corresponding constant: + +```rust +// Clientbound Play state disconnect +use pumpkin_data::packet::clientbound::play::DISCONNECT; + +// Serverbound Play state player position +use pumpkin_data::packet::serverbound::play::MOVE_PLAYER_POS; +``` + +--- + +## Step 2: Implementing a Clientbound Packet + +Clientbound packets are sent from Pumpkin to the Minecraft client. They implement the `ClientPacket` trait from `pumpkin-protocol`. + +### 1. Create the Struct File +Place the file in `crates/pumpkin-protocol/src/java/client//.rs` (e.g., `play/disconnect.rs`): + +```rust +use std::io::Write; +use pumpkin_data::packet::clientbound::play::DISCONNECT; +use pumpkin_macros::java_packet; +use pumpkin_protocol::ClientPacket; +use pumpkin_protocol::ser::{NetworkWriteExt, WritingError}; +use pumpkin_util::text::TextComponent; +use pumpkin_util::version::JavaMinecraftVersion; + +/// Displays a disconnect screen with a message to the player in Play state. +#[java_packet(DISCONNECT)] +pub struct CPlayDisconnect<'a> { + pub reason: &'a TextComponent, +} + +impl<'a> CPlayDisconnect<'a> { + #[must_use] + pub const fn new(reason: &'a TextComponent) -> Self { + Self { reason } + } +} + +impl ClientPacket for CPlayDisconnect<'_> { + fn write_packet_data( + &self, + mut write: impl Write, + version: &JavaMinecraftVersion, + ) -> Result<(), WritingError> { + // Use NetworkWriteExt methods to serialize fields + write.write_component(self.reason, version) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/java/client//mod.rs`: + +```rust +mod disconnect; +pub use disconnect::*; +``` + +--- + +## Step 3: Implementing a Serverbound Packet + +Serverbound packets are received by Pumpkin from the game client. They implement the `ServerPacket` trait from `pumpkin-protocol`. + +### 1. Create the Struct File +Place the file in `crates/pumpkin-protocol/src/java/server//.rs` (e.g., `play/player_position.rs`): + +```rust +use pumpkin_data::packet::serverbound::play::MOVE_PLAYER_POS; +use pumpkin_macros::java_packet; +use pumpkin_protocol::ServerPacket; +use pumpkin_protocol::ser::{NetworkReadExt, ReadingError}; +use pumpkin_util::math::vector3::Vector3; +use pumpkin_util::version::JavaMinecraftVersion; + +#[java_packet(MOVE_PLAYER_POS)] +pub struct SPlayerPosition { + pub position: Vector3, + pub collision: u8, +} + +impl<'a> ServerPacket<'a> for SPlayerPosition { + fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result { + let x = bytebuf.get_f64_be()?; + let y = bytebuf.get_f64_be()?; + + // Handle version-specific fields if necessary + if *version <= JavaMinecraftVersion::V_1_7_6 { + let _stance = bytebuf.get_f64_be()?; + } + + let z = bytebuf.get_f64_be()?; + let collision = bytebuf.get_u8()?; + + Ok(Self { + position: Vector3::new(x, y, z), + collision, + }) + } +} +``` + +### 2. Export the Module +In `crates/pumpkin-protocol/src/java/server//mod.rs`: + +```rust +mod player_position; +pub use player_position::*; +``` + +--- + +## Step 4: Network Codec Extension Helpers + +`NetworkWriteExt` and `NetworkReadExt` provide helper methods for standard Minecraft types: + +| Data Type | Reading Method | Writing Method | +| :--- | :--- | :--- | +| **`VarInt`** | `bytebuf.get_var_int()?` | `write.write_var_int(val)?` | +| **`VarLong`** | `bytebuf.get_var_long()?` | `write.write_var_long(val)?` | +| **`String`** | `bytebuf.get_string()?` | `write.write_string(val)?` | +| **`UUID`** | `bytebuf.get_uuid()?` | `write.write_uuid(&val)?` | +| **`TextComponent`** | `bytebuf.get_component()?` | `write.write_component(val, version)?` | +| **`Position`** | `bytebuf.get_block_pos()?` | `write.write_block_pos(val)?` | +| **`NBT`** | `bytebuf.get_nbt()?` | `write.write_nbt(&val)?` | + +--- + +## Step 5: Handling Incoming Packets in `pumpkin` + +Incoming serverbound packets must be handled in the appropriate state handler in `crates/pumpkin/src/net/java/`. + +For packets in the **Play state**, open `crates/pumpkin/src/net/java/mod.rs` and update `handle_play_packet`: + +```rust +// In crates/pumpkin/src/net/java/mod.rs +pub fn handle_play_packet( + &self, + player: &Arc, + server: &Arc, + packet: &RawPacket, +) -> Result<(), Box> { + let version = self.version.load(); + let mut payload = &packet.payload[..]; + + match packet.id { + // Match against version-specific packet ID + id if id == SPlayerPosition::to_id(version) => { + let packet = SPlayerPosition::read(&mut payload, &version)?; + self.handle_player_position(player, &packet); + } + _ => {} + } + Ok(()) +} +``` + +Then implement the handler function: + +```rust +impl JavaClient { + pub fn handle_player_position(&self, player: &Arc, packet: &SPlayerPosition) { + // Validate coordinates, update player position, check collisions + player.set_position(packet.position); + } +} +``` + +--- + +## Step 6: Sending Outgoing Packets + +To send a clientbound packet to a player: + +```rust +// 1. Send directly to a player's connection +player.client.send_packet(&CPlayDisconnect::new(&reason)); + +// 2. Broadcast to all players watching a chunk +world.broadcast_packet_except( + &[player.gameprofile.id], + &CBlockUpdate::new(block_pos, block_id), +).await; +``` diff --git a/docs/zh_cn/developer/networking/java/authentication.md b/docs/zh_cn/developer/networking/java/authentication.md new file mode 100644 index 0000000..861da51 --- /dev/null +++ b/docs/zh_cn/developer/networking/java/authentication.md @@ -0,0 +1,83 @@ +# Java Edition Authentication + +Authentication verifies the identity of players connecting to Pumpkin to prevent username spoofing and account impersonation. + +Pumpkin handles authentication in the [`pumpkin-auth`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin-auth) crate and orchestrates it during the `Login` state in `crates/pumpkin/src/net/java/login/`. + +--- + +## Why Authentication Matters + +Offline (non-authenticated) accounts generate player UUIDs strictly based on their offline username string (`UUID::new_v3(...)`). Without verification, any user can join using the username of an operator or administrator and gain privileged access. + +In online mode (`online_mode = true` in `pumpkin.toml`), Pumpkin cryptographically verifies with Mojang's session servers that the connecting client owns the requested account. + +--- + +## How Yggdrasil Authentication Works + +The authentication exchange between the client, Pumpkin, and Mojang's Yggdrasil servers follows these steps: + +1. **Initiation**: The client sends `SLoginStart` declaring its desired username and optional offline UUID. +2. **Key Exchange**: Pumpkin replies with `CEncryptionBegin`, providing a random 4-byte verify token, the server ID string, and Pumpkin's 1024-bit RSA public key. +3. **Session Join**: The client generates a random 16-byte shared secret (`AES-128`), computes a SHA-1 server hash, and calls Mojang's session server (`POST /session/minecraft/join`) using its authenticated Microsoft access token. +4. **Verification**: The client sends `SEncryptionResponse` containing the RSA-encrypted shared secret and encrypted verify token. Pumpkin decrypts the token with its RSA private key, recalculates the SHA-1 digest, and queries Mojang (`GET /session/minecraft/hasJoined`). +5. **Profile Retrieval**: Mojang validates that the client and server hashes match, returning the authenticated player's UUID, username, and signed skin/cape texture properties (`GameProfile`). +6. **Encryption Activation**: Pumpkin activates full-stream symmetric encryption (`AES-128/CFB8`) and completes the login sequence by sending `CLoginSuccess`. + +--- + +## Game Profile Structure + +When Mojang's `hasJoined` endpoint validates a session, it returns a `GameProfile` containing the player's authenticated UUID, username, and signed texture properties: + +```rust +pub struct GameProfile { + pub id: Uuid, + pub name: String, + pub properties: Vec, + pub profile_actions: Option>, +} + +pub struct Property { + pub name: String, + pub value: String, // Base64 encoded texture data + pub signature: Option, // Cryptographic signature verifying authenticity +} +``` + +Pumpkin verifies that skin and cape signatures are cryptographically signed with Mojang's public key to prevent clients from injecting malicious payloads or unauthorized textures. + +--- + +## Custom Authentication Endpoints + +For private networks or enterprise environments running custom Yggdrasil-compatible authentication servers, endpoints can be customized in `pumpkin.toml`: + +```toml +[networking.java.authentication] +enabled = true +fallbacks = [] +connect_timeout = 5000 +read_timeout = 5000 +prevent_proxy_connections = false + +[networking.java.authentication.player_profile] +allow_banned_players = false +allowed_actions = ["FORCED_NAME_CHANGE", "USING_BANNED_SKIN"] + +[networking.java.authentication.textures] +enabled = true +allowed_url_schemes = ["http", "https"] +allowed_url_domains = [".minecraft.net", ".mojang.com"] +``` + +--- + +## Proxy Authentication Forwarding + +When running Pumpkin behind proxies, client connections terminate at the proxy. Pumpkin supports the three primary proxy forwarding standards: + +- **Velocity Modern Forwarding**: Validates player data forwarded with HMAC-SHA256 secret tokens (`[networking.proxy.velocity]`). +- **BungeeGuard**: Token-based authentication header validation for BungeeCord setups. +- **Legacy BungeeCord**: Handshake forwarding appending IP, UUID, and properties to the server host string. diff --git a/docs/zh_cn/developer/networking/java/overview.md b/docs/zh_cn/developer/networking/java/overview.md new file mode 100644 index 0000000..e79753c --- /dev/null +++ b/docs/zh_cn/developer/networking/java/overview.md @@ -0,0 +1,74 @@ +# Java Edition Networking Overview + +The Java Edition network stack handles standard Minecraft PC clients connecting over TCP. Wire-level structures and serialization logic reside in `pumpkin-protocol::java`, while connection management and gameplay packet handlers reside in `pumpkin::net::java`. + +--- + +## The Five Protocol States + +The Minecraft Java protocol operates as an explicit finite state machine. Every connection begins in the `Handshake` state and transitions according to client requests and server responses: + +| State | Transition Trigger | Purpose & Next State | +| :--- | :--- | :--- | +| **Handshake** | Client initiates connection | Inspects protocol version and transitions to `Status`, `Login`, or `Transfer` | +| **Status** | `NextState = 1` | Streams MOTD, active player counts, favicon, and ping response | +| **Login** | `NextState = 2` | Authenticates account (Yggdrasil), negotiates encryption (AES-128), transitions to `Config` | +| **Transfer** | `NextState = 3` | Modern proxy / server-transfer negotiation | +| **Config** | Authentication success | Exchanges dynamic registries, data tags, feature flags, and resource packs | +| **Play** | Configuration acknowledged | Primary bidirectional gameplay loop (chunks, movement, combat, chat) | + +1. **Handshake (`0x00`)**: Always the first packet sent by the client. It declares the client's protocol version, server address, port, and desired next state (`Status`, `Login`, or `Transfer`). +2. **Status**: Lightweight query state. The client requests a server list ping, and Pumpkin responds with the server version, player counts, favicon, and MOTD. +3. **Login**: Authenticates the player. If `online_mode` is enabled, Pumpkin performs an encryption handshake (AES-128/CFB8) and verifies the player's account with Mojang's Yggdrasil session servers. +4. **Config**: Introduced in modern Minecraft (1.20.2+). The server sends feature flags, known registries, resource packs, and custom server links before the player joins the physical world. +5. **Play**: The primary gameplay state. Used for all in-game actions including block breaking, entity movement, combat, chat, and chunk streaming. + +--- + +## Connection Representation: `JavaClient` vs. `Player` + +To keep memory usage minimal and prevent unnecessary allocations, Pumpkin cleanly separates pre-play connections from active players: + +### `JavaClient` +- **Scope**: Exists across `Handshake`, `Status`, `Login`, and `Config` states. +- **Location**: `crates/pumpkin/src/net/java/mod.rs`. +- **Characteristics**: + - Contains connection metadata: client protocol version, remote address, encryption/compression state. + - Has no world coordinates, no physics, no inventory, and no living entity. + - Very low memory footprint. + +### `Player` +- **Scope**: Exists solely during the `Play` state. +- **Location**: `crates/pumpkin/src/entity/player/`. +- **Characteristics**: + - Implements `EntityBase`, representing a living entity inside a `World` and `Level`. + - Holds inventory screens, health, hunger, velocities, and chunk view subscription queues. + - Contains an `Arc` reference for network transmission. + +--- + +## Multi-Version Support (`JavaMinecraftVersion`) + +Pumpkin supports multiple Minecraft client versions. Protocol differences are handled cleanly via `pumpkin_util::version::JavaMinecraftVersion`: + +- Packet IDs may differ across Minecraft versions. The `#[java_packet(ID)]` macro works with `pumpkin-data` to resolve the correct ID for the target version. +- When reading or writing packets, methods accept a `&JavaMinecraftVersion` reference to conditionally include or omit fields that changed in specific Minecraft updates. + +```rust +if *version <= JavaMinecraftVersion::V_1_7_6 { + // Read legacy stance coordinate present in early Minecraft versions + let _stance = bytebuf.get_f64_be()?; +} +``` + +--- + +## The Packet Pipeline + +Incoming Java packets pass through a structured pipeline: + +1. **Length Framing**: `tokio_util::codec` decodes VarInt frame lengths. +2. **Decompression & Decryption**: If enabled, the raw buffer is decrypted via AES/CFB8 and decompressed via ZLib. +3. **Plugin Event Interception**: A `PacketReceivedEvent` is dispatched to the plugin manager. Plugins can inspect or cancel the packet before server processing. +4. **ID Dispatching**: The packet ID is matched against version-specific constants in `crates/pumpkin/src/net/java/mod.rs` (or the respective state module). +5. **Handling**: The packet struct is deserialized and processed by state-specific handler functions. diff --git a/docs/zh_cn/developer/networking/metadata.md b/docs/zh_cn/developer/networking/metadata.md new file mode 100644 index 0000000..9def4ae --- /dev/null +++ b/docs/zh_cn/developer/networking/metadata.md @@ -0,0 +1,123 @@ +# Entity Metadata & Synced Data + +Minecraft synchronizes persistent entity visual states, animation flags, poses, and properties via **Entity Metadata** (historically called *DataWatchers* in Java Edition and *Actor Data* in Bedrock Edition). + +Instead of transmitting full state packets whenever an entity crouches, catches fire, or takes damage, the server broadcasts compact metadata updates to nearby observers. + +--- + +## Edition Comparison + +| Feature | Minecraft: Java Edition | Minecraft: Bedrock Edition | +| :--- | :--- | :--- | +| **Packet Name** | `CSetEntityMetadata` (`SET_ENTITY_DATA`) | `CSetActorData` (Packet ID `39`) | +| **Storage Structure** | `TrackedData` list terminating with `0xFF` | `SyncedActorDataList` (`HashMap`) | +| **Key Type** | Index (`u8`) mapped to typed `MetaDataType` | Enum keys (`entity_data_key::*`) | +| **Flags** | Bitfield byte (fire, crouching, sprinting, glowing) | Bitfield int64 (`entity_data_flag::*`) | +| **Dynamic Properties** | N/A (baked into tracked indices) | `PropertySyncData` (client/server sync) | +| **Crate Location** | `crates/pumpkin-protocol/src/java/client/play/entity_metadata.rs` | `crates/pumpkin-protocol/src/bedrock/client/set_actor_data.rs` | + +--- + +## 1. Java Edition: `CSetEntityMetadata` + +In Java Edition, entity metadata consists of numbered index entries. Each entry defines an index, a `MetaDataType` ID, and the encoded value. The list terminates with a single `0xFF` byte. + +### Key Index Hierarchy + +Entity metadata follows an object-oriented inheritance model: + +1. **Entity (Base)**: + - `0`: State bitfield (flags: bit `0` = on fire, bit `1` = crouching, bit `3` = sprinting, bit `5` = invisible, bit `6` = glowing). + - `1`: Air supply (`VarInt`). + - `2`: Custom name (optional `TextComponent`). + - `3`: Custom name visible (`bool`). + - `4`: Silent (`bool`). + - `5`: No gravity (`bool`). + - `6`: Pose (`Standing`, `FallFlying`, `Sleeping`, `Swimming`, `SpinAttack`, `Crouching`, `Dying`). +2. **LivingEntity (extends Entity)**: + - `8`: Hand states (using item, active hand). + - `9`: Health (`f32`). + - `10`: Potion effect ambient color. +3. **Player (extends LivingEntity)**: + - Additional hearts, skin customization bitmask, main hand indicator. + +### Sending Metadata Updates in Pumpkin + +Pumpkin encodes entity metadata in `pumpkin-protocol`: + +```rust +use pumpkin_protocol::VarInt; +use pumpkin_protocol::java::client::play::CSetEntityMetadata; + +// Construct metadata update for entity with ID 42 +let packet = CSetEntityMetadata::new( + VarInt(entity.entity_id), + serialized_metadata_bytes, +); + +// Broadcast to chunk observers +world.broadcast_packet_all(&packet).await; +``` + +--- + +## 2. Bedrock Edition: `CSetActorData` + +Bedrock Edition uses `CSetActorData` (`packet(39)`), storing properties in a dictionary mapping `u32` keys to strongly-typed `MetadataValue` variants. + +### Key & Flag Constants + +Bedrock exposes flags as a 64-bit integer bitfield under `entity_data_key::FLAGS`: + +- `entity_data_flag::ON_FIRE` (`0`): Entity is burning. +- `entity_data_flag::SNEAKING` (`1`): Player is sneaking. +- `entity_data_flag::RIDING` (`2`): Entity is mounted on a vehicle. +- `entity_data_flag::SPRINTING` (`3`): Player is sprinting. +- `entity_data_flag::USING_ITEM` (`4`): Player is actively charging a bow, eating food, or using a shield. +- `entity_data_flag::INVISIBLE` (`5`): Entity is invisible. +- `entity_data_flag::GLOWING` (`26`): Entity has the glowing effect outline. + +### Constructing and Updating Actor Data in Pumpkin + +In `crates/pumpkin/src/entity/living.rs`: + +```rust +use pumpkin_protocol::bedrock::client::set_actor_data::{ + CSetActorData, MetadataValue, PropertySyncData, SyncedActorDataList, + entity_data_flag, entity_data_key, +}; + +let mut actor_data = SyncedActorDataList::new(); + +// Set entity flags (e.g. actively using an item) +let mut flags: i64 = 0; +if is_using_item { + flags |= 1i64 << entity_data_flag::USING_ITEM; +} +actor_data.set(entity_data_key::FLAGS, MetadataValue::Int64(flags)); + +// Set air supply +actor_data.set(entity_data_key::AIR_SUPPLY, MetadataValue::Short(air as i16)); + +// Set custom name tag +actor_data.set(entity_data_key::NAME, MetadataValue::String(name)); + +let packet = CSetActorData { + target_runtime_id: VarULong(entity_id), + actor_data, + synced_properties: PropertySyncData::default(), + tick: VarULong(current_tick), +}; + +client.try_enqueue_client_packet(&packet); +``` + +--- + +## Multi-Version Compatibility + +Because Mojang frequently shifts metadata indices between Minecraft releases, `pumpkin-protocol` leverages translation tables from `pumpkin-data`: + +- For Java Edition, `pumpkin_data::meta_data_type::MetaDataType` remaps indices dynamically based on the client's `JavaMinecraftVersion`. +- For Bedrock Edition, `SyncedActorDataList` validates keys against the active Bedrock protocol version to prevent client crashes. diff --git a/docs/zh_cn/developer/networking/proxy/bungeecord.md b/docs/zh_cn/developer/networking/proxy/bungeecord.md new file mode 100644 index 0000000..cb06ff5 --- /dev/null +++ b/docs/zh_cn/developer/networking/proxy/bungeecord.md @@ -0,0 +1,73 @@ +# BungeeCord Forwarding + +Pumpkin provides native support for BungeeCord and Waterfall proxy networks, including **BungeeGuard** authentication token verification. + +Implementation is located in [`crates/pumpkin/src/net/proxy/bungeecord.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/bungeecord.rs). + +--- + +## How BungeeCord Forwarding Operates + +Unlike modern protocols that use custom login channels, BungeeCord operates by encoding client metadata directly into the `server_address` field of the initial `SHandshake` packet. + +The string uses null character (`\0`) delimiters: + +```text +[original_host]\0[player_ip:port]\0[player_uuid]\0[properties_json] +``` + +1. **`original_host`**: The domain name the player originally typed into their client. +2. **`player_ip:port`**: The genuine IP address and ephemeral source port of the client. +3. **`player_uuid`**: The player's Mojang UUID (32 hex characters without hyphens or 36 characters with hyphens). +4. **`properties_json`**: A JSON array containing skin textures, cape textures, and Mojang cryptographic signatures. + +--- + +## The Security Risk: Proxy Bypasses + +Because BungeeCord's handshake string is unauthenticated plain text, an attacker could connect directly to an exposed backend port, inject an arbitrary UUID and operator username, and gain full server control. + +Traditionally, this required strict Linux firewall rules (`iptables` / `ufw`) to restrict port access exclusively to the proxy IP. + +--- + +## BungeeGuard Protection + +To eliminate proxy bypasses without relying solely on network firewalls, Pumpkin integrates **BungeeGuard** token verification: + +1. The BungeeCord proxy injects a shared secret into the player's profile properties under the key `bungeeguard-token`. +2. When Pumpkin processes the handshake in `bungeecord_login`: + - It checks whether `secret` is configured in `pumpkin.toml`. + - It inspects the forwarded properties for `bungeeguard-token`. + - If the token matches, Pumpkin **strips the secret property** from the player's profile (so other plugins cannot read it) and completes the login. + - If the token is missing or incorrect, Pumpkin rejects the connection with `BungeeCordError::MissingToken` or `BungeeCordError::InvalidToken`. + +```rust +// In crates/pumpkin/src/net/proxy/bungeecord.rs +if !secret.is_empty() { + let Some(token_index) = properties.iter().position(|p| p.name == BUNGEEGUARD_TOKEN_PROPERTY) else { + return Err(BungeeCordError::MissingToken); + }; + + if properties[token_index].value != secret { + return Err(BungeeCordError::InvalidToken); + } + properties.remove(token_index); +} +``` + +--- + +## Configuration in `pumpkin.toml` + +To enable BungeeCord forwarding with BungeeGuard: + +```toml +[networking.proxy] +enabled = true + +[networking.proxy.bungeecord] +enabled = true +# Optional: Configure the BungeeGuard shared secret to block direct connections +secret = "YOUR_BUNGEEGUARD_SECRET_HERE" +``` diff --git a/docs/zh_cn/developer/networking/proxy/index.md b/docs/zh_cn/developer/networking/proxy/index.md new file mode 100644 index 0000000..300c89e --- /dev/null +++ b/docs/zh_cn/developer/networking/proxy/index.md @@ -0,0 +1,46 @@ +# Proxy Forwarding Overview + +In multi-server Minecraft networks, players connect through a front-facing reverse proxy (such as Velocity, BungeeCord, or Vine) that routes connections between lobbies and sub-servers without requiring players to reconnect. + +Because backend servers receive TCP connections directly from the proxy machine rather than the player, the server must be informed of the player's genuine **IP address**, **UUID**, **username**, and **skin properties**. + +Pumpkin provides native support for all three major proxy forwarding protocols in [`crates/pumpkin/src/net/proxy/`](https://github.com/Pumpkin-MC/Pumpkin/tree/master/crates/pumpkin/src/net/proxy). + +--- + +## The Proxy Forwarding Problem + +Without secure proxy forwarding: +1. **IP Loss**: Every connected player appears with the proxy's loopback or internal IP (breaking IP bans and geo-location). +2. **UUID Spoofing & Bypasses**: Malicious users could connect directly to the backend server's port with any spoofed username or operator identity. + +To prevent proxy bypasses, Pumpkin validates cryptographic secrets, tokens, or digital signatures sent by the proxy. + +--- + +## Comparison of Supported Protocols + +| Protocol | Protocol Transport | Security / Cryptography | Replay Protection | Crate Module | +| :--- | :--- | :--- | :--- | :--- | +| **[BungeeCord](/developer/networking/proxy/bungeecord)** | `SHandshake` server address string | Optional token in profile properties (`bungeeguard-token`) | None | `pumpkin::net::proxy::bungeecord` | +| **[Velocity](/developer/networking/proxy/velocity)** | Login plugin message (`velocity:player_info`) | Symmetric HMAC-SHA256 signature | None | `pumpkin::net::proxy::velocity` | +| **[Vine](/developer/networking/proxy/vine)** | Login plugin message (`vine:player_info`) | Asymmetric **Ed25519** digital signatures | 16-byte nonce challenge + 30s timestamp window | `pumpkin::net::proxy::vine` | + +--- + +## Lifecycle Architecture + +1. **Player Authentication**: The player connects to the proxy (BungeeCord, Velocity, or Vine) and authenticates against Mojang's session servers. +2. **Backend Connection**: The proxy connects to Pumpkin over TCP and sends `SHandshake`. +3. **Data Forwarding**: + - **BungeeCord Mode**: The proxy injects the player's remote IP, UUID, and properties directly into the `SHandshake` hostname string (separated by null bytes `\0`). + - **Modern Mode (Velocity / Vine)**: Pumpkin sends `CLoginPluginRequest` on a dedicated channel (`velocity:player_info` or `vine:player_info`). The proxy signs the player profile using HMAC-SHA256 (Velocity) or Ed25519 (Vine) and replies with `SLoginPluginResponse`. +4. **Validation & Session Initialization**: Pumpkin validates the signature, binds the real IP and GameProfile to the `JavaClient`, and sends `CLoginSuccess`. + +--- + +## Proxy Documentation + +- **[BungeeCord](/developer/networking/proxy/bungeecord)**: Legacy host string injection and optional BungeeGuard token validation. +- **[Velocity Modern Forwarding](/developer/networking/proxy/velocity)**: HMAC-SHA256 signed player info during the Login state. +- **[Vine Modern Forwarding](/developer/networking/proxy/vine)**: Next-generation proxy forwarding with Ed25519 signatures and replay attack prevention. diff --git a/docs/zh_cn/developer/networking/proxy/velocity.md b/docs/zh_cn/developer/networking/proxy/velocity.md new file mode 100644 index 0000000..b0333e7 --- /dev/null +++ b/docs/zh_cn/developer/networking/proxy/velocity.md @@ -0,0 +1,88 @@ +# Velocity Modern Forwarding + +[Velocity](https://papermc.io/software/velocity) is a high-performance proxy created by PaperMC. It uses modern plugin messaging during the `Login` state to securely transmit player data with **HMAC-SHA256** message authentication codes. + +Pumpkin implements Velocity modern forwarding in [`crates/pumpkin/src/net/proxy/velocity.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/velocity.rs). + +--- + +## How Velocity Forwarding Operates + +Unlike BungeeCord, which injects unauthenticated data into the handshake string, Velocity operates during the **Login** state over a dedicated plugin channel: + +1. **Handshake & Login**: The player authenticates with Velocity. Velocity initiates a standard TCP connection to Pumpkin and transmits `SHandshake` and `SLoginStart`. +2. **Channel Query**: Recognizing that Velocity modern forwarding is enabled, Pumpkin pauses the login sequence and dispatches a `CLoginPluginRequest` on channel `velocity:player_info`. +3. **Payload Generation**: Velocity bundles the player's remote address, UUID, username, and skin textures. It calculates a 32-byte HMAC-SHA256 signature across this payload using the shared secret. +4. **Signature Verification**: Velocity sends `SLoginPluginResponse`. Pumpkin validates the HMAC-SHA256 digest against its configured secret. If valid, Pumpkin overwrites the connection's network address and GameProfile and transitions to `Config` / `Play`. + +--- + +## Wire Payload Structure + +The payload in `SLoginPluginResponse` is structured as follows: + +```text +┌─────────────────────────────────────────────────────────────┐ +│ 32-byte HMAC-SHA256 Signature │ +├─────────────────────────────────────────────────────────────┤ +│ Forwarding Protocol Version (VarInt: 1 - 4) │ +├─────────────────────────────────────────────────────────────┤ +│ Client Remote IP Address (String / IPv4 / IPv6) │ +├─────────────────────────────────────────────────────────────┤ +│ Player UUID (128-bit / UUID) │ +├─────────────────────────────────────────────────────────────┤ +│ Player Username (String) │ +├─────────────────────────────────────────────────────────────┤ +│ Profile Properties Count (VarInt) │ +│ ├─ Property Name (String) │ +│ ├─ Property Value (String) │ +│ └─ Optional Signature (String) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Cryptographic Validation in Pumpkin + +Inside `crates/pumpkin/src/net/proxy/velocity.rs`: + +```rust +use hmac::{Hmac, KeyInit, Mac}; +use sha2::Sha256; + +type HmacSha256 = Hmac; + +// Extract signature and payload +let (signature, data_to_verify) = data.split_at(32); + +// Initialize HMAC with secret from pumpkin.toml +let mut mac = HmacSha256::new_from_slice(secret.as_bytes()) + .map_err(|_| VelocityError::FailedVerifyIntegrity)?; + +mac.update(data_to_verify); + +// Constant-time verification prevents timing attacks +mac.verify_slice(signature) + .map_err(|_| VelocityError::FailedVerifyIntegrity)?; +``` + +If the signature does not match (e.g., if an attacker attempts to connect directly to the backend without knowing the secret), Pumpkin drops the connection immediately. + +--- + +## Configuration in `pumpkin.toml` + +1. Open `velocity.toml` on your Velocity proxy and set: + ```toml + player-info-forwarding-mode = "modern" + forwarding-secret-file = "forwarding.secret" + ``` +2. Copy the generated secret string from `forwarding.secret` into `pumpkin.toml`: + ```toml + [networking.proxy] + enabled = true + + [networking.proxy.velocity] + enabled = true + secret = "YOUR_VELOCITY_FORWARDING_SECRET_HERE" + ``` diff --git a/docs/zh_cn/developer/networking/proxy/vine.md b/docs/zh_cn/developer/networking/proxy/vine.md new file mode 100644 index 0000000..fed2a8a --- /dev/null +++ b/docs/zh_cn/developer/networking/proxy/vine.md @@ -0,0 +1,112 @@ +# Vine Modern Forwarding + +**Vine** is a next-generation, high-performance Minecraft proxy written in Rust. Designed for ultra-low latency, multi-threading, and enterprise-grade security, Vine introduces modern cryptographic forwarding based on **Ed25519 asymmetric signatures** and **replay attack protection**. + +Pumpkin provides native support for Vine forwarding in [`crates/pumpkin/src/net/proxy/vine.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/proxy/vine.rs). + +--- + +## Why Vine Forwarding? + +While Velocity's HMAC-SHA256 forwarding represents an improvement over legacy BungeeCord, it relies on a **symmetric shared secret**. If one backend server in a network is compromised, an attacker obtains the shared secret and can forge authenticated player logins across the entire server cluster. + +Vine solves this with: +1. **Asymmetric Public-Key Cryptography (Ed25519)**: The proxy signs player data using its private key. Backend servers only store the proxy's **Public Key** (`public_key`). Even if a backend server is compromised, the attacker cannot forge identities to other servers. +2. **Replay Attack Protection (16-byte Challenge Nonce)**: Pumpkin generates a cryptographic random 16-byte nonce for each connection. The proxy must sign and return this nonce. Replaying a captured login packet is impossible. +3. **Timestamp Drift Window**: Transmits a Unix timestamp that must be within ±30 seconds of the backend's system clock (`MAX_TIMESTAMP_DRIFT_SECS = 30`). + +--- + +## The Handshake Sequence + +1. **Connection**: The player connects and authenticates with Vine. Vine opens a backend connection to Pumpkin and sends `SHandshake` and `SLoginStart`. +2. **Challenge Query**: Pumpkin generates a cryptographically random 16-byte nonce and sends `CLoginPluginRequest` on the `vine:player_info` channel. +3. **Asymmetric Signing**: Vine serializes the version, timestamp, the returned 16-byte nonce, the client IP address, and the player GameProfile. It signs this byte stream with its Ed25519 private key. +4. **Tri-fold Validation**: Vine replies with `SLoginPluginResponse`. Pumpkin verifies: + - **Clock Skew**: The embedded timestamp is within ±30 seconds of UTC time. + - **Nonce Check**: The signed nonce exactly matches the 16-byte challenge generated in step 2. + - **Ed25519 Signature**: The signature is cryptographically valid against the configured `public_key`. +5. **Session Success**: If all three checks pass, Pumpkin applies the verified identity and emits `CLoginSuccess`. + +--- + +## Wire Payload Structure + +The `SLoginPluginResponse` data layout: + +```text +┌─────────────────────────────────────────────────────────────┐ +│ 64-byte Ed25519 Digital Signature │ +├─────────────────────────────────────────────────────────────┤ +│ Forwarding Protocol Version (i32: 1) │ +├─────────────────────────────────────────────────────────────┤ +│ Unix Timestamp in Seconds (i64) │ +├─────────────────────────────────────────────────────────────┤ +│ 16-byte Challenge Nonce │ +├─────────────────────────────────────────────────────────────┤ +│ Client Remote IP Address (String / IPv4 / IPv6) │ +├─────────────────────────────────────────────────────────────┤ +│ Player UUID (128-bit / UUID) │ +├─────────────────────────────────────────────────────────────┤ +│ Player Username (String) │ +├─────────────────────────────────────────────────────────────┤ +│ Profile Properties Count (VarInt) │ +│ ├─ Property Name (String) │ +│ ├─ Property Value (String) │ +│ └─ Optional Signature (String) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Verification in Pumpkin + +In `crates/pumpkin/src/net/proxy/vine.rs`: + +```rust +use ed25519_dalek::{Signature, Verifier, VerifyingKey}; + +// 1. Validate payload length (64-byte signature + 25-byte headers minimum) +if data.len() < 89 { + return Err(VineError::DataTooShort); +} + +// 2. Extract signature and signed payload +let (sig_bytes, signed_data) = data.split_at(64); +let signature = Signature::from_slice(sig_bytes) + .map_err(|_| VineError::InvalidSignature)?; + +// 3. Verify timestamp skew (30 second maximum drift) +let skew = (server_now - timestamp).abs(); +if skew > MAX_TIMESTAMP_DRIFT_SECS { + return Err(VineError::TimestampExpired(skew, MAX_TIMESTAMP_DRIFT_SECS)); +} + +// 4. Verify challenge nonce matches connection state +if received_nonce != expected_nonce { + return Err(VineError::ChallengeMismatch); +} + +// 5. Verify Ed25519 signature with proxy's public key +verifying_key.verify(signed_data, &signature) + .map_err(|_| VineError::InvalidSignature)?; +``` + +--- + +## Configuration in `pumpkin.toml` + +Configure Vine forwarding in `pumpkin.toml`: + +```toml +[networking.proxy] +enabled = true + +[networking.proxy.vine] +enabled = true +# 64-character hex-encoded Ed25519 public key of the Vine proxy +public_key = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + +# Alternatively, if secret is supplied, the public key is derived automatically +secret = "" +``` diff --git a/docs/zh_cn/developer/networking/query.md b/docs/zh_cn/developer/networking/query.md new file mode 100644 index 0000000..7d97a9c --- /dev/null +++ b/docs/zh_cn/developer/networking/query.md @@ -0,0 +1,97 @@ +# GameSpy4 Query Protocol + +Pumpkin includes native support for the **GameSpy4 Query** protocol over UDP. External monitoring systems, Discord bots, and server listing platforms use Query to poll real-time player counts, MOTD, server versions, and active player lists with minimal network overhead. + +Query wire encoding is implemented in [`crates/pumpkin-protocol/src/query.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-protocol/src/query.rs), and the asynchronous UDP listener is implemented in [`crates/pumpkin/src/net/query.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/net/query.rs). + +--- + +## Why Query? (UDP vs. Status Ping) + +While standard Minecraft clients ping servers via the TCP `Status` state (SLP), Query operates over stateless **UDP**: + +- **No TCP Handshake**: Avoids TCP connection overhead and connection state tables. +- **Detailed Server State**: Returns comprehensive key-value metadata and a full list of connected player usernames (not available via basic TCP ping). +- **Amplification Attack Protection**: Enforces cryptographic challenge tokens so malicious actors cannot abuse the UDP port for denial-of-service reflection attacks. + +--- + +## The Handshake & Challenge Token Flow + +To prevent UDP spoofing and reflection DDoS attacks, Query requires a two-step handshake: + +1. **Handshake Challenge**: The client transmits `SHandshake` (`0xFEFD`, type `0x09`, and a 32-bit session ID). Pumpkin generates a cryptographic 32-bit integer challenge token tied to the client's `SocketAddr` and responds with `CHandshake`. +2. **Status Query**: The client sends `SStatusRequest` containing the received challenge token: + - **Basic Status** (no padding): Returns MOTD, game type (`SMP`), map name, online player count, and max player capacity. + - **Full Status** (4-byte padding): Returns detailed server metadata key-value pairs (`hostname`, `gametype`, `game_id`, `version`, `plugins`, `map`, `numplayers`, `maxplayers`, `hostport`, `hostip`) followed by an array of online player usernames. + +### Challenge Token Invalidation +In `crates/pumpkin/src/net/query.rs`, challenge tokens are stored in a thread-safe map (`Arc>>`). A background Tokio timer purges all challenge tokens every 30 seconds to prevent replay abuse: + +```rust +// Tokens are strictly bound to the client's IP and port +let valid_challenge_tokens = Arc::new(RwLock::new(HashMap::new())); + +// Background expiration loop +tokio::spawn(async move { + let mut interval = time::interval(Duration::from_secs(30)); + loop { + interval.tick().await; + valid_challenge_tokens.write().await.clear(); + } +}); +``` + +--- + +## Packet Formats + +Every incoming Query packet starts with the 2-byte magic header `0xFEFD` followed by a 1-byte packet type: + +| Type Byte | Name | Direction | Description | +| :--- | :--- | :--- | :--- | +| `0x09` | Handshake Request | Client → Server | Client asks for a challenge token. | +| `0x09` | Handshake Response | Server → Client | Returns the numeric token encoded as an ASCII string. | +| `0x00` | Stat Request | Client → Server | Requests basic or full status with challenge token. | +| `0x00` | Basic Stat Response | Server → Client | 11-byte short summary. | +| `0x00` | Full Stat Response | Server → Client | Key-value dictionary + null-separated player list. | + +--- + +## Response Payloads + +### 1. Basic Status (`CBasicStatus`) +Contains essential server fields: +- **`motd`**: Server message of the day (stripped of color codes). +- **`gametype`**: Fixed string `"SMP"`. +- **`map`**: Active world/level name (e.g., `"world"`). +- **`numplayers`**: Online player count. +- **`maxplayers`**: Maximum configured player capacity. +- **`hostport`**: Port number (`u16` Little Endian). +- **`hostip`**: Server IP string. + +### 2. Full Status (`CFullStatus`) +Returns two null-terminated sections: + +1. **Key-Value Metadata Map**: + - `hostname`: Server description / MOTD. + - `gametype`: `"SMP"`. + - `game_id`: `"MINECRAFT"`. + - `version`: Server protocol / Minecraft version string. + - `plugins`: Installed server software and plugin names. + - `map`: Level name. + - `numplayers` / `maxplayers`: Capacity stats. + - `hostport` / `hostip`: Network bindings. +2. **Player List**: + - An array of null-terminated UTF-8 strings containing the usernames of all currently connected players. + - Concludes with a terminal null byte (`\0`). + +--- + +## Configuration in `pumpkin.toml` + +```toml +[networking.query] +enabled = true +address = "0.0.0.0:25565" +``` diff --git a/docs/zh_cn/developer/plugins/index.md b/docs/zh_cn/developer/plugins/index.md new file mode 100644 index 0000000..8cc993f --- /dev/null +++ b/docs/zh_cn/developer/plugins/index.md @@ -0,0 +1,56 @@ +# Plugin Engine Architecture + +Pumpkin features a modular, multi-runtime plugin engine designed for performance, safety, and cross-language interoperability. + +Unlike traditional Minecraft servers that rely on a single JVM class loader, Pumpkin isolates plugin execution through dedicated loaders supporting both **native high-performance Rust binaries** and **sandboxed WebAssembly components** compiled from Rust, Python, C#, C, Go, or Kotlin. + +--- + +## Architectural Overview + +### Architectural Breakdown + +| Subsystem | Primary Crate | Responsibility | +| :--- | :--- | :--- | +| **Plugin Manager** | `crates/pumpkin/src/plugin` | Lifecycle orchestration (load, enable, disable), event dispatching, and loader registration | +| **Native Loader** | `crates/pumpkin/src/plugin/loader/native` | Direct dynamic library loading (`.so`, `.dll`, `.dylib`) for zero-overhead Rust extensions | +| **WASM Loader** | `crates/pumpkin/src/plugin/loader/wasm` | Wasmtime execution engine for sandboxed components compiled from Rust, Python, Go, C#, C, and Kotlin | +| **Interface Specifications** | `crates/pumpkin-plugin-wit` | WebAssembly Interface Types (WIT) defining typed functions, types, and host imports | +| **Security & Verification** | `crates/pumpkin/src/plugin/loader/wasm/sign` | Cryptographic signature verification via W3C wasmsign2 / Ed25519 public keys | + +--- + +## The Dual Execution Model + +| Feature | Native Plugins | WebAssembly (Wasmtime) Plugins | +| :--- | :--- | :--- | +| **File Extension** | `.so` (Linux), `.dll` (Windows), `.dylib` (macOS) | `.wasm` (Component Model) | +| **Languages** | Rust (compiled dynamically) | Rust, Python, C#, C, Go, Kotlin | +| **Performance** | Native CPU execution (zero overhead) | JIT-compiled with near-native speed | +| **Sandboxing** | None (runs in host address space) | Memory-isolated sandboxed execution | +| **Security & Signing** | OS-level permissions | Cryptographic W3C Wasm-Sign verification | +| **Hot Unloading** | Limited (OS shared library constraints) | Fully supported memory reclamation | + +--- + +## Event Lifecycle & Dispatching + +Plugins interact with Pumpkin primarily through events: + +1. **Event Dispatch**: + - `server.plugin_manager.fire_blocking(&server, &mut event)`: Dispatches events synchronously on the executing thread (e.g., cancelling player block placement or modifying packet payloads). + - Asynchronous events run on non-blocking Tokio tasks. +2. **Cancellable Events**: + - Handlers can set `event.cancelled = true` to abort the default game logic (such as cancelling player movement, interactions, or packet processing). + +--- + +## Plugin Engine Documentation + +Explore the internal mechanics of Pumpkin's plugin system: + +- **[Adding a Plugin Loader](/developer/plugins/loaders)**: Step-by-step guide on implementing the `PluginLoader` trait for new scripting languages and runtimes. +- **[WASM Signing & Verification](/developer/plugins/wasm-signing)**: Detailed specification of Ed25519 cryptographic signing, W3C `wasmsign2` custom sections, and marketplace license verification. + +> [!NOTE] +> Looking to develop third-party plugins rather than working on Pumpkin's internal plugin engine? Check out the **[Plugin Development Guide](/plugin-dev/introduction)**. diff --git a/docs/zh_cn/developer/plugins/loaders.md b/docs/zh_cn/developer/plugins/loaders.md new file mode 100644 index 0000000..7e0f070 --- /dev/null +++ b/docs/zh_cn/developer/plugins/loaders.md @@ -0,0 +1,161 @@ +# Adding a Plugin Loader + +Pumpkin's plugin architecture is runtime-agnostic. The server does not assume all plugins run in WebAssembly or native shared libraries; instead, all execution runtimes implement the unified [`PluginLoader`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/plugin/loader/mod.rs) trait. + +This guide explains how `PluginLoader` operates and demonstrates how to implement a custom loader for new script runtimes or virtual machines (such as Lua, JavaScript/Boa, or GraalVM). + +--- + +## The `PluginLoader` Trait + +All plugin loaders implement the `PluginLoader` trait located in `crates/pumpkin/src/plugin/loader/mod.rs`: + +```rust +use std::{any::Any, path::Path, pin::Pin, sync::Arc}; +use crate::plugin::{PluginMetadata, api::Plugin}; + +pub type PluginLoadFuture<'a> = Pin< + Box< + dyn Future< + Output = Result< + (Arc, PluginMetadata, Box), + LoaderError, + >, + > + Send + + 'a, + >, +>; + +pub type PluginUnloadFuture<'a> = + Pin> + Send + 'a>>; + +pub trait PluginLoader: Send + Sync { + /// Determines whether this loader handles the target file. + fn can_load(&self, path: &Path) -> bool; + + /// Loads, parses, and initializes a plugin instance asynchronously. + fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a>; + + /// Unloads the plugin and reclaims associated resources. + fn unload(&self, data: Box) -> PluginUnloadFuture<'_>; + + /// Indicates whether the runtime supports safe unloading without memory leaks. + fn can_unload(&self) -> bool; +} +``` + +--- + +## Existing Loaders in Pumpkin + +Pumpkin currently provides two official loaders: + +1. **`NativePluginLoader` (`loader/native.rs`)**: + - Matches dynamic library files (`.so` on Linux, `.dll` on Windows, `.dylib` on macOS). + - Dynamically links the library using [`libloading`](https://docs.rs/libloading/). + - Locates and invokes the extern entrypoint function (`pumpkin_plugin_init`). + - `can_unload()` returns `false` due to OS-level shared library unloading hazards. +2. **`WasmPluginLoader` (`loader/wasm/mod.rs`)**: + - Matches `.wasm` files. + - Verifies cryptographic signatures (see [WASM Signing](/developer/plugins/wasm-signing)). + - JIT-compiles WebAssembly components using [Wasmtime](https://wasmtime.dev/). + - Links host WIT exports (`pumpkin-host-bindings`). + - `can_unload()` returns `true` because Wasmtime safely drops guest memory. + +--- + +## Step-by-Step: Adding a Custom Plugin Loader + +Here is how to create a custom loader (e.g., for a scripting runtime): + +### Step 1: Define the Loader Struct +Create a new file in `crates/pumpkin/src/plugin/loader/my_loader.rs`: + +```rust +use std::{any::Any, path::Path, sync::Arc}; +use crate::plugin::loader::{LoaderError, PluginLoadFuture, PluginLoader, PluginUnloadFuture}; +use crate::plugin::{PluginMetadata, api::Plugin}; + +pub struct MyCustomLoader; + +impl MyCustomLoader { + #[must_use] + pub fn new() -> Self { + Self + } +} +``` + +### Step 2: Implement File Recognition (`can_load`) +Check the file extension or inspect header bytes: + +```rust +impl PluginLoader for MyCustomLoader { + fn can_load(&self, path: &Path) -> bool { + path.extension() + .and_then(|ext| ext.to_str()) + .is_some_and(|ext| ext.eq_ignore_ascii_case("script")) + } +``` + +### Step 3: Implement Asynchronous Loading (`load`) +The `load` method reads the script, initializes the VM, registers server host callbacks, and returns a tuple containing: +1. `Arc`: The generic plugin handle. +2. `PluginMetadata`: Name, author, version, description. +3. `Box`: Loader-specific context (e.g., VM state) retained for unloading. + +```rust + fn load<'a>(&'a self, path: &'a Path) -> PluginLoadFuture<'a> { + Box::pin(async move { + let script_content = tokio::fs::read_to_string(path).await + .map_err(|e| LoaderError::InitializationFailed(e.to_string()))?; + + // 1. Parse plugin metadata from header or accompanying manifest + let metadata = PluginMetadata { + name: "ExampleScript".into(), + version: "1.0.0".into(), + authors: vec!["Author".into()], + description: Some("Custom script plugin".into()), + }; + + // 2. Initialize runtime instance + let plugin_instance = Arc::new(MyScriptPluginWrapper::new(script_content)); + let runtime_state: Box = Box::new(()); + + Ok((plugin_instance, metadata, runtime_state)) + }) + } +``` + +### Step 4: Implement Clean Unloading (`unload` & `can_unload`) +If your runtime can cleanly terminate without leaking resources: + +```rust + fn can_unload(&self) -> bool { + true + } + + fn unload(&self, data: Box) -> PluginUnloadFuture<'_> { + Box::pin(async move { + // Drop runtime state and disconnect event listeners + drop(data); + Ok(()) + }) + } +} +``` + +### Step 5: Register the Loader in `PluginManager` + +Open `crates/pumpkin/src/plugin/mod.rs` and register your loader in `PluginManager::new()`: + +```rust +// In crates/pumpkin/src/plugin/mod.rs +let mut loaders: Vec> = Vec::new(); + +loaders.push(Box::new(NativePluginLoader::new())); +loaders.push(Box::new(WasmPluginLoader::new())); +loaders.push(Box::new(MyCustomLoader::new())); // Add custom loader +``` + +During server startup, Pumpkin iterates through all registered loaders in sequence when scanning the `plugins/` folder, dispatching each file to the first loader whose `can_load` returns `true`. diff --git a/docs/zh_cn/developer/plugins/wasm-signing.md b/docs/zh_cn/developer/plugins/wasm-signing.md new file mode 100644 index 0000000..40a36eb --- /dev/null +++ b/docs/zh_cn/developer/plugins/wasm-signing.md @@ -0,0 +1,86 @@ +# WASM Plugin Signing & Verification + +Pumpkin implements a cryptographic signing and license verification system for WebAssembly plugins. This ensures plugin integrity, authenticates author identity, and protects verified or marketplace plugins against unauthorized tampering. + +The signing subsystem is implemented in [`crates/pumpkin/src/plugin/loader/wasm/wasm_host/signature.rs`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin/src/plugin/loader/wasm/wasm_host/signature.rs) based on the **W3C `wasmsign2`** specification using custom WebAssembly sections. + +--- + +## Why Sign WebAssembly Plugins? + +- **Tamper Protection**: Ensures that compiled bytecode has not been injected with malicious logic after compilation. +- **Author Identity**: Authenticates that a plugin was built and released by a verified developer. +- **Marketplace Licensing**: Validates license keys and entitlements for plugins distributed through the official [Pumpkin Marketplace](https://market.pumpkinmc.org). + +--- + +## Custom Sections in the WASM Binary + +Pumpkin embeds signing and verification metadata directly into two standard WebAssembly custom sections: +- **`pumpkin.metadata`**: Stores developer, publication, and license claims in JSON format. +- **`wasm_signature`**: Stores the cryptographic signature envelope compliant with the W3C `wasmsign2` standard. + +### 1. `pumpkin.metadata` Section +Contains a JSON string holding publication and license claims: + +```json +{ + "marketplace_url": "https://market.pumpkinmc.org", + "plugin_id": 1024, + "plugin_name": "EconomyPlus", + "version": "1.2.0", + "dev_id": 42, + "dev_name": "DevName", + "is_paid": true, + "user_id": 1001, + "license_key": "XXXX-XXXX-XXXX-XXXX", + "issued_at": "2026-09-14T05:00:00Z" +} +``` + +### 2. `wasm_signature` Section +Follows the W3C `wasmsign2` envelope structure: + +```json +{ + "version": 1, + "algorithm": "Ed25519", + "public_key_hex": "4a7b...", + "signature_hex": "8e1c..." +} +``` + +--- + +## The Signing Pipeline + +The signing process guarantees integrity by hashing and signing the clean WebAssembly bytecode combined with the metadata claims: + +### Signing Algorithm Steps +1. **Sanitization (`strip_pumpkin_sections`)**: Uses [`wasmparser`](https://docs.rs/wasmparser/) to strip any preexisting `pumpkin.metadata` or `wasm_signature` custom sections to avoid recursion. +2. **Payload Construction**: + ```rust + let mut sign_payload = Vec::new(); + sign_payload.extend_from_slice(&clean_wasm); + sign_payload.extend_from_slice(&metadata_json); + ``` +3. **Ed25519 Signature**: The payload is signed using [`ed25519-dalek`](https://docs.rs/ed25519-dalek/) with the developer's 32-byte secret key. +4. **Encoding**: Uses [`wasm-encoder`](https://docs.rs/wasm-encoder/) to append the `pumpkin.metadata` and `wasm_signature` custom sections to the clean binary. + +--- + +## The Verification Pipeline + +When Pumpkin boots and loads a WebAssembly plugin, the verification sequence executes before the plugin is instantiated: + +1. **Custom Section Extraction**: The `WasmPluginLoader` scans the binary for `pumpkin.metadata` and `wasm_signature`. +2. **Key Retrieval**: + - If the plugin is marked as marketplace-verified, Pumpkin verifies the public key against the Marketplace Public Key Endpoint (`https://market.pumpkinmc.org/api/v1/rest/public-key`). + - If signed by an independent developer, the key is matched against trusted developer keys configured in `pumpkin.toml`. +3. **Bytecode Verification**: + - Pumpkin strips the custom sections from the loaded binary to recover the original `clean_wasm`. + - Reconstructs the payload `(clean_wasm + metadata_json)`. + - Verifies the signature using `verifying_key.verify(&payload, &signature)`. +4. **License & Integrity Validation**: + - If verification succeeds, Wasmtime instantiates the plugin safely. + - If verification fails or the signature is invalid, loading is aborted with an error, protecting the server from running compromised code. diff --git a/docs/zh_cn/developer/porting-versions.md b/docs/zh_cn/developer/porting-versions.md new file mode 100644 index 0000000..0a5014a --- /dev/null +++ b/docs/zh_cn/developer/porting-versions.md @@ -0,0 +1,287 @@ +# Porting to Newer Minecraft Versions + +Upgrading Pumpkin to support a newer Minecraft release—whether a minor patch (e.g., `1.21.4` to `1.21.5`) or a major update (e.g., `1.21` to `26.1`/`26.2`)—is a structured, multi-step process. + +> [!IMPORTANT] +> **The Extractor mod must be ported first!** +> Pumpkin cannot be ported in isolation. The primary source for Pumpkin's assets (`blocks.json`, `items.json`, `entities.json`, `packets.json`, `tracked_data.json`, `synced_registries.json`, etc.) is our dedicated Fabric mod: [Pumpkin-MC/Extractor](https://github.com/Pumpkin-MC/Extractor). You **must first port and run the Extractor on the target Minecraft version** before running `pumpkin-codegen` or updating server crates. + +### Porting Lifecycle at a Glance + +| Stage | Responsibility | Primary Crates / Tools | +| :--- | :--- | :--- | +| **1. Port Extractor** | Extract vanilla block states, entities, items, packets, and registries | `Pumpkin-MC/Extractor` (Fabric mod) | +| **2. Update Datapacks** | Ingest new data packs, tags, recipes, and Bedrock mappings | `assets/datapacks/`, `assets/bedrock` | +| **3. Version Declarations** | Register protocol version and ViaVersion/ViaBackwards remap nodes | `pumpkin-util`, `tools/pumpkin-codegen` | +| **4. Code Generation** | Regenerate zero-cost Rust data tables and registries | `pumpkin-codegen` -> `pumpkin-data` | +| **5. Compiler Fixes** | Update packet layouts, codecs, and data components | `pumpkin-protocol`, `pumpkin-inventory` | +| **6. Gameplay Logic** | Implement new block behaviors, item mechanics, and mob AI | `crates/pumpkin/src/block`, `item`, `entity` | +| **7. Multi-Version QA** | Validate backward compatibility, test suites, and client joins | `cargo test`, `pumpkin-gametest` | + +--- + +## Phase 1: Port the Extractor Mod First (`Pumpkin-MC/Extractor`) + +The [Extractor](https://github.com/Pumpkin-MC/Extractor) is a Kotlin-based Fabric mod that boots an actual Minecraft client or server instance to introspect live game registries and extract data into the exact JSON formats Pumpkin expects. + +### 1. Clone or Update the Extractor Repository +```bash +git clone https://github.com/Pumpkin-MC/Extractor +cd Extractor +``` + +### 2. Update `gradle.properties` +Bump the dependency versions to match the target Minecraft version, Yarn mappings, and Fabric ecosystem: + +```properties +minecraft_version=26.2 +yarn_mappings=26.2+build.1:v2 +loader_version=0.16.10 +fabric_version=0.115.0+26.2 +``` + +### 3. Resolve Extractor Compilation Errors +Minecraft and Fabric internal APIs often change between releases. Check and fix any broken extractors in `src/main/kotlin/de/snowii/extractor/extractors/`: +- **`Blocks.kt`**: Extracts block states, hardness, resistance, luminance, and collision box offsets. +- **`Items.kt`**: Extracts item IDs, max stack sizes, and default components. +- **`Entities.kt`**: Extracts entity IDs, dimensions (width, height), and eye heights. +- **`Packets.kt`**: Extracts packet IDs for handshake, status, login, configuration, and play states. +- **`SyncedRegistries.kt`**: Extracts dynamic registries synchronized to clients. +- **`TrackedData.kt`**: Extracts entity tracked data indices and serializers. +- **`MultiNoise.kt` & `Biome.kt`**: Extracts world generation biome trees and noise parameters. + +### 4. Run the Extractor +Execute the server runner (or client runner for translation/screen dumps): + +```bash +./gradlew runServer +``` + +Once the extraction finishes, all dumped JSON files will be located in `run/pumpkin_extractor_output/`. + +### 5. Replace the Old Asset Files in Pumpkin +Copy the newly generated JSON files from `run/pumpkin_extractor_output/` and use them to **replace the old asset files** in Pumpkin: +- Replace the core game assets in `Pumpkin/assets/` (`blocks.json`, `items.json`, `entities.json`, `packets.json`, `properties.json`, `synced_registries.json`, `tracked_data.json`, `en_us.json`, etc.). +- Replace the world generation test files in `Pumpkin/crates/pumpkin-world/assets/`. + +--- + +## Phase 2: Update Assets & Datapacks + +Once the raw extractor assets are in place, update the versioned datapacks and supplemental files in Pumpkin: + +### 1. Add the Versioned Datapack +Extract the internal vanilla data pack from the new Minecraft server JAR and place it under `assets/datapacks//`: + +``` +assets/datapacks// +└── data/ + ├── minecraft/ + │ ├── advancement/ + │ ├── chat_type/ + │ ├── damage_type/ + │ ├── dimension_type/ + │ ├── enchantment/ + │ ├── item_modifier/ + │ ├── loot_table/ + │ ├── recipe/ + │ ├── tags/ + │ └── worldgen/ + └── c/ + └── tags/ (Conventional / Common Tags) +``` + +### 2. Update Versioned Assets +Update or generate the corresponding versioned metadata files: +- `assets/registry/_synced_registries.json`: Dynamic registries streamed to the client during the `Configuration` network state. +- `assets/tags/_tags.json`: Tag collections. +- `assets/tracked_data/_tracked_data.json`: Entity metadata indices. + +### 3. Update Bedrock Mappings +If porting Bedrock Edition support simultaneously, update the files in `assets/bedrock/` using the latest mappings from [GeyserMC](https://github.com/GeyserMC/Geyser) and Bedrock Dedicated Server (BDS). + +### 4. Decompile Sources for Reverse Engineering (Optional but Recommended) +For complex changes—such as worldgen noise router formulas and custom block collision shapes—decompile the matching vanilla JAR into `minecraft-sources-/` (using Vineflower or Fabric Loom with official Mojang mappings) for reference. + +--- + +## Phase 3: Register Version Constants & ViaVersion Mappings + +Pumpkin supports multi-version clients through automated protocol remapping powered by **ViaBackwards** / **ViaRewind** mapping tables. + +### 1. Declare Version in `pumpkin-util` +Open `crates/pumpkin-util/src/version.rs` and add the new version to [`JavaMinecraftVersion`](https://github.com/Pumpkin-MC/Pumpkin/blob/master/crates/pumpkin-util/src/version.rs): + +```rust +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum JavaMinecraftVersion { + // ... existing versions ... + V_1_21_4, + V_1_21_11, + V_26_2, // New target version +} + +impl JavaMinecraftVersion { + pub const fn protocol_version(&self) -> i32 { + match self { + // ... + Self::V_26_2 => 770, // Protocol version number + } + } + + pub const fn version_name(&self) -> &'static str { + match self { + // ... + Self::V_26_2 => "26.2", + } + } +} +``` + +### 2. Declare Version in `pumpkin-codegen` +Open `tools/pumpkin-codegen/src/version.rs` and add the identical enum variant to the codegen AST. + +### 3. Add ViaVersion / ViaBackwards Mappings +To enable clients on older versions to connect to the updated server (e.g., a `1.21.4` client connecting to a `26.2` server): +1. Obtain the NBT mapping file from the [ViaBackwards repository](https://github.com/ViaVersion/ViaBackwards): + `mappings-to.nbt` +2. Save it to: + `assets/viabackwards/data/mappings-to.nbt` +3. In `tools/pumpkin-codegen/src/remap/mod.rs`, link the new node into the `remap_nodes!` macro: + +```rust +let node_26_2 = $crate::remap::MappingNode { + version: $crate::version::JavaMinecraftVersion::V_26_2, + value: "../../assets/viabackwards/data/mappings-26.2to26.1.nbt", + child: Some(&node_26_1), +}; +$remapper.process(&node_26_2) +``` + +4. Check `tools/pumpkin-codegen/src/remap/` to ensure specific remap builders (`block_state.rs`, `item_id.rs`, `entity_id.rs`, `sound_id.rs`, `argument_type.rs`) correctly process any new remapping structures introduced by ViaVersion. + +--- + +## Phase 4: Run Code Generation (`pumpkin-codegen`) + +With all extractor assets, datapacks, and mapping nodes in place, execute the codegen tool from the workspace root: + +```bash +cargo run -p pumpkin-codegen +``` + +### What Gets Regenerated? +`pumpkin-codegen` generates zero-cost Rust data tables into `crates/pumpkin-data/src/generated/`: +- **`block.rs`**: All block IDs, properties, state masks, and collision shapes. +- **`item.rs`**: Items, default components, and tool tiers. +- **`entity_type.rs` & `tracked_data.rs`**: Entity type IDs, default bounding boxes, and metadata serializers. +- **`packet.rs`**: Serverbound and clientbound packet ID constants across protocol states. +- **`recipes.rs`**: All crafting, cooking, and stonecutting recipes parsed from the new datapack. +- **`tag.rs`**: Tag lookups (`#minecraft:mineable/axe`, `#c:iron_ingots`). +- **`remap/*.rs`**: State remap lookup tables (`block_state_remap.rs`, `item_id_remap.rs`, `entity_id_remap.rs`, `sound_id_remap.rs`). + +--- + +## Phase 5: Resolve Workspace Compilation Errors + +After regenerating `pumpkin-data`, the Rust compiler will flag breaking changes across the workspace. Run: + +```bash +cargo check --workspace +``` + +### Common Compiler Breakages & Fixes: + +#### 1. Packet ID & Layout Changes (`pumpkin-protocol`) +Mojang frequently adds, moves, or alters packet IDs and payloads: +- Update `pumpkin-protocol/src/java/client/` and `pumpkin-protocol/src/java/server/`. +- Inspect packets like `LoginSuccess`, `PlayLogin`, `Respawn`, and `PlayerSpawnData` for newly added fields (e.g., new game mode flags, dimension attributes, or teleportation IDs). +- For Bedrock Edition, check `StartGame`, `NetworkItem`, and container transaction packets in `pumpkin-protocol/src/bedrock/`. + +#### 2. Data Component Changes +If items switched from older NBT structures to new Data Components (or if components were renamed/restructured): +- Update `crates/pumpkin-data/src/data_component.rs`. +- Update serialization and deserialization in `pumpkin-inventory`. + +#### 3. Registry Synchronization +During the `Configuration` phase, servers send dynamic registry contents to Java clients. If new registries were added to vanilla Minecraft (e.g., trim materials, banner patterns, instrument sounds): +- Verify `crates/pumpkin-data/src/generated/registry.rs`. +- Ensure `pumpkin/src/net/java/config.rs` streams all required registry codecs to the client. + +--- + +## Phase 6: Implement Logic for New Blocks, Items & Entities + +Once the server compiles, any newly introduced gameplay content must be implemented. + +### 1. New Blocks +1. Locate new block identifiers in `pumpkin-data::Block`. +2. Create a behavior module under `crates/pumpkin/src/block/blocks/.rs` implementing [`BlockBehaviour`](/developer/blocks/adding-blocks): + ```rust + pub struct NewBlockBehaviour; + + impl BlockBehaviour for NewBlockBehaviour { + fn on_use(&self, block: &Block, player: &Arc, location: BlockPos, world: &Arc) { + // Interaction logic + } + } + ``` +3. Register the behavior in the block registry in `crates/pumpkin/src/block/mod.rs`. + +### 2. New Items +1. Locate new item identifiers in `pumpkin-data::Item`. +2. If the item has unique right-click or use behavior (e.g., weapons, projectiles, instruments), create an item behavior in `crates/pumpkin/src/item/items/.rs` implementing [`ItemBehaviour`](/developer/items/adding-items). +3. If the item places entities (e.g., spawn eggs, boats, minecarts), configure entity spawning logic in `pumpkin/src/item/items/spawn_egg.rs`. +4. Register the behavior in `crates/pumpkin/src/item/mod.rs`. + +### 3. New Entities +1. Check `pumpkin-data::entity_type::EntityType` for new entity IDs. +2. Create an entity struct in `crates/pumpkin/src/entity/mob/` or `living/` extending [`LivingEntity`](/developer/entities/). +3. Implement `TrackedData` fields to match vanilla client expectations (e.g., poses, animations, glowing states). +4. Assign AI goals and navigation tasks using the [Mob AI & Goals system](/developer/entities/ai-and-mobs). +5. Add spawn packet construction in `crates/pumpkin/src/entity/spawning.rs`. + +--- + +## Phase 7: Multi-Version & ViaVersion Verification + +Ensure that older clients can still connect seamlessly: + +1. **Verify Generated Remap Tables**: + Check `crates/pumpkin-data/src/generated/block_state_remap.rs` and `item_id_remap.rs`. Ensure that novel block states fall back gracefully to compatible equivalents on older client versions (e.g., new wood variants mapping to oak or spruce). +2. **Packet Translation**: + Verify that packet writers in `pumpkin-protocol` apply `remap` functions when serializing data for a connection whose negotiated `protocol_version` is older than the server's native version. + +--- + +## Phase 8: Testing & Verification Checklist + +Before opening a Pull Request for a version port, complete the following QA checklist: + +- [ ] **Automated Tests**: + ```bash + cargo test --workspace + ``` +- [ ] **Game Test Suite**: + ```bash + cargo test -p pumpkin-gametest + ``` +- [ ] **Strict Linter Rules**: + ```bash + cargo clippy --workspace --all-targets -- -D warnings + ``` +- [ ] **Spellcheck**: + ```bash + typos + ``` +- [ ] **In-Game Java Client Test**: + - Connect with the native new Minecraft client version. + - Test login, world loading, chunk generation, and player movement. + - Place and break new blocks; verify collision boxes and redstone reactivity. + - Use and equip new items; test attack speed, durability, and animations. + - Spawn and fight new entities; test animations, hurt sounds, and drops. +- [ ] **In-Game Older Version Client Test**: + - Connect with an older supported client version (e.g., 1.21.x). + - Verify that blocks, items, and entities translate without causing client crashes or desyncs. +- [ ] **In-Game Bedrock Client Test**: + - Verify that Bedrock clients can authenticate, join, load world chunks, and view custom items. diff --git a/docs/zh_cn/plugin-dev/c/first-command.md b/docs/zh_cn/plugin-dev/c/first-command.md new file mode 100644 index 0000000..3ac3ebd --- /dev/null +++ b/docs/zh_cn/plugin-dev/c/first-command.md @@ -0,0 +1,90 @@ +# Creating Your First Command + +Writing commands in C for Pumpkin is low-overhead, fast, and uses C WIT bindings. + +--- + +## 1. Quick Example + +Here is a complete C plugin file (`main.c`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```c [main.c] +#include +#include "pumpkin.h" + +void plugin_on_load(pumpkin_context_t ctx) { + // 1. Register permission node + pumpkin_permission_t perm = { + .node = "my_c_plugin:hello", + .description = "Allows executing the /hello command", + .default_access = PERMISSION_DEFAULT_ALLOW + }; + pumpkin_register_permission(ctx, &perm); + + // 2. Build and register command tree + const char* names[] = {"hello", "hi"}; + pumpkin_command_t cmd = pumpkin_command_new(names, 2, "Greets the player"); + + pumpkin_register_command(ctx, cmd, "my_c_plugin:hello"); +} + +int32_t plugin_handle_command(uint32_t command_id, pumpkin_sender_t sender, pumpkin_server_t server) { + pumpkin_sender_send_message(sender, "Hello from C Plugin!"); + return 1; +} + +pumpkin_metadata_t plugin_get_metadata() { + pumpkin_metadata_t meta = { + .name = "my_c_plugin", + .version = "0.1.0", + .description = "My first Pumpkin C plugin" + }; + return meta; +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Struct +Populate a `pumpkin_permission_t` struct and call `pumpkin_register_permission`: +```c +pumpkin_permission_t perm = { + .node = "my_c_plugin:hello", + .description = "Allows executing the /hello command", + .default_access = PERMISSION_DEFAULT_ALLOW +}; +pumpkin_register_permission(ctx, &perm); +``` + +### Step 2: Build the Command Tree +Create the command tree by passing name array and description: +```c +const char* names[] = {"hello", "hi"}; +pumpkin_command_t cmd = pumpkin_command_new(names, 2, "Greets the player"); +``` + +### Step 3: Register Command & Handle Execution +Pass the command handle to `pumpkin_register_command` and handle responses inside `plugin_handle_command`: +```c +pumpkin_register_command(ctx, cmd, "my_c_plugin:hello"); +``` + +::: + +--- + +## Next Steps + +- Check out the [C Quick Start Guide](./quick-start) to set up your compiler toolchain. diff --git a/docs/zh_cn/plugin-dev/csharp/first-command.md b/docs/zh_cn/plugin-dev/csharp/first-command.md new file mode 100644 index 0000000..c2a34ca --- /dev/null +++ b/docs/zh_cn/plugin-dev/csharp/first-command.md @@ -0,0 +1,110 @@ +# Creating Your First Command + +Registering custom commands in Pumpkin using C# provides a strongly-typed, clean structure. + +--- + +## 1. Quick Example + +Here is a complete C# plugin file (`MyPlugin.cs`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```csharp [MyPlugin.cs] +using PluginWorld; +using PluginWorld.wit.Exports.pumpkin.plugin.v0_1_0; +using PluginWorld.wit.Imports.pumpkin.plugin.v0_1_0; + +namespace MyPumpkinPlugin; + +public class MyPlugin : IPluginWorldExports, IMetadataExports +{ + public static void InitPlugin() { } + + public static void OnLoad(IContextImports.Context context) + { + // 1. Register permission node + var perm = new IPermissionImports.Permission( + "my_csharp_plugin:hello", + "Allows executing the /hello command", + IPermissionImports.PermissionDefault.Allow, + [] + ); + IContextImports.RegisterPermission(context, perm); + + // 2. Build and register command tree + var command = ICommandImports.Command.New(["hello", "hi"], "Greets the player"); + IContextImports.RegisterCommand(context, command, "my_csharp_plugin:hello"); + } + + public static void OnUnload(IContextImports.Context context) { } + + public static IEventImports.Event HandleEvent(uint eventId, IServerImports.Server server, IEventImports.Event @event) + { + return @event; + } + + public static int HandleCommand(uint commandId, ICommandImports.CommandSender sender, IServerImports.Server server, ICommandImports.ConsumedArgs args) + { + ICommandImports.SendMessage(sender, "Hello from C# Plugin!"); + return 1; + } + + public static void HandleTask(uint handlerId, IServerImports.Server server) { } + + public static IMetadataExports.PluginMetadata Metadata() + { + return new IMetadataExports.PluginMetadata( + "my_csharp_plugin", + "0.1.0", + "My first Pumpkin C# plugin", + ["Developer"], + [] + ); + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Node +Construct an `IPermissionImports.Permission` record and register it via `IContextImports.RegisterPermission`: +```csharp +var perm = new IPermissionImports.Permission( + "my_csharp_plugin:hello", + "Allows executing the /hello command", + IPermissionImports.PermissionDefault.Allow, + [] +); +IContextImports.RegisterPermission(context, perm); +``` + +### Step 2: Build the Command Tree +Instantiate your command with primary names and description: +```csharp +var command = ICommandImports.Command.New(["hello", "hi"], "Greets the player"); +``` + +### Step 3: Register and Handle +Register the command with `IContextImports.RegisterCommand` and process incoming executions inside `HandleCommand`: +```csharp +IContextImports.RegisterCommand(context, command, "my_csharp_plugin:hello"); +``` + +::: + +--- + +## Next Steps + +- Follow the [C# Quick Start Guide](./quick-start) to configure `.csproj` and build WebAssembly outputs. diff --git a/docs/zh_cn/plugin-dev/d/quick-start.md b/docs/zh_cn/plugin-dev/d/quick-start.md new file mode 100644 index 0000000..6e7983f --- /dev/null +++ b/docs/zh_cn/plugin-dev/d/quick-start.md @@ -0,0 +1,130 @@ +# 快速开始 + +本指南将帮助你开始使用 [D 语言](https://dlang.org/)编写 Pumpkin 服务器插件。 + +针对 Pumpkin 的 D 语言插件通过官方提供的 [`pumpkin-api-d`](https://github.com/Pumpkin-MC/pumpkin-api-d) 绑定编译为 WebAssembly(Wasm)组件。 + +--- + +## 前置要求 + +在构建 D 语言插件之前,请确保已安装以下工具: + +- **[LDC](https://github.com/ldc-developers/ldc)**:1.43 或更高版本(基于 LLVM 的 D 语言编译器)。 +- **`addon-wasi`**:LDC 的 WASI 目标支持包。 +- **[DUB](https://dub.pm/)**:D 语言的包管理器与构建工具(通常随 LDC 一起提供)。 + +--- + +## 1. 初始化项目 + +为你的插件项目创建一个新目录,并添加 `dub.json` 配置文件: + +```bash +mkdir my-d-plugin +cd my-d-plugin +``` + +在项目根目录下创建 `dub.json`: + +```json [dub.json] +{ + "name": "my-d-plugin", + "description": "基于 D 语言构建的极简 Pumpkin 插件。", + "license": "proprietary", + "authors": ["你的名字"], + "copyright": "Copyright © 2026, 你的名字", + + "dependencies": { + "pumpkin-api-d": "~>0.1.0" + }, + + "targetType": "executable", + "dflags": ["-Xcc=-mexec-model=reactor"], + "buildTypes": { + "debug": { + "buildOptions": ["debugMode", "debugInfo"] + }, + "release": { + "buildOptions": ["optimize", "inline"], + "dflags": ["-L-S"] + } + } +} +``` + +> [!NOTE] +> WebAssembly 链接器需要 `-Xcc=-mexec-model=reactor` 标志,以便将该插件作为响应器(暴露入口点的组件)处理,而不是作为具有退出点的命令行程序。 + +--- + +## 2. 编写插件代码 + +创建 `source` 目录并在 `source/app.d` 中添加代码: + +```d [source/app.d] +module plugin; + +import wit.pumpkin.plugin.plugin; +import wit.common; +import pumpkin.register; + +struct MyPlugin { + @GetMetadata PluginMetadata getMetadata() + { + immutable(WitString)[$] authors = [ + "你的名字".witList + ]; + + return (immutable PluginMetadata( + name: "my-d-plugin".witList, + version_: "0.1.0".witList, + authors: authors.witList, + description: "D 语言编写的示例插件。".witList, + dependencies: WitList!WitString(), + permissions: WitList!WitString() + )).witClone; + } + + @OnLoad + Result!(void, WitString) onLoad(Context ctx) + { + scope (exit) + ctx.witDrop; // 传入的资源在使用后必须释放 + + log(Level.info, "来自 D 语言插件的问候!".witList); + return ok!WitString; + } +} + +mixin RegisterPlugin!MyPlugin; +``` + +--- + +## 3. 构建插件 + +将插件编译为面向 `wasm32-wasip2` 的 WebAssembly 组件: + +```bash +dub build -b release -a wasm32-wasip2 +``` + +编译完成后,会在项目根目录生成 `my-d-plugin.wasm` 文件。 + +--- + +## 4. 运行插件 + +1. 将生成的 `.wasm` 文件复制到 Pumpkin 服务器的 `plugins/` 目录中: + ```bash + cp my-d-plugin.wasm /path/to/pumpkin/plugins/ + ``` +2. 启动或重启 Pumpkin 服务器: + ```bash + ./pumpkin + ``` +3. 查看控制台日志确认插件已成功加载: + ```text + [INFO] 来自 D 语言插件的问候! + ``` diff --git a/docs/zh_cn/plugin-dev/go/basic-logic.md b/docs/zh_cn/plugin-dev/go/basic-logic.md new file mode 100644 index 0000000..7e0e4a6 --- /dev/null +++ b/docs/zh_cn/plugin-dev/go/basic-logic.md @@ -0,0 +1,60 @@ +# Writing the basic logic + +## Plugin Entry Point + +In Go, your plugin must implement the `api.Plugin` interface and register itself in the `init()` function. You also need to import `wit_exports` for WebAssembly compatibility. + +:::code-group + +```go [main.go] +package main + +import ( + "github.com/Pumpkin-MC/pumpkin-api-go/api" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_context" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_logging" + _ "github.com/Pumpkin-MC/pumpkin-api-go/pkg/wit_exports" // Required for WASM exports +) + +type MyPlugin struct { + api.DefaultPlugin +} + +func (p *MyPlugin) Metadata() api.Metadata { + return api.Metadata{ + Name: "my-go-plugin", + Version: "0.1.0", + Authors: []string{"you"}, + } +} + +func (p *MyPlugin) OnLoad(ctx *pumpkin_plugin_context.Context) { + pumpkin_plugin_logging.Log(pumpkin_plugin_logging.LevelInfo(), "Go plugin loaded!") +} + +func init() { + api.RegisterPlugin(&MyPlugin{}) +} + +func main() {} +``` + +::: + +## Compiling the plugin + +To compile your plugin to WebAssembly, you must use **TinyGo**. The standard Go compiler does not yet support the specific WASI targets required by Pumpkin in the same way. + +Run the following command in your project folder: + +```bash +tinygo build -o my_plugin.wasm -target=wasi main.go +``` + +This will generate a `my_plugin.wasm` file that can be loaded by Pumpkin. + +## Testing the plugin + +Installing a plugin is as simple as putting the plugin binary (`.wasm`) into the `plugins/` folder of your Pumpkin server! + +When you start up the server and run the `/plugins` command, you should see your plugin listed. diff --git a/docs/zh_cn/plugin-dev/go/first-command.md b/docs/zh_cn/plugin-dev/go/first-command.md new file mode 100644 index 0000000..f20ffdd --- /dev/null +++ b/docs/zh_cn/plugin-dev/go/first-command.md @@ -0,0 +1,103 @@ +# Creating Your First Command + +Registering commands in Go with Pumpkin is built on standard Go idiom using TinyGo WASM compilation. + +--- + +## 1. Quick Example + +Here is a complete Go plugin file (`main.go`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```go [main.go] +package main + +import ( + "github.com/Pumpkin-MC/pumpkin-api-go/api" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_command" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_context" + "github.com/Pumpkin-MC/pumpkin-api-go/pkg/pumpkin_plugin_permission" + _ "github.com/Pumpkin-MC/pumpkin-api-go/pkg/wit_exports" +) + +type MyPlugin struct { + api.DefaultPlugin +} + +func (p *MyPlugin) Metadata() api.Metadata { + return api.Metadata{ + Name: "my_go_plugin", + Version: "0.1.0", + Authors: []string{"Developer"}, + Description: "My first Pumpkin Go plugin", + } +} + +func (p *MyPlugin) OnLoad(ctx *pumpkin_plugin_context.Context) { + // 1. Register permission node + ctx.RegisterPermission(pumpkin_plugin_permission.Permission{ + Node: "my_go_plugin:hello", + Description: "Allows executing the /hello command", + Default: pumpkin_plugin_permission.PermissionDefaultAllow(), + }) + + // 2. Build and register command tree + cmd := pumpkin_plugin_command.New([]string{"hello", "hi"}, "Greets the player") + ctx.RegisterCommand(cmd, "my_go_plugin:hello") +} + +func (p *MyPlugin) HandleCommand(cmdID uint32, sender pumpkin_plugin_command.CommandSender) int32 { + sender.SendMessage("Hello from Go Plugin!") + return 1 +} + +func init() { + api.RegisterPlugin(&MyPlugin{}) +} + +func main() {} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Register Permission +Call `ctx.RegisterPermission` inside `OnLoad`: +```go +ctx.RegisterPermission(pumpkin_plugin_permission.Permission{ + Node: "my_go_plugin:hello", + Description: "Allows executing the /hello command", + Default: pumpkin_plugin_permission.PermissionDefaultAllow(), +}) +``` + +### Step 2: Build the Command Tree +Instantiate the command tree with `pumpkin_plugin_command.New`: +```go +cmd := pumpkin_plugin_command.New([]string{"hello", "hi"}, "Greets the player") +``` + +### Step 3: Register & Execute +Register the command with `ctx.RegisterCommand` and handle execution in `HandleCommand`: +```go +ctx.RegisterCommand(cmd, "my_go_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Follow the [Go Quick Start Guide](./quick-start) to set up TinyGo compilation. +- Learn more about [Basic Logic](./basic-logic). diff --git a/docs/zh_cn/plugin-dev/go/quick-start.md b/docs/zh_cn/plugin-dev/go/quick-start.md new file mode 100644 index 0000000..90bacb4 --- /dev/null +++ b/docs/zh_cn/plugin-dev/go/quick-start.md @@ -0,0 +1,35 @@ +# Creating a new project + +Pumpkin plugins use the [Go](https://go.dev/) programming language and are compiled to WebAssembly. + +## Prerequisites + +Before we can start, make sure you have the following installed: +- [Go](https://go.dev/doc/install) (latest version recommended) +- [TinyGo](https://tinygo.org/getting-started/install/) (required for WASM compilation) + +## Initializing a new module + +First, create a new directory for your project and initialize a Go module: + +```bash +mkdir hello-pumpkin +cd hello-pumpkin +go mod init github.com/yourname/hello-pumpkin +``` + +Next, add the Pumpkin Go API as a dependency: + +```bash +go get github.com/Pumpkin-MC/pumpkin-api-go +``` + +## Project Structure + +Your project should have at least a `main.go` file. The structure should look like this: + +```text +├── go.mod +├── go.sum +└── main.go +``` diff --git a/docs/zh_cn/plugin-dev/introduction.md b/docs/zh_cn/plugin-dev/introduction.md index ff1283b..b7d8044 100644 --- a/docs/zh_cn/plugin-dev/introduction.md +++ b/docs/zh_cn/plugin-dev/introduction.md @@ -14,3 +14,5 @@ Pumpkin 插件 API 在很多方面受到 Spigot/Bukkit 插件 API 的启发, * [C# API](https://github.com/Pumpkin-MC/pumpkin-api-cs) * [Go API](https://github.com/Pumpkin-MC/pumpkin-api-go) * [C API](https://github.com/Pumpkin-MC/pumpkin-api-c) +* [Kotlin API](https://github.com/Pumpkin-MC/pumpkin-api-kt) +* [D API](https://github.com/Pumpkin-MC/pumpkin-api-d) diff --git a/docs/zh_cn/plugin-dev/kotlin/first-command.md b/docs/zh_cn/plugin-dev/kotlin/first-command.md new file mode 100644 index 0000000..12831f5 --- /dev/null +++ b/docs/zh_cn/plugin-dev/kotlin/first-command.md @@ -0,0 +1,94 @@ +# Creating Your First Command + +Registering custom commands in Pumpkin using Kotlin is concise, idiomatic, and strongly typed. + +--- + +## 1. Quick Example + +Here is a complete Kotlin plugin (`Plugin.kt`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```kotlin [src/wasmWasiMain/kotlin/plugin/Plugin.kt] +package plugin + +import pumpkin.plugin.context.Context +import pumpkin.plugin.command.Command +import pumpkin.plugin.permission.Permission +import pumpkin.plugin.permission.PermissionDefault + +class MyPlugin { + fun onLoad(ctx: Context) { + // 1. Register permission node + ctx.registerPermission( + Permission( + node = "my_kotlin_plugin:hello", + description = "Allows executing the /hello command", + default = PermissionDefault.ALLOW, + children = emptyList() + ) + ) + + // 2. Build and register command tree + val cmd = Command( + names = listOf("hello", "hi"), + description = "Greets the player" + ) + ctx.registerCommand(cmd, "my_kotlin_plugin:hello") + } + + fun handleCommand(sender: pumpkin.plugin.command.CommandSender): Int { + sender.sendMessage("Hello from Kotlin Plugin!") + return 1 + } +} +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission Node +Construct a `Permission` object and pass it to `ctx.registerPermission`: +```kotlin +ctx.registerPermission( + Permission( + node = "my_kotlin_plugin:hello", + description = "Allows executing the /hello command", + default = PermissionDefault.ALLOW, + children = emptyList() + ) +) +``` + +### Step 2: Build the Command Tree +Instantiate `Command` with primary names and aliases: +```kotlin +val cmd = Command( + names = listOf("hello", "hi"), + description = "Greets the player" +) +``` + +### Step 3: Register with Context +Register the command with `ctx.registerCommand` and handle calls inside `handleCommand`: +```kotlin +ctx.registerCommand(cmd, "my_kotlin_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Follow the [Kotlin Quick Start Guide](./quick-start) to configure your `Makefile` and Kotlin/Wasm build tools. diff --git a/docs/zh_cn/plugin-dev/kotlin/quick-start.md b/docs/zh_cn/plugin-dev/kotlin/quick-start.md new file mode 100644 index 0000000..acefd9c --- /dev/null +++ b/docs/zh_cn/plugin-dev/kotlin/quick-start.md @@ -0,0 +1,81 @@ +# Quick Start + +This guide will help you get started with writing Pumpkin server plugins using Kotlin. + +:::warning +Until the Kotlin + Wasm component toolchain matures more, there will be some oddity and inconvenience. + +Bugs are also expected. +::: + +## Prerequisites + +Before you start, ensure you have the following installed: +- JDK 17 or later + - To run Gradle 9.4 +- [Rust](https://rust-lang.org/) + - This is required as a key component (wit-bindgen) is written and Rust must be built from a particular Kotlin-enabled fork. + - You only need a default Rust install for your host platform. NOT for any WebAssembly targets +- [wasm-tools](https://github.com/bytecodealliance/wasm-tools) + - To bundle the Wasm Kotlin produces into a component +- Make (e.g. [GNU Make](https://www.gnu.org/software/make/)) + - To run the convenience `Makefile`. You may opt to do without and perform the contained steps manually. + +## Setting up the project + +Unlike most of the other APIs packages available, [pumpkin-api-kt](https://github.com/Pumpkin-MC/pumpkin-api-kt) is a TEMPLATE, not e.g. a Maven package. + +To start off, clone the template (and rename it as you please): +```sh +git clone --recurse-submodules https://github.com/Pumpkin-MC/pumpkin-api-kt +mv pumpkin-api-kt my_kotlin_plugin +cd my_kotlin_plugin +``` + +Next, we'll want to update the `wit` submodule so we are using the latest version of the Pumpkin plugin API. + +```sh +cd wit +git pull origin master +cd .. +``` + +Finally, rename the Gradle project. Change the `rootProject.name` in `settings.gradle.kts`, AND the `PROJECT_NAME` in `Makefile`. They must both match. Whatever you name the project, is going to be the filename of the Wasm produced. + +## Creating your first plugin + +As part of the template, a basic plugin is implemented in `src/wasmWasiMain/kotlin/plugin/Plugin.kt`. + +Feel free to modify the metadata at the bottom. However, before changing anything else, it's recommended that you build the plugin (see next section) first so that the bindings are generated and IDE completion works for `pumpkin`. + +## Building the plugin + +To build your plugin into a WebAssembly component: + +```sh +make +``` + +The compiled `.wasm` file will be located in `build`. You can place this file in the `plugins` folder of your Pumpkin server. + +Note that doing this the first time might take awhile, while it builds `wit-bindgen` from Rust source. + +Running `make` will check for `wit-bindgen` updates each time. You may want to run instead `make componentify` after the initial setup to avoid this. + +## Troubleshooting + +### Linker errors +If you start getting errors like +``` +main ThreadId(01) pumpkin::plugin: Failed to load plugin from +"./plugins/my_plugin.wasm": Wasm plugin initialization error: plugin failed +to load with error: component imports instance 'pumpkin:plugin/gui@0.1.0', but +a matching implementation was not found in the linker +``` +or other "linker" errors when loading your plugin into Pumpkin, update the `wit` submodule. + +```sh +cd wit +git pull origin master +cd .. +``` diff --git a/docs/zh_cn/plugin-dev/migrating-from-bukkit/commands.md b/docs/zh_cn/plugin-dev/migrating-from-bukkit/commands.md new file mode 100644 index 0000000..4320902 --- /dev/null +++ b/docs/zh_cn/plugin-dev/migrating-from-bukkit/commands.md @@ -0,0 +1,171 @@ +# 从 Bukkit 迁移命令 + +在 Bukkit 中,命令通常在 `plugin.yml` 中定义,并通过 `CommandExecutor` 类或 Brigadier 库进行处理。 + +而在 Pumpkin 中,命令使用**受 Brigadier 启发的命令树**构建,并通过 `context.register_command` 以编程方式进行注册。不需要使用任何 YAML 配置文件。 + +--- + +## 核心差异 + +| 特性 | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **命令声明** | 在 `plugin.yml` 的 `commands:` 下声明 | 在代码中使用 `Command::new(...)` 构建 | +| **权限控制** | 在 `plugin.yml` 中关联或通过 `player.hasPermission()` 检查 | 在 `register_command()` 时绑定显式权限节点 | +| **Tab 补全** | 在 `TabCompleter` 中手动进行字符串匹配 | 基于命令树在客户端自动完成 Tab 补全 | +| **子命令** | 手动判断 `if (args[0].equalsIgnoreCase("..."))` | 嵌套的 `Command` 命令树节点 | + +--- + +## 代码对比:简单命令 + +对比在 Bukkit 与 Pumpkin 中注册 `/feed` 命令的实现方式: + +### 1. Bukkit 实现 (Java) + +```java [FeedCommand.java] +// 1. Registered in plugin.yml: +// commands: +// feed: +// description: Feeds the player +// permission: myplugin.feed + +public class FeedCommand implements CommandExecutor { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("Only players can execute this command!"); + return true; + } + Player player = (Player) sender; + if (!player.hasPermission("myplugin.feed")) { + player.sendMessage("No permission!"); + return true; + } + player.setFoodLevel(20); + player.sendMessage("Your hunger has been satisfied!"); + return true; + } +} +``` + +### 2. Pumpkin 实现 + +::: code-group + +```rust [Rust] +use pumpkin_plugin_api::{ + command::{CommandHandler, CommandSender, ConsumedArgs, CommandError, Command}, + permission::{Permission, PermissionDefault}, + Context, Plugin, PluginMetadata, Server, +}; + +struct FeedExecutor; + +impl CommandHandler for FeedExecutor { + fn handle(&self, sender: CommandSender, _server: Server, _args: ConsumedArgs) -> Result { + sender.send_message("Your hunger has been satisfied!"); + Ok(1) + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { MyPlugin } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "feed_plugin".into(), + version: "1.0.0".into(), + authors: vec!["Developer".into()], + description: "Feed command plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Register permission node (Replaces plugin.yml permission) + context.register_permission(&Permission { + node: "feed_plugin:feed".to_string(), + description: "Allows executing /feed".to_string(), + default: PermissionDefault::Allow, + children: Vec::new(), + })?; + + // 2. Build command tree + let command = Command::new(&["feed".to_string(), "eat".to_string()], "Feeds the player") + .execute(FeedExecutor); + + // 3. Register with context + context.register_command(command, "feed_plugin:feed")?; + + Ok(()) + } +} +``` + +```python [Python] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + permission, command, context, server +) + +class FeedExecutor: + def handle(self, sender: command.CommandSender, srv: server.Server, args: command.ConsumedArgs) -> int: + sender.send_message("Your hunger has been satisfied!") + return 1 + +class FeedPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata(name="feed_plugin", version="1.0.0", authors=["Dev"], description="Feed command") + + def on_load(self, ctx: context.Context) -> None: + ctx.register_permission(permission.Permission( + node="feed_plugin:feed", + description="Allows executing /feed", + default=permission.PermissionDefault.ALLOW, + children=[] + )) + + cmd = command.Command(["feed", "eat"], "Feeds the player") + cmd.execute(FeedExecutor()) + ctx.register_command(cmd, "feed_plugin:feed") + + register_plugin(FeedPlugin) +``` + +```kotlin [Kotlin] +package plugin + +import pumpkin.plugin.context.Context +import pumpkin.plugin.command.Command +import pumpkin.plugin.command.CommandSender +import pumpkin.plugin.permission.Permission +import pumpkin.plugin.permission.PermissionDefault + +class FeedPlugin { + fun onLoad(ctx: Context) { + ctx.registerPermission( + Permission( + node = "feed_plugin:feed", + description = "Allows executing /feed", + default = PermissionDefault.ALLOW, + children = emptyList() + ) + ) + + val cmd = Command( + names = listOf("feed", "eat"), + description = "Feeds the player" + ) + ctx.registerCommand(cmd, "feed_plugin:feed") + } + + fun handleCommand(sender: CommandSender): Int { + sender.sendMessage("Your hunger has been satisfied!") + return 1 + } +} +``` + +::: diff --git a/docs/zh_cn/plugin-dev/migrating-from-bukkit/configuration.md b/docs/zh_cn/plugin-dev/migrating-from-bukkit/configuration.md new file mode 100644 index 0000000..d956ab6 --- /dev/null +++ b/docs/zh_cn/plugin-dev/migrating-from-bukkit/configuration.md @@ -0,0 +1,69 @@ +# 从 Bukkit 迁移配置与数据存储 + +Bukkit 插件严重依赖 `config.yml`、`YamlConfiguration` 以及 Bukkit 的配置序列化系统。 + +而在 Pumpkin 中,WebAssembly 插件运行在安全的沙箱环境中,可以使用 **TOML**、**JSON** 或 **YAML** 等标准格式以原生方式处理配置文件。 + +--- + +## 核心差异 + +| 特性 | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **配置文件格式** | `config.yml`(YAML 格式) | 灵活多样:TOML、JSON 或自定义文件 | +| **默认配置生成** | `saveDefaultConfig()` | 从插件目录或嵌入式 WASM 资源中读取配置 | +| **数据解析** | `getConfig().getString(...)` | 原生 serde / JSON / TOML 反序列化 | +| **文件 I/O 沙箱** | 可在任何位置直接访问文件系统 | 授予 WASM 插件沙箱化的目录访问权限 | + +--- + +## 代码对比:加载配置 + +### 1. Bukkit 实现 (Java) + +```java [ConfigExample.java] +public class ConfigExample extends JavaPlugin { + @Override + public void onEnable() { + saveDefaultConfig(); // Saves config.yml from jar resources + FileConfiguration config = getConfig(); + + String welcomeMsg = config.getString("welcome-message", "Default welcome!"); + int maxItems = config.getInt("max-items", 10); + getLogger().info("Loaded message: " + welcomeMsg); + } +} +``` + +### 2. Pumpkin 实现 (Rust) + +在 Pumpkin 中,可以通过派生 `serde::Deserialize` 来解析简洁的 TOML 配置文件: + +```rust [config.rs] +use serde::Deserialize; +use std::fs; + +#[derive(Deserialize, Debug)] +pub struct PluginConfig { + pub welcome_message: String, + pub max_items: i32, +} + +impl Default for PluginConfig { + fn default() -> Self { + Self { + welcome_message: "Default welcome!".to_string(), + max_items: 10, + } + } +} + +pub fn load_config() -> PluginConfig { + let config_path = "plugins/my_plugin/config.toml"; + if let Ok(content) = fs::read_to_string(config_path) { + toml::from_str(&content).unwrap_or_default() + } else { + PluginConfig::default() + } +} +``` diff --git a/docs/zh_cn/plugin-dev/migrating-from-bukkit/events.md b/docs/zh_cn/plugin-dev/migrating-from-bukkit/events.md new file mode 100644 index 0000000..92865fe --- /dev/null +++ b/docs/zh_cn/plugin-dev/migrating-from-bukkit/events.md @@ -0,0 +1,117 @@ +# 从 Bukkit 迁移事件 + +在 Bukkit 中,事件处理通过创建实现 `Listener` 接口的类,并用 `@EventHandler` 注解方法来完成。 + +而在 Pumpkin 中,事件处理器是结构体或对象,并通过 `context.register_event_handler(...)` 进行显式注册。此外,Pumpkin 还区分了**阻塞(Blocking)**与**非阻塞(Non-blocking)**事件,以最大化服务器的并发性能。 + +--- + +## 核心差异 + +| 特性 | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **监听器声明** | 方法上的 `@EventHandler` 注解 | 实现 `EventHandler` trait / 接口 | +| **事件注册** | `pm.registerEvents(listener, plugin)` | `context.register_event_handler(handler, priority, is_blocking)` | +| **执行模型** | 主线程执行(`EventPriority`) | 可选**阻塞**(顺序执行/可取消)与**非阻塞**(并发执行) | +| **事件取消** | `event.setCancelled(true)` | 在阻塞事件上调用 `event.cancel()` | + +--- + +## Pumpkin 中的阻塞与非阻塞事件 + +与 Bukkit 中所有事件处理器都在主服务器线程上按顺序执行不同,Pumpkin 允许您指定事件处理器是否为**阻塞**类型: + +```rust +// Registration signature in Rust: +context.register_event_handler(handler, priority, is_blocking)?; +``` + +- **阻塞(`is_blocking = true`)**:按优先级顺序依次执行。可以修改事件数据(例如修改进服消息)或取消事件。 +- **非阻塞(`is_blocking = false`)**:在工作线程间并发执行。非常适合日志记录、指标收集或无需取消事件的外部通知。 + +--- + +## 代码对比:玩家加入事件 + +### 1. Bukkit 实现 (Java) + +```java [JoinListener.java] +public class JoinListener implements Listener { + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + event.setJoinMessage("Welcome " + player.getName() + " to the server!"); + } +} + +// In JavaPlugin: +// getServer().getPluginManager().registerEvents(new JoinListener(), this); +``` + +### 2. Pumpkin 实现 + +::: code-group + +```rust [Rust] +use pumpkin_plugin_api::{ + Context, Plugin, PluginMetadata, Server, + events::{EventData, EventHandler, EventPriority, PlayerJoinEvent}, + text::TextComponent, +}; + +struct JoinHandler; + +impl EventHandler for JoinHandler { + fn handle<'a>( + &'a self, + _server: Server, + mut event: EventData, + ) -> EventData { + event.join_message = TextComponent::text("Welcome to the Pumpkin server!"); + event + } +} + +pub struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { MyPlugin } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "join_plugin".into(), + version: "1.0.0".into(), + authors: vec!["Developer".into()], + description: "Join event handler".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Register blocking event handler with Normal priority + context.register_event_handler(JoinHandler, EventPriority::Normal, true)?; + Ok(()) + } +} +``` + +```python [Python] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + event, server, context +) + +class MyPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata(name="join_plugin", version="1.0.0", authors=["Dev"], description="Join event") + + def on_load(self, ctx: context.Context) -> None: + self.register_event(ctx, event.EventType.PLAYER_JOIN_EVENT, self.on_player_join) + + def on_player_join(self, srv: server.Server, evt: event.PlayerJoinEventData) -> event.PlayerJoinEventData: + print(f"Player joined!") + return evt + +register_plugin(MyPlugin) +``` + +::: diff --git a/docs/zh_cn/plugin-dev/migrating-from-bukkit/index.md b/docs/zh_cn/plugin-dev/migrating-from-bukkit/index.md new file mode 100644 index 0000000..4d5a3ab --- /dev/null +++ b/docs/zh_cn/plugin-dev/migrating-from-bukkit/index.md @@ -0,0 +1,27 @@ +# 迁移概述与架构 + +从 **Bukkit / Spigot / Paper** 插件开发迁移到 Pumpkin,意味着从以 Java 为中心的面向对象模型转变为编译型的多语言 **WebAssembly (WASM)** 组件模型。 + +--- + +下表对比了传统的 Bukkit/Paper 范式与 Pumpkin 现代 WebAssembly 架构的差异: + +| 概念 | Bukkit / Spigot / Paper | Pumpkin | +| :--- | :--- | :--- | +| **语言支持** | Java / Kotlin / Scala (JVM) | Rust、Python、Kotlin、C#、Go、C | +| **二进制输出** | `.jar` Java 归档 | `.wasm` WebAssembly 组件 | +| **插件描述符** | `plugin.yml` 文件 | 声明式代码 `PluginMetadata` 结构体 | +| **生命周期钩子** | `onEnable()` / `onDisable()` | `on_load(context)` / `on_unload(context)` | +| **安全性与隔离性** | 无限制的 JVM 反射 | 沙箱化 WASM 权能模型 | +| **并发模型** | 单线程 Tick 循环 (`BukkitScheduler`) | 带有异步运行时的多线程原生执行 | + +--- + +## 详细迁移主题 + +探索关于迁移各个主要插件子系统的专属指南: + +- [迁移命令](./commands) — 从 `getCommand().setExecutor()` 和 `plugin.yml` 过渡到 Brigadier 命令树。 +- [迁移事件](./events) — 使用 Pumpkin 的阻塞与非阻塞事件系统替换 `@EventHandler` 和 `Listener` 接口。 +- [迁移物品栏与 GUI](./inventories) — 从 `Bukkit.createInventory()` 迁移到 Pumpkin 容器与窗口处理器。 +- [迁移配置与数据](./configuration) — 使用原生 TOML、JSON 或自定义存储替换 `getConfig()` / `config.yml`。 diff --git a/docs/zh_cn/plugin-dev/migrating-from-bukkit/inventories.md b/docs/zh_cn/plugin-dev/migrating-from-bukkit/inventories.md new file mode 100644 index 0000000..3a8a253 --- /dev/null +++ b/docs/zh_cn/plugin-dev/migrating-from-bukkit/inventories.md @@ -0,0 +1,72 @@ +# 从 Bukkit 迁移物品栏与 GUI + +在 Minecraft 插件开发中,创建物品栏 GUI(例如箱子菜单、商店界面或自定义玩家窗口)是一项常见任务。 + +在 Bukkit 中,自定义 GUI 通常使用 `Bukkit.createInventory()` 创建并通过 `InventoryClickEvent` 进行处理。而在 Pumpkin 中,物品栏窗口通过 **Window API** 和容器点击数据包进行管理。 + +--- + +## 核心差异 + +| 特性 | Bukkit / Spigot | Pumpkin | +| :--- | :--- | :--- | +| **物品栏创建** | `Bukkit.createInventory(owner, size, title)` | 窗口容器定义(`WindowType`) | +| **打开窗口** | `player.openInventory(inv)` | `player.open_window(window)` | +| **点击拦截** | 处理 `InventoryClickEvent` | 窗口点击回调处理器 | +| **物品堆叠与元数据** | 带有 `ItemMeta` 的 `ItemStack` | 带有组件 / NBT 的 `ItemStack` | + +--- + +## 代码对比:打开自定义菜单 GUI + +### 1. Bukkit 实现 (Java) + +```java [MenuGUI.java] +public class MenuGUI implements Listener { + + public void openMenu(Player player) { + Inventory inv = Bukkit.createInventory(null, 9, Component.text("Custom Menu")); + + ItemStack item = new ItemStack(Material.DIAMOND); + ItemMeta meta = item.getItemMeta(); + meta.displayName(Component.text("Click Me!")); + item.setItemMeta(meta); + + inv.setItem(4, item); + player.openInventory(inv); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (event.getView().getTitle().equals("Custom Menu")) { + event.setCancelled(true); + if (event.getSlot() == 4) { + event.getWhoClicked().sendMessage("Diamond clicked!"); + } + } + } +} +``` + +### 2. Pumpkin 实现 (Rust) + +```rust [gui.rs] +use pumpkin_plugin_api::{ + player::Player, + item::ItemStack, + window::{Window, WindowType}, + text::TextComponent, +}; + +pub fn open_custom_menu(player: &Player) { + // 1. Create a 9-slot chest window + let mut window = Window::new(WindowType::Generic9x1, TextComponent::text("Custom Menu")); + + // 2. Set item in slot 4 + let item = ItemStack::new("minecraft:diamond", 1); + window.set_item(4, Some(item)); + + // 3. Open window for player + player.open_window(window); +} +``` diff --git a/docs/zh_cn/plugin-dev/python/first-command.md b/docs/zh_cn/plugin-dev/python/first-command.md new file mode 100644 index 0000000..00058f4 --- /dev/null +++ b/docs/zh_cn/plugin-dev/python/first-command.md @@ -0,0 +1,95 @@ +# Creating Your First Command + +Registering and handling custom commands in Pumpkin with Python is designed to be straightforward and expressive. + +--- + +## 1. Quick Example + +Here is a complete, minimal Python plugin (`main.py`) that registers a `/hello` command with permission checks and sends a response back to the player. + +```python [main.py] +from pumpkin_api import ( + Plugin, PluginMetadata, register_plugin, + permission, command, context, server +) + +class HelloCommand: + def handle(self, sender: command.CommandSender, srv: server.Server, args: command.ConsumedArgs) -> int: + sender.send_message("Hello from Python Plugin!") + return 1 + +class MyPlugin(Plugin): + def metadata(self) -> PluginMetadata: + return PluginMetadata( + name="my_python_plugin", + version="0.1.0", + authors=["Developer"], + description="My first Pumpkin Python plugin" + ) + + def on_load(self, ctx: context.Context) -> None: + # 1. Register permission node + ctx.register_permission(permission.Permission( + node="my_python_plugin:hello", + description="Allows executing the /hello command", + default=permission.PermissionDefault.ALLOW, + children=[] + )) + + # 2. Build the command tree with aliases & executor + cmd = command.Command(["hello", "hi"], "Greets the player") + cmd.execute(HelloCommand()) + + # 3. Register the command + ctx.register_command(cmd, "my_python_plugin:hello") + +register_plugin(MyPlugin) +``` + +--- + +## 2. In-Game Preview + +Once registered, your command automatically gains client-side autocompletion, syntax validation, and color highlighting in Minecraft: + +In-Game Command Autocompletion Preview + +--- + +## 3. How It Works + +::: details Step-by-Step Breakdown + +### Step 1: Define the Permission +Commands in Pumpkin require a permission node. Register it with `permission.PermissionDefault.ALLOW` to allow execution by default: +```python +ctx.register_permission(permission.Permission( + node="my_python_plugin:hello", + description="Allows executing the /hello command", + default=permission.PermissionDefault.ALLOW, + children=[] +)) +``` + +### Step 2: Build the Command Tree +Create a `command.Command` instance with primary names and aliases, then attach your handler with `.execute(...)`: +```python +cmd = command.Command(["hello", "hi"], "Greets the player") +cmd.execute(HelloCommand()) +``` + +### Step 3: Register with Context +Pass the command tree and permission node string to `ctx.register_command`: +```python +ctx.register_command(cmd, "my_python_plugin:hello") +``` + +::: + +--- + +## Next Steps + +- Check out [Events](./events) to handle player actions and server events in Python. +- Learn more about [Basic Logic](./basic-logic). diff --git a/docs/zh_cn/plugin-dev/rust/Inventories.md b/docs/zh_cn/plugin-dev/rust/Inventories.md deleted file mode 100644 index c54b4e9..0000000 --- a/docs/zh_cn/plugin-dev/rust/Inventories.md +++ /dev/null @@ -1,347 +0,0 @@ -# 自定义背包界面系统指南 - -## 概述 - -Pumpkin 中的背包界面系统提供了一种灵活的方式来管理物品存储和操作。本指南将解释如何创建和实现自定义背包界面。 - -## 目录 - -- [自定义背包界面系统指南](#自定义背包界面系统指南) - - [概述](#概述) - - [目录](#目录) - - [基础背包界面实现](#基础背包界面实现) - - [需要的接口](#需要的接口) - - [背包界面接口](#背包界面接口) - - [可清空接口](#可清空接口) - - [玩家界面接口](#玩家界面接口) - - [使用通用玩家界面接口](#使用通用玩家界面接口) - - [创建自定义玩家界面接口](#创建自定义玩家界面接口) - - [最佳实践](#最佳实践) - - [示例](#示例) - - [基础背包界面使用](#基础背包界面使用) - -## 基础背包界面实现 - -`BasicInventory` 结构体提供了一个标准实现,包含 27 个物品槽位。以下是实现自定义背包界面的方法: - -```rust -use pumpkin_world::{ - inventory::{ - split_stack, {Clearable, Inventory}, - }, - item::ItemStack, -}; - -#[derive(Debug)] -pub struct BasicInventory { - pub items: [Arc>; 27], -} -``` - -## 需要的接口 - -### 背包界面接口 - -`Inventory` 接口定义了所有背包界面必须实现的核心功能: - -```rust -impl Inventory for BasicInventory { - // 获取背包界面中的总槽位数 - fn size(&self) -> usize { - self.items.len() - } - - // 检查物品栏是否完全为空。 - fn is_empty(&self) -> InventoryFuture<'_, bool> { - Box::pin(async move { - for slot in self.items.iter() { - if !slot.lock().await.is_empty() { - return false; - } - } - - true - }) - } - - // 获取特定槽位中物品堆栈的引用 - fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc>> { - Box::pin(async move { self.items[slot].clone() }) - } - - // 从槽位移除并返回整个物品堆叠。 - fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { - let mut removed = ItemStack::EMPTY.clone(); - let mut guard = self.items[slot].lock().await; - std::mem::swap(&mut removed, &mut *guard); - removed - }) - } - - // 从物品堆叠中移除指定数量的物品。 - fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { - Box::pin(async move { split_stack(&self.items, slot, amount).await }) - } - - // 设置特定槽位的内容 - fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { - Box::pin(async move { - *self.items[slot].lock().await = stack; - }) - } -} -``` - -### 可清空接口 - -`Clearable`接口提供了清空背包界面的功能,这是实现`Inventory`接口所需要的: - -```rust -impl Clearable for YourInventory { - fn clear(&self) -> Pin + Send + '_>> { - Box::pin(async move { - for item in self.items.iter() { - *item.lock().await = ItemStack::EMPTY.clone(); - } - }) - } -} -``` - -## 玩家界面接口 - -玩家界面接口用于创建和管理背包界面的用户界面。它们定义了物品如何在槽位之间移动以及背包界面如何与玩家背包界面交互。 - -### 使用通用玩家界面接口 - -Pumpkin为常见的背包界面布局提供了通用玩家界面接口。以下是使用方法: - -```rust -use pumpkin_inventory::generic_container_screen_handler::create_generic_9x3; -use pumpkin_inventory::player::player_inventory::PlayerInventory; -use pumpkin_inventory::screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerFactory}; - -struct MyScreenFactory { - inventory: Arc, -} - -impl ScreenHandlerFactory for MyScreenFactory { - fn create_screen_handler( - &self, - sync_id: u8, - player_inventory: &Arc, - _player: &dyn InventoryPlayer, - ) -> Option>> { - Some(Arc::new(Mutex::new(create_generic_9x3( - sync_id, - player_inventory, - self.inventory.clone(), - )))) - } - - fn get_display_name(&self) -> TextComponent { - TextComponent::translate("container.barrel", vec![]) - } -} - -// 创建界面 -let factory = MyScreenFactory { - inventory: inventory.clone(), -}; - -// 为玩家打开背包界面 -player.open_handled_screen(factory).await; - -// 屏幕将自动处理: -// - 槽位间的物品移动 -// - 快速移动功能 -// - 玩家背包界面交互 -// - 界面关闭 -``` - -### 创建自定义玩家界面接口 - -您可以通过实现`ScreenHandler`接口来创建自定义玩家界面接口。这提供了更大的灵活性,并允许您创建不仅仅是背包界面的屏幕。 - -要创建自定义玩家界面接口,您可以创建一个实现`Slot`接口的新结构体。`NormalSlot`是Pumpkin提供的一个槽位,它只是作为背包界面的索引,没有任何限制。 - -TODO: 自定义槽位示例 - -以下是通用玩家界面接口的源代码示例: - -```rust -use std::{any::Any, sync::Arc}; - - -use pumpkin_data::screen::WindowType; -use pumpkin_world::{inventory::Inventory, item::ItemStack}; - -use pumpkin_world::{ - player::player_inventory::PlayerInventory, - screen_handler::{InventoryPlayer, ScreenHandler, ScreenHandlerBehaviour}, - slot::NormalSlot, -}; - -pub fn create_generic_9x3( - sync_id: u8, - player_inventory: &Arc, - inventory: Arc, -) -> GenericContainerScreenHandler { - GenericContainerScreenHandler::new( - WindowType::Generic9x3, - sync_id, - player_inventory, - inventory, - 3, - ) -} - -pub struct GenericContainerScreenHandler { - pub inventory: Arc, - pub rows: u8, - behaviour: ScreenHandlerBehaviour, -} - -impl GenericContainerScreenHandler { - fn new( - screen_type: WindowType, - sync_id: u8, - player_inventory: &Arc, - inventory: Arc, - rows: u8, - ) -> Self { - let mut handler = Self { - inventory, - rows, - behaviour: ScreenHandlerBehaviour::new(sync_id, Some(screen_type)), - }; - - handler.add_inventory_slots(); - let player_inventory: Arc = player_inventory.clone(); - handler.add_player_slots(&player_inventory); - - handler - } - - fn add_inventory_slots(&mut self) { - for i in 0..self.rows { - for j in 0..9 { - self.add_slot(Arc::new(NormalSlot::new( - self.inventory.clone(), - (j + i * 9) as usize, - ))); - } - } - } -} - -impl ScreenHandler for GenericContainerScreenHandler { - fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { - Box::pin(async move { - self.default_on_closed(player).await; - self.inventory.on_close().await; - }) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn get_behaviour(&self) -> &ScreenHandlerBehaviour { - &self.behaviour - } - - fn get_behaviour_mut(&mut self) -> &mut ScreenHandlerBehaviour { - &mut self.behaviour - } - - fn quick_move<'a>( - &'a mut self, - _player: &'a dyn InventoryPlayer, - slot_index: i32, - ) -> ItemStackFuture<'a> { - Box::pin(async move { - let mut stack_left = ItemStack::EMPTY.clone(); - // 假设调用者或 `quick_move` 规范内部已为 `slot_index` 通过边界检查 - let slot = self.get_behaviour().slots[slot_index as usize].clone(); - - if slot.has_stack().await { - let slot_stack_lock = slot.get_stack().await; - let slot_stack_guard = slot_stack_lock.lock().await; - stack_left = slot_stack_guard.clone(); - // 在调用 `insert_item` 之前释放 guard,因为 `insert_item` 需要获取自己的锁 - drop(slot_stack_guard); - - // 为 `insert_item` 重新获取锁(该方法需要 `&mut ItemStack`) - let mut slot_stack_mut = slot_stack_lock.lock().await; - - if slot_index < (self.rows * 9) as i32 { - // 从物品栏移动到玩家区域(末尾) - if !self - .insert_item( - &mut slot_stack_mut, - (self.rows * 9).into(), - self.get_behaviour().slots.len() as i32, - true, - ) - .await - { - return ItemStack::EMPTY.clone(); - } - } else if !self - .insert_item(&mut slot_stack_mut, 0, (self.rows * 9).into(), false) - .await - { - // 从玩家区域移动到物品栏(起始位置) - return ItemStack::EMPTY.clone(); - } - - // 检查 `insert_item` 操作后槽位堆叠的最终状态 - if slot_stack_mut.is_empty() { - drop(slot_stack_mut); // 释放锁 - slot.set_stack(ItemStack::EMPTY.clone()).await; - } else { - drop(slot_stack_mut); // 释放锁 - slot.mark_dirty().await; - } - } - - stack_left - }) - } -} -``` - -## 最佳实践 - -1. **线程安全** - - 为槽位边界检查实现适当的错误处理 - - 在使用`inventory.set_stack()`或`slot.get_cloned_stack()`之前记得释放堆栈锁,以防止死锁 - -2. **背包界面管理** - - 使用`ItemStack::EMPTY`常量来清空槽位或初始化空背包界面 - -3. **玩家界面接口实现** - - 正确处理背包界面关闭以防止物品丢失 - - 确保槽位数量与窗口类型中的槽位总数一致 - -## 示例 - -### 基础背包界面使用 - -```rust -// 创建新背包界面 -let inventory = BasicInventory { - items: [(); 27].map(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), -}; - -// 向槽位添加物品 -inventory.set_stack(0, ItemStack::new(1, &Items::OAK_LOG)).await; - -// 从槽位移除物品 -let removed = inventory.remove_stack(0).await; - -// 检查背包界面是否为空 -let is_empty = inventory.is_empty().await; -``` diff --git a/docs/zh_cn/plugin-dev/rust/join-event.md b/docs/zh_cn/plugin-dev/rust/events.md similarity index 100% rename from docs/zh_cn/plugin-dev/rust/join-event.md rename to docs/zh_cn/plugin-dev/rust/events.md diff --git a/docs/zh_cn/plugin-dev/rust/introduction.md b/docs/zh_cn/plugin-dev/rust/introduction.md deleted file mode 100644 index f40b55c..0000000 --- a/docs/zh_cn/plugin-dev/rust/introduction.md +++ /dev/null @@ -1,11 +0,0 @@ -# 编写示例插件 - -本教程将指导您创建一个新的插件。 - -本教程假设您具有一定的 Rust 和命令行使用经验。 - -本教程涵盖的主题: - -- 修改玩家加入消息 -- 创建石头-剪刀-布命令(即将推出) -- 在插件目录中保存插件数据(即将推出) diff --git a/docs/zh_cn/plugin-dev/rust/plugin-utils.md b/docs/zh_cn/plugin-dev/rust/plugin-utils.md new file mode 100644 index 0000000..3adfbbe --- /dev/null +++ b/docs/zh_cn/plugin-dev/rust/plugin-utils.md @@ -0,0 +1,348 @@ +# Licensing & Updates (`pumpkin-plugin-utils`) + +`pumpkin-plugin-utils` is an official utility crate for Pumpkin Rust plugins. It provides built-in tools for **licensing checks**, **cryptographic signature verification**, and **update checks** against the [Pumpkin Marketplace](https://market.pumpkinmc.org). + +## Features + +- **Offline Ed25519 Signature Verification**: Verifies plugin integrity and embedded marketplace metadata in `< 1ms` on startup without blocking the server. +- **Global Metadata Caching**: Call `init(&context)` once in your plugin's `on_load` lifecycle method to cache verified metadata globally. +- **Online License Verification**: Query the marketplace `/api/v1/rest/check-license` endpoint with automatic 7-day lease caching (`license_lease.json`). +- **Offline Grace Period**: Resilient offline lease evaluation ensures your plugin keeps working during marketplace maintenance or network outages. +- **Zero-Argument Update Checking**: Easily check if newer plugin releases are available on the marketplace. +- **Dynamic Public Key Resolution**: Resolves the marketplace public key dynamically via host WIT imports or HTTPS fallback without hardcoding keys. + +--- + +## Adding the Dependency + +Add `pumpkin-plugin-utils` alongside `pumpkin-plugin-api` in your plugin's `Cargo.toml`: + +:::code-group + +```toml [Cargo.toml] +[package] +name = "my-plugin" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +pumpkin-plugin-api = "0.1.0-dev+26.2-26.45" +pumpkin-plugin-utils = "0.1.0-dev+26.2-26.45" +tracing = "0.1" +``` + +::: + +--- + +## Initialization + +Before performing license or update checks, initialize the utility crate inside your plugin's `on_load` method using the plugin's `Context`: + +```rust +use pumpkin_plugin_api::{Context, Plugin, PluginMetadata}; +use tracing::{info, warn}; + +struct MyPlugin; + +impl Plugin for MyPlugin { + fn new() -> Self { + Self + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "MyPlugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Author".into()], + description: "Plugin with license and update checks".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // Initialize pumpkin-plugin-utils (verifies signature & caches metadata) + let metadata = pumpkin_plugin_utils::init(&context) + .map_err(|e| format!("Initialization failed: {e}"))?; + + info!( + "Loaded plugin '{}' v{} (Dev: {})", + metadata.plugin_name, metadata.version, metadata.dev_name + ); + + Ok(()) + } +} +``` + +::: info What happens during `init`? +1. Locates the plugin's `.wasm` binary in the server directory. +2. Extracts the `pumpkin.metadata` and `wasm_signature` custom sections. +3. Cryptographically verifies the Ed25519 signature over the WASM binary and metadata. +4. Caches the verified `PumpkinMetadata` globally for subsequent calls. +::: + +--- + +## Checking for Updates + +`pumpkin-plugin-utils` provides both zero-argument automatic checking and custom manual checking. + +### 1. Automatic Update Check (Recommended) + +Once initialized, call `pumpkin_plugin_utils::check_for_updates()` without arguments. It automatically uses the cached plugin name, current version, and marketplace endpoint: + +```rust +use tracing::{info, warn}; + +fn check_updates() { + match pumpkin_plugin_utils::check_for_updates() { + Ok(update) => { + if update.update_available { + info!( + "A new update is available: {}!", + update.latest_version.as_deref().unwrap_or("unknown") + ); + } else { + info!("Plugin is up to date."); + } + } + Err(err) => { + warn!("Failed to check for updates: {err}"); + } + } +} +``` + +### 2. Manual Update Check (`UpdateChecker`) + +If you want to manually specify the plugin name, version, or a custom marketplace URL, use `UpdateChecker`: + +```rust +use pumpkin_plugin_utils::UpdateChecker; + +let checker = UpdateChecker::new(); +let response = checker.check_for_updates( + "my-plugin", + "1.0.0", + "https://market.pumpkinmc.org", +)?; + +if response.update_available { + println!("Update found: {:?}", response.latest_version); +} +``` + +### Update Response Structure + +The update check returns a `CheckUpdateResponse`: + +```rust +pub struct CheckUpdateResponse { + /// Whether a newer stable release exists on the marketplace. + pub update_available: bool, + /// The latest stable version string, if available. + pub latest_version: Option, +} +``` + +--- + +## Checking and Verifying Licenses + +`pumpkin-plugin-utils` supports online verification against the marketplace API as well as offline lease management with configurable grace periods. + +### 1. Online License Check + +To verify a license against the marketplace (`GET /api/v1/rest/check-license`): + +```rust +// Pass `None` to use the embedded license key from metadata +let response = pumpkin_plugin_utils::check_license_online(None) + .map_err(|e| format!("License verification failed: {e}"))?; + +if response.valid { + info!("License is valid (status: {})", response.status); +} else { + warn!("License is invalid: {}", response.status); + return Err(format!("Invalid license status: {}", response.status).into()); +} +``` + +#### Providing a Custom License Key Override +If your plugin allows server owners to configure a license key in a `config.toml` file, pass `Some(&key)`: + +```rust +let config_key = "LIC-CUSTOM-KEY-12345"; +let response = pumpkin_plugin_utils::check_license_online(Some(config_key))?; +``` + +::: tip 7-Day Lease Caching +Successful online checks automatically save a verified lease file (`license_lease.json`) into the plugin's data folder with a 7-day expiration. +::: + +### 2. Evaluating License with Grace Period + +To keep paid plugins functional when the server is offline or the marketplace is unreachable, use `evaluate_license`: + +```rust +use pumpkin_plugin_utils::LicenseStatus; +use tracing::{info, warn}; + +// Specify grace period duration in days (e.g., 7 days) +let grace_period_days = 7; + +match pumpkin_plugin_utils::evaluate_license(grace_period_days) { + LicenseStatus::Valid(meta) => { + info!("License valid for buyer (User ID: {})", meta.user_id); + } + LicenseStatus::GracePeriod { metadata, days_remaining, reason } => { + warn!( + "Running in offline grace period ({} days remaining). Reason: {}", + days_remaining, reason + ); + } + LicenseStatus::Invalid(reason) => { + return Err(format!("Plugin license invalid or tampered: {reason}").into()); + } + LicenseStatus::Unsigned => { + info!("Plugin is running in unsigned / development mode."); + } +} +``` + +### `LicenseStatus` Enum + +| Variant | Description | +| :--- | :--- | +| `LicenseStatus::Valid(PumpkinMetadata)` | License is valid, verified, or the plugin is free / open-source. | +| `LicenseStatus::GracePeriod { metadata, days_remaining, reason }` | Operating offline with a previously validated lease that is within the allowable grace period. | +| `LicenseStatus::Invalid(String)` | Signature is invalid, expired, revoked, or metadata was tampered with. | +| `LicenseStatus::Unsigned` | Binary does not contain marketplace metadata/signature (useful during local development). | + +--- + +## Accessing Plugin Metadata + +You can access the verified plugin metadata anywhere in your codebase using `get_metadata` or `metadata()`: + +```rust +if let Some(meta) = pumpkin_plugin_utils::get_metadata() { + println!("Plugin Name: {}", meta.plugin_name); + println!("Plugin Version: {}", meta.version); + println!("Is Paid: {}", meta.is_paid); + println!("License Key: {:?}", meta.license_key); + println!("Buyer User ID: {}", meta.user_id); + println!("Developer: {} (ID: {})", meta.dev_name, meta.dev_id); + println!("Marketplace URL: {}", meta.marketplace_url); + println!("Issued At: {}", meta.issued_at); +} +``` + +### `PumpkinMetadata` Fields + +| Field | Type | Description | +| :--- | :--- | :--- | +| `plugin_name` | `String` | Canonical plugin name on the marketplace. | +| `version` | `String` | SemVer version string of the plugin binary. | +| `plugin_id` | `i64` | Unique plugin identifier on the marketplace. | +| `dev_id` | `i64` | Developer account ID. | +| `dev_name` | `String` | Developer display name. | +| `is_paid` | `bool` | `true` if this is a premium/commercial plugin. | +| `user_id` | `i64` | Buyer / licensee user ID (`0` for free plugins). | +| `license_key` | `Option` | Unique license key issued to the licensee. | +| `marketplace_url` | `String` | Base marketplace URL (`https://market.pumpkinmc.org`). | +| `issued_at` | `String` | ISO-8601 timestamp when the binary/license was issued. | + +--- + +## Complete Example + +Here is a full plugin implementation combining initialization, online licensing checks, offline grace periods, and update checking: + +:::code-group + +```rust:line-numbers [src/lib.rs] +use pumpkin_plugin_api::{Context, Plugin, PluginMetadata}; +use pumpkin_plugin_utils::LicenseStatus; +use tracing::{error, info, warn}; + +struct CommercialPlugin; + +impl Plugin for CommercialPlugin { + fn new() -> Self { + Self + } + + fn metadata(&self) -> PluginMetadata { + PluginMetadata { + name: "CommercialPlugin".into(), + version: env!("CARGO_PKG_VERSION").into(), + authors: vec!["Developer".into()], + description: "A commercial Pumpkin plugin".into(), + } + } + + fn on_load(&mut self, context: Context) -> pumpkin_plugin_api::Result<()> { + // 1. Initialize plugin-utils (verifies Ed25519 signature & caches metadata) + let meta = pumpkin_plugin_utils::init(&context).map_err(|err| { + error!("Signature verification failed: {err}"); + format!("Security verification error: {err}") + })?; + + info!("Loaded {} v{}", meta.plugin_name, meta.version); + + // 2. Validate license (offline evaluation + online check) + match pumpkin_plugin_utils::evaluate_license(7) { + LicenseStatus::Valid(_) => { + info!("Local license status: Valid"); + } + LicenseStatus::GracePeriod { days_remaining, .. } => { + warn!("Local license in grace period: {days_remaining} days remaining"); + } + LicenseStatus::Unsigned => { + info!("Running in development mode (unsigned binary)"); + } + LicenseStatus::Invalid(reason) => { + error!("Invalid license: {reason}"); + return Err(format!("License verification failed: {reason}").into()); + } + } + + // 3. Online license check if paid + if meta.is_paid { + if let Ok(check) = pumpkin_plugin_utils::check_license_online(None) { + if !check.valid { + return Err(format!("Online license rejected: {}", check.status).into()); + } + info!("Online license check confirmed: {}", check.status); + } else { + warn!("Online license verification unreachable, falling back to local lease"); + } + } + + // 4. Check for updates + if let Ok(update) = pumpkin_plugin_utils::check_for_updates() { + if update.update_available { + info!( + "A new version is available on the marketplace: {}", + update.latest_version.as_deref().unwrap_or("unknown") + ); + } + } + + Ok(()) + } + + fn on_unload(&mut self, _context: Context) -> pumpkin_plugin_api::Result<()> { + info!("CommercialPlugin unloaded."); + Ok(()) + } +} + +pumpkin_plugin_api::register_plugin!(CommercialPlugin); +``` + +::: diff --git a/docs/zh_cn/troubleshooting/common_issues.md b/docs/zh_cn/troubleshooting/common_issues.md index 2c96447..483d4db 100644 --- a/docs/zh_cn/troubleshooting/common_issues.md +++ b/docs/zh_cn/troubleshooting/common_issues.md @@ -7,3 +7,14 @@ **原因:** 这与身份验证有关,通常与 `prevent_proxy_connections` 设置有关。 **解决方法:** 在 `pumpkin.toml` 中禁用 `prevent_proxy_connections` + +2. ## 无法使用 0.0.0.0 连接到服务器 + + **问题:** 在本地电脑上运行服务器的玩家在输入 `0.0.0.0` 或 `0.0.0.0:25565` 时无法连接(例如遇到“Connection refused”错误)。 + + **原因:** 在 `pumpkin.toml` 中,`0.0.0.0` 指示 Pumpkin 监听所有本地网络接口。这是服务器的绑定地址,并不是客户端可以连接的有效目标地址。 + + **解决方法:** + - 如果在**同一台电脑**上连接,请在 Minecraft 客户端中输入 `localhost` 或 `127.0.0.1`。 + - 如果在**同一局域网(LAN)中的其他设备**上连接,请使用主机的局域网 IP 地址(例如 `192.168.x.x`)。 + - 如果通过**互联网**连接,其他玩家必须使用你的公网 IP 地址或域名(并需要在路由器上设置端口转发)。 diff --git a/package.json b/package.json index 4c46adb..fe368e9 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,8 @@ }, "devDependencies": { "@types/bun": "^1.4.2", - "vitepress": "^2.0.0-alpha.19", - "vue": "^3.6.0-rc.2" + "vitepress": "^2.0.0-alpha.20", + "vue": "^3.6.0-rc.8" }, "dependencies": { "or": "^0.2.0"