diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 811bfbd75..be8fd9818 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,8 @@ To ensure a smooth and collaborative contribution process, please follow the gui 5. [Version tags](#version-tags) 6. [Automatic constant replacement](#automatic-constant-replacement) 7. [Linking to Javadocs](#linking-to-javadocs) -8. [Code of Conduct](#code-of-conduct) +8. [Referencing a build system dependency](#referencing-a-build-system-dependency) +9. [Code of Conduct](#code-of-conduct) ## Introduction @@ -181,6 +182,45 @@ For that, you can use the `jd:project_name[:module_name][:class_or_member_refere [java.sql's Connection](jd:java:java.sql:java.sql.Connection) ``` +## Referencing a build system dependency + +If you wish to reference a build system (i.e. Gradle or Maven) dependency, you can use the `Dependency` component. + +```mdxjs +import { LATEST_ADVENTURE_API_RELEASE } from "/src/utils/versions"; + +{/* uses the "default" template */} + +``` + +The `default` template is fit for use with a simple `implementation`/`compile`-scope dependency from Maven Central, +however you can also make your own template. + +If you need to declare the dependency in a unique way and/or need to add other configuration in the build script, simply use +the `Tabs` component with the corresponding code blocks - **do not make a template unless you plan to use it more than once**. + +```mdxjs +import { Tabs, TabItem } from "@astrojs/starlight/components"; + + + + ```kotlin title="build.gradle.kts" + // my awesome build script in Kotlin + ``` + + + ```groovy title="build.gradle" + // my awesome build script in Groovy + ``` + + + ```xml title="pom.xml" + + ``` + + +``` + ## Code of Conduct Contributors are expected to follow the [Community Guidelines](https://papermc.io/community/guidelines) of the PaperMC organization in all diff --git a/_redirects b/_redirects index 116e5cead..a868609df 100644 --- a/_redirects +++ b/_redirects @@ -19,3 +19,5 @@ /paper/dev/commands /paper/dev/command-api/basics/introduction /paper/dev/command-api/commands /paper/dev/command-api/basics/introduction /paper/dev/command-api/arguments /paper/dev/command-api/basics/arguments-and-literals +/adventure/contributing https://github.com/PaperMC/docs/blob/main/CONTRIBUTING.md +/adventure/version-history/adventure-platform-fabric /adventure/version-history/adventure-platform-mod diff --git a/astro.config.ts b/astro.config.ts index a1a2c9f73..1818ba0d9 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -6,7 +6,14 @@ import starlightLinksValidator from "starlight-links-validator"; import starlightSidebarTopics from "starlight-sidebar-topics"; import codeConstantsPlugin from "./src/utils/remark/code_const"; import javadocPlugin from "./src/utils/remark/javadoc"; +import miniMessageHighlight from "./src/utils/shiki/mm.tmLanguage.json"; import { + LATEST_ADVENTURE_API_RELEASE, + LATEST_ADVENTURE_PLATFORM_MOD_RELEASE, + LATEST_ADVENTURE_PLATFORM_RELEASE, + LATEST_ADVENTURE_SUPPORTED_MC, + LATEST_ADVENTURE_SUPPORTED_MC_RANGE, + LATEST_ANSI_RELEASE, LATEST_FOLIA_RELEASE, LATEST_MC_RELEASE, LATEST_PAPER_RELEASE, @@ -47,6 +54,9 @@ export default defineConfig({ { icon: "github", label: "folia:GitHub", href: "https://github.com/PaperMC/Folia" }, { icon: "seti:java", label: "folia:Javadoc", href: `https://jd.papermc.io/folia/${LATEST_FOLIA_RELEASE}` }, + { icon: "github", label: "adventure:GitHub", href: "https://github.com/KyoriPowered/adventure" }, + { icon: "seti:java", label: "adventure:Javadoc", href: "https://jd.advntr.dev" }, + { icon: "github", label: "waterfall:GitHub", href: "https://github.com/PaperMC/Waterfall" }, { icon: "seti:java", @@ -85,6 +95,9 @@ export default defineConfig({ }, plugins: [ starlightLinksValidator({ + exclude: [ + "/adventure/version-history/*", // custom pages + ], errorOnInvalidHashes: false, // enable if you want to check hashes - it doesn't work with config diagrams }), starlightSidebarTopics( @@ -345,6 +358,91 @@ export default defineConfig({ }, ], }, + { + id: "adventure", + label: "Adventure", + link: "/adventure/", + icon: "adventure", + items: [ + "adventure/getting-started", + "adventure/community-libraries", + "adventure/faq", + "adventure/audiences", + "adventure/text", + { + label: "Text serializers", + items: [ + "adventure/serializer", + "adventure/serializer/json", + "adventure/serializer/gson", + "adventure/serializer/legacy", + "adventure/serializer/plain", + { + label: "MiniMessage", + items: [ + "adventure/minimessage", + "adventure/minimessage/format", + "adventure/minimessage/api", + "adventure/minimessage/dynamic-replacements", + "adventure/minimessage/translator", + ], + }, + "adventure/serializer/ansi", + ], + }, + "adventure/bossbar", + "adventure/sound", + "adventure/titles", + "adventure/book", + "adventure/tablist", + "adventure/resource-pack", + { + label: "MiniMessage", + items: [ + "adventure/minimessage", + "adventure/minimessage/format", + "adventure/minimessage/api", + "adventure/minimessage/dynamic-replacements", + "adventure/minimessage/translator", + ], + }, + "adventure/localization", + { + label: "Platforms", + items: [ + "adventure/platform", + "adventure/platform/native", + "adventure/platform/bukkit", + "adventure/platform/bungeecord", + "adventure/platform/spongeapi", + "adventure/platform/modded", + "adventure/platform/fabric", + "adventure/platform/neoforge", + "adventure/platform/viaversion", + "adventure/platform/implementing", + ], + }, + { + label: "Migrating to Adventure from other APIs", + items: [ + "adventure/migration", + "adventure/migration/bungeecord-chat-api", + "adventure/migration/text-3.x", + ], + }, + { + label: "Version history", + items: [ + { label: "adventure", link: "/adventure/version-history/adventure" }, + { label: "adventure-platform", link: "/adventure/version-history/adventure-platform" }, + { + label: "adventure-platform-mod", + link: "/adventure/version-history/adventure-platform-mod", + }, + ], + }, + ], + }, { id: "waterfall", label: "Waterfall", @@ -352,12 +450,6 @@ export default defineConfig({ icon: "waterfall", items: ["waterfall/getting-started", "waterfall/configuration"], }, - { - id: "adventure", - label: "Adventure", - link: "https://docs.advntr.dev/", - icon: "adventure", - }, { id: "misc", label: "Miscellaneous", @@ -414,6 +506,7 @@ export default defineConfig({ "/velocity/dev/api", ], folia: ["/folia/admin", "/folia/admin/reference"], + adventure: ["/adventure/version-history"], waterfall: ["/waterfall"], misc: ["/misc", "/misc/tools"], }, @@ -425,6 +518,9 @@ export default defineConfig({ extractFileNameFromCode: false, }, emitExternalStylesheet: false, + shiki: { + langs: [miniMessageHighlight], + }, }, }), svelte(), @@ -458,6 +554,12 @@ export default defineConfig({ LATEST_FOLIA_RELEASE, LATEST_WATERFALL_RELEASE, LATEST_USERDEV_RELEASE, + LATEST_ADVENTURE_SUPPORTED_MC, + LATEST_ADVENTURE_SUPPORTED_MC_RANGE, + LATEST_ADVENTURE_API_RELEASE, + LATEST_ADVENTURE_PLATFORM_RELEASE, + LATEST_ADVENTURE_PLATFORM_MOD_RELEASE, + LATEST_ANSI_RELEASE, }, }, ], diff --git a/package.json b/package.json index 5d6974bc8..93a0fc322 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "format:check": "prettier . --check" }, "dependencies": { + "@ascorbic/feed-loader": "^1.0.4", "@astrojs/markdown-remark": "^6.3.1", "@astrojs/starlight": "^0.34.3", "@astrojs/svelte": "^7.0.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f5b29a9b5..9fad7ec04 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,6 +13,9 @@ importers: .: dependencies: + '@ascorbic/feed-loader': + specifier: ^1.0.4 + version: 1.0.4(astro@5.7.11(@types/node@22.15.16)(rollup@4.40.2)(typescript@5.8.3)(yaml@2.7.1)) '@astrojs/markdown-remark': specifier: ^6.3.1 version: 6.3.1 @@ -94,6 +97,16 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} + '@ascorbic/feed-loader@1.0.4': + resolution: {integrity: sha512-vUdoUWBsLdDPwOXF1bKCkBPTiOnu0wQCvw7Nal9d2CyHaDOrgNBwYmKYD1BPNY9yRQMguzUYS2AfFhXZVUuZlw==} + peerDependencies: + astro: ^4.14.0 || ^5.0.0-beta.0 + + '@ascorbic/loader-utils@1.0.2': + resolution: {integrity: sha512-pg43g83gojVtEsAkXfjWuzJhuXneJp4wM/leBftGkCPV3yxKgB92EWA+nWu735BgbBMph3P7DrVqVc3ikt+dJA==} + peerDependencies: + astro: ^4.14.0 || ^5.0.0-beta.0 + '@astrojs/compiler@2.12.0': resolution: {integrity: sha512-7bCjW6tVDpUurQLeKBUN9tZ5kSv5qYrGmcn0sG0IwacL7isR2ZbyyA3AdZ4uxsuUFOS2SlgReTH7wkxO6zpqWA==} @@ -810,6 +823,9 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + addressparser@1.0.1: + resolution: {integrity: sha512-aQX7AISOMM7HFE0iZ3+YnD07oIeJqWGVnJ+ZIKaBZAk03ftmVYVqsGas/rbXKR21n4D/hKCSHypvcyOkds/xzg==} + ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} @@ -839,6 +855,9 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} + array-indexofobject@0.0.1: + resolution: {integrity: sha512-tpdPBIBm4TMNxSp8O3pZgC7mF4+wn9SmJlhE+7bi5so6x39PvzUqChQMbv93R5ilYGZ1HV+Neki4IH/i+87AoQ==} + array-iterate@2.0.1: resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} @@ -976,6 +995,9 @@ packages: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cross-fetch@3.2.0: resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} @@ -1142,6 +1164,11 @@ packages: picomatch: optional: true + feedparser@2.2.10: + resolution: {integrity: sha512-WoAOooa61V8/xuKMi2pEtK86qQ3ZH/M72EEGdqlOTxxb3m6ve1NPvZcmPFs3wEDfcBbFLId2GqZ4YjsYi+h1xA==} + engines: {node: '>= 10.18.1'} + hasBin: true + flat-cache@3.2.0: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} @@ -1322,6 +1349,9 @@ packages: resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} engines: {node: '>=16'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -1351,6 +1381,19 @@ packages: locate-character@3.0.0: resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + lodash.assign@4.2.0: + resolution: {integrity: sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==} + + lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. + + lodash.has@4.5.2: + resolution: {integrity: sha512-rnYUdIo6xRCJnQmbVFEwcxF144erlD+M3YcJUVesflU9paQaE8p+fJDcIQrlMYbxoANFL+AB9hZrzSBBk5PL+g==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -1541,6 +1584,10 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} @@ -1704,6 +1751,9 @@ packages: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -1717,6 +1767,9 @@ packages: radix3@1.1.2: resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -1813,6 +1866,9 @@ packages: s.color@0.0.15: resolution: {integrity: sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + sass-formatter@0.7.9: resolution: {integrity: sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw==} @@ -1897,6 +1953,9 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} @@ -2230,6 +2289,16 @@ snapshots: '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 + '@ascorbic/feed-loader@1.0.4(astro@5.7.11(@types/node@22.15.16)(rollup@4.40.2)(typescript@5.8.3)(yaml@2.7.1))': + dependencies: + '@ascorbic/loader-utils': 1.0.2(astro@5.7.11(@types/node@22.15.16)(rollup@4.40.2)(typescript@5.8.3)(yaml@2.7.1)) + astro: 5.7.11(@types/node@22.15.16)(rollup@4.40.2)(typescript@5.8.3)(yaml@2.7.1) + feedparser: 2.2.10 + + '@ascorbic/loader-utils@1.0.2(astro@5.7.11(@types/node@22.15.16)(rollup@4.40.2)(typescript@5.8.3)(yaml@2.7.1))': + dependencies: + astro: 5.7.11(@types/node@22.15.16)(rollup@4.40.2)(typescript@5.8.3)(yaml@2.7.1) + '@astrojs/compiler@2.12.0': {} '@astrojs/internal-helpers@0.6.1': {} @@ -2892,6 +2961,8 @@ snapshots: acorn@8.14.1: {} + addressparser@1.0.1: {} + ansi-align@3.0.1: dependencies: string-width: 4.2.3 @@ -2913,6 +2984,8 @@ snapshots: aria-query@5.3.2: {} + array-indexofobject@0.0.1: {} + array-iterate@2.0.1: {} astring@1.9.0: {} @@ -3123,6 +3196,8 @@ snapshots: cookie@1.0.2: {} + core-util-is@1.0.3: {} + cross-fetch@3.2.0: dependencies: node-fetch: 2.7.0 @@ -3309,6 +3384,18 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + feedparser@2.2.10: + dependencies: + addressparser: 1.0.1 + array-indexofobject: 0.0.1 + lodash.assign: 4.2.0 + lodash.get: 4.4.2 + lodash.has: 4.5.2 + lodash.uniq: 4.5.0 + mri: 1.2.0 + readable-stream: 2.3.8 + sax: 1.4.1 + flat-cache@3.2.0: dependencies: flatted: 3.3.3 @@ -3623,6 +3710,8 @@ snapshots: dependencies: is-inside-container: 1.0.0 + isarray@1.0.0: {} + js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -3643,6 +3732,14 @@ snapshots: locate-character@3.0.0: {} + lodash.assign@4.2.0: {} + + lodash.get@4.4.2: {} + + lodash.has@4.5.2: {} + + lodash.uniq@4.5.0: {} + longest-streak@3.1.0: {} lower-case@2.0.2: @@ -4128,6 +4225,8 @@ snapshots: dependencies: brace-expansion: 1.1.11 + mri@1.2.0: {} + mrmime@2.0.1: {} ms@2.1.3: {} @@ -4285,6 +4384,8 @@ snapshots: prismjs@1.30.0: {} + process-nextick-args@2.0.1: {} + prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -4296,6 +4397,16 @@ snapshots: radix3@1.1.2: {} + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readdirp@4.1.2: {} recma-build-jsx@1.0.0: @@ -4496,6 +4607,8 @@ snapshots: s.color@0.0.15: {} + safe-buffer@5.1.2: {} + sass-formatter@0.7.9: dependencies: suf-log: 2.5.3 @@ -4638,6 +4751,10 @@ snapshots: get-east-asian-width: 1.3.0 strip-ansi: 7.1.0 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 diff --git a/src/components/Dependency.astro b/src/components/Dependency.astro new file mode 100644 index 000000000..9946d8d13 --- /dev/null +++ b/src/components/Dependency.astro @@ -0,0 +1,124 @@ +--- +import { Code, TabItem, Tabs } from "@astrojs/starlight/components"; +import { LATEST_ADVENTURE_SUPPORTED_MC_RANGE, LATEST_ADVENTURE_PLATFORM_MOD_RELEASE } from "../utils/versions"; + +type TemplateKey = "default" | "adventure-platform-mod"; + +interface Template { + label: string; + lang?: string; + title?: string; + code: string; +} + +const templates: Record = { + default: [ + { + label: "Gradle (Kotlin)", + lang: "kotlin", + title: "build.gradle.kts", + code: `repositories { + mavenCentral() +} + +dependencies { + implementation("\{GROUP}:\{NAME}:\{VERSION}") +}`, + }, + { + label: "Gradle (Groovy)", + lang: "groovy", + title: "build.gradle", + code: `repositories { + mavenCentral() +} + +dependencies { + implementation '\{GROUP}:\{NAME}:\{VERSION}' +}`, + }, + { + label: "Maven", + lang: "xml", + title: "pom.xml", + code: ` + + + \{GROUP} + \{NAME} + \{VERSION} + + +`, + }, + ], + "adventure-platform-mod": [ + { + label: "Gradle (Kotlin)", + lang: "kotlin", + title: "build.gradle.kts", + code: `repositories { + // for development builds + maven(url = "https://s01.oss.sonatype.org/content/repositories/snapshots/") { + name = "sonatype-oss-snapshots1" + mavenContent { snapshotsOnly() } + } + // for releases + mavenCentral() +} + +dependencies { + modImplementation(include("net.kyori:adventure-platform-\{NAME}:${LATEST_ADVENTURE_PLATFORM_MOD_RELEASE}")!!) // for Minecraft ${LATEST_ADVENTURE_SUPPORTED_MC_RANGE} +}`, + }, + { + label: "Gradle (Groovy)", + lang: "groovy", + title: "build.gradle", + code: `repositories { + // for development builds + maven { + name = 'sonatype-oss-snapshots1' + url = 'https://s01.oss.sonatype.org/content/repositories/snapshots/' + mavenContent { snapshotsOnly() } + } + // for releases + mavenCentral() +} + +dependencies { + modImplementation include('net.kyori:adventure-platform-\{NAME}:${LATEST_ADVENTURE_PLATFORM_MOD_RELEASE}') // for Minecraft ${LATEST_ADVENTURE_SUPPORTED_MC_RANGE} +}`, + }, + ], +}; + +interface Props { + group?: string; + name?: string; + version?: string; + template?: TemplateKey; +} + +let { group = "", name = "", version = "", template } = Astro.props; +template = template ?? "default"; + +const templateData = templates[template]; +if (!templateData) { + throw new Error(`Template '${template}' not found`); +} + +const formatCode = (code: string): string => { + return code.replaceAll("\{GROUP}", group).replaceAll("\{NAME}", name).replaceAll("\{VERSION}", version); +}; +--- + + + { + templateData.map(({ label, lang, title, code }) => ( + + + + )) + } + diff --git a/src/components/changelog/Release.astro b/src/components/changelog/Release.astro new file mode 100644 index 000000000..7a5f1da44 --- /dev/null +++ b/src/components/changelog/Release.astro @@ -0,0 +1,38 @@ +--- +import AnchorHeading from "@astrojs/starlight/components/AnchorHeading.astro"; +import { render, type CollectionEntry, type CollectionKey } from "astro:content"; + +interface Props { + id: string; + entry: CollectionEntry; +} + +const { id, entry } = Astro.props; +const { Content } = await render(entry); +--- + +
+ {entry.data.title} +

+ Released on {(entry.data.pubdate ?? new Date(0)).toLocaleDateString("en", { dateStyle: "medium", timeZone: "UTC" })} + { + entry.data.link && ( + <> + - GitHub + + ) + } +

+ +
+ + diff --git a/src/components/changelog/Releases.astro b/src/components/changelog/Releases.astro new file mode 100644 index 000000000..16dd8d95d --- /dev/null +++ b/src/components/changelog/Releases.astro @@ -0,0 +1,27 @@ +--- +import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro"; +import { getCollection, type CollectionKey } from "astro:content"; +import Release from "./Release.astro"; + +interface Props { + title: string; + collection: CollectionKey & `${string}-releases`; +} + +const { title, collection } = Astro.props; + +const entries = await getCollection(collection); +const headings = entries.map((entry) => { + const id = entry.id.substring(entry.id.lastIndexOf("/") + 1); + + return { + slug: id, + text: entry.data.title ?? id, + depth: 2, + }; +}); +--- + + + {entries.map((entry) => )} + diff --git a/src/content.config.ts b/src/content.config.ts index a50c1abf6..2ea842a90 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -1,3 +1,4 @@ +import { feedLoader } from "@ascorbic/feed-loader"; import { docsLoader } from "@astrojs/starlight/loaders"; import { docsSchema } from "@astrojs/starlight/schema"; import { defineCollection, z } from "astro:content"; @@ -16,4 +17,19 @@ export const collections = { }), }), }), + "adventure-releases": defineCollection({ + loader: feedLoader({ + url: "https://github.com/KyoriPowered/adventure/releases.atom", + }), + }), + "adventure-platform-releases": defineCollection({ + loader: feedLoader({ + url: "https://github.com/KyoriPowered/adventure-platform/releases.atom", + }), + }), + "adventure-platform-mod-releases": defineCollection({ + loader: feedLoader({ + url: "https://github.com/KyoriPowered/adventure-platform-mod/releases.atom", + }), + }), }; diff --git a/src/content/docs/adventure/assets/tablist.png b/src/content/docs/adventure/assets/tablist.png new file mode 100644 index 000000000..3e42c857a Binary files /dev/null and b/src/content/docs/adventure/assets/tablist.png differ diff --git a/src/content/docs/adventure/audiences.md b/src/content/docs/adventure/audiences.md new file mode 100644 index 000000000..d39b45086 --- /dev/null +++ b/src/content/docs/adventure/audiences.md @@ -0,0 +1,43 @@ +--- +title: Audiences +description: A guide to Adventure Audiences. +--- + +An audience, at its core, is a grouping of 0 or more viewers of some content. +The concept of an audience is where Adventure makes its most clear break from +other Minecraft platforms. + +As an API, `Audience` is designed to be a universal interface for any player, +command sender, console, or otherwise who can receive text, titles, boss bars, +and other Minecraft media. This allows extending audiences to cover more than +one individual receiver - possible "audiences" could include a team, server, +world, or all players that satisfy some predicate (such as having a certain +permission). The universal interface also allows reducing boilerplate by +gracefully degrading functionality if it is not applicable. For instance, it +does not make much sense to send a boss bar to a command sender, and you can't +send titles to Minecraft 1.7 clients. + +You will normally get audience instances from one of the [Platforms](/adventure/platform). +The Adventure API includes two audience implementations itself: one that does not +support any action (and thus does nothing). `Audience.empty()`, and one that +forwards an action to each member in the audience, `Audience.audience()` and related +methods, along with the `ForwardingAudience` that implements the forwarding logic +for you. + +Most users using will primarily use this API to show content created by other parts +of the API. + +## Pointers + +Audiences can also provide arbitrary information, such as display name or UUID. +This is done using the pointer system. + +Examples: + +```java +// get the uuid from an audience member, returning an Optional +audience.get(Identity.UUID); + +// get the display name, returning a default +audience.getOrDefault(Identity.DISPLAY_NAME, Component.text("no display name!")); +``` diff --git a/src/content/docs/adventure/book.md b/src/content/docs/adventure/book.md new file mode 100644 index 000000000..3406e7de7 --- /dev/null +++ b/src/content/docs/adventure/book.md @@ -0,0 +1,33 @@ +--- +title: Books +description: A guide to Adventure Books. +--- + +## Constructing a Book + +Books are composed of: + * A component used for the title of the book + * A component used for the author of the book + * A collection of components used for the book pages + +**Example:** + +```java +// Create and open a book about cats for the target audience +public void openMyBook(final @NonNull Audience target) { + Component bookTitle = Component.text("Encyclopedia of cats"); + Component bookAuthor = Component.text("kashike"); + Collection bookPages = Cats.getCatKnowledge(); + + Book myBook = Book.book(bookTitle, bookAuthor, bookPages); + target.openBook(myBook); +} +``` + +## Extra info regarding Books + +Books in adventure are not necessarily connected to an interactable book item in the client. +As of the current release such a connection needs to be implemented outside of adventure. + +Any component that surpasses the game limit for text per page will be truncated client side, the same applies +to the amount of components (pages). Further reading about these limits can be done at the [Minecraft Wiki](https://minecraft.wiki/w/Book_and_Quill#Writing). diff --git a/src/content/docs/adventure/bossbar.md b/src/content/docs/adventure/bossbar.md new file mode 100644 index 000000000..8bb0fbab1 --- /dev/null +++ b/src/content/docs/adventure/bossbar.md @@ -0,0 +1,60 @@ +--- +title: Boss bars +description: A guide to Adventure BossBars. +--- + +## Constructing a BossBar + +Boss Bars are composed of: + * A component used for the title of the boss bar + * A number from 0 to 1 used to determine how full the boss bar should be + * A color, will be downsampled for clients <1.9 + * An overlay that determines the amount of visual segments on the boss bar + + +**Examples:** + +```java +private @Nullable BossBar activeBar; + +public void showMyBossBar(final @NonNull Audience target) { + final Component name = Component.text("Awesome BossBar"); + // Creates a red boss bar which has no progress and no notches + final BossBar emptyBar = BossBar.bossBar(name, 0, BossBar.Color.RED, BossBar.Overlay.PROGRESS); + // Creates a green boss bar which has 50% progress and 10 notches + final BossBar halfBar = BossBar.bossBar(name, 0.5f, BossBar.Color.GREEN, BossBar.Overlay.NOTCHED_10); + // etc.. + final BossBar fullBar = BossBar.bossBar(name, 1, BossBar.Color.BLUE, BossBar.Overlay.NOTCHED_20); + + // Send a bossbar to your audience + target.showBossBar(fullBar); + + // Store it locally to be able to hide it manually later + this.activeBar = fullBar; +} + +public void hideActiveBossBar(final @NonNull Audience target) { + target.hideBossBar(this.activeBar); + this.activeBar = null; +} +``` + +## Changing an active BossBar + +Boss bars are mutable and listen for changes on their object, +the in-game view will change automatically without having to manually refresh it! + +Therefore, if this boss bar is currently active + +```java +final BossBar bossBar = BossBar.bossBar(Component.text("Cat counter"), 0, BossBar.Color.RED, BossBar.Overlay.PROGRESS); +``` + +and `BossBar.name()` with a component is called + +```java +final Component newText = Component.text("Duck counter"); +bossBar.name(newText); +``` + +the boss bar will be updated automatically. The same thing goes for `progress`, `color` and `overlay`. diff --git a/src/content/docs/adventure/community-libraries.mdx b/src/content/docs/adventure/community-libraries.mdx new file mode 100644 index 000000000..120e055e2 --- /dev/null +++ b/src/content/docs/adventure/community-libraries.mdx @@ -0,0 +1,55 @@ +--- +title: Community libraries +description: Libraries utilizing the Adventure API. +--- + +Adventure aims to provide the core libraries needed for interacting with chat components. However, with +our limited time and the sheer number of possible use cases, we can't hope to provide direct solutions for every problem. + +Luckily, many of our community members have produced libraries that complement Adventure, providing additional features and integrations with other software. + +:::note + +This list of libraries is provided for reference only. The Kyori team does not endorse any specific ones, and cannot provide any information beyond the provided links. + +If you have a library that you'd like included, please open a pull request on the [PaperMC/docs](https://github.com/PaperMC/docs) repository. + +::: + + +## Libraries for Adventure + +These are libraries focused around providing additional functionality using Adventure components. +They typically have no dependencies on a specific platform, just Adventure and potentially a library with which they integrate. + +{/* Elements in this table should be alphabetized */} + +{/* spellchecker:off */} +Name | Description | Link +----------------------------|-----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------- +adventure-binary-serializer | Serializer for converting to packed bytes | [Moulberry/adventure-binary-serializer](https://github.com/Moulberry/adventure-binary-serializer/>) +EnhancedLegacyText | Alternative input format that is legacy compatible with new features | [Vankka/EnhancedLegacyText](https://github.com/Vankka/EnhancedLegacyText>) +MCDiscordReserializer | Serializers for going between Minecraft & Discord | [Vankka/MCDiscordReserializer](https://github.com/Vankka/MCDiscordReserializer>) +Minedown | A markdown-style format for representing components | [Phoenix616/MineDown](https://github.com/Phoenix616/MineDown>) +{/* spellchecker:on */} + +## Libraries that use Adventure + +These are libraries with a focus on something other than chat components, that use Adventure in their API. +These libraries will often depend on one or more specific platforms to support their functionality. + +{/* Elements in this table should be alphabetized */} + +{/* spellchecker:off */} +Name | Description | Link +--------------------|-----------------------------------------------------------------------|---------------------------------------------------- +Cloud | A general-purpose Java command dispatcher & framework | [Incendo/cloud](https://github.com/Incendo/cloud) +Core | The Core allows you to register (mini)messages to a central database in multiple languages and access them via a very intuitive "key" and "locale" query. | [JuliGamesCore](https://github.com/JuliGames/JuliGamesCore) +Creative | A resource-pack library for Minecraft: Java Edition | [Creative](https://github.com/unnamed/creative) +Inventory Framework | An inventory framework for managing GUIs | [Inventory Framework](https://github.com/stefvanschie/IF) +LiteCommands | A annotation based command framework for Velocity, Bukkit, BungeeCord | [LiteCommands](https://github.com/Rollczi/LiteCommands) +MiniPlaceholders | A platform-agnostic MiniMessage Component-based Placeholders library | [MiniPlaceholders](https://github.com/MiniPlaceholders/MiniPlaceholders) +ProtocolSidebar | An easy to use sidebar library for Paper/Spigot servers | [CatCoderr/ProtocolSidebar](https://github.com/CatCoderr/ProtocolSidebar) +ScoreboardLibrary | A scoreboard library for Paper/Spigot servers | [MegavexNetwork/scoreboard-library](https://github.com/MegavexNetwork/scoreboard-library) +Triumph GUI | A library made to simplify the creation of inventory GUIs | [Triumph GUI](https://triumphteam.dev/docs/triumph-gui/introduction) +{/* spellchecker:on */} diff --git a/src/content/docs/adventure/faq.md b/src/content/docs/adventure/faq.md new file mode 100644 index 000000000..133d3a350 --- /dev/null +++ b/src/content/docs/adventure/faq.md @@ -0,0 +1,72 @@ +--- +title: FAQ +description: Frequently asked questions. +--- + +We find that there are some issues users come across relatively frequently while applying the Adventure library in certain contexts. These may not be directly related to Adventure itself, but these answers are published here those that ask them: + +## Why is my lore in italics? + +Components will inherit style from their parent. For example, in the following code snippet, each word will be red, despite red not being explicitly set on the appended component: `text("hi", RED).append(text("also red!"))`. + +In vanilla Minecraft, some places where components are rendered have parent styles. For example, lore text has a parent style that makes all text italic. This means that you will need to set italic to false if you do not want any component you are storing in lore to be italic. The `Component.decorationIfAbsent()` method can apply this to existing components without overriding any formatting specifically set by users. + +## Messages not sending? Hex colors not working? Events not appearing? Fonts messed up? + +- Test on a vanilla client, without any mods or resource packs. Modded clients (such as Badlion), client mods, and even resource packs can break many elements of the modern JSON chat format and mess with incoming chat packets in ways that cause a myriad of issues. +- Try without other plugins/mods. If another plugin/mod is modifying outgoing packets or formatting chat messages, this could cause a loss of formatting in the messages you send. Try without any other plugins to see if any are causing issues. +- For RGB colors, test on a client of at least version *1.16*. Mojang added RGB support in this version. The JSON message format has evolved over time and has had many new additions since its introduction many, many years ago. For a full version history, see [the Minecraft wiki](https://minecraft.wiki/w/Raw_JSON_text_format). + +## How can I support both MiniMessage and legacy (§-code) formatting? + +If you have legacy in configuration files, or other places, it is suggested that you migrate them once using the legacy deserializer to turn them into a component and then MiniMessage to serialize them into proper MiniMessage format. + +There are no working, recommended, or supported ways of using both MiniMessage and legacy color codes and there never will be. Even simple find-and-replace style techniques do not work and will fail to take into account the quirks of style resetting in legacy formatting. + +## How can I use Bukkit's PlaceholderAPI in MiniMessage messages? + +PlaceholderAPI placeholders are not supported in MiniMessage. However, you can easily create a custom tag resolver that can allow users to use PlaceholderAPI placeholders in MiniMessage strings, like in the following example: + +
+ Example + Example method to create a MiniMessage placeholder that parses PlaceholderAPI placeholders for a player. + + The tag added is of the format ``. For example, ``. + + Credit to `mbaxter`. + + ```java + /** + * Creates a tag resolver capable of resolving PlaceholderAPI tags for a given player. + * + * @param player the player + * @return the tag resolver + */ + public @NotNull TagResolver papiTag(final @NotNull Player player) { + return TagResolver.resolver("papi", (argumentQueue, context) -> { + // Get the string placeholder that they want to use. + final String papiPlaceholder = argumentQueue.popOr("papi tag requires an argument").value(); + + // Then get PAPI to parse the placeholder for the given player. + final String parsedPlaceholder = PlaceholderAPI.setPlaceholders(player, '%' + papiPlaceholder + '%'); + + // We need to turn this ugly legacy string into a nice component. + final Component componentPlaceholder = LegacyComponentSerializer.legacySection().deserialize(parsedPlaceholder); + + // Finally, return the tag instance to insert the placeholder! + return Tag.selfClosingInserting(componentPlaceholder); + }); + } + ``` +
+ +## Why am I getting a `NoSuchFieldError`, `NoSuchMethodError`, `ClassNotFoundException` or similar when updating/using `adventure-platform-*`, `adventure-text-minimessage`, `adventure-api` or other related libraries/tools? + +In the case of `adventure-platform-fabric`, you need to make sure you are properly `include()`-`ing` the mod. For legacy platform implementations, you need to make sure you are properly shading and relocating your specific dependencies. Specific issues may include: + +- Not shading the correct version of `adventure-api`. You can check your dependency tree to see what or why your build tool is not including the correct version of the API that matches the one used by the platform version you are using. +- Not relocating your dependencies. If you are running on a platform that includes an older version of the API, or another mod/plugin is also not properly relocating their dependencies, you will use their out-of-date version of the API, causing errors. +- Building/running against a native implementation of `adventure-api`. If you are running on a platform that includes an older version of the API, this could cause issues if the library depends on newer features that are not available in the outdated version of the API, your library will not be able to find these methods, causing errors. +- Relocating `adventure-api` and trying to use native/library methods. If you relocate the API, you will not be able to use any methods that use the API in native implementations or libraries as method signatures will differ. Either shade and relocate this software, or do not use native methods. Alternatively, if you are shading and relocating a library but want to use the API, make sure you are only relocating the packages that you are shading. + +Please consult the documentation for your build tool for more information on how to shade, relocate and manage your dependencies. We do not provide one-on-one support for these sorts of issues, as there are far too many project-specific variables that make isolating issues difficult. diff --git a/src/content/docs/adventure/getting-started.mdx b/src/content/docs/adventure/getting-started.mdx new file mode 100644 index 000000000..cacebb0d2 --- /dev/null +++ b/src/content/docs/adventure/getting-started.mdx @@ -0,0 +1,60 @@ +--- +title: Getting started +description: A guide to getting started with Adventure. +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +import Dependency from "/src/components/Dependency.astro"; +import { LATEST_ADVENTURE_API_RELEASE } from "/src/utils/versions"; + +To use Adventure in your project, you will need to add the following dependency (and repository if using Gradle): + +Declaring the dependency: + + + +Need development/snapshot builds? [Using Snapshot Builds](#using-snapshot-builds) + +Some platforms already use Adventure natively. +In this case, you will not need to add Adventure as a dependency. +To view the list of platforms that include Adventure, see [Native Support](/adventure/platform/native). + +To use Adventure with other platforms, you may wish to look at the platform-specific adapters. +A list of platforms with supported adapters can be found at [Platforms](/adventure/platform). + +## Using snapshot builds + +To use snapshot builds, you will need to add the following repository: + + + + ```kotlin title="build.gradle.kts" + repositories { + maven(url = "https://s01.oss.sonatype.org/content/repositories/snapshots/") { + name = "sonatype-oss-snapshots" + } + } + ``` + + + ```groovy title="build.gradle" + repositories { + maven { + name = 'sonatype-oss-snapshots1' + url = 'https://s01.oss.sonatype.org/content/repositories/snapshots/' + } + } + ``` + + + ```xml title="pom.xml" + + + sonatype-oss-snapshots1 + https://s01.oss.sonatype.org/content/repositories/snapshots/ + + + ``` + + diff --git a/src/content/docs/adventure/index.mdx b/src/content/docs/adventure/index.mdx new file mode 100644 index 000000000..69cc3294d --- /dev/null +++ b/src/content/docs/adventure/index.mdx @@ -0,0 +1,10 @@ +--- +title: Adventure +description: An overview of the Adventure library. +--- + +import PageCards from "/src/components/PageCards.astro"; + +Documentation for Adventure and its many libraries. + + diff --git a/src/content/docs/adventure/localization.md b/src/content/docs/adventure/localization.md new file mode 100644 index 000000000..fccd15ba7 --- /dev/null +++ b/src/content/docs/adventure/localization.md @@ -0,0 +1,124 @@ +--- +title: Localization +description: Utilizing localization in Adventure. +--- + +Adventure provides a way to utilize Minecraft's built-in localization system for client-side translations as well as an additional Adventure-specific system for translating text. + +## Using Minecraft's localization + +To send text to a player that will be translated in the language they have selected in their client settings, use a translatable component. +For example, `Component.translatable("block.minecraft.diamond_block")` will render as "Block of Diamond" (or translated to another language) when viewed by the client. +Some translation keys have arguments which are inserted into the translated content. +For example, `Component.translatable("block.minecraft.player_head.named", Component.text("Mark"))` will render as "Mark's Head". +Translatable components can have styling, hover/click events and children components just like any other component type. + +### Resource pack language files + +You can provide translation files in a resource pack in order to change existing translations or add new ones. +For a guide on how to do that, see the [Minecraft Wiki](https://minecraft.wiki/w/Resource_pack#Language). + +### Using Adventure's localization + +Adventure also provides a way to handle localization in Adventure itself. +This can be useful in environments where you do not have access to resource packs, or wish to do translations yourself, without relying on Minecraft's translation system. + +Any component that is sent to a client is ran through the `GlobalTranslator` using the locale of the client. +This means that if you wish to have automatic translation of components using your own translation data, you can add a `Translator` to the `GlobalTranslator`. +You can either provide your own implementation of `Translator` or use one of the implementations that Adventure provides. + +Once you have a `Translator` instance, you can register it to the `GlobalTranslator` using `GlobalTranslator.translator().addSource(myTranslator)`. +This will then make it available for automatic translation across the platform. + +:::caution + +Some implementations may not use the `GlobalTranslator` in every area, or at all. +For example, Paper does not use it for items, and Minestom does not use it unless specifically enabled. +Please consult the documentation for your platform for any limitations. + +::: + +## Using a custom `Translator` + +A `Translator` is a simple interface that provides two ways of translating content. + +The first `translate` method provides the translation key and locale as an argument and expects a nullable `MessageFormat` in return. +This system is comparable to Minecraft's built-in localization system, using the standard Java +[message format](jd:java:java.text.MessageFormat) for arguments. + +If the first `translate` method returns `null`, the second method which provides the translatable component and locale as an argument can be used. +This method allows for much richer customization of the translation process as you can return an entire component. +This means you can, for example, customize the color and styling of the translated component, rather than relying solely on strings for the message format system. + +:::caution + +If you are overriding the component `translate` method, you should be careful not to unintentionally lose the children of the translatable component. +See the Javadocs for the translate method for a code example of how to avoid this common error. + +::: + +Below is an example of how one might implement a custom `Translator`. + +```java title="MyTranslator.java" +public class MyTranslator implements Translator { + + @Override + public @NotNull Key name() { + // Every translator has a name which is used to identify this specific translator instance. + return Key.key("mynamespace:mykey"); + } + + @Override + public @Nullable MessageFormat translate(final @NotNull String key, final @NotNull Locale locale) { + // You could retrieve a string from a properties file, a config file, or some other system. + // An an example, we will hard-code a check for a specific key here. + if (key.equals("mytranslation.key") && locale == Locale.US) { + return new MessageFormat("Hello %s!", locale); + } else { + // If you only want to use component translation, you can override this method and always return `null`. + return null; + } + } + + @Override + public @Nullable Component translate(@NotNull TranslatableComponent component, @NotNull Locale locale) { + // As above, we will hardcode a check here, but you should be reading this from elsewhere. + if (key.equals("mytranslation.colorful") && locale == Locale.US) { + return Component.text("Hello, ", NamedTextColor.GREEN) + .append(component.arguments().get(0).color(NamedTextColor.RED)) + .append(component.children()); // Always make sure to copy the children over! + } else { + return null; + } + } +} +``` + +### Using a `TranslationStore` + +A `TranslationStore` is a store of translations. +It provides a simpler way creating a `Translator` without having to implement the logic for determining and storing translations yourself. +You can create a translation store and then add or remove translations at will, even after registering it to the global translator. + +Adventure provides two translation stores, one for message format translating and one for component translating. +An example of how to use a translation store is below. + +```java +// As above, every translator needs an identifying name! +// Could also use TranslationStore#component(Key) to work with components instead. +final TranslationStore myStore = TranslationStore.messageFormat(Key.key("mynamespace:mykey")); + +// You can add translations one-by-one, or in bulk. Consult the Javadocs for a full list of methods. +myStore.register("mytranslation.key", Locale.US, new MessageFormat("Hello %s!", Locale.US)); + +// You can then register this to the global translator so the translations are available there! +GlobalTranslator.translator().addSource(myStore); +``` + +There are additional methods on the message format translation store to bulk register from [resource bundles](jd:java:java.util.ResourceBundle). +You may also want to use Adventure's `UTF8ResourceBundleControl` utility class to create your bundle. + +### Using MiniMessage for translations + +Adventure also provides a translator that can use MiniMessage strings, with automatic support for placeholders and arguments. +For more information, see [MiniMessage Translator](/adventure/minimessage/translator). diff --git a/src/content/docs/adventure/migration/bungeecord-chat-api.md b/src/content/docs/adventure/migration/bungeecord-chat-api.md new file mode 100644 index 000000000..d47e7b9bf --- /dev/null +++ b/src/content/docs/adventure/migration/bungeecord-chat-api.md @@ -0,0 +1,149 @@ +--- +title: Migrating from the BungeeCord Chat API +description: Move from the BungeeCord Chat API to the Adventure API. +--- + +Adventure's text API and the BungeeCord Chat API are designed along very different +lines. This page goes over some notable differences. + +## Audiences + +It is strongly recommended you read about [Audiences](/adventure/audiences) first. Unlike BungeeCord, +which limits functionality to specific user types, Adventure allows only the specific +operations that apply to an audience to be taken. + +## Decoration and styling + +The BungeeCord Chat API stores all decorations in the `BaseComponent`. Adventure separates +out styles into their own `Style` class. + +BungeeCord allows you to merge the styles from one component into another. Adventure provides +equivalent methods that merge styles together, or allows you to replace the styles on one +component with another. + +## Chat colors + +Adventure's chat color and styling hierarchy differs from that of BungeeCord's `ChatColor` +API. This is probably where the most stark contrast between the Adventure API and BungeeCord/Bukkit +will manifest. + +### Replacement for `ChatColor` + +Adventure's equivalents for `ChatColor` are split over three types: + +* Formatting types (such as `BOLD` or `ITALIC`) are in `TextDecoration`, and can be set + on a component or a style with the `decoration` method. Decorations also use a tristate to + specify if they are enabled, disabled, or not set (in which case the component inherits the + setting from its parent component). +* Named colors (also called the legacy Mojang color codes) now exist in the `NamedTextColor` + class. +* RGB colors are constructed using the `TextColor.color()` methods (this is equivalent to the + `ChatColor.of()` method in the BungeeCord `ChatColor` 1.16 API. + +### Legacy strings can't be constructed + +The BungeeCord `ChatColor` API's heritage is in the Bukkit API. The Bukkit `ChatColor` API in turn +dates from the early days of Minecraft (Beta 1.0), when the normal and accepted way of sending formatted +messages to the client was to concatenate magical strings that told the client what to format. A formatted +chat message would be sent to the client like this: + +```java +player.sendMessage(ChatColor.GREEN + "Hi everyone, " + ChatColor.BOLD + "this message is in green and bold" + ChatColor.RESET + ChatColor.GREEN + "!"); +``` + +This style of sending messages has persisted to this day, even as Mojang introduced rich chat components +into Minecraft 1.7.2. Bukkit preserved this backwards-compatible behavior, and BungeeCord introduced the +change as a result of being compatible with the Bukkit `ChatColor` class. + +In Adventure, you can't concatenate magical formatting codes. The equivalent of `ChatColor` in Adventure, +`TextColor`, instead returns descriptive text describing the color when its `toString()` is called. The +recommended replacement is to convert all legacy messages to components. + +### `ChatColor.stripColor()` + +`ChatColor.stripColor()` does not exist in Adventure. An equivalent would be to use +`PlainTextComponentSerializer.plainText().serialize(LegacyComponentSerializer.legacySection().deserialize(input))`. + +### `ChatColor.translateAlternateColorCodes()` + +`ChatColor.translateAlternateColorCodes()` does not exist in Adventure. Instead you should use +`LegacyComponentSerializer.legacy(altChar).deserialize(input)` when deserializing a legacy +string. + +## Differences in `ComponentBuilder` + +The BungeeCord `ComponentBuilder` treats each component independently and allows you +to manually carry over styles from a prior component. In Adventure, there are multiple +component builders. The closest equivalent for a BungeeCord `ComponentBuilder` is +to append components to a top-level empty component using `Component.text()` +as a base. To replicate the behavior of `ComponentBuilder`, consider doing the +following: + +* Use the `Style` class to store common styles and the `mergeStyle` and `style` + methods to merge and replace styles on a component. +* Use the Adventure `TextComponent` builder to create one component at a time and + then append to a top-level text component builder that is empty. + +As an example, this BungeeCord component: + +```java +new ComponentBuilder("hello") + .color(ChatColor.GOLD) + .append(" world", FormatRetention.NONE) + .build() +``` + +becomes this Adventure equivalent: + +```java +Component.text() + .append(Component.text("hello", NamedTextColor.GOLD) + .append(Component.text(" world")) + .build() +``` + +Likewise, + +```java +new ComponentBuilder("hello") + .color(ChatColor.GOLD) + .bold(true) + .append(" world") + .build() +``` + +becomes + +```java +Style style = Style.style(NamedTextColor.GOLD, TextDecoration.BOLD); +Component.text() + .append(Component.text("hello", style) + .append(Component.text(" world", style)) + .build() +``` + +## Immutability + +In the BungeeCord Chat API, all components are mutable. Adventure text components, +however, are immutable - any attempt to change a component results in a new component +being created that is a copy of the original component with the change you requested. + +## Serializers + +The BungeeCord Chat API includes three serializers. All three have equivalents in Adventure: + +* The `TextComponent.fromLegacyText()` deserialization method is equivalent to the + `deserialize` method of the [Legacy](/adventure/serializer/legacy] text serializer. Likewise, the + `BaseComponent.toLegacyText()` serialization method is equivalent to the `serialize` + method on the legacy text serializer. +* The `TextComponent.toPlainText()` serialization method is equivalent to the + `serialize` method of the [Plain](/adventure/serializer/plain) text serializer. A component can be + created from a plain-text string using `Component.text(string)` +* The Adventure equivalent of `ComponentSerializer` is the [Gson](/adventure/serializer/gson) text + serializer. + +## Backwards compatibility + +The `BungeeCordComponentSerializer` allows you to convert between Adventure [Components](/adventure/text) +and the native BungeeCord chat component API and back. This can be used when native platform support is +unavailable. The serializer is available in the `adventure-platform-text-serializer-bungeecord` artifact. diff --git a/src/content/docs/adventure/migration/index.md b/src/content/docs/adventure/migration/index.md new file mode 100644 index 000000000..7ba413614 --- /dev/null +++ b/src/content/docs/adventure/migration/index.md @@ -0,0 +1,23 @@ +--- +title: Migrating to Adventure from other APIs +description: Moving from other chat APIs to Adventure. +tableOfContents: false +sidebar: + label: Overview +--- + +Moving to Adventure from other APIs is fairly straightforward. These guides +provide advice and replacements for tasks in other APIs. + +* [Migrating from the BungeeCord Chat API](/adventure/migration/bungeecord-chat-api) + * [Audiences](/adventure/migration/bungeecord-chat-api#audiences) + * [Decoration and styling](/adventure/migration/bungeecord-chat-api#decoration-and-styling) + * [Chat colors](/adventure/migration/bungeecord-chat-api#chat-colors) + * [Differences in `ComponentBuilder`](/adventure/migration/bungeecord-chat-api#differences-in-componentbuilder) + * [Immutability](/adventure/migration/bungeecord-chat-api#immutability) + * [Serializers](/adventure/migration/bungeecord-chat-api#serializers) + * [Backwards compatibility](/adventure/migration/bungeecord-chat-api#backwards-compatibility) +* [Migrating from text 3.x](/adventure/migration/text-3.x) + * [A word of caution](/adventure/migration/text-3.x#a-word-of-caution) + * [Breaking changes from text 3.x](/adventure/migration/text-3.x#breaking-changes-from-text-3x) + * [Serializer](/adventure/migration/text-3.x#serializer) diff --git a/src/content/docs/adventure/migration/text-3.x.md b/src/content/docs/adventure/migration/text-3.x.md new file mode 100644 index 000000000..d232ace38 --- /dev/null +++ b/src/content/docs/adventure/migration/text-3.x.md @@ -0,0 +1,62 @@ +--- +title: Migrating from text 3.x +slug: adventure/migration/text-3.x +description: Moving from text 3.x to Adventure. +--- + +Adventure is an evolution of the text 3.x API. If you've worked with +the text API before, the switch to Adventure should be relatively quick. +For the most part, you'll just need to depend on the Adventure API +and the relevant [Platform](/adventure/platform) you support and replace references +to classes in `net.kyori.text` to `net.kyori.adventure.text`, though see +below for major breaking changes. + +## A word of caution + +However, before you continue, it is strongly recommended you read about +[Audiences](/adventure/audiences). Unlike text, Adventure defines a standard interface for +sending content (including chat messages) to viewers. In addition, Adventure +defines interfaces for other game play mechanics that can be arbitrarily sent +to players. + +## Breaking changes from text 3.x + +### Factory methods renamed +In text 3.x, components could be constructed using the `Component.of()` methods. +In Adventure, we've changed to using `Component.(/*...*/)` style methods to allow +for easier static imports. + +Similarly, `Style.of(/*...*/)` is changed to `Style.style(/*...*/)`. + +### `.builder()` +Builders are now created by calling the aforementioned factory methods with no parameters. +For example, `TextComponent.builder()` becomes `Component.text()`. + +Note that the equivalent of `TextComponent.builder("hello")` is `Component.text().content("hello")`. + +### `.append()` with a String argument +Component builders in 3.x had a shorthand for appending a new text component: `builder.append("wow")`. +In Adventure you have to write it in full, `builder.append(Component.text("wow"))` in this case. + +### `LegacyComponentSerializer` + +In text 3.x, you would deserialize a component that used a color code prefix that +differed from the section symbol normally used by using `LegacyComponentSerializer.legacy().deserialize(string, altChar)`. +In Adventure, the API to use is `LegacyComponentSerializer.legacy(altChar).deserialize(string)`. + +To make a linking serializer you have to use the builder. +Change `LegacyComponentSerializer.legacyLinking(style)` +to `LegacyComponentSerializer.builder().extractUrl(style).build()`. + +### `TextColor` renamed to `NamedTextColor` + +In order to accommodate the new RGB colors introduced in 1.16, all the named text colors +were moved to the `NamedTextColor` class. References to the old `TextColor` class +should be updated to refer to `NamedTextColor`. + +## Serializer + +If you have a need to interoperate with clients using the old text 3.x API, you +can use the `adventure-text-serializer-legacy-text3` artifact, which includes a +`LegacyText3ComponentSerializer` that can convert from Adventure to text 3.x +components and back. Note that RGB colors will be downsampled. diff --git a/src/content/docs/adventure/minimessage/api.mdx b/src/content/docs/adventure/minimessage/api.mdx new file mode 100644 index 000000000..2a937d994 --- /dev/null +++ b/src/content/docs/adventure/minimessage/api.mdx @@ -0,0 +1,199 @@ +--- +title: API +description: A guide on using MiniMessage in your code. +--- + +import Dependency from "/src/components/Dependency.astro"; +import { LATEST_ADVENTURE_API_RELEASE } from "/src/utils/versions"; + +## Dependency + +Declaring the dependency: + + + +:::note + +Some platforms already provide MiniMessage natively. In this case you will not need to add MiniMessage as a dependency. + +::: + +## Getting started + +MiniMessage exposes a simple API via the `MiniMessage` class. + +A standard instance of the serializer is available through the `miniMessage()` method. This uses the default set of tags and is not in strict mode. + +Additional customization of MiniMessage is possible via the [Builder](#builder). + +MiniMessage allows you to both serialize components into MiniMessage strings and to parse/deserialize MiniMessage strings into components. + +Here's a short example to try things out: + +```java +Audience player = ...; +MiniMessage mm = MiniMessage.miniMessage(); + +Component parsed = mm.deserialize("Hello world, isn't MiniMessage fun?"); + +player.sendMessage(parsed); +``` + +For more advanced uses, additional tag resolvers can be registered, which when given a tag name and arguments will produce a `Tag` instance. These are described in more detail below. + +### Builder + +To make customizing MiniMessage easier, we provide a Builder. The specific methods on the builder are explained in the javadoc. + +```java +MiniMessage minimessage = MiniMessage.builder() + .tags(TagResolver.builder() + .resolver(StandardTags.color()) + .resolver(StandardTags.decorations()) + .resolver(this.someResolvers) + .build() + ) + .build(); +``` + +:::tip + +It's a good idea to initialize such a MiniMessage instance once, in a central location, and then use it for all your messages. +Exception being if you want to customize MiniMessage based on permissions of a user (for example, admins should be allowed to use color and decoration in the message, normal users not) + +::: + +### Error handling + +By default, MiniMessage will never throw an exception caused by user input. Instead, it will treat any invalid tags as normal text. `MiniMessage.Builder#strict(true)` mode will enable strict mode, +which throws exceptions on unclosed tags, but still will allow any improperly specified tags through. + +To capture information on why a parse may have failed, `MiniMessage.Builder#debug(Consumer)` can be provided, which will accept debug logging for an input string. + + +## Tag resolvers + +All tag resolution goes through tag resolvers. There is one global tag resolver, which describes the tags available through a `MiniMessage` instance, plus parse-specific resolvers which can provide additional input-specific tags. + +Tag resolvers are the binding between a name and arguments, and the logic to produce a `Component` contained in a `Tag` instance. They are composable so a `TagResolver` can produce any number of actual `Tag` instances. The tag name passed to resolvers will always be lower-cased, to ensure case-insensitive searches. + +Tag names are only allowed to contain the characters a-z, 0-9, `_`, and `-`. They can also optionally start with any of the following characters: `!?#`. + +You can create your own `TagResolver` by using the static factory methods in `TagResolver`. To replace tags dynamically with text MiniMessage has built-in `Placeholder` and `Formatter`. +Where possible, these built-in resolvers should be used, as MiniMessage can flatten combinations of these resolvers into a more efficient format. +For built-in dynamic replacements take a look [here](/adventure/minimessage/dynamic-replacements). + +To combine multiple resolvers, take a look at the tag resolver builder, `TagResolver.builder()`. + +The builder for `MiniMessage` allows providing a custom tag resolver rather than the default (`StandardTags.all()`), allowing + +MiniMessage also provides convenience methods to do that: + +```java +MiniMessage serializer = MiniMessage.builder() + .tags(TagResolver.builder() + .resolver(StandardTags.color()) + .build() + ) + .build(); + +Component parsed = serializer.deserialize("Hai"); + +// Assertion passes +assertEquals(Component.text("Hai", NamedTextColor.GREEN), parsed); +``` + +Because the `` tag is not enabled on this builder, the bold tag is interpreted as literal text. + +### Handling arguments + +Tag resolvers have an `ArgumentQueue` parameter, which provides any tag arguments that are present in the input. Helper methods on `Tag.Argument` can assist with conversions of the tag. + +Exceptions thrown by the `popOr()` methods will interrupt execution, but are not currently exposed to users outside of debug output. We plan to add an auto-completion function that can +reveal some of this information to the user, so please do try to write useful error messages in custom tag resolvers. + +## Tags + +Once a tag resolver has handled arguments, it returns a `Tag` object. These objects implement the logic of producing or modifying a component tree. There are three main kinds of `Tag` -- all custom implementations must implement one of these interfaces. + +### Pre-process + +These tags implement the `PreProcess` interface, and have a value of a raw MiniMessage string that is replaced into the user input before parsing continues. + +Due to limitations in the current parser implementation, note that pre-process tags will adjust offsets in error messages, and may inhibit tab completion. However, they are currently the only way to integrate markup fragments into a message. + +### Inserting + +These tags are fairly straightforward: they represent a literal `Component`. The vast majority of Tag implementations will want +to be `Inserting` tags. `Inserting` tags may also optionally be self-closing -- by default, this is only true for tags created by `Placeholder.unparsed(String)` and `Placeholder.component(Component)`, +so that placeholders are self-contained. + +Most `standard tags <./format>` are `Inserting`. These tags will either directly insert a component, or use the helper `Tag.styling(StyleBuilderApplicable...)` to apply style to components. + +This helper can be used to efficiently apply a collection of styles with one tag. For example, to create a `Title` tag, that makes the `Title` text into a link that opens a URL with traditional link styling, this could be used: + +```java +Component aTagExample() { + final String input = "Hello, click me! but not me!"; + final MiniMessage extendedInstance = MiniMessage.builder() + .editTags(b -> b.tag("a", MiniMessageTest::createA)) + .build(); + + return extendedInstance.deserialize(input); +} + +static Tag createA(final ArgumentQueue args, final Context ctx) { + final String link = args.popOr("The tag requires exactly one argument, the link to open").value(); + + return Tag.styling( + NamedTextColor.BLUE, + TextDecoration.UNDERLINED, + ClickEvent.openUrl(link), + HoverEvent.showText(Component.text("Open " + link)) + ); +} +``` + +This allows producing rich styling relatively quickly. + + +### Modifying + +Modifying tags are the most complex, and most specialized of the tag types available. These tags receive the node tree and have an opportunity to analyze it before +components are constructed, and then receive every produced child component and can modify those children. This is used for the built-in `` and `` tags, +but can be applied for similar complex transformations. + +Modifying tags are first given an opportunity to visit every node of the tree in a depth-first traversal. If a `Modifying` instance stores any state during this traversal, its resolver should return a new instance every time to prevent state corruption. + +:::note + +The `Node` API in 4.10.0 is currently not very well developed -- most aspects are still internal. Additional information can be exposed as needed by tag developers. + +::: + +Once the whole parse tree has been visited, the `postVisit()` method is called. This method can optionally be overridden if any additional calculations must be performed. + +Next, the `Modifying` instance enters the application phase, where the component tree is presented to the tag for transformation. This allows the tag to *modify* the contents of these components, giving it its name. + +### Parser directives + +Parser directives are a special kind of tag, as they are instructions for the parser, and therefore cannot be implemented by end users. + +There is currently only one, but more may be added at any time. + +| RESET | This indicates to the parser that this tag should close all currently open tags. | + + +This can be used to provide the functionality of a `` tag under a different name. For example: + +```java +final var clearTag = TagResolver.resolver("clear", ParserDirective.RESET); + +final var parser = MiniMessage.builder() + .editTags(t -> t.resolver(clearTag)) + .build(); + +final Component parsed = parser.deserialize("hello world, how are you?"); +``` + +This code would add a `` tag, behaving identically to the `` tag available by default — ", how are you?" would not be bold or colored red. diff --git a/src/content/docs/adventure/minimessage/assets/click_1.png b/src/content/docs/adventure/minimessage/assets/click_1.png new file mode 100644 index 000000000..e774bc8ee Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/click_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/color_1.png b/src/content/docs/adventure/minimessage/assets/color_1.png new file mode 100644 index 000000000..71ecc51a1 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/color_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/color_2.png b/src/content/docs/adventure/minimessage/assets/color_2.png new file mode 100644 index 000000000..729302a65 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/color_2.png differ diff --git a/src/content/docs/adventure/minimessage/assets/color_verbose_1.png b/src/content/docs/adventure/minimessage/assets/color_verbose_1.png new file mode 100644 index 000000000..71ecc51a1 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/color_verbose_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/color_verbose_2.png b/src/content/docs/adventure/minimessage/assets/color_verbose_2.png new file mode 100644 index 000000000..729302a65 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/color_verbose_2.png differ diff --git a/src/content/docs/adventure/minimessage/assets/decoration_1.png b/src/content/docs/adventure/minimessage/assets/decoration_1.png new file mode 100644 index 000000000..79972e538 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/decoration_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/font_1.png b/src/content/docs/adventure/minimessage/assets/font_1.png new file mode 100644 index 000000000..197c5c2e4 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/font_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/gradient_1.png b/src/content/docs/adventure/minimessage/assets/gradient_1.png new file mode 100644 index 000000000..160397938 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/gradient_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/hover_1.png b/src/content/docs/adventure/minimessage/assets/hover_1.png new file mode 100644 index 000000000..baa81b4a9 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/hover_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/insertion_1.png b/src/content/docs/adventure/minimessage/assets/insertion_1.png new file mode 100644 index 000000000..c7134e290 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/insertion_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/key_1.png b/src/content/docs/adventure/minimessage/assets/key_1.png new file mode 100644 index 000000000..ee3ff7bef Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/key_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/newline_1.png b/src/content/docs/adventure/minimessage/assets/newline_1.png new file mode 100644 index 000000000..b63e65ce2 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/newline_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/rainbow_1.png b/src/content/docs/adventure/minimessage/assets/rainbow_1.png new file mode 100644 index 000000000..eabd0ee98 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/rainbow_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/reset_1.png b/src/content/docs/adventure/minimessage/assets/reset_1.png new file mode 100644 index 000000000..931608693 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/reset_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/selector_1.png b/src/content/docs/adventure/minimessage/assets/selector_1.png new file mode 100644 index 000000000..9b8eedb74 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/selector_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/shadow_1.png b/src/content/docs/adventure/minimessage/assets/shadow_1.png new file mode 100644 index 000000000..1cdf7d919 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/shadow_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/shadow_2.png b/src/content/docs/adventure/minimessage/assets/shadow_2.png new file mode 100644 index 000000000..0314501df Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/shadow_2.png differ diff --git a/src/content/docs/adventure/minimessage/assets/shadow_3.png b/src/content/docs/adventure/minimessage/assets/shadow_3.png new file mode 100644 index 000000000..934358dff Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/shadow_3.png differ diff --git a/src/content/docs/adventure/minimessage/assets/transition_1.png b/src/content/docs/adventure/minimessage/assets/transition_1.png new file mode 100644 index 000000000..35e3e19f1 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/transition_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/translatable_1.png b/src/content/docs/adventure/minimessage/assets/translatable_1.png new file mode 100644 index 000000000..8c73bbe05 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/translatable_1.png differ diff --git a/src/content/docs/adventure/minimessage/assets/translatable_2.png b/src/content/docs/adventure/minimessage/assets/translatable_2.png new file mode 100644 index 000000000..1a6afd578 Binary files /dev/null and b/src/content/docs/adventure/minimessage/assets/translatable_2.png differ diff --git a/src/content/docs/adventure/minimessage/dynamic-replacements.md b/src/content/docs/adventure/minimessage/dynamic-replacements.md new file mode 100644 index 000000000..4128f337f --- /dev/null +++ b/src/content/docs/adventure/minimessage/dynamic-replacements.md @@ -0,0 +1,149 @@ +--- +title: Dynamic replacements +description: A guide on tag resolvers. +--- + +MiniMessage has some included `TagResolver` s which can replace tags dynamically when parsing those. Those resolvers can replace a tag with dynamic input such as a string or a formatted number. + +## Placeholders + +Placeholders replace the tag with a specific text. Those are the most basic replacements: + +### Insert a component + +You can simply insert a component for the tag with the component placeholder. + +```java +MiniMessage.miniMessage().deserialize("Hello :)", Placeholder.component("name", Component.text("TEST", NamedTextColor.RED))); +``` + +This will insert the red text component "TEST" for the tag name. + +### Insert some unparsed text + +Sometimes it's better to not parse dynamic text such as user inputs. For those things MiniMessage provides the unparsed placeholder. +With this method you can sanitize user input without escaping the tags directly. + +```java +MiniMessage.miniMessage().deserialize("Hello ", Placeholder.unparsed("name", "TEST :)")); +``` + +This will insert the text without parsing. The result will be a gray text with `Hello TEST :)`. + +### Insert and parse text + +When you want to insert a text and allow MiniMessage to parse the tags you can use the parsed placeholder. +The parsed placeholder will insert the replacement before parsing the string. The tags in the placeholder can affect the parsed result after the placeholder. + +```java +MiniMessage.miniMessage().deserialize("Hello :)", Placeholder.parsed("name", "TEST")); +// returns Component.text("Hello ", NamedTextColor.GRAY).append(Component.text("TEST :)", NamedTextColor.RED)); +``` + +This will insert and parse the text. + +### Insert a style + +When you want to create your own styling tag you can use the styling placeholder. + +```java +MiniMessage.miniMessage().deserialize("Hello :) How are you?", + Placeholder.styling("my-style", ClickEvent.suggestCommand("/say hello"), NamedTextColor.RED, TextDecoration.BOLD)); +// will apply a click even, a red text color and bold decoration to the text +``` + +This will insert the style with a click event and a red text. Styling placeholders can be used for any style, e.g. colors, text decoration and events. + +Create your own styling tags: + +```java +Placeholder.styling("fancy", TextColor.color(150, 200, 150)); // will replace the color between "" and "" +Placeholder.styling("myhover", HoverEvent.showText(Component.text("test"))); // will display your custom text as hover +Placeholder.styling("mycmd", ClickEvent.runCommand("/mycmd is cool")); // will create a clickable text which will run your specified command. +``` + +:::tip + +Styling placeholders can be used to sanitize input from players in click events. Instead of using a parsed placeholder the string can be used directly. + +::: + + +## Formatters + +Not everything is a text, sometimes its useful to display a number or a date. +For that you can use the provided formatters from MiniMessage + +### Insert a number + +You can insert a `Number` by using the number formatter in MiniMessage. + +To specify the locale and format of the number the formatter accepts optionally tag arguments. +You can specify the locale and the number format. It's possible to pass both as arguments to the tag but you have provide the locale first. + +```java +MiniMessage.miniMessage().deserialize("Hello my number !", Formatter.number("no", 250.25d)); +MiniMessage.miniMessage().deserialize("Hello my number !", Formatter.number("no", 250.25d)); +MiniMessage.miniMessage().deserialize("Hello my number !", Formatter.number("no", 250.25d)); +MiniMessage.miniMessage().deserialize("Hello my number !", Formatter.number("no", 250.25d)); +``` + +All those examples are valid and will insert the number as the tag. + +Refer to Locale and DecimalFormat for valid locale tags and usable patterns. + +:::tip + +You can change the style such as the color by a more complex pattern: + +```java +MiniMessage.miniMessage().deserialize("Your current balance is #.00;-#.00'>.", Formatter.number("no", 250.25d)); +``` + +This will display the balance in red for negative numbers, otherwise the number will be green. + +::: + + +### Insert a date + +To insert an instance of an `TemporalAccessor` such as a `LocalDateTime` you can use the date formatter. + +The tag resolver requires a tag argument for the format. Refer to DateTimeFormatter for a usable patterns. + +```java +MiniMessage.miniMessage().deserialize("Current date is: !", Formatter.date("date", LocalDateTime.now(ZoneId.systemDefault())); +``` + +This will display the current date with the specified format. E.g. as `2022-05-27 11:30:25`. + +### Insert a choice + +To insert a number and format some text based on the number you can use the choice formatter. + +This will accept a ChoiceFormat pattern. + +```java +MiniMessage.miniMessage().deserialize("I met !", Formatter.choice("choice", 5)); +``` + +This will format your input based on the provided ChoiceFormat. In this case it will be `I met many developers!` + + +## Complex placeholders + +You can simply create your own placeholders. Take a look at the Formatter and Placeholder class from MiniMessage for examples. + +### Examples + +Create a custom tag which makes its contents clickable: + +```java +TagResolver.resolver("click-by-version", (args, context) -> { + final String version = args.popOr("version expected").value(); + return Tag.styling(ClickEvent.openUrl("https://jd.advntr.dev/api/ " + version + "/")); +}); +// creates a tag to get javadocs of adventure by the version: +``` + +You can create your own complex placeholders with multiple arguments and their own logic. diff --git a/src/content/docs/adventure/minimessage/format.mdx b/src/content/docs/adventure/minimessage/format.mdx new file mode 100644 index 000000000..87d8b6059 --- /dev/null +++ b/src/content/docs/adventure/minimessage/format.mdx @@ -0,0 +1,555 @@ +--- +title: Format +description: The MiniMessage format documentation. +--- + +import Shadow1 from "./assets/shadow_1.png"; +import Shadow2 from "./assets/shadow_2.png"; +import Shadow3 from "./assets/shadow_3.png"; +import Color1 from "./assets/color_1.png"; +import Color2 from "./assets/color_2.png"; +import ColorVerbose1 from "./assets/color_verbose_1.png"; +import ColorVerbose2 from "./assets/color_verbose_2.png"; +import Decoration1 from "./assets/decoration_1.png"; +import Reset1 from "./assets/reset_1.png"; +import Click1 from "./assets/click_1.png"; +import Hover1 from "./assets/hover_1.png"; +import Key1 from "./assets/key_1.png"; +import Translatable1 from "./assets/translatable_1.png"; +import Translatable2 from "./assets/translatable_2.png"; +import Insertion1 from "./assets/insertion_1.png"; +import Rainbow1 from "./assets/rainbow_1.png"; +import Gradient1 from "./assets/gradient_1.png"; +import Transition1 from "./assets/transition_1.png"; +import Font1 from "./assets/font_1.png"; +import Newline1 from "./assets/newline_1.png"; +import Selector1 from "./assets/selector_1.png"; + +The MiniMessage language uses tags. Everything you do will be defined with tags. Tags have a start tag and an end tag (the `` tag is an exception here). +Start tags are mandatory (obviously), but end tags aren't outside of `strict` mode. The following are all visually identical: +```mm +Hello World! +Hello World! +Hello World! +``` + +For tags with no content, tags can be auto-closed by using the format ``. With this format, even in strict mode no separate closing tag should be provided. + +All tag names are case-insensitive to reduce the possibility for conflict, but we recommend keeping all tag names lowercase (or at the very least, being consistent). + +Some tags have argument. Those look like this: `stuff`. +For example: +```mm +test:TEST">TEST +TEST +``` + +As you can see, those sometimes contain components, sometimes just numbers, strings, or other types. Refer to the detailed docs below. + +Single (`'`) and double (`"`) quotes can be used interchangeably. We recommend staying consistent, though in order to minimize escaping it might make more sense to switch quote types for some arguments. + +Any meaningful token can be escaped in the locations where they have influence. In plain text, tag open characters (`<`) can be escaped with a leading backslash (`\`). Within quoted strings, +the opening quote character can be escaped (`'` or `"`). In either place, the escape character can be escaped in places where it would otherwise be relevant. Unquoted tag arguments cannot have escapes, for simplicity. +In locations where escaping is not supported, the literal escape character will be passed through. In locations where escaping *is* supported but a literal escape character is desired, the escape character can itself be escaped to produce a `\`. + +The default tags try to represent components in a manner compatible with Vanilla, but simplifying some elements. It might be helpful to +use [the Minecraft wiki](https://minecraft.wiki/w/Raw_JSON_text_format) as a reference for the Vanilla component system, especially +for things like the actions and values of click and hover events. + +The [MiniMessage Web Viewer](https://webui.advntr.dev) allows testing MiniMessage text locally, without having to spin up a Minecraft instance. +It can be helpful to put examples from these docs into the viewer while learning. + +## Strict mode + +By default, MiniMessage is extremely lenient, and any invalid tags will just be ignored. Any tags left unclosed at the end of an input string will be automatically closed. + +Applications can optionally enable *strict mode*, which prohibits using ``, and requires all tags to be closed in reverse order of opening. Any application +using MiniMessage should make it clear to end users which language variant is being used. + +## Standard tags + +These are the tags included and enabled by default in MiniMessage. Specific parses of MiniMessage may add custom tags to this list, or restrict the available tags to a subset of this list. Consult application documentation for details. + +### Color + +{/* spellchecker:off */} +Color the next parts + +Tag + * `<_colorname_>` + +Arguments + * `_colorname_`, any minecraft color constant: `black`, `dark_blue`, `dark_green`, `dark_aqua`, `dark_red`, `dark_purple`, `gold`, `gray`, `dark_gray`, `blue`, `green`, `aqua`, `red`, `light_purple`, `yellow`, or `white`. + + + `dark_grey` can be used in place of `dark_gray`, and so can `grey` in place of `gray`. + Hex colors are supported as well, with the format `#RRGGBB`. +{/* spellchecker:on */} + +Examples +```mm +Hello World! +This is a test! +<#00ff00>R G B! +``` + +Preview +
+The result of parsing `<yellow>Hello <blue>World</blue>!`, shown in-game in the Minecraft client's chat window +The result of parsing `<red>This is a <green>test!`, shown in-game in the Minecraft client's chat window +
+ +### Color (verbose) + +A more verbose way of defining colors + +Tag + * `` + +{/* spellchecker:off */} +Aliases + * `colour`, `c` +{/* spellchecker:on */} + +Arguments + * `_colorNameOrHex_`, can be any of the values from above (so named colors or hex colors) + +Examples +```mm +Hello World! +This is a test! +``` + +Preview +
+The result of parsing `<color:yellow>Hello <color:blue>World</color:blue>!`, shown in-game in the Minecraft client's chat window +The result of parsing `<color:#FF5555>This is a <color:#55FF55>test!`, shown in-game in the Minecraft client's chat window +
+ +### Shadow Color + +Color the shadow of the next parts + +Tag + * `` + * `` as an alias to disable the shadow (equivalent to ``) + +Arguments + * `_colorNameOrHex_`, a named color or hex color string with the format `#RRGGBB` or `#RRGGBBAA` + * `[alpha_as_float]`, a float value between 0 and 1, representing the alpha value of the shadow. Optional, defaults to 0.25. Has no effect if an alpha value is already provided in the hex color string. + +Examples +```mm +Hello World! +This is a test! +Thicc +``` + +Preview +
+ The result of parsing `<shadow:yellow>Hello <shadow:aqua:0.5>World</shadow>!` shown in-game in the Minecraft client's chat window + The result of parsing `<shadow:#FF5555>This is a <shadow:#55FF55>test!` shown in-game in the Minecraft client's chat window + The result of parsing `<shadow:#000000FF><b>Thicc` shown in-game in the Minecraft client's chat window +
+ +### Decoration + +Decorate the next parts + +Tag + * `<_decorationname_[:false]>`, or `` as an alias to invert the decoration. + +Arguments + * `_decorationname_`, Any decoration supported in Minecraft: + +| Decoration | Aliases | +| ----------------- | --------------- | +| `bold` | `b` | +| `italic` | `em` or `i` | +| `underlined` | `u` | +| `strikethrough` | `st` | +| `obfuscated` | `obf` | + +Examples +```mm +This is important! +``` + +Preview +
+ The result of parsing `<underlined>This is <bold>important</bold>!`, shown in-game in the Minecraft client's chat window +
+ +### Reset + +Close all currently open tags, resetting color/decoration/etc. The reset tag cannot be closed. + +In strict mode, reset tags are forbidden. + +Tag + * `` + +Arguments + * none + +Examples +```mm +Hello world! +``` + +Preview +
+The result of parsing `<yellow><bold>Hello <reset>world!`, shown in-game in the Minecraft client's chat window +
+ +### Click + +Allows doing multiple things when clicking on the component. + +Tag + * `` + +Arguments + * `_action_`, the type of click event, one of [this list](https://jd.advntr.dev/api/latest/net/kyori/adventure/text/event/ClickEvent.Action.html#enum.constant.summary) + * `_value_`, the argument for that particular event, refer to [the minecraft wiki](https://minecraft.wiki/w/Raw_JSON_text_format) + +Examples +```mm +Click to show the world seed! +Click this to copy your score! +``` + +Preview +
+The result of parsing `<click:run_command:/seed>Click</click> to show the world seed!`, shown in-game in the Minecraft client's chat window +
+ +:::caution + +Since the introduction of chat signatures in 1.19.1, the client no longer executes commands that require signed arguments +like the `/say` or `/tell` command to prevent the server from sending signed messages on the client's behalf. + +::: + +### Hover + +Allows doing multiple things when hovering on the component. + +Tag + * `` + +Arguments + * `_action_`, the type of hover event, one of this [list](https://jd.advntr.dev/api/latest/net/kyori/adventure/text/event/HoverEvent.Action.html#field.summary) + * `_value..._`, arguments specific to each event action: + +| Action | Value | +|---------------|---------------------------------------------------------------------------------------------------------------------------| +| `show_text` | `_text_` (a MiniMessage string) | +| `show_item` | `_type_[:_count_[:tag]]` (a `Key` for the item's type, optionally followed by count (an integer) and tag (a SNBT string)) | +| `show_entity` | `_type_:_uuid_[:_name_]` (a `Key` ID of the entity type, the entity's UUID, and an optional custom name) | + +Examples +```mm +test'>TEST +``` + +Preview +
+The result of parsing `<hover:show_text:'<red>test'>TEST`, shown in-game in the Minecraft client's chat window +
+ +### Keybind + +Allows displaying the configured key for actions + +Tag + * `` + +Arguments + * `_key_`, the keybind identifier of the action + +Examples +```mm +Press to jump! +``` + +Preview +
+The result of parsing `Press <red><key:key.jump> to jump!`, shown in-game in the Minecraft client's chat window +
+ +### Translatable + +Allows displaying minecraft messages using the player locale + +Tag + * `` + +Aliases + * `tr`, `translate` + +Arguments + * `_key_`, the translation key + * `_valueX_`, optional values that are used for placeholders in the key (they will end up in the `with` tag in the JSON) + +Examples +```mm +You should get a ! +1':'Stone'>! +``` + +Preview +
+The result of parsing `You should get a <lang:block.minecraft.diamond_block>!`, shown in-game in the Minecraft client's chat window in English +The result of parsing `<lang:commands.drop.success.single:'<red>1':'<blue>Stone'>!`, shown in-game in the Minecraft client's chat window in English +
+ +### Fallback + +:::note + +The fallback option is only available since Minecraft 1.19.4. + +::: + +Allows displaying minecraft messages using the player locale, or a fallback if no text is available + +Tag + * `` + +Aliases + * `tr_or`, `translate_or` + +Arguments + * `_key_`, the translation key + * `_fallback_`, the fallback text to display + * `_valueX_`, optional values that are used for placeholders in the key (they will end up in the `with` tag in the JSON) + +Examples +```mm +You should get a ! +``` + +### Insertion + +Allow insertion of text into chat via shift click + +Tag + * `` + +Arguments + * `_text_`, the text to insert + +Examples +```mm +Shift-click this to insert! +``` + +Preview +
+The result of parsing `Shift-click <insert:test>this</insert> to insert!`, shown in-game in the Minecraft client's chat window +
+ +### Rainbow + +Rainbow-colored text?! + +Tag + * `` + +Arguments + * phase, optional + * `!`, literal value which reverses the rainbow, optional + +Examples +```mm +Woo: ||||||||||||||||||||||||! +Woo: ||||||||||||||||||||||||
! +Woo: ||||||||||||||||||||||||! +Woo: ||||||||||||||||||||||||! +``` + +Preview +
+The result of parsing all four examples in series, shown in-game in the Minecraft client's chat window +
+ +### Gradient + +Gradient colored text + +Tag + * `` + +Arguments + * a list of 1 to n colors, either hex or named colors and an optional phase parameter (range -1 to 1) allows you to shift the gradient around, creating animations. + +Examples +```mm +Woo: ||||||||||||||||||||||||! +Woo: ||||||||||||||||||||||||! +Woo: ||||||||||||||||||||||||! +Woo: ||||||||||||||||||||||||! +``` + +Preview +
+The result of parsing the examples for the gradient tag, shown in-game in the Minecraft client's chat window +
+ + +### Transition + +Transitions between colors. +Similar to a gradient, but everything is the same color and the phase chooses that color + +Tag + * `` +Arguments + * a list of 1 to n colors, either hex or named colors and an optional phase parameter (range -1 to 1) allows you to shift the transition around, creating animations. +Examples +```mm +||||||||| +Hello world [phase] +``` + +Preview +
+The result of parsing `<transition:white:black:red:[phase]>Hello World [phase]</transition>`, shown in-game in the Minecraft client's chat window +
+ + +### Font + +Allows to change the font of the text + +Tag + * `` + +Arguments + * the namespaced key of the font, defaulting to `minecraft` + +Examples +```mm +Nothing Uniform Alt Uniform +Uses a custom font from a resource pack +``` + +Preview +
+The result of parsing `Nothing <font:uniform>Uniform <font:alt>Alt  </font> Uniform`, shown in-game in the Minecraft client's chat window +
+ +### Newline + +Insert a newline character. + +Tag + * `` + +Aliases + * `br` + +Arguments + * none + +Examples +```mm +Let me insert a line break here. +Hover with aline break'>Text withline break +``` + +Preview +
+The result of parsing `<hover:show_text:'<red>Hover with a<newline><green>line break'>Text with<newline>line break</hover>`, shown in-game in the Minecraft client's chat window +
+ +### Selector + +*(since v4.11.0)* Insert a selector component + +Tag + * `` + +Aliases + * `sel` + +Arguments + * `_sel_`, the selector pattern to insert + * `_separator_` (optional), the separator to insert between values the selector matches + +Examples +```mm +Hello , I'm ! +``` + +Preview +
+The result of parsing `Hello <selector:@e[limit=5]>, I'm <selector:@s>!`, show in-game in the Minecraft client's chat window +
+ +### Score + +*(since v4.13.0)* Insert a score component. + +:::note + +The score component requires *rendering* on the server to be seen by clients. This is a platform-specific operation. + +::: + +Tag + * `` + +Arguments: + * `_name_`, the name of the score holder on the server scoreboard, or a selector resolved with receiver context + * `_objective_`, the name of the objective to get `name`'s score in + +Examples +```mm +You have won games! +``` + +### NBT + +*(since v4.13.0)* Insert a NBT component. The syntax of this tag is intended to be familiar to users of vanilla Minecraft's `/data` command. + +:::note + +The produced NBT component requires *rendering* on the server to be seen by clients. This is a platform-specific operation. + +::: + +Tag + * `` + +Aliases + * `data` + +Arguments: + * `block|entity|storage` the type of data source to read from -- a `block` entity, an `entity` selector, or the persistent command `storage` container + * `_id_`, the position for a block NBT component, a selector for an entity NBT component, or a key (resource location) for a storage NBT component + * `_path_`, the NBT path to resolve from within the data source + * `_separator_`, the separator between multiple values, if (primarily for entity NBT) the data source returns more than one + * `interpret`, the literal text `interpret` if the result should be parsed as component JSON + +Examples +```mm +Your health is +``` + +### Pride + +*(since v4.18.0)* Colors the text inside the tags with a gradient corresponding to a pride flag. + +Tag + * `` + +Arguments + * `flag` the flag to use, may be one of pride, progress, trans, bi, pan, nb, lesbian, ace, agender, demisexual, genderqueer, genderfluid, intersex, aro, baker, philly, queer, gay, bigender or demigender. + +Examples +```mm +Happy pride month! +Kyori supports trans rights! +``` diff --git a/src/content/docs/adventure/minimessage/index.md b/src/content/docs/adventure/minimessage/index.md new file mode 100644 index 000000000..06c2c1401 --- /dev/null +++ b/src/content/docs/adventure/minimessage/index.md @@ -0,0 +1,20 @@ +--- +title: MiniMessage +description: Documentation regarding MiniMessage. +tableOfContents: false +sidebar: + label: Overview +--- + +The MiniMessage format is a simple string representation of chat components, designed to be easy for end users to learn, and for developers to extend. + +```mm +Hello world, isn't MiniMessage fun? +``` + +If you're looking to write messages with MiniMessage, take a look at the [MiniMessage Format](/adventure/minimessage/format), or if you're looking to develop software that uses MiniMessage, take a look at the [API overview](/adventure/minimessage/api). + +- [Format](/adventure/minimessage/format) +- [API](/adventure/minimessage/api) +- [Dynamic replacements](/adventure/minimessage/dynamic-replacements) +- [MiniMessage translator](/adventure/minimessage/translator) diff --git a/src/content/docs/adventure/minimessage/translator.md b/src/content/docs/adventure/minimessage/translator.md new file mode 100644 index 000000000..b25b956a1 --- /dev/null +++ b/src/content/docs/adventure/minimessage/translator.md @@ -0,0 +1,63 @@ +--- +title: MiniMessage translator +description: A guide on the MiniMessage translator. +--- + +:::note + +For more information about both Minecraft and Adventure's localization systems, see `localization`. + +::: + +MiniMessage provides a `Translator` implementation that allows you to use MiniMessage as translation strings. +It also provides automatic support for argument placeholders, letting you use simple translatable components throughout your codebase. + +## Creating a MiniMessage translator + +To start, create an implementation of the `MiniMessageTranslator` and register it to the `GlobalTranslator`. +This can be done using `GlobalTranslator.translator().addSource(myMiniMessageTranslator)`. +For an example of how to create your own `MiniMessageTranslator`, see the below code block. + +```java +public class MyMiniMessageTranslator extends MiniMessageTranslator { + + public MyMiniMessageTranslator() { + // By default, the standard MiniMessage instance will be used. + // You can specify a custom one in the super constructor. + super(MiniMessage.miniMessage()); + } + + @Override + public @Nullable String getMiniMessageString(final @NotNull String key, final @NotNull Locale locale) { + // Creating a custom MiniMessage translator is as simple as overriding this one method. + // All you need to do is return a MiniMessage string for the provided key and locale. + // In this example we will hardcode this, but you could pull it from a resource bundle, a properties file, a config file or something else entirely. + if (key.equals("mykey") && locale == Locale.US) { + return "Hello, ! Today is ." + } else { + // Returning null "ignores" this translation. + return null; + } + } +} +``` + +### MiniMessage translation store + +In order to make managing a `MiniMessageTranslator` easier, we also provide a `TranslationStore` implementation using MiniMessage strings. +For documentation on how to use translation stores, see `localization`. + +Note that the `MiniMessageTranslationStore` contains the same methods as the message format translation store for populating a translation store using resource bundles. + +## Using a MiniMessage translator + +The MiniMessage translator will automatically turn translatable component arguments into a custom tag. +This tag will be `` or `` where `index` is the zero indexed position of the argument. +For example, this component `Component.translatable(key, Component.text("Kezz"))` with the MiniMessage string `Hello, !` will produce "Hello, Kezz!". + +You can also use the `Argument` class to create named tags for ease of use. +For example, this component `Component.translatable(key, Argument.component("name", Component.text("Kezz"))` will produce the string "Hello, Kezz!" +when used with either `Hello, !` or `Hello, !`. + +Finally, you can also add entirely custom tags or tag resolvers to the deserialization by using the rest of the methods on `Argument`. +For a full list, please see the Javadocs for the `Argument` class. diff --git a/src/content/docs/adventure/platform/bukkit.mdx b/src/content/docs/adventure/platform/bukkit.mdx new file mode 100644 index 000000000..17d1a7b76 --- /dev/null +++ b/src/content/docs/adventure/platform/bukkit.mdx @@ -0,0 +1,68 @@ +--- +title: Bukkit +description: The Bukkit Adventure implementation. +--- + +import Dependency from "/src/components/Dependency.astro"; +import { LATEST_ADVENTURE_SUPPORTED_MC, LATEST_ADVENTURE_PLATFORM_RELEASE } from "/src/utils/versions"; + +The Adventure platform implementation for Bukkit targets Paper, Spigot, and Bukkit for +Minecraft 1.7.10 through {LATEST_ADVENTURE_SUPPORTED_MC}. + +:::caution + +This page documents only the *legacy* platform adapter for Bukkit. Most users should use [Paper](/paper)'s native implementation instead. +This native implementation provides more functionality, better integration with the server, and does not require the `BukkitAudiences` adapter. + +::: + +Declaring the dependency: + + + +## Usage + +You should first obtain a `BukkitAudiences` object by using `BukkitAudiences.create(plugin)`. This object is thread-safe +and can be reused from different threads if needed. From here, Bukkit `CommandSender`s and `Player`s may be converted into +`Audience`s using the appropriate methods on `BukkitAudiences`. + +The audiences object should also be closed when a plugin is disabled in order to clean up resources and increase the likelihood of a successful `/reload`. + +```java +public class MyPlugin extends JavaPlugin { + + private BukkitAudiences adventure; + + @NonNull + public BukkitAudiences adventure() { + if (this.adventure == null) { + throw new IllegalStateException("Tried to access Adventure when the plugin was disabled!"); + } + return this.adventure; + } + + @Override + public void onEnable() { + // Initialize an audiences instance for the plugin + this.adventure = BukkitAudiences.create(this); + // then do any other initialization + } + + @Override + public void onDisable() { + if (this.adventure != null) { + this.adventure.close(); + this.adventure = null; + } + } +} +``` + +This audience provider should be used over the serializers directly, since it will handle compatibility measures for sending messages across versions. + +## Component serializers + +For areas that aren't covered by the `Audience` interface, the Bukkit platform provides the `MinecraftComponentSerializer` +(available on CraftBukkit-based servers), and the `BungeeComponentSerializer` (available on Spigot and Paper servers) +to convert directly between Adventure [Components](/adventure/text) and other component types. For uses that don't integrate +directly with native types, JSON and legacy format serializers for the running server version are exposed in `BukkitComponentSerializer`. diff --git a/src/content/docs/adventure/platform/bungeecord.mdx b/src/content/docs/adventure/platform/bungeecord.mdx new file mode 100644 index 000000000..915e7f32c --- /dev/null +++ b/src/content/docs/adventure/platform/bungeecord.mdx @@ -0,0 +1,72 @@ +--- +title: BungeeCord +description: The BungeeCord Adventure implementation. +--- + +import Dependency from "/src/components/Dependency.astro"; +import { LATEST_ADVENTURE_PLATFORM_RELEASE } from "/src/utils/versions"; + +Adventure targets the latest version of BungeeCord and BungeeCord-compatible +forks, such as Waterfall. + +:::caution + +The BungeeCord platform is intended for legacy environments only. +Most developers will want to write plugins for [Velocity](/velocity), which natively implements the Adventure API. No adapters required! + +::: + +Declaring the dependency: + + + +## Usage + +You should first obtain a `BungeeAudiences` object by using `BungeeAudiences.create(plugin)`. This object is thread-safe +and can be reused from different threads if needed. This object should also be *closed* when the plugin is disabled. + +Note that not all functionality is available on the proxy. Sending chat messages, action bar messages, titles, and boss bars, +and tab list header and footer are supported, but all other requests will fail silently. + +A simple example of how to appropriately initialize this platform follows: + +```java +public class MyPlugin extends Plugin { + + private BungeeAudiences adventure; + + @NonNull + public BungeeAudiences adventure() { + if (this.adventure == null) { + throw new IllegalStateException("Cannot retrieve audience provider while plugin is not enabled"); + } + return this.adventure; + } + + @Override + public void onEnable() { + this.adventure = BungeeAudiences.create(this); + } + + @Override + public void onDisable() { + if (this.adventure != null) { + this.adventure.close(); + this.adventure = null; + } + } +} +``` + +## Component serializers + +For functionality not already supported by `Audience`, the `BungeeComponentSerializer` allows you to convert between +Adventure [Components](/adventure/text) and the native BungeeCord chat component API and back. + +:::cautions + +For some areas of the proxy (notably, sending server list responses), the component serializer cannot be appropriately +injected unless a `BungeeAudiences` instance has been initialized. Using Adventure `Component` instances **will not** +work without a created `BungeeAudiences` instance. + +::: diff --git a/src/content/docs/adventure/platform/fabric.mdx b/src/content/docs/adventure/platform/fabric.mdx new file mode 100644 index 000000000..45360f585 --- /dev/null +++ b/src/content/docs/adventure/platform/fabric.mdx @@ -0,0 +1,207 @@ +--- +title: Fabric +description: The Fabric Adventure implementation. +--- + +import Dependency from "/src/components/Dependency.astro"; + +Adventure supports Fabric on *Minecraft: Java Edition* 1.16 and up, for both server-side and client-side use. Each major version of Minecraft +will usually require a new release of the platform. + +The platform supports all features, including localization and custom renderers. + +When using at least version 5.3.0, this platform provides a *near-native* experience by directly implementing Adventure interfaces on Minecraft classes where possible. + +:::danger[Attention] + +Version 6.x of adventure-platform-fabric, utilizing a shared implementation with NeoForge (see [Modded (Fabric and NeoForge shared API)](/adventure/platform/modded)) +is not published for Minecraft 1.20-1.21.1. This is to avoid conflicts with existing mods using 5.x. +Starting with Minecraft 1.21.2, both platforms have version 6.x published. + +::: + +## Dependency + +The Fabric platform is packaged as a mod, designed to be included in mods via jar-in-jar packaging. +As with the rest of the Adventure projects, releases are distributed on Maven Central, and snapshots on Sonatype OSS: + + + +The Fabric platform requires *fabric-api-base* in order to provide the locale change event, *fabric-command-api-v2* for the callback click event, +and can optionally use [Colonel](https://gitlab.com/stellardrift/colonel) (or *fabric-networking-api-v1*) to allow the `Component` and `Key` argument +types to be used on clients without the mod installed. There are no other dependencies. + +:::danger[Attention] + +Each major Minecraft release will require different platform versions. For older Minecraft versions, consult the table below. + +
+ Historic Versions + | Minecraft Version | Adventure version | `adventure-platform-fabric` version | + |-------------------|-------------------|-------------------------------------| + | 1.16.2-1.16.4 | 4.9.3 | 4.0.0 | + | 1.17.x | 4.9.3 | 4.1.0 | + | 1.18, 1.18.1 | 4.10.0 | 5.1.0 | + | 1.18.2 | 4.11.0 | 5.3.1 | + | 1.19 | 4.11.0 | 5.4.0 | + | 1.19.1-1.19.2 | 4.12.0 | 5.5.2 | + | 1.19.3 | 4.13.0 | 5.7.0 | + | 1.19.4 | 4.13.0 | 5.8.0 | + | 1.20-1.20.1 | 4.14.0 | 5.9.0 | + | 1.20.2 | 4.14.0 | 5.10.1 | + | 1.20.4 | 4.16.0 | 5.12.0 | + | 1.20.5-1.20.6 | 4.17.0 | 5.13.0 | + | 1.21-1.21.1 | 4.17.0 | 5.14.1 | +
+ +::: + +## Basic use + +The easiest way to get started with this platform is to work with the Minecraft game objects that directly implement Adventure interfaces (requires Loom 0.11 or newer). + +This covers almost all cases where the default renderer is used. + +The following Adventure interfaces are directly implemented: + +`Audience` + - `net.minecraft.commands.CommandSourceStack`, `net.minecraft.server.MinecraftServer`, `net.minecraft.server.rcon.RconConsoleSource`, + - `net.minecraft.server.level.ServerPlayer`, `net.minecraft.client.player.LocalPlayer` + +`Sound.Emitter` + - `net.minecraft.world.entity.Entity` + +`Sound.Type` + - `net.minecraft.sounds.SoundEvent` + +`Identified` + - `net.minecraft.world.entity.player.Player` + +`ComponentLike` + - `net.minecraft.network.chat.Component` + +`Key` + - `net.minecraft.resources.ResourceLocation` + +`Keyed` + - `net.minecraft.resources.ResourceKey` + +`HoverEventSource` + - `net.minecraft.world.entity.Entity`, + - `net.minecraft.world.item.ItemStack` + +`SignedMessage` + - `net.minecraft.network.chat.PlayerChatMessage` + +`SignedMessage.Signature` + - `net.minecraft.network.chat.MessageSignature` + +Additionally, all `Key`s created will be `ResourceLocation` instances (on Loader 0.14.0+) + +Using these injections, getting started is as simple as: + +```java +void greet(final ServerPlayer player) { + Component message = Component.text() + .content("Hello ") + .append(player.get(Identity.DISPLAY_NAME) + .get() + .color(NamedTextColor.RED) + ); + player.sendMessage(message); +} +``` + +For more complex use cases, `FabricServerAudiences` or `FabricClientAudiences` provide additional API. + +## Server + +The logical-server side of the Fabric platform can be accessed any time a server is available, through a `MinecraftServerAudiences` instance. By default, translatable components will be rendered with the global translator, but a custom renderer can be passed when initializing the platform. + +All `AudienceProvider` interface methods are supported, except for the `permission` method. This will become supported as soon as Fabric gets a suitable permissions API. + +To get started with Adventure, set up an audience provider like this: + +```java +public class MyMod implements ModInitializer { + + private volatile MinecraftServerAudiences adventure; + + public MinecraftServerAudiences adventure() { + if (this.adventure == null) { + throw new IllegalStateException("Tried to access Adventure without a running server!"); + } + return this.adventure; + } + + @Override + public void onInitialize() { + // Register with the server lifecycle callbacks + // This will ensure any platform data is cleared between game instances + // This is important on the integrated server, where multiple server instances + // can exist for one mod initialization. + ServerLifecycleEvents.SERVER_STARTING.register(server -> this.adventure = MinecraftServerAudiences.of(server)); + ServerLifecycleEvents.SERVER_STOPPED.register(server -> this.adventure = null); + } +} +``` + +From here, audiences can be acquired for players and any other `CommandSource`. Specialized serializer instances are also available, to allow using +game information in component serialization. + +### Localization + +As part of the platform's translation support, the `PlayerLocales.CHANGED_EVENT` callback will be called any time a player on the server receives an +updated language from their client, and allows accessing the current locale for a player. + +### Commands + +The Fabric platform provides custom argument types to specify `Key` and `Component` parameters in Brigadier commands, and has helpers to easily get an +`Audience` from a `CommandSourceStack` (yarn: `ServerCommandSource`) instance. + +:::caution + +If these custom argument types are used (pre-1.19), Vanilla clients will not be able to join unless the [Colonel](https://gitlab.com/stellardrift/colonel) +mod is installed on the server. Like the platform, it is small and easily included in your mod jar. + +::: + +As an example, here's a simple command that will echo whatever is provided as input: + +```java +// A potential method to be in the mod initializer class above +private static final String ARG_MESSAGE = "message"; + +void registerCommands(final CommandDispatcher dispatcher, final boolean isDedicated) { + dispatcher.register(literal("echo").then(argument(ARG_MESSAGE, component()).executes(ctx -> { + final Component message = component(ctx, ARG_MESSAGE); + ctx.getSource().sendMessage(Component.text("You said: ").append(message)); + })); +} +``` + +## Client + +Special for the Fabric platform, purely client-side operations are supported. The setup is less involved than it is for the server, +since the client is a singleton, and there is only one subject that can be acted on: the client's player. + +This means that for most users the `MinecraftClientAudiences` object can be treated as a singleton. The only exception is users using a +custom renderer. This makes using Adventure audiences fairly simple, as this code example shows: + +```java +void doThing() { + // Get the audience + final Audience client = MinecraftClientAudiences.of().audience(); + + // Do something. This will only work when the player is in game. + client.sendMessage(Component.text("meow", NamedTextColor.DARK_PURPLE)); +} +``` + +The full functionality of the `Audience` interface is available, including localization! + +## Working with native types + +Sadly, Adventure can't provide API for every place chat components are used in the game. However, for areas not covered by the API in `Audience`, +it's possible to convert components between native and Adventure types. See certain native types which implement +Adventure interfaces, and the methods on `FabricAudiences` for other available conversions. diff --git a/src/content/docs/adventure/platform/implementing.md b/src/content/docs/adventure/platform/implementing.md new file mode 100644 index 000000000..5edd18404 --- /dev/null +++ b/src/content/docs/adventure/platform/implementing.md @@ -0,0 +1,63 @@ +--- +title: Implementing platforms +description: Implementing Adventure for your own platform. +--- + +Most users will be here to look at information about existing platform implementations, but for those who are looking to build their own platform integrations, look no further. + +While at its core Adventure 'just' provides data structures and serializers, as the game evolves and more functionality is added there are more tunable options and platform hooks +necessary to produce the correct output for the applicable game version. This has led to the introduction of a variety of services that platforms can provide implementations of using +Java's `ServiceLoader` mechanism. Some other behaviors are expected by convention. As there are not that many platforms that integrate with Adventure, this page is an attempt to +cover the common points. Please don't be afraid to ask us questions, and together we can work on fleshing out this page. + +## Services + +### `ComponentSerializer` services + +Most of the serializers (Gson, legacy, etc.) have `Provider` SPI's that allow customizing the default behaviors of serializers. These are most applicable for the Gson/other JSON +serializers where the data structures have changed over time, but the legacy serializer's options can be worth referencing too. See the Javadoc for each serializer for more information. + +For any `JSONComponentSerializer` subtype, we have tried to gather relevant tunable options within a single system, keyed by the game's active +[data version](https://minecraft.wiki/w/Data_version). To handle hover events in pre-1.16 game versions, there's the additional `LegacyHoverEventSerializer` +interface. We offer an implementation that uses `adventure-nbt` as a separate submodule, but platforms may wish to use a native NBT library for this instead. +Both of these options should be set on builders in the appropriate `Provider` implementation. + +### Data component values + +To handle storing platform-specific data on `show_item` hover events, we expose opaque data objects in-API. Platforms should provide logic to convert between different +implementations by providing an implementation of `DataComponentValueConverterRegistry.Provider`. For the most part this is just converting between platform-specific types +and the generic `TagSerializable` and `Removed` types, but platforms should make sure to include converters to `GsonDataComponentValue` (from both platform types +*and* the generic `TagSerializable` that requires parsing SNBT for a conversion to occur). + +### Click callbacks + +As callbacks are a commonly desired feature, Adventure provides a 'virtual' click event type for callback functions. This action is not persistent between runs, and needs +platforms to register a command to trigger callbacks to execute. This is implemented via the `ClickCallback.Provider` SPI. This command should not be sent as part of the +command tree that clients receive to avoid spamming them. + +Platforms implementing the click callback provider must register a command at the appropriate time, and maintain a registry of active callbacks that is added to any time a +callback command is requested. The platform is responsible for ensuring any execution conditions apply and implementing the effects of any `Option`s that may be set on the callback. + +### Component logging + +`ComponentLogger`, as part of the `adventure-text-logger-slf4j` module, provides a logging interface that extends SLF4J and wraps any existing SLF4J logger (compatible with +v1 and v2). Platforms are responsible for providing the adapter that looks up the appropriate logger by name and serializes components to text. + +This should involve performing any translations if necessary. The default behavior of the logger is to serialize to plain text, but platforms may want to look at the +`/serializer/ansi` serializer instead for colored output. + +### Boss bars + +Boss bars are logistically somewhat complicated. As one of the few holders of mutable state in the library, they have to re-sync any state changes to their viewers. +In order to track viewers and link up to any internal state, the `BossBarImplementation.Provider` SPI allows platforms to provide their own implementation hooks per-bar. + +## Conventional behaviors + +Some behaviors are expected by platforms beyond what is explicitly required by implementing certain interfaces. These are: + +* When implementing `Audience`, any unsupported operations should fail silently. +* When sending components to a player, they should be passed through `GlobalTranslator` before sending to perform any translations (note: `GlobalTranslator` is only for + custom translations, and should not contain vanilla resource pack translations - they have a different interpolation syntax than `GlobalTranslator` uses) +* There is no specific required list of Adventure modules to distribute with your platform, but we recommend `adventure-api`, `adventure-text-minimessage`, + `adventure-text-logger-slf4j`, plus whatever serializers are required for the integration. We specifically do not recommend distributing the + `adventure-text-serializer-legacy` module unless it is necessary for backwards compatibility within your platform. diff --git a/src/content/docs/adventure/platform/index.md b/src/content/docs/adventure/platform/index.md new file mode 100644 index 000000000..24e084c85 --- /dev/null +++ b/src/content/docs/adventure/platform/index.md @@ -0,0 +1,59 @@ +--- +title: Platforms +description: Documentation regarding various implementations of the Adventure API. +tableOfContents: false +sidebar: + label: Overview +--- + +Adventure integrates with many of the Minecraft platforms out there. Some platforms support +Adventure natively, but other legacy platforms have their own types and need an adapter to handle Adventure types. To enable you to use Adventure with these platforms, Adventure provides a number of platform-specific adapters to +allow you to obtain `Audience` instances from native user types. + +Contents: +* [Native support](/adventure/platform/native) +* [Bukkit](/adventure/platform/bukkit) + * [Usage](/adventure/platform/bukkit#usage) + * [Component serializers](/adventure/platform/bukkit#component-serializers) +* [BungeeCord](/adventure/platform/bungeecord) + * [Usage](/adventure/platform/bungeecord#usage) + * [Component serializers](/adventure/platform/bungeecord#component-serializers) +* [SpongeAPI](/adventure/platform/spongeapi) + * [Usage](/adventure/platform/spongeapi#usage) +* [Modded (Fabric and NeoForge shared API)](/adventure/platform/modded) + * [Dependency](/adventure/platform/modded#dependency) + * [Basic use](/adventure/platform/modded#basic-use) + * [Working with native types](/adventure/platform/modded#working-with-native-types) +* [Fabric](/adventure/platform/fabric) + * [Dependency](/adventure/platform/fabric#dependency) + * [Basic use](/adventure/platform/fabric#basic-use) + * [Server](/adventure/platform/fabric#server) + * [Client](/adventure/platform/fabric#dependency) + * [Working with native types](/adventure/platform/fabric#working-with-native-types) +* [NeoForge](/adventure/platform/neoforge) + * [Dependency](/adventure/platform/neoforge#dependency) + * [Basic use](/adventure/platform/neoforge#basic-use) + * [Server](/adventure/platform/neoforge#server) + * [Commands](/adventure/platform/neoforge#commands) + * [Client](/adventure/platform/neoforge#dependency) +* [ViaVersion](/adventure/platform/viaversion) +* [Implementing platforms](/adventure/platform/implementing) + * [Services](/adventure/platform/implementing#services) + * [Conventional behaviors](/adventure/platform/implementing#conventional-behaviors) + + +:::note + +**Why is adventure-platform not sending any messages or not working correctly?** + +Firstly, please ensure you are on the latest stable version. It can be found on [Maven Central](https://central.sonatype.com/search?q=g%3Anet.kyori+adventure-platform*). + +Next, make sure that the feature you are using exists on the client version that is receiving the action. +For example, hex color codes won't work on clients older than 1.16, so hex colors will be down-sampled. + +If it's still not working, it is useful to enable debug mode by setting the system property `net.kyori.adventure.debug` to `true` and +looking at the output. This will show what facets are being selected which will help point towards why it is not working for you. +If you still cannot figure out the issue by yourself, you can always ask in the +[`#adventure-platform-help`](https://discord.com/channels/289587909051416579/1342379165663363112) channel in the PaperMC Discord! + +::: diff --git a/src/content/docs/adventure/platform/modded.mdx b/src/content/docs/adventure/platform/modded.mdx new file mode 100644 index 000000000..4241cedca --- /dev/null +++ b/src/content/docs/adventure/platform/modded.mdx @@ -0,0 +1,179 @@ +--- +title: Modded (Fabric and NeoForge shared API) +description: Fabric and NeoForge shared Adventure implementation. +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +Starting with *Minecraft: Java Edition* 1.21.2, Adventure is implemented using mostly shared code between NeoForge and Fabric. +Each major version of Minecraft will usually require a new release of the platform. + +The platform supports all features, including localization and custom renderers. + +## Dependency + +When building multi-loader mods, we often want to move as much code as possible into a loader-agnostic part of our projects. + +Adventure facilities this through the `net.kyori:adventure-platform-mod-shared` artifact. + +A second variant, `net.kyori:adventure-platform-mod-shared-fabric-repack`, is also published. This variant should be used +when your common code is managed by `fabric-loom`. + +If you are building a Fabric-only or NeoForge-only mod, or do not care to use the platform API in shared code, +then you don't need to use this artifact explicitly. This is because both platforms depend on it transitively. + +As with the rest of the Adventure projects, releases are distributed on Maven Central, and snapshots on Sonatype OSS: + + + + ```kotlin title="build.gradle.kts" replace + repositories { + // for development builds + maven(url = "https://s01.oss.sonatype.org/content/repositories/snapshots/") { + name = "sonatype-oss-snapshots1" + mavenContent { snapshotsOnly() } + } + // for releases + mavenCentral() + } + + dependencies { + // Loom project + modCompileOnly("net.kyori:adventure-platform-mod-shared-fabric-repack:\{LATEST_ADVENTURE_PLATFORM_MOD_RELEASE}") // for Minecraft \{LATEST_ADVENTURE_SUPPORTED_MC_RANGE} + + // NeoGradle/ModDevGradle/VanillaGradle project + compileOnly("net.kyori:adventure-platform-mod-shared:\{LATEST_ADVENTURE_PLATFORM_MOD_RELEASE}") // for Minecraft \{LATEST_ADVENTURE_SUPPORTED_MC_RANGE} + } + ``` + + + ```groovy title="build.gradle" replace + repositories { + // for development builds + maven { + name = 'sonatype-oss-snapshots1' + url = 'https://s01.oss.sonatype.org/content/repositories/snapshots/' + mavenContent { snapshotsOnly() } + } + // for releases + mavenCentral() + } + + dependencies { + // Loom project + modCompileOnly('net.kyori:adventure-platform-mod-shared-fabric-repack:\{LATEST_ADVENTURE_PLATFORM_MOD_RELEASE}') // for Minecraft \{LATEST_ADVENTURE_SUPPORTED_MC_RANGE} + + // NeoGradle/ModDevGradle/VanillaGradle project + compileOnly('net.kyori:adventure-platform-mod-shared:\{LATEST_ADVENTURE_PLATFORM_MOD_RELEASE}') // for Minecraft \{LATEST_ADVENTURE_SUPPORTED_MC_RANGE} + } + ``` + + + + +:::danger[Attention] + +Each major Minecraft release will require different platform versions. For older Minecraft versions, consult the table below. + +
+ Historic Versions + + | Minecraft Version | Adventure version | `adventure-platform-(mod-shared/fabric/neoforge)` version | + |-------------------|-------------------|-----------------------------------------------------------| + | 1.21.2-1.21.4 | 4.20.0 | 6.3.0 | + | 1.21-1.21.1 | 4.17.0 | 6.0.0 | + +
+ +::: + +## Basic use + +The easiest way to get started with this platform is to work with the Minecraft game objects that directly implement Adventure interfaces. + +This covers almost all cases where the default renderer is used. + +On Fabric, interface injection is used so that you can directly call interface methods on Minecraft objects (with loom 0.11+). + +On NeoForge, you must manually cast or use the helpers provided in `MinecraftAudiences`. + +The following Adventure interfaces are directly implemented: + +`Audience` + - `net.minecraft.commands.CommandSourceStack`, `net.minecraft.server.MinecraftServer`, `net.minecraft.server.rcon.RconConsoleSource`, + - `net.minecraft.server.level.ServerPlayer`, `net.minecraft.client.player.LocalPlayer` + +`AdventureCommandSourceStack` + - `net.minecraft.commands.CommandSourceStack` + +`Sound.Emitter` + - `net.minecraft.world.entity.Entity` + +`Sound.Type` + - `net.minecraft.sounds.SoundEvent` + +`Identified` + - `net.minecraft.world.entity.player.Player` + +`ComponentLike` + - `net.minecraft.network.chat.Component` + +`Key` + - `net.minecraft.resources.ResourceLocation` + +`Keyed` + - `net.minecraft.resources.ResourceKey` + +`HoverEventSource` + - `net.minecraft.world.entity.Entity`, + - `net.minecraft.world.item.ItemStack` + +`SignedMessage.Signature` + - `net.minecraft.network.chat.MessageSignature` + + +Using these injections, getting started is as simple as: + +```java +void greet(final ServerPlayer player) { + Component message = Component.text() + .content("Hello ") + .append(player.get(Identity.DISPLAY_NAME) + .get() + .color(NamedTextColor.RED) + ); + player.sendMessage(message); +} +``` + +For more complex use cases, `MinecraftServerAudiences` or `MinecraftClientAudiences` provide additional API. + +### Commands + +The platform provides custom argument types to specify `Key` and `Component` parameters in Brigadier commands. + +:::caution + +See the platform-specific documentation for details on registration and syncing of these argument types. + +::: + +As an example, here's a simple command that will echo whatever is provided as input: + +```java +// A potential method to be in the mod initializer class above +private static final String ARG_MESSAGE = "message"; + +void registerCommands(final CommandDispatcher dispatcher, final boolean isDedicated) { + dispatcher.register(literal("echo").then(argument(ARG_MESSAGE, component()).executes(ctx -> { + final Component message = component(ctx, ARG_MESSAGE); + ((Audience) ctx.getSource()).sendMessage(Component.text("You said: ").append(message)); + })); +} +``` + +## Working with native types + +Sadly, Adventure can't provide API for every place chat components are used in the game. However, for areas not covered by the API in `Audience`, +it's possible to convert components between native and Adventure types. See certain native types which implement +Adventure interfaces, and the methods on `MinecraftAudiences` for other available conversions. diff --git a/src/content/docs/adventure/platform/native.md b/src/content/docs/adventure/platform/native.md new file mode 100644 index 000000000..c9a9aed48 --- /dev/null +++ b/src/content/docs/adventure/platform/native.md @@ -0,0 +1,18 @@ +--- +title: Native support +description: All native supported software. +--- + +Native platforms integrate Adventure directly with their platform's provided API, and bundle Adventure automatically. +This allows them to more tightly integrate Adventure with the rest of the game, and avoids users having to handle distributing +Adventure and some platform adapter themselves. + +The following software provide native support for Adventure. + +| Platform | Minimum Version | Additional Notes | +|-----------|--------------------------------------|------------------------------------------------------------------------------------------------------------------| +| Sponge | Sponge 8 (1.16.5) | | +| Velocity | 1.1.0 build 158 | For more information, see the [Velocity Docs](/velocity/dev/pitfalls#audience-operations-are-not-fully-supported) | +| Paper | 1.16.5 build 473 | | +| Minestom | Build 7494725 | For more information, see the [Minestom Wiki](https://minestom.net/docs/feature/adventure) | +| Fabric | `adventure-platform-fabric` 5.3.0 | This is not strictly native, but injected interfaces provide a near-native experience | diff --git a/src/content/docs/adventure/platform/neoforge.mdx b/src/content/docs/adventure/platform/neoforge.mdx new file mode 100644 index 000000000..948a52d3f --- /dev/null +++ b/src/content/docs/adventure/platform/neoforge.mdx @@ -0,0 +1,98 @@ +--- +title: NeoForge +description: The NeoForge Adventure implementation. +--- + +import Dependency from "/src/components/Dependency.astro"; + +Adventure supports NeoForge on *Minecraft: Java Edition* 1.21 and up, for both server-side and client-side use. Each major version of Minecraft will usually require +a new release of the platform. + +The platform supports all features, including localization and custom renderers. + +## Dependency + +The NeoForge platform is packaged as a mod, designed to be included in mods via jar-in-jar packaging. As with the rest of the Adventure projects, +releases are distributed on Maven Central, and snapshots on Sonatype OSS: + + + +:::danger[Attention] + +Each major Minecraft release will require different platform versions. For older Minecraft versions, consult the table +at [Modded (Fabric and NeoForge shared API)](/adventure/platform/modded). + +::: + +## Basic use + +See [Modded (Fabric and NeoForge shared API)](/adventure/platform/modded) for usage details common between NeoForge and Fabric. + +## Server + +The logical-server side of the modded platform can be accessed any time a server is available, through a `MinecraftServerAudiences` +instance. By default, translatable components will be rendered with the global translator, but a custom renderer can be passed when initializing the platform. + +All `AudienceProvider` interface methods are supported. + +To get started with Adventure, set up an audience provider like this: + +```java +@Mod("my_mod") +public class MyMod { + + private volatile MinecraftServerAudiences adventure; + + public MinecraftServerAudiences adventure() { + if (this.adventure == null) { + throw new IllegalStateException("Tried to access Adventure without a running server!"); + } + return this.adventure; + } + + public MyMod() { + // Register with the server lifecycle callbacks + // This will ensure any platform data is cleared between game instances + // This is important on the integrated server, where multiple server instances + // can exist for one mod initialization. + NeoForge.EVENT_BUS.addListener((ServerStartingEvent e) -> + this.adventure = MinecraftServerAudiences.of(e.getServer()) + ); + NeoForge.EVENT_BUS.addListener((ServerStoppedEvent e) -> + this.adventure = null + ); + } +} +``` + +From here, audiences can be acquired for players and any other `CommandSource`. Specialized serializer instances are also available, to allow using +game information in component serialization. + +## Commands + +The NeoForge platform includes a method to register the `KeyArgumentType` and `ComponentArgumentType`: + - `AdventureArgumentTypes.register();` + +This should be called from the constructor of your `@Mod`-annotated class. +Registering the argument types on the server will require all clients that join to have the argument types +registered as well. + +## Client + +Special for the modded platform, purely client-side operations are supported. The setup is less involved than it is for the server, since the client is a singleton, and there +is only one subject that can be acted on: the client's player. + +This means that for most users the `MinecraftClientAudiences` object can be treated as a singleton. The only exception is users using a custom renderer. This makes using Adventure +audiences fairly simple, as this code example shows: + +```java +void doThing() { + // Get the audience + final Audience client = MinecraftClientAudiences.of().audience(); + + // Do something. This will only work when the player is ingame. + client.sendMessage(Component.text("meow", NamedTextColor.DARK_PURPLE)); +} +``` + +The full functionality of the `Audience` interface is available, including localization! diff --git a/src/content/docs/adventure/platform/spongeapi.mdx b/src/content/docs/adventure/platform/spongeapi.mdx new file mode 100644 index 000000000..b96bead94 --- /dev/null +++ b/src/content/docs/adventure/platform/spongeapi.mdx @@ -0,0 +1,45 @@ +--- +title: SpongeAPI +description: The SpongeAPI Adventure implementation. +--- + +import Dependency from "/src/components/Dependency.astro"; +import { LATEST_ADVENTURE_PLATFORM_RELEASE } from "/src/utils/versions"; + +Adventure provides a platform for SpongeAPI 7 for *Minecraft: Java Edition* 1.12. + +:::caution[Warning] + +For SpongeAPI 8 and up (targeting *Minecraft: Java Edition* 1.16.4), Adventure is the native text library, so no platform adapter is needed. +Sponge's API interfaces directly extend Adventure's rather than needing a `SpongeAudiences` adapter. + +::: + +Declaring the dependency: + + + +## Usage + +The SpongeAPI platform can either be created through Guice dependency injection, or created directly. We recommend using injection, since less boilerplate is required. + +An example plugin is fairly straightforward: + +```java +@Plugin(/* [...] */) +public class MyPlugin { + private final SpongeAudiences adventure; + + @Inject + MyPlugin(final SpongeAudiences adventure) { + this.adventure = adventure; + } + + @NonNull + public SpongeAudiences adventure() { + return this.adventure; + } +} +``` + +This sets up a `SpongeAudiences` instance that can provide audiences for players, or any `MessageReceiver`. diff --git a/src/content/docs/adventure/platform/viaversion.md b/src/content/docs/adventure/platform/viaversion.md new file mode 100644 index 000000000..cba79e179 --- /dev/null +++ b/src/content/docs/adventure/platform/viaversion.md @@ -0,0 +1,12 @@ +--- +title: ViaVersion +description: Using Adventure with ViaVersion. +--- + +On supported platforms (Sponge 7 and Bukkit), Adventure is able to enhance its functionality +by using the [ViaVersion](https://hangar.papermc.io/ViaVersion/ViaVersion) API +to send packets directly to the client. This allows, for instance, for a plugin on a Minecraft +1.7 server to send RGB chat messages and titles to clients on newer versions of Minecraft. + +If you include the Sponge or Bukkit platforms, no further action is required: ViaVersion will +be detected and support for it will be enabled. diff --git a/src/content/docs/adventure/resource-pack.md b/src/content/docs/adventure/resource-pack.md new file mode 100644 index 000000000..fd0c680f7 --- /dev/null +++ b/src/content/docs/adventure/resource-pack.md @@ -0,0 +1,69 @@ +--- +title: Resource packs +description: A guide to using resource packs with Adventure. +--- + +On top of the resource packs controlled by each player on their client, the game allows servers to send resource pack URLs to clients that the players can choose to accept. This allows servers to provide customized styling. + +Initially this just allowed sending a single resource pack, but starting with *Minecraft 1.20.3* the server can send multiple resource packs to be stacked, and if needed removed individually. + +## Sending resource packs + +A resource pack is identified by: + * its UUID + * a URI to the resource pack ZIP file + * the SHA-1 hash of the resource pack ZIP file as a hex string + +This is referred to as `ResourcePackInfo`. + +For every batch of resource packs being sent, a `ResourcePackRequest`, there is: + * one or more resource packs + * a callback to perform actions based on the responses from the client + * a toggle for whether to replace any existing server-provided resource packs, or stack the most recent packs on top + * whether these resource packs are required + * a prompt to display to the user if they have not yet chosen whether to allow server resource packs + +## Examples + +Send a single resource pack to a client that is required, with a UUID computed based on its name. + +```java +private static final ResourcePackInfo PACK_INFO = ResourcePackInfo.resourcePackInfo() + .uri(URI.create("https://example.com/resourcepack.zip")) + .hash("2849ace6aa689a8c610907a41c03537310949294") + .build(); + +public void sendResourcePack(final @NonNull Audience target) { + final ResourcePackRequest request = ResourcePackRequest.resourcePackRequest() + .packs(PACK_INFO) + .prompt(Component.text("Please download the resource pack!")) + .required(true) + .build(); + + // Send the resource pack request to the target audience + target.sendResourcePacks(request); +} + +public void sendOptionalResourcePack(final @NonNull Audience target) { + final ResourcePackRequest request = ResourcePackRequest.resourcePackRequest() + .packs(PACK_INFO) + .prompt(Component.text("Please download the resource pack!")) + .required(false) + .build(); + + // Send the resource pack request to the target audience + target.sendResourcePacks(request); +} +``` + +## Callbacks + +The callback function allows servers to respond to pack download feedback sent by the client. Newer versions of the game provide more information about different phases, but any version will provide basic status info about download and application. Keep in mind that the responses are entirely driven by the client, so modded clients may send incorrect information (for example, saying a required resource pack has been applied when it has not), none at all, or even nonsensical information (status updates after a terminal update has been received). Any action taken based on a callback should therefore be defensively designed to cope with client creativity. + +The audience provided in the callback aims to be the exact same audience the resource pack was sent to in the case of wrapping audiences, re-wrapping any underlying returned value where necessary. + +## Removing resource packs + +Resource packs can be removed, either some quantity at a time with `Audience.removeResourcePacks()`, or all at once with `Audience.clearResourcePacks()`. + +The removal methods have multiple overloads, allowing removal by a bare UUID, or by reusing the data structures used for applying resource packs. diff --git a/src/content/docs/adventure/serializer/ansi.mdx b/src/content/docs/adventure/serializer/ansi.mdx new file mode 100644 index 000000000..82fd5170d --- /dev/null +++ b/src/content/docs/adventure/serializer/ansi.mdx @@ -0,0 +1,81 @@ +--- +title: ANSI +description: Serializing components to ANSI. +--- + +import Dependency from "/src/components/Dependency.astro"; +import { LATEST_ANSI_RELEASE, LATEST_ADVENTURE_API_RELEASE } from "/src/utils/versions"; + +The ANSI text serializer is an encoder that converts components to text containing +[ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code), which allows +for styled text in a terminal. This can then be used to, for example, output server +logs containing components while preserving their color and style. + +Note that since it's an encoder, it can only serialize components, and can't +deserialize text back into components. + +Declaring the dependency: + + + +## Usage + +Different kind of ANSI escape codes exist, allowing for different levels of color +precision, however, not all terminals support newer kinds of ANSI escape sequences. +By default, `ANSIComponentSerializer` will attempt to guess the supported kinds of +escape sequences based on the system's environment variables. + +This can be overridden individually for a single serializer instance using the +`colorLevel` method of the builder. + +It can also be overridden globally using system properties, using the property +`net.kyori.ansi.colorLevel`, which can be set when launching the JVM using the +command-line option `-Dnet.kyori.ansi.colorLevel=value`. 4 different values can +be set: + +* `none`: Prevent any ANSI escape sequences from being emitted at all. +* `indexed16`: The original set of 16 colors. +* `indexed256`: Slightly newer set of 256 colors. +* `truecolor`: Full 24-bit spectrum of colors. + +ANSI escape sequences can also be disabled using the `terminal.ansi` system property, +by setting it to `false`. + +## ANSI library + +:::note + +This section talks about the component-agnostic library. If you are only interested in +the Adventure component-specific implementation, you do not need to read this section. + +::: + +The `AnsiComponentSerializer` is built upon a separate ANSI library, which deals with +the lower-level ANSI escape sequence logic, and also allows for creating an ANSI +converter for any kind of component, not just those by Adventure. + +Declaring the dependency: + + + +### Implementation usage + +To begin with, you need to create a class that implements `StyleOps`, where `S` is +the "style" type for your component type. This adapter class allows for the ANSI logic to +access properties about the style. + +To actually begin conversion, create an instance of a `ANSIComponentRenderer`, by using +one of the static methods, the simplest of which is `ANSIComponentRenderer.toString()`, +passing it an instance of your `StyleOps` adapter described above. + +Then, you will need to traverse the structure of your component's tree, using the +`pushStyle()`, `text()` and `popStyle()` methods of the renderer instance. + +Finally, call `complete()` after traversing the tree has finished. The renderer's job +is now concluded. In the case of the `ToString` renderer, you can access the result +using the `asString()` method. + +As described in the [ANSI Usage](#usage) section, the library will, by default, try to guess the +supported colors of the current environment. This may be overridden by passing a custom +`ColorLevel` when creating the renderer, or by using the system properties as previously +described. diff --git a/src/content/docs/adventure/serializer/gson.mdx b/src/content/docs/adventure/serializer/gson.mdx new file mode 100644 index 000000000..a7c4f1ccf --- /dev/null +++ b/src/content/docs/adventure/serializer/gson.mdx @@ -0,0 +1,50 @@ +--- +title: Gson +description: Serializing components to JSON with Gson. +--- + +import Dependency from "/src/components/Dependency.astro"; +import { LATEST_ADVENTURE_API_RELEASE } from "/src/utils/versions"; + +The Gson text serializer converts chat components to their JSON representation +and back using the Gson library. If you are interested in sending a chat component +for display in a Minecraft client, or want to support advanced chat component features, +you should use the Gson text serializer. + +An average user of this text serializer will typically want to only deserialize a +component from an external source - serialization is done automatically by the +[Platforms](/adventure/platform) when the component is sent to the user. + +Declaring the dependency: + + + +## Usage + +In Minecraft 1.16, Mojang made several major changes to the JSON chat format, adding +RGB chat colors and changing how hover events are serialized. Components generated for +older versions of Minecraft will still be able to be displayed in a 1.16 client, +however components serialized for a 1.16 client will not be able to be displayed in +a Minecraft 1.15.2 client or lower. + +To get a serializer that works with 1.16 clients and above, use +`GsonComponentSerializer.gson()`. To get a serializer that works with all versions +of Minecraft that support text components, use `GsonComponentSerializer.colorDownsamplingGson()`. +This serializer downsamples RGB colors to the closest Mojang legacy color and serializes +hover events in a way that is backwards compatible with older clients. + +### Which serializer should I use? + +If all you're doing is loading and saving components to a configuration file or a database, +you probably want to use the default 1.16 serializer. + +If you're looking to send a component to a client, first consider whether you can one of the +provided platforms. If you can't use a platform, generally you should prefer the default +serializer for deserializing components (as it is backwards-compatible), and make a decision +on whether to use the default or the color downsampling serializer based on the version the +client is on. + +### Advanced usage + +The Gson serializer exposes both the backing `Gson` instance and a populator that allows +registering Adventure serializers on any `GsonBuilder` instance. diff --git a/src/content/docs/adventure/serializer/index.md b/src/content/docs/adventure/serializer/index.md new file mode 100644 index 000000000..b5611bc06 --- /dev/null +++ b/src/content/docs/adventure/serializer/index.md @@ -0,0 +1,57 @@ +--- +title: Text serializers +description: Everything to know about text/component serializers. +sidebar: + label: Overview +--- + +The lowest-level way to convert between Adventure's data and other formats +are serializers. Some serializers convert to standard formats, while others +convert to Adventure's own formats. + +- [JSON](/adventure/serializer/json) +- [Gson](/adventure/serializer/gson) +- [Legacy](/adventure/serializer/legacy) +- [Plain](/adventure/serializer/plain) +- [MiniMessage](/adventure/minimessage) + +Components can be converted using any of these serializers: + +```java +// Creates a text component +final TextComponent textComponent = Component.text() + .content("Hello ") + .color(NamedTextColor.GOLD) + .append(Component.text("world", NamedTextColor.AQUA, TextDecoration.BOLD)) + .append(Component.text("!", NamedTextColor.RED)) + .build(); + +// Converts textComponent to the JSON form used for serialization by Minecraft. +final String json = JSONComponentSerializer.json().serialize(textComponent); + +// Converts textComponent to a legacy string - "&6Hello &b&lworld&c!" +final String legacy = LegacyComponentSerializer.legacyAmpersand().serialize(textComponent); + +// Converts textComponent to a plain string - "Hello world!" +final String plain = PlainTextComponentSerializer.plainText().serialize(textComponent); +``` + +The same is of course also possible in reverse for deserialization. + +```java +// Converts JSON in the form used for serialization by Minecraft to a Component +final Component component = JSONComponentSerializer.json().deserialize(json); + +// Converts a legacy string (using formatting codes) to a TextComponent +final Component component = LegacyComponentSerializer.legacyAmpersand().deserialize("&6Hello &b&lworld&c!"); + +// Converts a plain string to a TextComponent +final Component component = PlainTextComponentSerializer.plainText().deserialize("Hello world!"); +``` + +## Text encoders + +Text encoders are similar to serializers, but they only provide one-way +operations, allowing for serialization but not deserialization. + +- [ANSI](/adventure/serializer/ansi) diff --git a/src/content/docs/adventure/serializer/json.md b/src/content/docs/adventure/serializer/json.md new file mode 100644 index 000000000..75c60bcff --- /dev/null +++ b/src/content/docs/adventure/serializer/json.md @@ -0,0 +1,26 @@ +--- +title: JSON +description: Serializing components to their JSON representation. +--- + +The JSON serializer provides a common interface for serializer implementations that translate between a Component and JSON strings. This allows a library to support any underlying JSON library than an application may want to use. + +## Use + +The JSON serializer works similar to all others, providing the basic serialize and deserialize operations: + +```java +// Component to text +final String jsonText = JSONComponentSerializer.json().serialize(Component.text("Hello world", NamedTextColor.LIGHT_PURPLE)); + +// JSON string to component +final Component comp = JSONComponentSerializer.json().deserialize(jsonText); +``` + +Additionally, there is a `JSONComponentSerializer.builder()` available for advanced use that requires configuring legacy compatibility options. + +## Known implementations + +| Name | Description | +| ------------------------------------------------------------ | ------------------------------------------------------- | +| [adventure-text-serializer-gson](/adventure/serializer/gson) | A mature serializer working with Google's Gson library. | diff --git a/src/content/docs/adventure/serializer/legacy.mdx b/src/content/docs/adventure/serializer/legacy.mdx new file mode 100644 index 000000000..c8c147238 --- /dev/null +++ b/src/content/docs/adventure/serializer/legacy.mdx @@ -0,0 +1,52 @@ +--- +title: Legacy +description: (Deprecated) Serializing components to legacy format. +--- + +import Dependency from "/src/components/Dependency.astro"; +import { LATEST_ADVENTURE_API_RELEASE } from "/src/utils/versions"; + +The legacy text serializer converts text to and from the traditional chat format used +in Minecraft prior to Minecraft 1.7, and continues to be used to this day for its +familiarity to server owners. + +The legacy text serializer does not support most advanced features, including hover +and click events, components besides text components, and insertions. RGB colors +are supported (see more in the [RGB support](#rgb-support) section) and URLs can be transformed +into clickable components if explicitly requested (note, however, that click events +containing a URL will *not* be serialized). If advanced features are desired, consider +using [MiniMessage](/adventure/minimessage). + +Declaring the dependency: + + + +## Usage + +The legacy text serializer is accessed using the `LegacyComponentSerializer`. The default +pre-provided serializers include one that uses the section symbol (§) (for display in +old clients) and another that uses an ampersand (&) typically used in configuration and +commands to specify color codes. + +The default configuration for the legacy text serializer will deserialize all three of +the RGB formats supported by Adventure but will only serialize legacy Mojang colors +(downsampling to the nearest color as needed) and does not transform URLs in text to +links. You can configure an instance to automatically add click events to URLs in +components and allow the serializer to serialize RGB colors in either the Adventure +RGB format or the BungeeCord RGB format using the builder. + +## RGB support + +The legacy serializer supports deserializing three different formats: + +* Legacy Mojang color and formatting codes (such as `§a` or `§l`). +* An Adventure-specific RGB format that is intended to be easy to edit + (such as `§#a25981`). +* A BungeeCord RGB color code format that is backwards compatible with + older deserialization routines but is difficult to manipulate and makes + it the user's responsibility to assign a fallback for non-RGB clients (such + as `§x§a§2§5§9§8§1`). + +The legacy serializer downsamples RGB colors by default, but you can create a serializer +that serializes RGB colors in either the Adventure or BungeeCord RGB formats using the +builder. diff --git a/src/content/docs/adventure/serializer/plain.mdx b/src/content/docs/adventure/serializer/plain.mdx new file mode 100644 index 000000000..4872380fa --- /dev/null +++ b/src/content/docs/adventure/serializer/plain.mdx @@ -0,0 +1,30 @@ +--- +title: Plain +description: Retrieving plain text content from components. +--- + +import Dependency from "/src/components/Dependency.astro"; +import { LATEST_ADVENTURE_API_RELEASE } from "/src/utils/versions"; + +The plain text serializer converts chat components to their plain-text representation +and back. It is thus the simplest text serializer in Adventure. This serializer is +useful for supporting legacy clients, logging, clearing formatting from a component that +originates from external source, and provides a small, self-contained example of a +text serializer. + +The plain text serializer, by its nature, does not support any advanced features, including +color, hover and click events, URL linking, or insertions. If advanced features are desired, +consider using [MiniMessage](/adventure/minimessage). + +Declaring the dependency: + + + +## Usage + +This produces a default instance that silently ignores keybind and translatable components. You can also construct your own +`PlainTextComponentSerializer` that maps the components to some plain-text representation. + +The deserialization of plain text is equivalent to `Component.text(string)`. No +preprocessing is done on the input. The deserialization is implemented in order to provide +API consistency. diff --git a/src/content/docs/adventure/sound.md b/src/content/docs/adventure/sound.md new file mode 100644 index 000000000..c28963960 --- /dev/null +++ b/src/content/docs/adventure/sound.md @@ -0,0 +1,82 @@ +--- +title: Sound +description: A guide to playing sound with Adventure. +--- + +Adventure contains an API to play any built-in or resource pack-provided sound. Note that +not all platforms implement playing sound. + +## Constructing a Sound + +Sounds are composed of: + * A Key (also known as `Identifier` or `ResourceLocation`) that decides which sound to play. Any custom sounds from resource packs can be used. If a client does not know about sounds, it will ignore the sound (though a warning will be printed to the client log). + * A Sound source, used to tell the client what type of sound its hearing. The clients sound settings are also attributed to a source. + * A number, determining the radius where the sound can be heard + * A number from 0 to 2 determining the pitch the sound will be played at + +**Examples:** + +```java +// Create a built-in sound using standard volume and pitch +Sound musicDisc = Sound.sound(Key.key("music_disc.13"), Sound.Source.MUSIC, 1f, 1f); + +// Create a sound from our resource pack with a higher pitch +Sound myCustomSound = Sound.sound(Key.key("adventure", "rawr"), Sound.Source.AMBIENT, 1f, 1.1f); +``` + +## Playing a Sound + +:::caution + +The client can play multiple sounds at once, but as of version 1.16 is limited to 8 sounds playing at once. + +In 1.15.2-1.16.5, due to `MC-138832`, the volume and pitch of sounds played with an emitter are ignored. + +As documented in `MC-146721`, any stereo sounds will not play at a specific position or following an entity, therefore, the location or emitter parameters will be ignored. + +::: + +Once you've created a sound, they can be played to an audience using multiple methods: + +```java +// Play a sound at the location of the audience +audience.playSound(sound); + +// Play a sound at a specific location +audience.playSound(sound, 100, 0, 150); + +// Play a sound that follows the audience member +audience.playSound(sound, Sound.Emitter.self()); + +// Play a sound that follows another emitter (usually an entity) +audience.playSound(sound, someEntity); +``` + +## Stopping Sounds + +A sound stop will stop the chosen sounds -- ranging from every sound the client is playing, to specific named sounds. + +```java +public void stopMySound(final @NonNull Audience target) { +// Stop a sound for the target +target.stopSound(SoundStop.named(Key.key("music_disc.13")); +// Stop all weather sounds for the target +target.stopSound(SoundStop.source(Sound.Source.WEATHER)); +// Stop all sounds for the target +target.stopSound(SoundStop.all()); +``` + +Sound stops can be constructed using the methods in the example block above. +Alternatively, they can be constructed directly from a sound. + +```java +// Get a sound stop that will stop a specific sound +mySound.asStop(); + +// Sounds can also be stopped directly using the stopSound method +audience.stopSound(mySound); +``` + +## Creating a custom sound + +Use the `sounds.json` file to define sounds in a resource pack. Further reading about this limits can be done at the [Minecraft Wiki](https://minecraft.wiki/w/Sounds.json) diff --git a/src/content/docs/adventure/tablist.md b/src/content/docs/adventure/tablist.md new file mode 100644 index 000000000..810ca635c --- /dev/null +++ b/src/content/docs/adventure/tablist.md @@ -0,0 +1,36 @@ +--- +title: Player list/Tab list +description: Setting the player list with Adventure. +--- + +Adventure only supports changing the header (above the players) and footer (below the players) of the tab list. + +![Image showing a tab list from a multiplayer server with the header and footer encased, shown through the vanilla Minecraft client](./assets/tablist.png) + +**Usage** + +With any `Audience` use `Audience.sendPlayerListHeader(Component)`, `Audience.sendPlayerListFooter(Component)` +and/or `Audience.sendPlayerListHeaderAndFooter(Component, Component)`. + +Whether sending a header or footer by itself will display another existing header or footer will vary depending on which platform +you are working on. Servers will most likely support keeping headers or footers when sending them separately, while proxies are +more likely to only let you send everything at once. + +**Examples** + +```java +public void onPlayerJoin(final Audience player) { + final Component header = Component.text("My Cool Server", NamedTextColor.BLUE); + final Component footer = Component.text("It is: today!"); + player.sendPlayerListHeaderAndFooter(header, footer); +} +``` + +Depending on your platform this next example might display an existing header as well + +```java +public void onDayChange(final Audience server) { + final Component footer = Component.text("It is: tomorrow!"); + server.sendPlayerListFooter(footer); +} +``` diff --git a/src/content/docs/adventure/text.md b/src/content/docs/adventure/text.md new file mode 100644 index 000000000..a479e10f1 --- /dev/null +++ b/src/content/docs/adventure/text.md @@ -0,0 +1,93 @@ +--- +title: Text (Chat Components) +description: Everything you need to know about Components. +--- + +Components represent Minecraft chat components. + +## Creating components + +```java +// Creates a line of text saying "You're a Bunny! Press to jump!", with some coloring and styling. +final TextComponent textComponent = Component.text("You're a ") + .color(TextColor.color(0x443344)) + .append(Component.text("Bunny", NamedTextColor.LIGHT_PURPLE)) + .append(Component.text("! Press ")) + .append( + Component.keybind("key.jump") + .color(NamedTextColor.LIGHT_PURPLE) + .decoration(TextDecoration.BOLD, true) + ) + .append(Component.text(" to jump!")); +// now you can send `textComponent` to something, such as a client +``` + +You can also use a builder, which is mutable, and creates one final +component with the children. + +```java +// Creates a line of text saying "You're a Bunny! Press to jump!", with some coloring and styling. +final TextComponent textComponent2 = Component.text() + .content("You're a ") + .color(TextColor.color(0x443344)) + .append(Component.text().content("Bunny").color(NamedTextColor.LIGHT_PURPLE)) + .append(Component.text("! Press ")) + .append( + Component.keybind().keybind("key.jump") + .color(NamedTextColor.LIGHT_PURPLE) + .decoration(TextDecoration.BOLD, true) + .build() + ) + .append(Component.text(" to jump!")) + .build(); +// now you can send `textComponent2` to something, such as a client +``` + +## Styling components + +Styles are a superset of TextColor and TextDecoration and can be applied to text components. +TextColor represents any color in the RGB spectrum. +You can also use NamedTextColor to choose from the default color palette. +The following TextDecorations are available: + +* *Italic* +* **Bold** +* Strikethrough +* Underlined +* Obfuscated + +## Events + +There are currently two types of events available for text components. +Hover events allow you to show another component, item or entity when a user hovers their mouse over the text. +When a user clicks on the text component, a click event is fired which can perform one of the following actions: + +* Open a URL +* Open a file +* Run a command +* Suggest a command +* Change a book's page +* Copy a string to clipboard + +## Serializing and deserializing components + +Serialization to JSON, legacy, and plain representations is also +supported. + +Components can be serialized with [Text Serializers](/adventure/serializer). + +## Using components within your application + +The way you use components within your application will of course vary +depending on what you're aiming to achieve. + +However, the most common task is likely to be sending a component to +some sort of Minecraft client. The method for doing this will depend on +the platform your program is running on, however it is likely to involve +serializing the component to Minecraft's JSON format, and then sending +the JSON through another method provided by the platform. + +The text library is platform-agnostic and therefore doesn't provide any +way to send components to clients. Some platforms implement [Adventure natively](/adventure/platform/native), so `Components` +can be directly used with their API. For other platforms (Spigot/Bukkit, BungeeCord, and SpongeAPI 7), +we provide compatibility bridges as [Platforms](/adventure/platform) which can be distributed with your own plugins. diff --git a/src/content/docs/adventure/titles.md b/src/content/docs/adventure/titles.md new file mode 100644 index 000000000..a522d25d0 --- /dev/null +++ b/src/content/docs/adventure/titles.md @@ -0,0 +1,36 @@ +--- +title: Titles +description: Displaying titles to players. +--- + +## Constructing a Title + +Titles are composed of: + * A component used for the main title + * A component used for the subtitle + * Optionally, a `Title.Times` object can be used to determine the fade-in, stay on screen and fade-out durations + + +**Examples:** + +```java +public void showMyTitle(final @NonNull Audience target) { + final Component mainTitle = Component.text("This is the main title", NamedTextColor.WHITE); + final Component subtitle = Component.text("This is the subtitle", NamedTextColor.GRAY); + + // Creates a simple title with the default values for fade-in, stay on screen and fade-out durations + final Title title = Title.title(mainTitle, subtitle); + + // Send the title to your audience + target.showTitle(title); +} + +public void showMyTitleWithDurations(final @NonNull Audience target) { + final Title.Times times = Title.Times.times(Duration.ofMillis(500), Duration.ofMillis(3000), Duration.ofMillis(1000)); + // Using the times object this title will use 500ms to fade in, stay on screen for 3000ms and then fade out for 1000ms + final Title title = Title.title(Component.text("Hello!"), Component.empty(), times); + + // Send the title, you can also use Audience#clearTitle() to remove the title at any time + target.showTitle(title); +} +``` diff --git a/src/content/docs/adventure/version-history/index.mdx b/src/content/docs/adventure/version-history/index.mdx new file mode 100644 index 000000000..84d8f6357 --- /dev/null +++ b/src/content/docs/adventure/version-history/index.mdx @@ -0,0 +1,15 @@ +--- +title: Version history +description: Pages dedicated to displaying the latest changelogs! +tableOfContents: false +--- + +import { CardGrid, LinkCard } from "@astrojs/starlight/components"; + +These changelogs for individual projects mirror those posted on the GitHub Releases page of each project's repository. + + + + + + diff --git a/src/content/docs/index.mdx b/src/content/docs/index.mdx index 823576994..8e43c850f 100644 --- a/src/content/docs/index.mdx +++ b/src/content/docs/index.mdx @@ -25,12 +25,12 @@ import LinkCard from "/src/components/LinkCard.astro"; A fork of Paper which adds regionized multithreading to the dedicated server. + + A Java library for server-controllable user interface elements in Minecraft: Java Edition. + A discontinued BungeeCord proxy fork that aimed to improve performance and stability. - - A Java library for server-controllable user interface elements in Minecraft: Java Edition. - Various other documentation and tools. diff --git a/src/content/docs/misc/hangar-publishing.md b/src/content/docs/misc/hangar-publishing.md index f53ea78b0..ad66c43f5 100644 --- a/src/content/docs/misc/hangar-publishing.md +++ b/src/content/docs/misc/hangar-publishing.md @@ -75,7 +75,7 @@ waterfallVersion=1.20 In the plugins block of your `build.gradle.kts` build script, add the publish plugin: -```kotlin +```kotlin title="build.gradle.kts" plugins { id("io.papermc.hangar-publish-plugin") version "0.1.2" } diff --git a/src/content/docs/paper/dev/getting-started/project-setup.mdx b/src/content/docs/paper/dev/getting-started/project-setup.mdx index a983ff6b3..6ee1a2007 100644 --- a/src/content/docs/paper/dev/getting-started/project-setup.mdx +++ b/src/content/docs/paper/dev/getting-started/project-setup.mdx @@ -26,8 +26,8 @@ You will land into the `build.gradle.kts` file where you can add your dependenci To add Paper as a dependency, you will need to add the Paper repository to your `build.gradle.kts` or `pom.xml` file as well as the dependency itself. - - + + ```kotlin title="build.gradle.kts" replace repositories { maven { @@ -45,7 +45,7 @@ To add Paper as a dependency, you will need to add the Paper repository to your } ``` - + ```groovy title="build.gradle" replace repositories { maven { @@ -59,7 +59,7 @@ To add Paper as a dependency, you will need to add the Paper repository to your } ``` - + ```xml title="pom.xml" replace @@ -244,9 +244,9 @@ If you are using Paper plugins, this step is not needed as plugins will be assum ::: - - - ```kotlin + + + ```kotlin title="build.gradle.kts" tasks.jar { manifest { attributes["paperweight-mappings-namespace"] = "mojang" @@ -260,8 +260,27 @@ If you are using Paper plugins, this step is not needed as plugins will be assum } ``` - - ```xml + + ```groovy title="build.gradle" + jar { + manifest { + attributes( + 'paperweight-mappings-namespace': 'mojang' + ) + } + } + // if you have shadowJar configured + shadowJar { + manifest { + attributes( + 'paperweight-mappings-namespace': 'mojang' + ) + } + } + ``` + + + ```xml title="pom.xml" org.apache.maven.plugins maven-jar-plugin @@ -282,9 +301,9 @@ If you are using Paper plugins, this step is not needed as plugins will be assum If you explicitly want to tell the server that your plugin is Spigot-mapped, you need to add the following code to your build script: - - - ```kotlin + + + ```kotlin title="build.gradle.kts" tasks.jar { manifest { attributes["paperweight-mappings-namespace"] = "spigot" @@ -298,8 +317,27 @@ If you explicitly want to tell the server that your plugin is Spigot-mapped, you } ``` - - ```xml + + ```groovy title="build.gradle" + jar { + manifest { + attributes( + 'paperweight-mappings-namespace': 'spigot' + ) + } + } + // if you have shadowJar configured + shadowJar { + manifest { + attributes( + 'paperweight-mappings-namespace': 'spigot' + ) + } + } + ``` + + + ```xml title="pom.xml" org.apache.maven.plugins maven-jar-plugin diff --git a/src/content/docs/paper/dev/getting-started/userdev.md b/src/content/docs/paper/dev/getting-started/userdev.md index a4dbf9341..3746b9a0c 100644 --- a/src/content/docs/paper/dev/getting-started/userdev.md +++ b/src/content/docs/paper/dev/getting-started/userdev.md @@ -106,7 +106,7 @@ The `-dev-all.jar` file in `build/libs` is the shaded, but not re-obfuscated JAR ::: You can make the `reobfJar` task run on the default `build` task with: -```kotlin +```kotlin title="build.gradle(.kts)" tasks.assemble { dependsOn(tasks.reobfJar) } diff --git a/src/content/docs/paper/dev/misc/databases.md b/src/content/docs/paper/dev/misc/databases.md index 93bf00277..099125611 100644 --- a/src/content/docs/paper/dev/misc/databases.md +++ b/src/content/docs/paper/dev/misc/databases.md @@ -101,7 +101,7 @@ This will require a running MySQL database to connect to. First, add the dependency to your project with the following dependency: ##### Maven -```xml +```xml title="pom.xml" com.zaxxer HikariCP @@ -111,7 +111,7 @@ First, add the dependency to your project with the following dependency: ``` ##### Gradle -```kotlin +```kotlin title="build.gradle(.kts)" dependencies { implementation("com.zaxxer:HikariCP:4.0.3") } diff --git a/src/content/docs/velocity/dev/getting-started/creating-your-first-plugin.mdx b/src/content/docs/velocity/dev/getting-started/creating-your-first-plugin.mdx index 44e192b2a..edf784b97 100644 --- a/src/content/docs/velocity/dev/getting-started/creating-your-first-plugin.mdx +++ b/src/content/docs/velocity/dev/getting-started/creating-your-first-plugin.mdx @@ -59,8 +59,8 @@ system's documentation ([Gradle](https://docs.gradle.org/current/userguide/userg ### Setting up the dependency - - + + ```kotlin title="build.gradle.kts" replace repositories { maven { @@ -75,7 +75,7 @@ system's documentation ([Gradle](https://docs.gradle.org/current/userguide/userg } ``` - + ```groovy title="build.gradle" replace repositories { maven { @@ -90,7 +90,7 @@ system's documentation ([Gradle](https://docs.gradle.org/current/userguide/userg } ``` - + ```xml title="pom.xml" replace diff --git a/src/pages/adventure/version-history/adventure-platform-mod.astro b/src/pages/adventure/version-history/adventure-platform-mod.astro new file mode 100644 index 000000000..59bf026ba --- /dev/null +++ b/src/pages/adventure/version-history/adventure-platform-mod.astro @@ -0,0 +1,5 @@ +--- +import Releases from "/src/components/changelog/Releases.astro"; +--- + + diff --git a/src/pages/adventure/version-history/adventure-platform.astro b/src/pages/adventure/version-history/adventure-platform.astro new file mode 100644 index 000000000..24c6f98a0 --- /dev/null +++ b/src/pages/adventure/version-history/adventure-platform.astro @@ -0,0 +1,5 @@ +--- +import Releases from "/src/components/changelog/Releases.astro"; +--- + + diff --git a/src/pages/adventure/version-history/adventure.astro b/src/pages/adventure/version-history/adventure.astro new file mode 100644 index 000000000..9f1d8209b --- /dev/null +++ b/src/pages/adventure/version-history/adventure.astro @@ -0,0 +1,5 @@ +--- +import Releases from "/src/components/changelog/Releases.astro"; +--- + + diff --git a/src/utils/shiki/mm.tmLanguage.json b/src/utils/shiki/mm.tmLanguage.json new file mode 100644 index 000000000..5788d0d86 --- /dev/null +++ b/src/utils/shiki/mm.tmLanguage.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": "mm", + "patterns": [ + { + "name": "meta.tag.mm", + "begin": "(<)(/?|!?)([a-zA-Z0-9_#]+)", + "beginCaptures": { + "1": { "name": "constant.language.tag.mm" }, + "2": { "name": "constant.language.tag.mm" }, + "3": { "name": "constant.language.tag.mm" } + }, + "end": "(>)", + "endCaptures": { + "1": { "name": "constant.language.tag.mm" } + }, + "patterns": [ + { + "name": "string.quoted.single.argument.mm", + "match": "(:)'([^']*)'", + "captures": { + "1": { "name": "constant.language.tag.mm" }, + "2": { "name": "string.quoted.single.argument.mm" } + } + }, + { + "name": "string.quoted.double.argument.mm", + "match": "(:)\"([^\"]*)\"", + "captures": { + "1": { "name": "constant.language.tag.mm" }, + "2": { "name": "string.quoted.double.argument.mm" } + } + }, + { + "name": "variable.language.argument.mm", + "match": "(:)([^:\\s\"'>]+(?:\\[[^\\]]*\\])?)", + "captures": { + "1": { "name": "constant.language.tag.mm" }, + "2": { "name": "variable.language.argument.mm" } + } + } + ] + } + ], + "repository": {}, + "scopeName": "text.mm" +} diff --git a/src/utils/versions.ts b/src/utils/versions.ts index ba7eac311..6ca16cd2f 100644 --- a/src/utils/versions.ts +++ b/src/utils/versions.ts @@ -17,6 +17,15 @@ interface Manifest { versions: Version[]; } +interface Tag { + name: string; +} + +const fetchGitHubTags = async (repo: string) => + await fetch(`https://api.github.com/repos/${repo}/tags`, GITHUB_OPTIONS) + .then((r) => (r.ok ? r.json() : [{ name: "v0.0.0" }])) + .then((tags: Tag[]) => tags.map((t) => t.name.substring(1))); + // prettier-ignore const manifest: Manifest = await fetch("https://piston-meta.mojang.com/mc/game/version_manifest_v2.json") .then((r) => r.json()); @@ -46,12 +55,13 @@ const waterfallProject: Project = await fetch("https://api.papermc.io/v2/project export const LATEST_WATERFALL_RELEASE = waterfallProject.versions[waterfallProject.versions.length - 1]; -interface Tag { - name: string; -} - -const userdevVersions: string[] = await fetch("https://api.github.com/repos/PaperMC/paperweight/tags", GITHUB_OPTIONS) - .then((r) => (r.ok ? r.json() : [{ name: "v0.0.0" }])) - .then((tags: Tag[]) => tags.map((t) => t.name.substring(1))); +const userdevVersions: string[] = await fetchGitHubTags("PaperMC/paperweight"); export const LATEST_USERDEV_RELEASE = userdevVersions[0]; + +export const LATEST_ADVENTURE_SUPPORTED_MC = "1.21.5"; +export const LATEST_ADVENTURE_SUPPORTED_MC_RANGE = LATEST_ADVENTURE_SUPPORTED_MC; +export const LATEST_ADVENTURE_API_RELEASE = "4.22.0"; +export const LATEST_ADVENTURE_PLATFORM_RELEASE = "4.4.0"; +export const LATEST_ADVENTURE_PLATFORM_MOD_RELEASE = "6.4.0"; +export const LATEST_ANSI_RELEASE = "1.1.1";