diff --git a/README.md b/README.md index 245c4e3..1c2f611 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ static website generator. ## Installation ```shell -pnpm +pnpm i ``` ## Local Development diff --git a/docusaurus.config.js b/docusaurus.config.js index 643d5b5..18abc60 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -44,7 +44,21 @@ const config = { i18n: { defaultLocale: 'en', - locales: ['en'], + locales: ['en', 'zh-Hans'], + path: 'i18n', + localeConfigs: { + en: { + label: 'English', + direction: 'ltr', + htmlLang: 'en-US', + calendar: 'gregory', + path: 'en', + }, + 'zh-Hans': { + label: '中文(中国)', + path: 'zh-Hans', + }, + }, }, presets: [ @@ -125,6 +139,10 @@ const config = { position: 'right', docsPluginId: 'api', }, + { + type: 'localeDropdown', + position: 'right', + }, { href: 'https://github.com/goatcorp/Dalamud', position: 'right', diff --git a/i18n/en/code.json b/i18n/en/code.json new file mode 100644 index 0000000..888216e --- /dev/null +++ b/i18n/en/code.json @@ -0,0 +1,412 @@ +{ + "theme.ErrorPageContent.title": { + "message": "This page crashed.", + "description": "The title of the fallback page when the page crashed" + }, + "theme.BackToTopButton.buttonAriaLabel": { + "message": "Scroll back to top", + "description": "The ARIA label for the back to top button" + }, + "theme.blog.archive.title": { + "message": "Archive", + "description": "The page & hero title of the blog archive page" + }, + "theme.blog.archive.description": { + "message": "Archive", + "description": "The page & hero description of the blog archive page" + }, + "theme.blog.paginator.navAriaLabel": { + "message": "Blog list page navigation", + "description": "The ARIA label for the blog pagination" + }, + "theme.blog.paginator.newerEntries": { + "message": "Newer Entries", + "description": "The label used to navigate to the newer blog posts page (previous page)" + }, + "theme.blog.paginator.olderEntries": { + "message": "Older Entries", + "description": "The label used to navigate to the older blog posts page (next page)" + }, + "theme.blog.post.paginator.navAriaLabel": { + "message": "Blog post page navigation", + "description": "The ARIA label for the blog posts pagination" + }, + "theme.blog.post.paginator.newerPost": { + "message": "Newer Post", + "description": "The blog post button label to navigate to the newer/previous post" + }, + "theme.blog.post.paginator.olderPost": { + "message": "Older Post", + "description": "The blog post button label to navigate to the older/next post" + }, + "theme.blog.post.plurals": { + "message": "One post|{count} posts", + "description": "Pluralized label for \"{count} posts\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.blog.tagTitle": { + "message": "{nPosts} tagged with \"{tagName}\"", + "description": "The title of the page for a blog tag" + }, + "theme.tags.tagsPageLink": { + "message": "View All Tags", + "description": "The label of the link targeting the tag list page" + }, + "theme.colorToggle.ariaLabel": { + "message": "Switch between dark and light mode (currently {mode})", + "description": "The ARIA label for the navbar color mode toggle" + }, + "theme.colorToggle.ariaLabel.mode.dark": { + "message": "dark mode", + "description": "The name for the dark color mode" + }, + "theme.colorToggle.ariaLabel.mode.light": { + "message": "light mode", + "description": "The name for the light color mode" + }, + "theme.docs.breadcrumbs.navAriaLabel": { + "message": "Breadcrumbs", + "description": "The ARIA label for the breadcrumbs" + }, + "theme.docs.DocCard.categoryDescription": { + "message": "{count} items", + "description": "The default description for a category card in the generated index about how many items this category includes" + }, + "theme.docs.paginator.navAriaLabel": { + "message": "Docs pages", + "description": "The ARIA label for the docs pagination" + }, + "theme.docs.paginator.previous": { + "message": "Previous", + "description": "The label used to navigate to the previous doc" + }, + "theme.docs.paginator.next": { + "message": "Next", + "description": "The label used to navigate to the next doc" + }, + "theme.docs.tagDocListPageTitle.nDocsTagged": { + "message": "One doc tagged|{count} docs tagged", + "description": "Pluralized label for \"{count} docs tagged\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.tagDocListPageTitle": { + "message": "{nDocsTagged} with \"{tagName}\"", + "description": "The title of the page for a docs tag" + }, + "theme.docs.versionBadge.label": { + "message": "Version: {versionLabel}" + }, + "theme.docs.versions.unreleasedVersionLabel": { + "message": "This is unreleased documentation for {siteTitle} {versionLabel} version.", + "description": "The label used to tell the user that he's browsing an unreleased doc version" + }, + "theme.docs.versions.unmaintainedVersionLabel": { + "message": "This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.", + "description": "The label used to tell the user that he's browsing an unmaintained doc version" + }, + "theme.docs.versions.latestVersionSuggestionLabel": { + "message": "For up-to-date documentation, see the {latestVersionLink} ({versionLabel}).", + "description": "The label used to tell the user to check the latest version" + }, + "theme.docs.versions.latestVersionLinkLabel": { + "message": "latest version", + "description": "The label used for the latest version suggestion link label" + }, + "theme.common.editThisPage": { + "message": "Edit this page", + "description": "The link label to edit the current page" + }, + "theme.common.headingLinkTitle": { + "message": "Direct link to {heading}", + "description": "Title for link to heading" + }, + "theme.lastUpdated.atDate": { + "message": " on {date}", + "description": "The words used to describe on which date a page has been last updated" + }, + "theme.lastUpdated.byUser": { + "message": " by {user}", + "description": "The words used to describe by who the page has been last updated" + }, + "theme.lastUpdated.lastUpdatedAtBy": { + "message": "Last updated{atDate}{byUser}", + "description": "The sentence used to display when a page has been last updated, and by who" + }, + "theme.NotFound.title": { + "message": "Page Not Found", + "description": "The title of the 404 page" + }, + "theme.navbar.mobileVersionsDropdown.label": { + "message": "Versions", + "description": "The label for the navbar versions dropdown on mobile view" + }, + "theme.tags.tagsListLabel": { + "message": "Tags:", + "description": "The label alongside a tag list" + }, + "theme.AnnouncementBar.closeButtonAriaLabel": { + "message": "Close", + "description": "The ARIA label for close button of announcement bar" + }, + "theme.admonition.caution": { + "message": "caution", + "description": "The default label used for the Caution admonition (:::caution)" + }, + "theme.admonition.danger": { + "message": "danger", + "description": "The default label used for the Danger admonition (:::danger)" + }, + "theme.admonition.info": { + "message": "info", + "description": "The default label used for the Info admonition (:::info)" + }, + "theme.admonition.note": { + "message": "note", + "description": "The default label used for the Note admonition (:::note)" + }, + "theme.admonition.tip": { + "message": "tip", + "description": "The default label used for the Tip admonition (:::tip)" + }, + "theme.blog.sidebar.navAriaLabel": { + "message": "Blog recent posts navigation", + "description": "The ARIA label for recent posts in the blog sidebar" + }, + "theme.CodeBlock.copied": { + "message": "Copied", + "description": "The copied button label on code blocks" + }, + "theme.CodeBlock.copyButtonAriaLabel": { + "message": "Copy code to clipboard", + "description": "The ARIA label for copy code blocks button" + }, + "theme.CodeBlock.copy": { + "message": "Copy", + "description": "The copy button label on code blocks" + }, + "theme.CodeBlock.wordWrapToggle": { + "message": "Toggle word wrap", + "description": "The title attribute for toggle word wrapping button of code block lines" + }, + "theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": { + "message": "Toggle the collapsible sidebar category '{label}'", + "description": "The ARIA label to toggle the collapsible sidebar category" + }, + "theme.NavBar.navAriaLabel": { + "message": "Main", + "description": "The ARIA label for the main navigation" + }, + "theme.NotFound.p1": { + "message": "We could not find what you were looking for.", + "description": "The first paragraph of the 404 page" + }, + "theme.NotFound.p2": { + "message": "Please contact the owner of the site that linked you to the original URL and let them know their link is broken.", + "description": "The 2nd paragraph of the 404 page" + }, + "theme.TOCCollapsible.toggleButtonLabel": { + "message": "On this page", + "description": "The label used by the button on the collapsible TOC component" + }, + "theme.blog.post.readingTime.plurals": { + "message": "One min read|{readingTime} min read", + "description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.blog.post.readMore": { + "message": "Read More", + "description": "The label used in blog post item excerpts to link to full blog posts" + }, + "theme.blog.post.readMoreLabel": { + "message": "Read more about {title}", + "description": "The ARIA label for the link to full blog posts from excerpts" + }, + "theme.navbar.mobileLanguageDropdown.label": { + "message": "Languages", + "description": "The label for the mobile language switcher dropdown" + }, + "theme.docs.breadcrumbs.home": { + "message": "Home page", + "description": "The ARIA label for the home page in the breadcrumbs" + }, + "theme.docs.sidebar.collapseButtonTitle": { + "message": "Collapse sidebar", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.collapseButtonAriaLabel": { + "message": "Collapse sidebar", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.navAriaLabel": { + "message": "Docs sidebar", + "description": "The ARIA label for the sidebar navigation" + }, + "theme.docs.sidebar.closeSidebarButtonAriaLabel": { + "message": "Close navigation bar", + "description": "The ARIA label for close button of mobile sidebar" + }, + "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": { + "message": "← Back to main menu", + "description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)" + }, + "theme.docs.sidebar.toggleSidebarButtonAriaLabel": { + "message": "Toggle navigation bar", + "description": "The ARIA label for hamburger menu button of mobile navigation" + }, + "theme.docs.sidebar.expandButtonTitle": { + "message": "Expand sidebar", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.docs.sidebar.expandButtonAriaLabel": { + "message": "Expand sidebar", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.SearchBar.seeAll": { + "message": "See all {count} results" + }, + "theme.SearchPage.documentsFound.plurals": { + "message": "One document found|{count} documents found", + "description": "Pluralized label for \"{count} documents found\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.SearchPage.existingResultsTitle": { + "message": "Search results for \"{query}\"", + "description": "The search page title for non-empty query" + }, + "theme.SearchPage.emptyResultsTitle": { + "message": "Search the documentation", + "description": "The search page title for empty query" + }, + "theme.SearchPage.inputPlaceholder": { + "message": "Type your search here", + "description": "The placeholder for search page input" + }, + "theme.SearchPage.inputLabel": { + "message": "Search", + "description": "The ARIA label for search page input" + }, + "theme.SearchPage.algoliaLabel": { + "message": "Search by Algolia", + "description": "The ARIA label for Algolia mention" + }, + "theme.SearchPage.noResultsText": { + "message": "No results were found", + "description": "The paragraph for empty search result" + }, + "theme.SearchPage.fetchingNewResults": { + "message": "Fetching new results...", + "description": "The paragraph for fetching new search results" + }, + "theme.SearchBar.label": { + "message": "Search", + "description": "The ARIA label and placeholder for search button" + }, + "theme.SearchModal.searchBox.resetButtonTitle": { + "message": "Clear the query", + "description": "The label and ARIA label for search box reset button" + }, + "theme.SearchModal.searchBox.cancelButtonText": { + "message": "Cancel", + "description": "The label and ARIA label for search box cancel button" + }, + "theme.SearchModal.startScreen.recentSearchesTitle": { + "message": "Recent", + "description": "The title for recent searches" + }, + "theme.SearchModal.startScreen.noRecentSearchesText": { + "message": "No recent searches", + "description": "The text when no recent searches" + }, + "theme.SearchModal.startScreen.saveRecentSearchButtonTitle": { + "message": "Save this search", + "description": "The label for save recent search button" + }, + "theme.SearchModal.startScreen.removeRecentSearchButtonTitle": { + "message": "Remove this search from history", + "description": "The label for remove recent search button" + }, + "theme.SearchModal.startScreen.favoriteSearchesTitle": { + "message": "Favorite", + "description": "The title for favorite searches" + }, + "theme.SearchModal.startScreen.removeFavoriteSearchButtonTitle": { + "message": "Remove this search from favorites", + "description": "The label for remove favorite search button" + }, + "theme.SearchModal.errorScreen.titleText": { + "message": "Unable to fetch results", + "description": "The title for error screen of search modal" + }, + "theme.SearchModal.errorScreen.helpText": { + "message": "You might want to check your network connection.", + "description": "The help text for error screen of search modal" + }, + "theme.SearchModal.footer.selectText": { + "message": "to select", + "description": "The explanatory text of the action for the enter key" + }, + "theme.SearchModal.footer.selectKeyAriaLabel": { + "message": "Enter key", + "description": "The ARIA label for the Enter key button that makes the selection" + }, + "theme.SearchModal.footer.navigateText": { + "message": "to navigate", + "description": "The explanatory text of the action for the Arrow up and Arrow down key" + }, + "theme.SearchModal.footer.navigateUpKeyAriaLabel": { + "message": "Arrow up", + "description": "The ARIA label for the Arrow up key button that makes the navigation" + }, + "theme.SearchModal.footer.navigateDownKeyAriaLabel": { + "message": "Arrow down", + "description": "The ARIA label for the Arrow down key button that makes the navigation" + }, + "theme.SearchModal.footer.closeText": { + "message": "to close", + "description": "The explanatory text of the action for Escape key" + }, + "theme.SearchModal.footer.closeKeyAriaLabel": { + "message": "Escape key", + "description": "The ARIA label for the Escape key button that close the modal" + }, + "theme.SearchModal.footer.searchByText": { + "message": "Search by", + "description": "The text explain that the search is making by Algolia" + }, + "theme.SearchModal.noResultsScreen.noResultsText": { + "message": "No results for", + "description": "The text explains that there are no results for the following search" + }, + "theme.SearchModal.noResultsScreen.suggestedQueryText": { + "message": "Try searching for", + "description": "The text for the suggested query when no results are found for the following search" + }, + "theme.SearchModal.noResultsScreen.reportMissingResultsText": { + "message": "Believe this query should return results?", + "description": "The text for the question where the user thinks there are missing results" + }, + "theme.SearchModal.noResultsScreen.reportMissingResultsLinkText": { + "message": "Let us know.", + "description": "The text for the link to report missing results" + }, + "theme.SearchModal.placeholder": { + "message": "Search docs", + "description": "The placeholder of the input of the DocSearch pop-up modal" + }, + "theme.ErrorPageContent.tryAgain": { + "message": "Try again", + "description": "The label of the button to try again rendering when the React error boundary captures an error" + }, + "theme.common.skipToMainContent": { + "message": "Skip to main content", + "description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation" + }, + "theme.tags.tagsPageTitle": { + "message": "Tags", + "description": "The title of the tag list page" + }, + "theme.unlistedContent.title": { + "message": "Unlisted page", + "description": "The unlisted content banner title" + }, + "theme.unlistedContent.message": { + "message": "This page is unlisted. Search engines will not index it, and only users having a direct link can access it.", + "description": "The unlisted content banner message" + } +} \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-blog/options.json b/i18n/en/docusaurus-plugin-content-blog/options.json new file mode 100644 index 0000000..9239ff7 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-blog/options.json @@ -0,0 +1,14 @@ +{ + "title": { + "message": "Blog", + "description": "The title for the blog used in SEO" + }, + "description": { + "message": "Blog", + "description": "The description for the blog used in SEO" + }, + "sidebar.title": { + "message": "Recent posts", + "description": "The label for the left sidebar" + } +} diff --git a/i18n/en/docusaurus-plugin-content-docs-api/version-placeholder.json b/i18n/en/docusaurus-plugin-content-docs-api/version-placeholder.json new file mode 100644 index 0000000..cd80afa --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs-api/version-placeholder.json @@ -0,0 +1,6 @@ +{ + "version.label": { + "message": "X.x (Placeholder)", + "description": "The label for version placeholder" + } +} diff --git a/i18n/en/docusaurus-plugin-content-docs/current.json b/i18n/en/docusaurus-plugin-content-docs/current.json new file mode 100644 index 0000000..1df7c6a --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current.json @@ -0,0 +1,38 @@ +{ + "version.label": { + "message": "Next", + "description": "The label for version current" + }, + "sidebar.docSidebar.category.Plugin Development": { + "message": "Plugin Development", + "description": "The label for category Plugin Development in sidebar docSidebar" + }, + "sidebar.docSidebar.category.Plugin Development.link.generated-index.title": { + "message": "Plugin Development", + "description": "The generated-index page title for category Plugin Development in sidebar docSidebar" + }, + "sidebar.docSidebar.category.Plugin Development.link.generated-index.description": { + "message": "Learn the basics of Dalamud plugin development.", + "description": "The generated-index page description for category Plugin Development in sidebar docSidebar" + }, + "sidebar.docSidebar.category.How To's": { + "message": "How To's", + "description": "The label for category How To's in sidebar docSidebar" + }, + "sidebar.docSidebar.category.How To's.link.generated-index.title": { + "message": "How To's", + "description": "The generated-index page title for category How To's in sidebar docSidebar" + }, + "sidebar.docSidebar.category.How To's.link.generated-index.description": { + "message": "Learn more about specific APIs", + "description": "The generated-index page description for category How To's in sidebar docSidebar" + }, + "sidebar.docSidebar.category.Versions & Channels": { + "message": "Versions & Channels", + "description": "The label for category Versions & Channels in sidebar docSidebar" + }, + "sidebar.docSidebar.category.Development FAQ (Legacy)": { + "message": "Development FAQ (Legacy)", + "description": "The label for category Development FAQ (Legacy) in sidebar docSidebar" + } +} diff --git a/i18n/en/docusaurus-theme-classic/footer.json b/i18n/en/docusaurus-theme-classic/footer.json new file mode 100644 index 0000000..7d0042b --- /dev/null +++ b/i18n/en/docusaurus-theme-classic/footer.json @@ -0,0 +1,22 @@ +{ + "link.title.Community": { + "message": "Community", + "description": "The title of the footer links column with title=Community in the footer" + }, + "link.title.More": { + "message": "More", + "description": "The title of the footer links column with title=More in the footer" + }, + "link.item.label.Discord": { + "message": "Discord", + "description": "The label of footer link with label=Discord linking to https://discord.gg/holdshift" + }, + "link.item.label.GitHub": { + "message": "GitHub", + "description": "The label of footer link with label=GitHub linking to https://github.com/goatcorp/Dalamud" + }, + "copyright": { + "message": "Final Fantasy XIV © 2010-2023 SQUARE ENIX CO., LTD. All Rights Reserved. We are not affiliated with SQUARE ENIX CO., LTD. in any way.", + "description": "The footer copyright" + } +} diff --git a/i18n/en/docusaurus-theme-classic/navbar.json b/i18n/en/docusaurus-theme-classic/navbar.json new file mode 100644 index 0000000..39cd79d --- /dev/null +++ b/i18n/en/docusaurus-theme-classic/navbar.json @@ -0,0 +1,22 @@ +{ + "title": { + "message": "Dalamud.dev", + "description": "The title in the navbar" + }, + "logo.alt": { + "message": "Dalamud logo", + "description": "The alt text of navbar logo" + }, + "item.label.Docs": { + "message": "Docs", + "description": "Navbar item with label Docs" + }, + "item.label.API": { + "message": "API", + "description": "Navbar item with label API" + }, + "item.label.News": { + "message": "News", + "description": "Navbar item with label News" + } +} diff --git a/i18n/zh-Hans/code.json b/i18n/zh-Hans/code.json new file mode 100644 index 0000000..888216e --- /dev/null +++ b/i18n/zh-Hans/code.json @@ -0,0 +1,412 @@ +{ + "theme.ErrorPageContent.title": { + "message": "This page crashed.", + "description": "The title of the fallback page when the page crashed" + }, + "theme.BackToTopButton.buttonAriaLabel": { + "message": "Scroll back to top", + "description": "The ARIA label for the back to top button" + }, + "theme.blog.archive.title": { + "message": "Archive", + "description": "The page & hero title of the blog archive page" + }, + "theme.blog.archive.description": { + "message": "Archive", + "description": "The page & hero description of the blog archive page" + }, + "theme.blog.paginator.navAriaLabel": { + "message": "Blog list page navigation", + "description": "The ARIA label for the blog pagination" + }, + "theme.blog.paginator.newerEntries": { + "message": "Newer Entries", + "description": "The label used to navigate to the newer blog posts page (previous page)" + }, + "theme.blog.paginator.olderEntries": { + "message": "Older Entries", + "description": "The label used to navigate to the older blog posts page (next page)" + }, + "theme.blog.post.paginator.navAriaLabel": { + "message": "Blog post page navigation", + "description": "The ARIA label for the blog posts pagination" + }, + "theme.blog.post.paginator.newerPost": { + "message": "Newer Post", + "description": "The blog post button label to navigate to the newer/previous post" + }, + "theme.blog.post.paginator.olderPost": { + "message": "Older Post", + "description": "The blog post button label to navigate to the older/next post" + }, + "theme.blog.post.plurals": { + "message": "One post|{count} posts", + "description": "Pluralized label for \"{count} posts\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.blog.tagTitle": { + "message": "{nPosts} tagged with \"{tagName}\"", + "description": "The title of the page for a blog tag" + }, + "theme.tags.tagsPageLink": { + "message": "View All Tags", + "description": "The label of the link targeting the tag list page" + }, + "theme.colorToggle.ariaLabel": { + "message": "Switch between dark and light mode (currently {mode})", + "description": "The ARIA label for the navbar color mode toggle" + }, + "theme.colorToggle.ariaLabel.mode.dark": { + "message": "dark mode", + "description": "The name for the dark color mode" + }, + "theme.colorToggle.ariaLabel.mode.light": { + "message": "light mode", + "description": "The name for the light color mode" + }, + "theme.docs.breadcrumbs.navAriaLabel": { + "message": "Breadcrumbs", + "description": "The ARIA label for the breadcrumbs" + }, + "theme.docs.DocCard.categoryDescription": { + "message": "{count} items", + "description": "The default description for a category card in the generated index about how many items this category includes" + }, + "theme.docs.paginator.navAriaLabel": { + "message": "Docs pages", + "description": "The ARIA label for the docs pagination" + }, + "theme.docs.paginator.previous": { + "message": "Previous", + "description": "The label used to navigate to the previous doc" + }, + "theme.docs.paginator.next": { + "message": "Next", + "description": "The label used to navigate to the next doc" + }, + "theme.docs.tagDocListPageTitle.nDocsTagged": { + "message": "One doc tagged|{count} docs tagged", + "description": "Pluralized label for \"{count} docs tagged\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.tagDocListPageTitle": { + "message": "{nDocsTagged} with \"{tagName}\"", + "description": "The title of the page for a docs tag" + }, + "theme.docs.versionBadge.label": { + "message": "Version: {versionLabel}" + }, + "theme.docs.versions.unreleasedVersionLabel": { + "message": "This is unreleased documentation for {siteTitle} {versionLabel} version.", + "description": "The label used to tell the user that he's browsing an unreleased doc version" + }, + "theme.docs.versions.unmaintainedVersionLabel": { + "message": "This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.", + "description": "The label used to tell the user that he's browsing an unmaintained doc version" + }, + "theme.docs.versions.latestVersionSuggestionLabel": { + "message": "For up-to-date documentation, see the {latestVersionLink} ({versionLabel}).", + "description": "The label used to tell the user to check the latest version" + }, + "theme.docs.versions.latestVersionLinkLabel": { + "message": "latest version", + "description": "The label used for the latest version suggestion link label" + }, + "theme.common.editThisPage": { + "message": "Edit this page", + "description": "The link label to edit the current page" + }, + "theme.common.headingLinkTitle": { + "message": "Direct link to {heading}", + "description": "Title for link to heading" + }, + "theme.lastUpdated.atDate": { + "message": " on {date}", + "description": "The words used to describe on which date a page has been last updated" + }, + "theme.lastUpdated.byUser": { + "message": " by {user}", + "description": "The words used to describe by who the page has been last updated" + }, + "theme.lastUpdated.lastUpdatedAtBy": { + "message": "Last updated{atDate}{byUser}", + "description": "The sentence used to display when a page has been last updated, and by who" + }, + "theme.NotFound.title": { + "message": "Page Not Found", + "description": "The title of the 404 page" + }, + "theme.navbar.mobileVersionsDropdown.label": { + "message": "Versions", + "description": "The label for the navbar versions dropdown on mobile view" + }, + "theme.tags.tagsListLabel": { + "message": "Tags:", + "description": "The label alongside a tag list" + }, + "theme.AnnouncementBar.closeButtonAriaLabel": { + "message": "Close", + "description": "The ARIA label for close button of announcement bar" + }, + "theme.admonition.caution": { + "message": "caution", + "description": "The default label used for the Caution admonition (:::caution)" + }, + "theme.admonition.danger": { + "message": "danger", + "description": "The default label used for the Danger admonition (:::danger)" + }, + "theme.admonition.info": { + "message": "info", + "description": "The default label used for the Info admonition (:::info)" + }, + "theme.admonition.note": { + "message": "note", + "description": "The default label used for the Note admonition (:::note)" + }, + "theme.admonition.tip": { + "message": "tip", + "description": "The default label used for the Tip admonition (:::tip)" + }, + "theme.blog.sidebar.navAriaLabel": { + "message": "Blog recent posts navigation", + "description": "The ARIA label for recent posts in the blog sidebar" + }, + "theme.CodeBlock.copied": { + "message": "Copied", + "description": "The copied button label on code blocks" + }, + "theme.CodeBlock.copyButtonAriaLabel": { + "message": "Copy code to clipboard", + "description": "The ARIA label for copy code blocks button" + }, + "theme.CodeBlock.copy": { + "message": "Copy", + "description": "The copy button label on code blocks" + }, + "theme.CodeBlock.wordWrapToggle": { + "message": "Toggle word wrap", + "description": "The title attribute for toggle word wrapping button of code block lines" + }, + "theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": { + "message": "Toggle the collapsible sidebar category '{label}'", + "description": "The ARIA label to toggle the collapsible sidebar category" + }, + "theme.NavBar.navAriaLabel": { + "message": "Main", + "description": "The ARIA label for the main navigation" + }, + "theme.NotFound.p1": { + "message": "We could not find what you were looking for.", + "description": "The first paragraph of the 404 page" + }, + "theme.NotFound.p2": { + "message": "Please contact the owner of the site that linked you to the original URL and let them know their link is broken.", + "description": "The 2nd paragraph of the 404 page" + }, + "theme.TOCCollapsible.toggleButtonLabel": { + "message": "On this page", + "description": "The label used by the button on the collapsible TOC component" + }, + "theme.blog.post.readingTime.plurals": { + "message": "One min read|{readingTime} min read", + "description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.blog.post.readMore": { + "message": "Read More", + "description": "The label used in blog post item excerpts to link to full blog posts" + }, + "theme.blog.post.readMoreLabel": { + "message": "Read more about {title}", + "description": "The ARIA label for the link to full blog posts from excerpts" + }, + "theme.navbar.mobileLanguageDropdown.label": { + "message": "Languages", + "description": "The label for the mobile language switcher dropdown" + }, + "theme.docs.breadcrumbs.home": { + "message": "Home page", + "description": "The ARIA label for the home page in the breadcrumbs" + }, + "theme.docs.sidebar.collapseButtonTitle": { + "message": "Collapse sidebar", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.collapseButtonAriaLabel": { + "message": "Collapse sidebar", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.navAriaLabel": { + "message": "Docs sidebar", + "description": "The ARIA label for the sidebar navigation" + }, + "theme.docs.sidebar.closeSidebarButtonAriaLabel": { + "message": "Close navigation bar", + "description": "The ARIA label for close button of mobile sidebar" + }, + "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": { + "message": "← Back to main menu", + "description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)" + }, + "theme.docs.sidebar.toggleSidebarButtonAriaLabel": { + "message": "Toggle navigation bar", + "description": "The ARIA label for hamburger menu button of mobile navigation" + }, + "theme.docs.sidebar.expandButtonTitle": { + "message": "Expand sidebar", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.docs.sidebar.expandButtonAriaLabel": { + "message": "Expand sidebar", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.SearchBar.seeAll": { + "message": "See all {count} results" + }, + "theme.SearchPage.documentsFound.plurals": { + "message": "One document found|{count} documents found", + "description": "Pluralized label for \"{count} documents found\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.SearchPage.existingResultsTitle": { + "message": "Search results for \"{query}\"", + "description": "The search page title for non-empty query" + }, + "theme.SearchPage.emptyResultsTitle": { + "message": "Search the documentation", + "description": "The search page title for empty query" + }, + "theme.SearchPage.inputPlaceholder": { + "message": "Type your search here", + "description": "The placeholder for search page input" + }, + "theme.SearchPage.inputLabel": { + "message": "Search", + "description": "The ARIA label for search page input" + }, + "theme.SearchPage.algoliaLabel": { + "message": "Search by Algolia", + "description": "The ARIA label for Algolia mention" + }, + "theme.SearchPage.noResultsText": { + "message": "No results were found", + "description": "The paragraph for empty search result" + }, + "theme.SearchPage.fetchingNewResults": { + "message": "Fetching new results...", + "description": "The paragraph for fetching new search results" + }, + "theme.SearchBar.label": { + "message": "Search", + "description": "The ARIA label and placeholder for search button" + }, + "theme.SearchModal.searchBox.resetButtonTitle": { + "message": "Clear the query", + "description": "The label and ARIA label for search box reset button" + }, + "theme.SearchModal.searchBox.cancelButtonText": { + "message": "Cancel", + "description": "The label and ARIA label for search box cancel button" + }, + "theme.SearchModal.startScreen.recentSearchesTitle": { + "message": "Recent", + "description": "The title for recent searches" + }, + "theme.SearchModal.startScreen.noRecentSearchesText": { + "message": "No recent searches", + "description": "The text when no recent searches" + }, + "theme.SearchModal.startScreen.saveRecentSearchButtonTitle": { + "message": "Save this search", + "description": "The label for save recent search button" + }, + "theme.SearchModal.startScreen.removeRecentSearchButtonTitle": { + "message": "Remove this search from history", + "description": "The label for remove recent search button" + }, + "theme.SearchModal.startScreen.favoriteSearchesTitle": { + "message": "Favorite", + "description": "The title for favorite searches" + }, + "theme.SearchModal.startScreen.removeFavoriteSearchButtonTitle": { + "message": "Remove this search from favorites", + "description": "The label for remove favorite search button" + }, + "theme.SearchModal.errorScreen.titleText": { + "message": "Unable to fetch results", + "description": "The title for error screen of search modal" + }, + "theme.SearchModal.errorScreen.helpText": { + "message": "You might want to check your network connection.", + "description": "The help text for error screen of search modal" + }, + "theme.SearchModal.footer.selectText": { + "message": "to select", + "description": "The explanatory text of the action for the enter key" + }, + "theme.SearchModal.footer.selectKeyAriaLabel": { + "message": "Enter key", + "description": "The ARIA label for the Enter key button that makes the selection" + }, + "theme.SearchModal.footer.navigateText": { + "message": "to navigate", + "description": "The explanatory text of the action for the Arrow up and Arrow down key" + }, + "theme.SearchModal.footer.navigateUpKeyAriaLabel": { + "message": "Arrow up", + "description": "The ARIA label for the Arrow up key button that makes the navigation" + }, + "theme.SearchModal.footer.navigateDownKeyAriaLabel": { + "message": "Arrow down", + "description": "The ARIA label for the Arrow down key button that makes the navigation" + }, + "theme.SearchModal.footer.closeText": { + "message": "to close", + "description": "The explanatory text of the action for Escape key" + }, + "theme.SearchModal.footer.closeKeyAriaLabel": { + "message": "Escape key", + "description": "The ARIA label for the Escape key button that close the modal" + }, + "theme.SearchModal.footer.searchByText": { + "message": "Search by", + "description": "The text explain that the search is making by Algolia" + }, + "theme.SearchModal.noResultsScreen.noResultsText": { + "message": "No results for", + "description": "The text explains that there are no results for the following search" + }, + "theme.SearchModal.noResultsScreen.suggestedQueryText": { + "message": "Try searching for", + "description": "The text for the suggested query when no results are found for the following search" + }, + "theme.SearchModal.noResultsScreen.reportMissingResultsText": { + "message": "Believe this query should return results?", + "description": "The text for the question where the user thinks there are missing results" + }, + "theme.SearchModal.noResultsScreen.reportMissingResultsLinkText": { + "message": "Let us know.", + "description": "The text for the link to report missing results" + }, + "theme.SearchModal.placeholder": { + "message": "Search docs", + "description": "The placeholder of the input of the DocSearch pop-up modal" + }, + "theme.ErrorPageContent.tryAgain": { + "message": "Try again", + "description": "The label of the button to try again rendering when the React error boundary captures an error" + }, + "theme.common.skipToMainContent": { + "message": "Skip to main content", + "description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation" + }, + "theme.tags.tagsPageTitle": { + "message": "Tags", + "description": "The title of the tag list page" + }, + "theme.unlistedContent.title": { + "message": "Unlisted page", + "description": "The unlisted content banner title" + }, + "theme.unlistedContent.message": { + "message": "This page is unlisted. Search engines will not index it, and only users having a direct link can access it.", + "description": "The unlisted content banner message" + } +} \ No newline at end of file diff --git a/i18n/zh-Hans/docusaurus-plugin-content-blog/options.json b/i18n/zh-Hans/docusaurus-plugin-content-blog/options.json new file mode 100644 index 0000000..9239ff7 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-blog/options.json @@ -0,0 +1,14 @@ +{ + "title": { + "message": "Blog", + "description": "The title for the blog used in SEO" + }, + "description": { + "message": "Blog", + "description": "The description for the blog used in SEO" + }, + "sidebar.title": { + "message": "Recent posts", + "description": "The label for the left sidebar" + } +} diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs-api/version-placeholder.json b/i18n/zh-Hans/docusaurus-plugin-content-docs-api/version-placeholder.json new file mode 100644 index 0000000..cd80afa --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs-api/version-placeholder.json @@ -0,0 +1,6 @@ +{ + "version.label": { + "message": "X.x (Placeholder)", + "description": "The label for version placeholder" + } +} diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current.json b/i18n/zh-Hans/docusaurus-plugin-content-docs/current.json new file mode 100644 index 0000000..c264b6e --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current.json @@ -0,0 +1,38 @@ +{ + "version.label": { + "message": "下一个", + "description": "版本当前的标签" + }, + "sidebar.docSidebar.category.Plugin Development": { + "message": "插件开发", + "description": "侧边栏 docSidebar 中类别插件开发的标签" + }, + "sidebar.docSidebar.category.Plugin Development.link.generated-index.title": { + "message": "插件开发", + "description": "侧边栏 docSidebar 中类别插件开发的生成索引页面标题" + }, + "sidebar.docSidebar.category.Plugin Development.link.generated-index.description": { + "message": "了解 Dalamud 插件开发的基础知识。", + "description": "侧边栏 docSidebar 中类别插件开发的生成索引页面描述" + }, + "sidebar.docSidebar.category.How To's": { + "message": "如何", + "description": "侧边栏 docSidebar 中类别如何的标签" + }, + "sidebar.docSidebar.category.How To's.link.generated-index.title": { + "message": "如何", + "description": "侧边栏 docSidebar 中类别如何的生成索引页面标题" + }, + "sidebar.docSidebar.category.How To's.link.generated-index.description": { + "message": "了解更多关于特定 API 的信息", + "description": "侧边栏 docSidebar 中类别如何的生成索引页面描述" + }, + "sidebar.docSidebar.category.Versions & Channels": { + "message": "版本和渠道", + "description": "侧边栏 docSidebar 中类别版本和渠道的标签" + }, + "sidebar.docSidebar.category.Development FAQ (Legacy)": { + "message": "开发常见问题(旧版)", + "description": "侧边栏 docSidebar 中类别开发常见问题(旧版)的标签" + } +} \ No newline at end of file diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/building/images/xl-login-right-click.png b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/building/images/xl-login-right-click.png new file mode 100644 index 0000000..fa3acc4 Binary files /dev/null and b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/building/images/xl-login-right-click.png differ diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/building/images/xl-settings-disable-dalamud.png b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/building/images/xl-settings-disable-dalamud.png new file mode 100644 index 0000000..c633c4e Binary files /dev/null and b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/building/images/xl-settings-disable-dalamud.png differ diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/building/index.md b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/building/index.md new file mode 100644 index 0000000..b8efab7 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/building/index.md @@ -0,0 +1,150 @@ +--- +sidebar_position: 2 +--- + +# 构建 Dalamud + +:::tip + +本指南介绍如何从源代码构建 Dalamud,适用于对 Dalamud 本身进行更改的人。 + +**如果您只是想创建插件,则无需遵循本指南!** 您可以跳过本节,前 +往[插件开发](/category/plugin-development)部分。 + +::: + +Dalamud 使用 [Nuke](https://nuke.build) 构建,这是一个用于 C#/.NET 项目的代码优 +先构建系统。本指南将指导您设置开发环境并构建 Dalamud。 + +## 先决条件 + +- Windows 10/Windows Server 2016 或更高版本 +- [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) + - 需要安装 "使用 C++ 的桌面开发" 和 "使用 .NET 的桌面开发" 工作负载。 +- [.NET 7.0 SDK](https://dotnet.microsoft.com/download/dotnet/7.0) + - 这已包含在 Visual Studio 2022 中,但如果需要,也可以单独安装。 +- [Git](https://git-scm.com/downloads) + +## 获取源代码 + +Dalamud 的源代码托管在 GitHub 上。您可以使用以下命令克隆存储库: + +```shell +git clone --recursive https://github.com/goatcorp/Dalamud.git +``` + +:::info + +Dalamud 有几个 Git 子模块。如果您没有使用 `--recursive` 标志克隆存储库,则需要使 +用以下命令手动初始化子模块: + +```shell +git submodule update --init --recursive +``` + +您还可以使用此命令稍后更新本地存储库中的子模块。 + +::: + +## 构建 + +在 PowerShell 中,导航到存储库的根目录并运行以下命令: + +```pwsh +.\build.ps1 +``` + +这将构建 Nuke 项目并运行默认目标,该目标将构建 Dalamud 及其所有组件(包括注入器 +)。 + +:::tip + +您也可以尝试直接使用 MSBuild 或您的 IDE 的构建系统构建 Dalamud。但是,CI 构建使 +用 Nuke,因此如果遇到任何问题,您应该尝试使用 Nuke 进行构建以排除构建系统问题。 + +::: + +:::info + +目前不支持在其他操作系统上构建,因为依赖于 Windows API 的本机组件(例如注入器) +。 + +但是,您可以尝试使用 `./build.sh` 而不是 `.\build.ps1` 或直接使用 `dotnet build` +构建 `Dalamud` 项目,在 Linux/macOS 上进行 _部分_ 构建。 + +::: + +## 运行 + +:::danger + +如果您使用这些说明来绕过游戏补丁后的 Dalamud 禁用,**您将自行承担风险!** + +我们在每次补丁后禁用 Dalamud,直到它以相对稳定和安全的方式得到验证。如果您选择忽 +略此警告,您将会遇到崩溃和其他问题,我们将不会为您提供帮助。 + +再次强调:**手动将 Dalamud 注入运行中的游戏仅供开发人员使用!** 如果您想在补丁后 +帮助我们测试 Dalamud 和插件,请加入 Discord 并选择测试人员角色。我们需要帮助时会 +通知您。 + +::: + +构建过程将注入器输出到 `bin\Debug\Dalamud.Injector.exe`。 + +为了测试目的,您可以以几种不同的方式使用此注入器。 + +### 模拟启动 + +如果您想在不完全登录游戏的情况下测试 Dalamud,则可以使用注入器的“模拟启动”功能。 +这将启动游戏,但不进行任何身份验证,因此您将无法进入游戏。 + +要使用此功能,您应确保游戏未运行,然后使用以下参数运行注入器: + +```shell +.\Dalamud.Injector.exe launch -f +``` + +:::caution + +如果您的游戏安装在非标准位置,则必须使用 `-g` 参数传递 `ffxiv_dx11.exe` 的完整路 +径,例如: + +```shell +.\Dalamud.Injector.exe launch -f -g "D:\dev\ffxiv\game-root\game\ffxiv_dx11.exe" +``` + +::: + +### 手动注入 + +要完全测试 Dalamud,您需要将其注入到正在运行的游戏中。为此,您需要确保以禁用其 +ACL 保护的方法启动游戏。通过官方启动器启动将不允许您注入 Dalamud。 + +XIVLauncher 是此操作的推荐方法,因为它会自动为您禁用 ACL 保护。只需通过 +XIVLauncher 启动游戏,并确保使用以下选项之一禁用 Dalamud 注入: + +- 右键单击“登录”按钮,然后选择“Start w/o Dalamud”。 + ![preview](images/xl-login-right-click.png) +- 在 XIVLauncher 设置中禁用 Dalamud。 + ![preview](images/xl-settings-disable-dalamud.png) + +游戏运行后,您可以使用以下参数运行注入器: + +```shell +.\Dalamud.Injector.exe inject -a +``` + +Dalamud 现在应该已经被注入到游戏中。您可以通过查看屏幕左上角的 Dalamud 徽标来验 +证此操作(假设您尚未登录)。 + +:::tip + +您可以使用 `help` 命令调用注入器以查看所有可用参数: + +```shell +.\Dalamud.Injector.exe help +``` + +大多数额外的参数都不会有帮助,但它们在帮助输出中轻微地记录,以保证完整性。 + +::: diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/adoption.mdx b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/adoption.mdx new file mode 100644 index 0000000..68a8e0a --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/adoption.mdx @@ -0,0 +1,48 @@ +--- +sidebar_position: 3 +--- + +# Adoption + +```mdx-code-block +import TOCInline from '@theme/TOCInline'; +import Construction from '@site/src/components/Construction'; + + + +``` + +## What happens if I don't want to work on my plugin anymore? + +Are you OK with another developer taking over the plugin? If so, inform the +community in Discord (#plugin-dev) that the plugin is up for adoption. This will +allow an interested developer to take over. + +Is there already a specific developer that wants to take over the plugin? If so, +let the mods know in Discord that they may submit PRs. + +Do you wish to discontinue the plugin and block other developers from adopting +it? If so, let the mods know in Discord and provide your rationale. The requests +are evaluated on a case-by-case basis. + +## What happens if I stop updating my plugin? + +If your plugin has fallen behind the current API level for over three months, +other developers may adopt your plugin if they make a reasonable effort to reach +you. If they are unable to reach you, they may adopt the plugin and take over +development. + +If your plugin has fallen behind the current API level for over six months, +other developers may adopt your plugin without your permission. + +## How do I adopt an existing plugin? + +You may adopt a plugin if you meet one of the following criteria: + +- Consent of the current maintainer. +- The plugin has fallen behind the current API level for over three months. You + have made reasonable efforts to reach the original developer. +- The plugin has fallen behind the current API level for over six months. + +Simply inform the community in Discord (#plugin-dev) and submit a PR to add +yourself as an owner and update the repo url. diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/debug.md b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/debug.md new file mode 100644 index 0000000..717024e --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/debug.md @@ -0,0 +1,100 @@ +--- +sidebar_position: 7 +--- + +# Debugging the Game + +## Installing Visual Studio + +First of all, you need to install Visual Studio. In this guide I will be using +Visual Studio 2022, however 2019 will work as well. + +Download it from +[Microsoft's official site](https://visualstudio.microsoft.com/vs/). + +Select "Visual Studio Community" and run downloaded executable. Wait until +visual studio installer gets installed. Then go to "Individual components", +select "Just-In-Time debugger" and press "Install" button. You only need this +component, however, if you wish, you may install any additional ones. + +![image_451](images/debug/1_install_vs.png) + +If it gives you any confirmations, just press "Continue". Now wait until it will +install. Reboot if it will ask you to do so. + +## Configuring Visual Studio + +Before you will start debugging, you will need to perform some configuration. +Launch Visual Studio and it will prompt you to log in, you may do so or skip. + +Select "Continue without code" on starting screen. + +![image_452](images/debug/2_startup_window.png) + +Navigate to "Debug - Options - Debugging - General" and disable "Enable Just My +Code" option. Don't forget to press "Ok" button. + +![image_453](images/debug/3_config.png) + +## Configuring Dalamud + +Before you will be able to attach a debugger, you will need to disable +anti-debug protection. Type `/xldev` in game, open "Dalamud" menu and select +"Enable Anti-debug" menu item if it's unchecked. + +![image_454](images/debug/4_dalamud_config.png) + +## Attaching a debugger + +Navigate to "Debug - Attach to process". In "Attach to" section click on +"Select" button, check "Debug these code types" checkbox, and check "Managed +(.NET core, .NET 5+)" and "Native" checkboxes. Press "Ok" button and then find +"ffxiv_dx11.exe" process in process list. Select it and click "Attach" button. + +![image](images/debug/5_attach_debugger.png) + +Debugging session should begin now. Navigate to "Debug - Windows - Exception +settings" and uncheck "Common Language Runtime Exceptions". We don't need them. + +![image](images/debug/6_exception_settings.png) + +## Crash + +When a crash occurs, you will see it in Visual Studio. It may prompt you to load +source or PDB file for crashed module, in that case just close that window. + +You will see something like this: + +![image_456](images/debug/7_crash.png) + +You can close exception alert modal window. We don't need it. Look on a Call +stack. If you don't see Call stack window, navigate to "Debug - Windows - Call +stack". You can see crash happened on a Test plugin function call. Usually there +will be some plugin mentioned there, but not always. + +Generally, try to disable that plugin and reproduce the crash again. If you can +confirm that the crash stopped happening, please report the crash to a plugin +developer. + +## Reporting a problem + +You will need to include following things into your report: + +- Call stack + - Right click any call stack entry, select "Select all", right click again, + "Copy" (or Ctrl+A - Ctrl+C). +- Exception + - Navigate to "Debug - Windows - Output", find last error and copy it. + - It will look [like this](images/debug/8_output.png). +- Base address + - Please include process base address. Navigate to "Debug - Windows - + Modules", find `ffxiv_dx11.exe`, right click on it, select "Copy". The + information copied will contain process base address. **Please note that you + must copy base address from the same game session, base address changes + every game restart**. + - It will look [like this](images/debug/9_base_address.png). +- Dalamud log + +## Exiting debugger + +To detach a debugger, navigate to "Debug - Stop debugging". diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/development.mdx b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/development.mdx new file mode 100644 index 0000000..e1852a0 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/development.mdx @@ -0,0 +1,89 @@ +--- +sidebar_position: 5 +--- + +# Development + +```mdx-code-block +import TOCInline from '@theme/TOCInline'; +import Construction from '@site/src/components/Construction'; + + + +``` + +## How do the services in Dalamud work? + +Dalamud is composed of many services, with the +`Dalamud.IoC.PluginInterfaceAttribute` attribute, that provide you access to +game and Dalamud state. You can opt into these services by including them in the +constructor of the plugin, like so, + +```csharp +public Plugin( + [RequiredVersion("1.0")] DalamudPluginInterface pluginInterface, + [RequiredVersion("1.0")] CommandManager commandManager) +``` + +or by including them as an appropriately-attributed static variables in a class, +and then using the plugin interface you get in the constructor (it's mandatory!) +to initialise said class: + +```csharp +public class Dalamud +{ + public static void Initialize(DalamudPluginInterface pluginInterface) => + pluginInterface.Create(); + + [PluginService] + [RequiredVersion("1.0")] + public static DalamudPluginInterface PluginInterface { get; private set; } = null!; + [PluginService] + [RequiredVersion("1.0")] + public static CommandManager Commands { get; private set; } = null!; +} + +public Plugin(DalamudPluginInterface pluginInterface) +{ + Dalamud.Initialize(pluginInterface); +} +``` + +## What are the currently available Dalamud services? + +As of Dalamud 6.3, these are all of the currently available services. Please +update this list if you spot a discrepancy! + +- [`Dalamud.Data.DataManager`](https://goatcorp.github.io/Dalamud/api/Dalamud.Data.DataManager.html) +- [`Dalamud.Game.ClientState.Aetherytes.AetheryteList`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.ClientState.Aetherytes.AetheryteList.html) +- [`Dalamud.Game.ClientState.Buddy.BuddyList`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.ClientState.Buddy.BuddyList.html) +- [`Dalamud.Game.ClientState.Conditions.Condition`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.ClientState.Conditions.Condition.html) +- [`Dalamud.Game.ClientState.Fates.FateTable`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.ClientState.Fates.FateTable.html) +- [`Dalamud.Game.ClientState.GamePad.GamepadState`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.ClientState.GamePad.GamepadState.html) +- [`Dalamud.Game.ClientState.JobGauge.JobGauges`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.ClientState.JobGauge.JobGauges.html) +- [`Dalamud.Game.ClientState.Keys.KeyState`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.ClientState.Keys.KeyState.html) +- [`Dalamud.Game.ClientState.Objects.ObjectTable`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.ClientState.Objects.ObjectTable.html) +- [`Dalamud.Game.ClientState.Objects.TargetManager`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.ClientState.Objects.TargetManager.html) +- [`Dalamud.Game.ClientState.Party.PartyList`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.ClientState.Party.PartyList.html) +- [`Dalamud.Game.ClientState.ClientState`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.ClientState.ClientState.html) +- [`Dalamud.Game.Command.CommandManager`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.Command.CommandManager.html) +- [`Dalamud.Game.Gui.ContextMenus.ContextMenu`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.Gui.ContextMenus.ContextMenu.html) +- [`Dalamud.Game.Gui.Dtr.DtrBar`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.Gui.Dtr.DtrBar.html) +- [`Dalamud.Game.Gui.FlyText.FlyTextGui`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.Gui.FlyText.FlyTextGui.html) +- [`Dalamud.Game.Gui.PartyFinder.PartyFinderGui`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.Gui.PartyFinder.PartyFinderGui.html) +- [`Dalamud.Game.Gui.Toast.ToastGui`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.Gui.Toast.ToastGui.html) +- [`Dalamud.Game.Gui.ChatGui`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.Gui.ChatGui.html) +- [`Dalamud.Game.Gui.GameGui`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.Gui.GameGui.html) +- [`Dalamud.Game.Libc.LibcFunction`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.Libc.LibcFunction.html) +- [`Dalamud.Game.Network.GameNetwork`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.Network.GameNetwork.html) +- [`Dalamud.Game.Text.SeStringHandling.SeStringManager`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.Text.SeStringHandling.SeStringManager.html) +- [`Dalamud.Game.ChatHandlers`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.ChatHandlers.html) +- [`Dalamud.Game.Framework`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.Framework.html) +- [`Dalamud.Game.SigScanner`](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.SigScanner.html) +- [`Dalamud.Interface.TitleScreenMenu`](https://goatcorp.github.io/Dalamud/api/Dalamud.Interface.TitleScreenMenu.html) + +## How do I convert from world coordinates to map coordinates and vice versa? + +Please consult the +[ffxiv-datamining documentation on MapCoordinates](https://github.com/xivapi/ffxiv-datamining/blob/master/docs/MapCoordinates.md), +which details how to convert between the various kinds of coordinates. diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/getting-started.mdx b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/getting-started.mdx new file mode 100644 index 0000000..7a727a8 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/getting-started.mdx @@ -0,0 +1,140 @@ +--- +sidebar_position: 1 +--- + +# Getting Started + +```mdx-code-block +import TOCInline from '@theme/TOCInline'; +import Construction from '@site/src/components/Construction'; + + + +``` + +## How do I get started? + +The majority of the XIVLauncher and Dalamud ecosystem is written in C# for its +usability, convenience, and robustness. It is recommended that anything you work +on is also in C#, unless you're working on something with a significant amount +of interoperation with native code (in which C++/CLI may be useful) or you're +experimenting with other .NET languages. + +To get started, you'll want to get the latest version of Visual Studio found +[here](https://visualstudio.microsoft.com/downloads/); the Community edition +will work fine. After doing so, you can clone any of the following projects and +get to work with their Visual Studio solutions. Alternatively, you may want to +use another IDE such as [JetBrains Rider](https://www.jetbrains.com/rider/). + +### Dalamud Plugins + +Plugins allow you to interact with the game and add features, modify +functionality, and do much more. We ask you to be respectful of +[our guidelines](#q-what-am-i-allowed-to-do-in-my-plugin) to ensure that your +plugin is approved into the primary repository, and to minimise the risk of +action by Square Enix. You can read more about this +[here](#q-why-do-you-discourage-certain-types-of-plugins). + +We recommend that you start by cloning one of the following templates, and then +customising it to your requirements. While `SamplePlugin` is the most actively +maintained, the others are updated as required: + +- [@goatcorp/SamplePlugin](https://github.com/goatcorp/SamplePlugin) +- [@karashiiro/DalamudPluginProjectTemplate](https://karashiiro/DalamudPluginProjectTemplate) +- [@lmcintyre/PluginTemplate](https://github.com/lmcintyre/PluginTemplate) + +To distribute a plugin, it needs to be packaged correctly. This can be done +manually or [with DalamudPackager](https://github.com/goatcorp/DalamudPackager). + +When your plugin is ready for testing/release, it should be PRed over to the +[DalamudPluginsD17](https://github.com/goatcorp/DalamudPluginsD17) repo. +**Please place testing plugins in the testing/live folder**. + +### Dalamud + +Dalamud is the core addon/plugin system. It is loaded by XIVLauncher into your +game, and is responsible for loading your plugins and providing them with a core +set of functionality. + +The Dalamud source code +[can be found on GitHub](https://github.com/goatcorp/Dalamud). + +Other assets required by Dalamud can be found +[in the DalamudAssets repository](https://github.com/goatcorp/DalamudAssets). + +You can find the [Dalamud API documentation](api) on this site. + +### XIVLauncher + +XIVLauncher is a custom launcher for FFXIV that offers a number of benefits, +including faster launching, saved credentials, and automatically injecting +Dalamud into the game. + +The XIVLauncher source code can be found +[on GitHub](https://github.com/goatcorp/FFXIVQuickLauncher). + +## Where do I ask for help? + +The best place to ask for help is the +[#dev channel](https://discord.gg/wwYXnzWYqY) of the Discord; we're a helpful +bunch and will do our best to answer your query as long as you explain what +you've tried and looked at so far. + +## How do I hot-reload my plugin? + +As of API 4/Dalamud 6, hot-reloading is part of Dalamud. To use it, go to +Dalamud Settings > Experimental > Dev Plugin Locations, and then add either the +folder that your plugin is in or the plugin itself. If you add a folder, Dalamud +will attempt to load all DLLs within the folder. + +Your configuration should look something like this: +![image](https://user-images.githubusercontent.com/707827/166122747-97f0f7c7-e1a4-4093-adeb-5c7c8eab935c.png) + +## How do I debug my plugin and/or the game? + +To debug, you'll need to attach a debugger to the game. This will usually be +from your development environment, such as Visual Studio. + +However, the game has antidebug protection on by default. To turn this off, use +the Dalamud dev menu (`/xldev`), then go to Dalamud > Enable AntiDebug; this +setting is persisted between launches, so you do not need to turn it on each +time. + +Once you've done this, you can attach to the game with your debugger. In Visual +Studio, you can go to Debug > Attach to Process (Ctrl+Alt+P), and then select +the FF14 process. For the full debugging experience, make sure to change "Attach +to" to include both `Native code` and `Managed (.NET 4.x) code`; this will +ensure that the debugger will work for both the game and for Dalamud plugins. + +This functionality is only supported for debugging your plugins. You will not +receive support if you use it for anything else. + +[Detailed instructions are available here.](debug) + +## How do I use FFXIVClientStructs in my own code? + +[FFXIVClientStructs](https://github.com/aers/FFXIVClientStructs) is a communal +project to provide an interface to the game's classes, data, and more to C# +users and reverse engineers. + +To use FFXIVClientStructs in your own code, you'll need to add a reference to +it. This can be done by opening the `csproj` for your plugin and adding the +reference with the other references: + +```xml + + $(AppData)\XIVLauncher\addon\Hooks\dev\FFXIVClientStructs.dll + +``` + +or through right-clicking the project in VS, going to Add, and then adding an +Assembly Reference to the same path. Note that you will likely need to still +open the `csproj` after doing this to ensure that the path uses `$(AppData)` and +not the path specific to your system. + +The version of FFXIVClientStructs included with Dalamud is updated with +reasonable frequency, and has several patches to ensure that backwards +compatibility with existing plugins is not broken. You can bundle your own +version if you'd like for the latest findings, but be aware that this may make +it difficult for you to maintain compatibility with new versions of the game +and/or Dalamud. diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/1_install_vs.png b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/1_install_vs.png new file mode 100644 index 0000000..b13e378 Binary files /dev/null and b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/1_install_vs.png differ diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/2_startup_window.png b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/2_startup_window.png new file mode 100644 index 0000000..3b2e90c Binary files /dev/null and b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/2_startup_window.png differ diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/3_config.png b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/3_config.png new file mode 100644 index 0000000..66acc2e Binary files /dev/null and b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/3_config.png differ diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/4_dalamud_config.png b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/4_dalamud_config.png new file mode 100644 index 0000000..a43c92b Binary files /dev/null and b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/4_dalamud_config.png differ diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/5_attach_debugger.png b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/5_attach_debugger.png new file mode 100644 index 0000000..ae468dc Binary files /dev/null and b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/5_attach_debugger.png differ diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/6_exception_settings.png b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/6_exception_settings.png new file mode 100644 index 0000000..f4ad8c4 Binary files /dev/null and b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/6_exception_settings.png differ diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/7_crash.png b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/7_crash.png new file mode 100644 index 0000000..f9dc0c5 Binary files /dev/null and b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/7_crash.png differ diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/8_output.png b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/8_output.png new file mode 100644 index 0000000..a1536a8 Binary files /dev/null and b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/8_output.png differ diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/9_base_address.png b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/9_base_address.png new file mode 100644 index 0000000..49bd2b8 Binary files /dev/null and b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/images/debug/9_base_address.png differ diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/index.mdx b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/index.mdx new file mode 100644 index 0000000..e1a8f91 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/index.mdx @@ -0,0 +1,13 @@ +--- +sidebar_position: 100 +--- + +# Development FAQ (Legacy) + +```mdx-code-block +import Construction from '@site/src/components/Construction'; +import DocCardList from '@theme/DocCardList'; + + + +``` diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/reverse-engineering.mdx b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/reverse-engineering.mdx new file mode 100644 index 0000000..af4f52c --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/reverse-engineering.mdx @@ -0,0 +1,159 @@ +--- +sidebar_position: 6 +--- + +# Reverse Engineering + +```mdx-code-block +import TOCInline from '@theme/TOCInline'; +import Construction from '@site/src/components/Construction'; + + + +``` + +## How do I get started with reverse-engineering the game so that I can do things Dalamud doesn't expose? + +Reverse-engineering isn't easy, and it's even more difficult when reversing a +large game like FFXIV. It's a large subject and hard to explore, but here are +some rough pointers. + +Fundamentally, the game is running on your machine, which means it's executing +code constantly to run the game logic and to render the scene. In addition, it +is constantly talking to the game server, which is what allows you to interact +with other players and the world at large. + +If you'd like to learn more about how the game communicates with the server, +your best bet is the +[Sapphire project](https://github.com/SapphireServer/Sapphire). The Dalamud +project generally strays away from interfering with client-server communication +for the reasons outlined in +[What am I allowed to do in my plugin?](#q-what-am-i-allowed-to-do-in-my-plugin), +but understanding the flow may help with other things. + +Otherwise, you'll be reverse-engineering the game client, as that's what Dalamud +hooks into. The game binary you have is compiled machine code; the original +source code for the game is unavailable, which means you have to figure out what +it's doing without knowing any of the original context. + +To do this, you'll need an interactive disassembler/decompiler to conduct +_static analysis_. The gold standard for this is +[IDA Pro](https://hex-rays.com/ida-pro/), but it is very expensive for hobbyists +and most people take an approach generally frowned upon by the Maelstrom. A +popular free alternative is [Ghidra](https://ghidra-sre.org/), which is very +powerful and extensible, if not a little clunky. There are other disassemblers, +but these are the two primarily used by the Dalamud community. + +Once you have your disassembler ready, you'll need to disassemble the +`ffxiv_dx11.exe` file in the game's directory. After doing this, it is highly +recommended that you use +[the excellent script provided by FFXIVClientStructs](https://github.com/aers/FFXIVClientStructs/tree/main/ida); +this script will automatically populate your disassembler's database with +community findings for the current version, saving you a great deal of time. + +After that, well... it's time for you to work on the puzzle. You'll want to find +resources for how to reverse-engineer things online; there are tutorials that +specifically look at reversing games, which may help you build up an intuition +for the thought process you'll need. + +Another approach you can take is _dynamic analysis_. You can attach a debugger +to the game, like [x64dbg](https://x64dbg.com/) or +[Cheat Engine](https://www.cheatengine.org/), and use these to explore the +game's memory and execution at runtime (e.g. searching for a value and finding +what changes it in the code). Both approaches are valid, but you'll likely need +to use both to make headway as they can provide context for each other. + +Reversing is a large and complex field, and it takes years to get proficient and +recognise patterns. Asking the Discord for help is encouraged, but be aware that +you have a long journey ahead of you regardless. + +## How do I hook a game function? + +Hooking a function refers to intercepting its execution, so that your code runs +in lieu of or as an extension to it. This could allow you to, for example, +detect when a certain action occurs in-game and change its behaviour. + +Assuming that you've successfully found a function you'd like to hook through +reverse-engineering, Dalamud and EasyHook make hooking functions fairly easy. +You'll need the address of the function to hook; this can be retrieved from your +disassembler, or by getting a "signature" for the function, which is a unique +string of instructions that can be used to find the address. + +Using signatures is preferred where possible as it improves the chances of your +plugin surviving a game update (as addresses always change, while signatures do +not). To get a signature, you'll need to look up how to do it with your +preferred disassembler. For IDA, +[Caraxi's fork of SigMaker-x64](https://github.com/Caraxi/SigMaker-x64) is +recommended. + +If you have a signature, you will need to resolve it to an address in your code. +For a traditional string-of-instruction-bytes signature, you can use +[ScanText](https://goatcorp.github.io/Dalamud/api/Dalamud.Game.SigScanner.html#Dalamud_Game_SigScanner_ScanText_System_String_). +For other types of signatures, please look at the other methods for `SigScanner` +and choose an appropriate one. + +After that: + +- Import `Dalamud.Hooking`. +- Create a delegate type with the same type signature as the function you're + hooking. +- Create a `Hook` variable to represent the hook. +- Create a function in which your custom code will execute. It should match the + type signature of the delegate. +- In your `Initialize` function: +- Get the address for the function (either as-is or through a signature). +- Initialize your `Hook` variable by calling its constructor with the address, + delegate-ified version of your custom code function, and `this`. +- Enable the hook. +- In your `Dispose` function: +- Disable the hook. +- Dispose of the hook. + +You should then be ready to go. An abbreviated example (only relevant portions +shown) of hooking the main `Render` function for the game follows: + +```c# +using Dalamud.Hooking; +using Dalamud.Plugin; + +namespace SamplePlugin +{ + public class Plugin : IDalamudPlugin + { + public string Name => "SamplePlugin"; + + private DalamudPluginInterface pi; + + public delegate IntPtr RenderDelegate(IntPtr renderManager); + private Hook renderDelegateHook; + + public void Initialize(DalamudPluginInterface pluginInterface) + { + this.pi = pluginInterface; + + // Render::Manager::Render + var Signature = "40 53 55 57 41 56 41 57 48 83 EC 60"; + var renderAddress = this.pi.TargetModuleScanner.ScanText(Signature); + + this.renderDelegateHook = new Hook(renderAddress, this.RenderDetour); + this.renderDelegateHook.Enable(); + } + + private unsafe IntPtr RenderDetour(IntPtr renderManager) + { + PluginLog.Information("Before render"); + var res = this.renderDelegateHook.Original(renderManager); + PluginLog.Information("After render"); + + return res; + } + + public void Dispose() + { + this.renderDelegateHook.Disable(); + this.renderDelegateHook.Dispose(); + this.pi.Dispose(); + } + } +} +``` diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/updates.mdx b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/updates.mdx new file mode 100644 index 0000000..f4db9bd --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/faq/updates.mdx @@ -0,0 +1,59 @@ +--- +sidebar_position: 2 +--- + +# Updates + +```mdx-code-block +import TOCInline from '@theme/TOCInline'; +import Construction from '@site/src/components/Construction'; + + + +``` + +## What happens when there's an API version bump? + +When there's an API version bump, your plugin will no longer be loaded by +Dalamud. To fix this, you'll need to update to the latest version of the API by +updating the JSON file for your plugin and repackaging your plugin for the +repository. + +## How can I stay up to date with API changes? + +The best place to stay up to date with upcoming API changes is to frequent the +[Discord server](https://goat.place/). Changes will be announced with notice so +that you can adapt your plugin as appropriate. + +After submitting your first plugin to the repository, you will be given a Plugin +Developer role so that you will be pinged whenever breaking changes occur. + +## How do I fix `Nothing inherits from IDalamudPlugin`? + +This occurs because you have the dependencies for your plugin in the same folder +as the plugin (e.g. `Dalamud.dll` and such). This was supported prior to .NET 5, +but is no longer supported. + +To fix this, open the `csproj` file and add `false` to each +of your `Reference`s like so: + +```xml + + $(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll + false + +``` + +After doing this, clean out your output folder and rebuild. It will no longer +copy the dependencies, and your plugin should now be able to load correctly. + +## What happens when the game is updated? + +When the game is updated, it is likely that your plugin will stop working and/or +Dalamud will refuse to load it. To fix this, you'll need to: + +- Wait for Dalamud to update to accommodate the new game version. +- Update your plugin to ensure it's using the latest API version and still works + with the game. Ensure that any non-Dalamud interactions with the game (e.g. + direct interop, etc) have been updated. +- Repackage and reupload your plugin. diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/index.md b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/index.md new file mode 100644 index 0000000..ef6ca12 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/index.md @@ -0,0 +1,12 @@ +--- +slug: / +sidebar_position: 1 +--- + +# 介绍 + +欢迎来到新的 Dalamud 开发者文档! + +这份文档仍在不断更新中,会随着时间的推移而更新。 + +[你可以通过为这份文档做出贡献来帮助我们!](https://github.com/goatcorp/dalamud-docs) diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/_category_.yml b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/_category_.yml new file mode 100644 index 0000000..4095fe5 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/_category_.yml @@ -0,0 +1,6 @@ +label: Plugin Development +position: 3 +link: + type: generated-index + title: Plugin Development + description: Learn the basics of Dalamud plugin development. diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/getting-started.md b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/getting-started.md new file mode 100644 index 0000000..874c94e --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/getting-started.md @@ -0,0 +1,20 @@ +# 入门指南 + +插件允许您与游戏进行交互并添加功能、修改功能等等。我们要求您遵守 +[我们的指南](restrictions#我在插件中可以做什么),以确保您的插件被批准进入官方插 +件存储库,并最大限度地减少 Square Enix 采取行动的风险。您可以 +在[这里](restrictions#为什么您不鼓励某些类型的插件)了解更多信息。 + +我们建议您从克隆以下模板之一开始,然后根据您的要求进行自定义。虽然 +`SamplePlugin` 是最活跃的维护者,但其他模板也会根据需要进行更新: + +- [@goatcorp/SamplePlugin](https://github.com/goatcorp/SamplePlugin) +- [@karashiiro/DalamudPluginProjectTemplate](https://github.com/karashiiro/DalamudPluginProjectTemplate) +- [@lmcintyre/PluginTemplate](https://github.com/lmcintyre/PluginTemplate) + +要分发插件,它需要正确打包。这可以手动完成,也可以使用 +[DalamudPackager](https://github.com/goatcorp/DalamudPackager) 完成。 + +当您的插件准备好进行测试/发布时,您应该向 +[DalamudPluginsD17](https://github.com/goatcorp/DalamudPluginsD17) 存储库发出拉 +取请求。**请将测试插件放在`testing/live`文件夹中**。 diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/how-tos/AddonEventManager.md b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/how-tos/AddonEventManager.md new file mode 100644 index 0000000..51e18f7 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/how-tos/AddonEventManager.md @@ -0,0 +1,178 @@ +# AddonEventManager + +此服务提供了一个管理器,用于向本机游戏 UI 添加和删除自定义事件。这些事件由 +Dalamud 跟踪和管理,并在您的插件卸载或附加到的插件卸载时自动删除。 + +添加事件时,您将获得该事件的句柄,您需要该句柄才能手动注销该事件。 + +## 提供的接口 + +```cs +public interface IAddonEventManager +{ + public delegate void AddonEventHandler(AddonEventType atkEventType, nint atkUnitBase, nint atkResNode); + + IAddonEventHandle? AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType eventType, AddonEventHandler eventHandler); + + void RemoveEvent(IAddonEventHandle eventHandle); + + void SetCursor(AddonCursorType cursor); + + void ResetCursor(); +} +``` + +## Node 和 Components + +_您必须为事件注册一个 `Node`,不能注册 `Component`,尝试注册 `Component` 的地址 +将导致崩溃。_ + +有效的 Node 类型将以 `Node` 结尾。 + +在下面的图片中,如果您有一个 `AtkComponentButton*`,您可能需要访问 `OwnerNode` +属性并使用该节点进行注册。 + +![image](https://github.com/MidoriKami/dalamud-docs/assets/9083275/e4c00a43-67e4-4164-8338-6862e4e12182) + +## 注册事件 + +在注册事件时,您需要提供插件的指针(AtkUnitBase\*)、节点的指针(AtkResNode\*) +、要触发它的事件以及您的委托。 + +您可能需要修改节点标志以允许该节点响应事件。 + +此系统非常适用于 IAddonLifecycle 服务,以便您可以轻松地在插件设置时注册事件。 + +```cs +// Register listener for MonsterNote setup +AddonLifecycle.RegisterListener(AddonEvent.PostSetup, "MonsterNote", OnPostSetup); + +// PostSetup for MonsterNode provided by AddonLifecycle +private void OnPostSetup(AddonEvent type, AddonArgs args) +{ + var addon = (AtkUnitBase*) args.Addon; + + var targetNode = addon->GetNodeById(22); + + targetNode->NodeFlags |= NodeFlags.EmitsEvents | NodeFlags.RespondToMouse | NodeFlags.HasCollision; + + EventManager.AddEvent((nint) addon, (nint) targetNode, AddonEventType.MouseOver, TooltipHandler); + EventManager.AddEvent((nint) addon, (nint) targetNode, AddonEventType.MouseOut, TooltipHandler); +} + +private void TooltipHandler(AddonEventType type, IntPtr addon, IntPtr node) +{ + var addonId = ((AtkUnitBase*) addon)->ID; + + switch (type) + { + case AddonEventType.MouseOver: + AtkStage.GetSingleton()->TooltipManager.ShowTooltip(addonId, (AtkResNode*)node, "This is a tooltip."); + break; + + case AddonEventType.MouseOut: + AtkStage.GetSingleton()->TooltipManager.HideTooltip(addonId); + break; + } +} +``` + +此示例代码的结果是,包含当前选择的狩猎日志类的文本节点将在鼠标悬停时显示工具提示 +。 + +![image](https://github.com/goatcorp/dalamud-docs/assets/9083275/0b859b62-085c-4879-9316-2136232a3fc5) + +## 注销事件 + +对于添加到非持久性插件(在打开和关闭时设置和完成的插件)的事件,它们不需要手动删 +除,因为系统将在插件关闭时自动取消跟踪和删除任何已注册的事件。 + +对于添加到持久性插件(始终存在的插件,例如“\_BagWidget”、“NamePlate”)的任何事件 +,您需要保存从 AddEvent 返回的事件句柄,并使用它来注销您的事件。 + +所有已注册的事件在您的插件卸载时都会自动从本机中删除。 + +以下是保存和删除事件句柄的示例。 + +```cs +// Class members +IAddonEventHandle MouseOver; +IAddonEventHandle MouseOut; + +// Register listener for MonsterNote setup +AddonLifecycle.RegisterListener(AddonEvent.PostSetup, "MonsterNote", OnPostSetup); + +// PostSetup for MonsterNode provided by AddonLifecycle +private void OnPostSetup(AddonEvent type, AddonArgs args) +{ + var addon = (AtkUnitBase*) args.Addon; + + var targetNode = addon->GetNodeById(22); + + targetNode->NodeFlags |= NodeFlags.EmitsEvents | NodeFlags.RespondToMouse | NodeFlags.HasCollision; + + MouseOver = EventManager.AddEvent((nint) addon, (nint) targetNode, AddonEventType.MouseOver, TooltipHandler); + MouseOut = EventManager.AddEvent((nint) addon, (nint) targetNode, AddonEventType.MouseOut, TooltipHandler); +} + +private void UnregisterEvents() +{ + EventManager.RemoveEvent(MouseOver); + EventManager.RemoveEvent(MouseOut); +} +``` + +## 光标 API + +此服务还提供了访问更改游戏光标图标的函数的功能。 + +这对于向用户指示可以单击的元素以触发单击事件非常有用。 + +```cs +// Register listener for MonsterNote setup +AddonLifecycle.RegisterListener(AddonEvent.PostSetup, "MonsterNote", OnPostSetup); + +// PostSetup for MonsterNode provided by AddonLifecycle +private void OnPostSetup(AddonEvent type, AddonArgs args) +{ + var addon = (AtkUnitBase*) args.Addon; + + var targetNode = addon->GetNodeById(22); + + targetNode->NodeFlags |= NodeFlags.EmitsEvents | NodeFlags.RespondToMouse | NodeFlags.HasCollision; + + EventManager.AddEvent((nint) addon, (nint) targetNode, AddonEventType.MouseOver, OnClickHandler); + EventManager.AddEvent((nint) addon, (nint) targetNode, AddonEventType.MouseOut, OnClickHandler); + EventManager.AddEvent((nint) addon, (nint) targetNode, AddonEventType.MouseClick, OnClickHandler); +} + +private void OnClickHandler(AddonEventType type, IntPtr addon, IntPtr node) +{ + switch (type) + { + case AddonEventType.MouseOver: + EventManager.SetCursor(AddonCursorType.Clickable); + break; + + case AddonEventType.MouseOut: + EventManager.ResetCursor(); + break; + + case AddonEventType.MouseClick: + // Do custom click logic here. + break; + } +} +``` + +此示例代码的结果是,包含当前选择的狩猎日志类的文本节点将在单击时触发您的函数,并 +更改游戏光标以指示它是可单击的。 + +![image](https://github.com/goatcorp/dalamud-docs/assets/9083275/78566abc-1f03-41cf-8973-dc3d3186b717) + +## 日志记录 + +每当添加或删除事件时,它们都会记录在 `AddonEventManager` 的详细日志通道下,这对 +于查看事件的情况非常有用。 + +![image](https://github.com/goatcorp/dalamud-docs/assets/9083275/77cb00ed-e5ea-4219-82fa-ce22b92a41ad) diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/how-tos/AddonLifecycle.md b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/how-tos/AddonLifecycle.md new file mode 100644 index 0000000..9ab6d97 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/how-tos/AddonLifecycle.md @@ -0,0 +1,89 @@ +# AddonLifecycle + +此服务为您提供了访问 `Addons` 的各种状态和状态更改的非常简单的方式。 + +该服务的主要目标是使修改本机 UI 或从插件中获取数据变得容易,而无需逆向工程并随后 +挂钩每个需要交互的插件。 + +有时,插件不会实现自己的 Draw 或其他函数,这使得挂钩触发您需要触发代码的东西变得 +非常具有挑战性。 + +此服务允许您按名称监听任何插件事件,无需地址。 + +### 提供的接口 + +```cs +public interface IAddonLifecycle +{ + public delegate void AddonEventDelegate(AddonEvent type, AddonArgs args); + + void RegisterListener(AddonEvent eventType, IEnumerable addonNames, AddonEventDelegate handler); + void RegisterListener(AddonEvent eventType, string addonName, AddonEventDelegate handler); + void RegisterListener(AddonEvent eventType, AddonEventDelegate handler); + + void UnregisterListener(AddonEvent eventType, IEnumerable addonNames, [Optional] AddonEventDelegate handler); + void UnregisterListener(AddonEvent eventType, string addonName, [Optional] AddonEventDelegate handler); + void UnregisterListener(AddonEvent eventType, [Optional] AddonEventDelegate handler); + + void UnregisterListener(params AddonEventDelegate[] handlers); +} +``` + +### 注册事件 + +您可以通过指定要监听的事件,然后选择性地指定要监听的插件名称来注册事件。 + +如果未提供插件名称,则服务将向您发送有关**所有**插件的通知。通常建议指定要监听的 +插件。 + +以下是注册事件的示例代码: + +```cs +AddonLifecycle.RegisterListener(AddonEvent.PreDraw, "FieldMarker", OnPreDraw); +AddonLifecycle.RegisterListener(AddonEvent.PostUpdate, "FieldMarker", OnPostUpdate); +AddonLifecycle.RegisterListener(AddonEvent.PostDraw, new[] { "Character", "FieldMarker", "NamePlate" }, OnPostDraw); +``` + +### 注销事件 + +您有几种注销事件的选项,可以使用与注册相同的语法进行注销,也可以直接注销函数,而 +无需指定要注销哪些事件或插件。以下是注销事件的示例代码: + +```cs +AddonLifecycle.UnregisterListener(AddonEvent.PostDraw, new[] { "Character", "FieldMarker", "NamePlate" }, OnPostDraw); +AddonLifecycle.UnregisterListener(OnPreDraw, OnPostUpdate); +``` + +### 可用事件 + +此服务提供了几个可以监听的事件: + +Setup、Update、Draw、RequestedUpdate、Refresh、Finalize + +每个事件都在 Pre 或 Post 监听器中使用,例如,如果您想在插件即将刷新时得到通知, +可以订阅 PreRefresh。 + +_注意:没有提供 PostFinalize 事件。那将是在插件已从内存中释放之后。如果您有需要 +PostFinalize 事件的有效用例,请告诉我们。_ + +### 可用数据 + +当调用您的委托时,它会传递一个 AddonArgs 对象,您可以将其转换为更具体的对象以获 +取原始调用中使用的参数数据。 + +每个事件都有自己特定的 AddonArgs 实现,其中包含可用的参数数据。如果您不确定自己 +拥有哪种类型的 AddonArgs,则可以检查 `Type` 属性。 + +例如: + +```cs +private void OnPostSetup(AddonEvent type, AddonArgs args) +{ + if (args is AddonSetupArgs setupArgs) + { + var valueCount = setupArgs.AtkValueCount; + var values = (AtkValue*) setupArgs.AtkValues; + var valueSpan = setupArgs.AtkValueSpan; + } +} +``` diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/how-tos/_category_.yml b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/how-tos/_category_.yml new file mode 100644 index 0000000..e16f835 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/how-tos/_category_.yml @@ -0,0 +1,5 @@ +label: "How To's" +link: + type: generated-index + title: "How To's" + description: Learn more about specific APIs diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/interacting-with-the-game.md b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/interacting-with-the-game.md new file mode 100644 index 0000000..64ec0fa --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/interacting-with-the-game.md @@ -0,0 +1,264 @@ +# 与游戏交互 + +几乎任何插件最终都希望以某种方式与游戏本身进行交互,无论是响应某些事件还是根据游 +戏中正在发生的情况做出决策。这可以采取几种不同的路径,每种路径都取决于开发人员的 +意图和他们想要做什么。 + +一般来说,鼓励开发人员按以下优先级使用基于游戏的交互: + +1. 在可能的情况下,使用 Dalamud 提供的 API 与游戏进行交互。这通常是与游戏交互的 + 最安全方式,并提供了稳定的 API,这些 API 不会在 API 升级之外发生变化 + 。Dalamud API 还可以将复杂或烦人的概念包装或抽象成更简单易用的模式,并提供某 + 些保护措施,以确保无效数据不会对游戏产生太大影响。这些方法往往有很好的文档, + 并且易于使用。 +2. 如果 Dalamud API 没有公开所需的行为,开发人员可以使用 Client Structs 项目。 + 这是随 Dalamud 一起提供的,实际上允许插件将游戏用作库。它相对容易地访问游戏 + 的代码和结构,尽管它通常会使用指针和其他不安全的代码,因此开发人员需要对自己 + 的安全负责。 +3. 如果 Client Structs 项目没有公开所需的行为,Dalamud 提供了逃生口,以原始内存 + 和原始函数的形式工作,允许插件从未记录的结构中读取信息,并使用它们的签名或其 + 他引用调用或挂钩方法。 + +大多数插件将牢牢地停留在阶段 1 和 2 的领域,阶段 3 则用于尚未 +被[逆向工程](reverse-engineering.md)或完全理解的新颖概念。在可能的情况下,鼓励作 +为插件开发的一部分进行逆向工程的开发人员将其发现贡献回 Client Structs 项目,以便 +其他开发人员在未来可以使用它们。 + +本文档页面不会解释如何使用 Dalamud 提供的 API 与游戏进行交互,因为它们都有其他文 +档并且希望可以访问。相反,本文档将重点放在更高级的概念上;也就是说,Dalamud API +无法完全实现的地方。 + +## 我想这样做! + +有时,请求游戏本身执行某些操作比自己执行操作更有益。实际上,这意味着将游戏代码用 +作库,可以随意调用任何任意函数并使用其结果。这使得插件可以以与游戏相同的方式执行 +计算,或者以游戏本身的方式执行操作,就好像 Dalamud 甚至不存在一样。 + +例如,插件可能希望检查玩家是否是导师: + +```csharp +public unsafe bool IsPlayerMentor() { + var playerStatePtr = PlayerState.Instance(); + return playerStatePtr->IsMentor(); +} +``` + +这种方法将从 Client Structs 中获取 `PlayerState` 实例,并调用适当的检查。 + +### 创建自己的委托 + +有时,您感兴趣的方法可能不在 Client Structs 中。当这种情况发生时,开发人员可以利 +用他们的逆向工程技能生成签名,然后使用它们创建自己的委托: + +```csharp +public class GameFunctions { + private delegate byte IsQuestCompletedDelegate(ushort questId); + + [Signature("E8 ?? ?? ?? ?? 41 88 84 2C")] + private readonly IsQuestCompletedDelegate? _isQuestCompleted = null; + + public GameFunctions() { + SignatureHelper.Initialise(this); + } + + public bool IsQuestCompleted(ushort questId) { + if (this._isQuestCompleted == null) + throw new InvalidOperationException("IsQuestCompleted signature wasn't found!"); + + return this._isQuestCompleted(questId) > 0; + } +} +``` + +这是很多代码,所以让我们将其分解一下。 + +首先,开发人员声明了一个[委托][delegate-doc],用于调用他们想要调用的函数。这通知 +编译器和代码返回类型(在本例中为 `byte`)以及函数的参数。仅此一行是纯声明性的, +除了定义之外没有任何影响。如果特定参数是对未记录的指针的引用(或者开发人员根本不 +关心访问结构目标中的任何数据),则通常会使用 `nint` 类型。 + +接下来,开发人员声明了一个可空的*实例*,其默认值设置为 `null`。然后,该实例标记 +为 `[Signature(string signature)]` 属性。此属性由 Dalamud 的 `SignatureHelper` +类提供,并指定标识我们感兴趣的函数的签名。 + +然后,类的构造函数调用了 `SignatureHelper#Initialise`。此方法将扫描引用的对象( +在本例中为 `this`),并使用反射查找所有带有 `[Signature()]` 标记的类成员。然后, +它将自动解析签名并将适当的指针注入该变量。如果无法解析签名,则委托实例将设置为 +`null`,以供开发人员处理。 + +最后,定义了 `IsQuestCompleted()` 方法。这存在于“托管代码”中(因此,在 C# 中), +并提供了一些围绕原始方法的使用便利性。例如,如果委托为空,则我们的方法将引发异常 +,并将返回的 `byte` 转换为 `bool`。这些包装方法通常保持简单,但通常也会包含重要 +的安全或健全性检查,以确保 C# 和游戏的本机代码之间有一个干净的桥梁。 + +[delegate-doc]: + https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/ +[unmanaged-doc]: + https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/unmanaged-types + +#### 另一种委托方式 + +在查看某些插件时,您可能会注意到一个略微不同的模式: + +```csharp +[Signature("E8 ?? ?? ?? ?? 41 88 84 2C")] +private readonly delegate* unmanaged _isQuestCompletedDelegate; +``` + +这是一种更短(但可能稍微更复杂)的解决方案。不需要预先声明委托和其他信息,所有有 +关委托参数和返回值的信息都包含在 `unmanaged<>` 段中。 +[`unmanaged`][unmanaged-doc] 关键字表示此函数不是插件的 C# 代码的一部分,而是来 +自较低级别。`<>` 中的部分表示函数的参数和返回类型。返回类型始终是列表中的*最后一 +个*类型,所有其他类型都是参数类型,按相同顺序排列。例如,`` +是一个函数,例如 `MyFunction(uint someNumber, string someString)`,返回一个 +`byte`。其他所有内容都与上面的内容相同,包括可空性。 + +## 告诉我那时发生了什么! + +插件可能希望在游戏中发生某些事件时得到通知。如果 Dalamud 中不存在事件或适当的回 +调,则插件可以采用一些策略来获得通知。 + +### 轮询 + +也许是获得特定更改通知的最简单(尽管可能不是最有效)方法就是观察该更改。例如,希 +望观察玩家健康状况变化的插件可以使用类似于以下代码片段的内容: + +```csharp +public class HealthWatcher : IDisposable { + private int? _lastHealth; + + public HealthWatcher() { + Services.Framework.Update += this.OnFrameworkTick; + } + + public void Dispose() { + // Remember to unregister any events you create! + Services.Framework.Update -= this.OnFrameworkTick; + } + + private void OnFrameworkTick() { + var player = Services.ClientState.LocalPlayer; + + if (player == null) return; // Player is not logged in, nothing we can do. + if (player.CurrentHp == this._lastHealth) return; + + this._lastHealth = currentHealth; + PluginLog.Information("The player's health has updated to {health}.", currentHealth); + } +} +``` + +上面的代码片段创建了一个事件处理程序,每个框架刻度运行一次(每帧运行一次)。在每 +个帧中,`OnFrameworkTick()` 方法将检查玩家是否存在,并将其 HP 与缓存值进行比较。 +如果玩家的 HP 与缓存值不同,则会向插件日志发送消息。 + +:::tip + +当您完成事件时,注销事件始终是一个好主意!上面的代码片段通过 `Dispose()` 方法实 +现了这一点,该方法旨在由创建此方法的任何内容调用。 + +当不再需要事件时未注销事件(或者至少在插件卸载时注销事件)意味着代码仍将被调用, +并可能导致意外行为。作为经验法则,对于每个使用 `+=` 订阅的事件,您需要在其他地方 +使用 `-=`。 + +::: + +当然,上面的代码片段和概念可以自由适应。如果插件需要,可以通过每秒检查某些内容来 +观察事件,检查代码可以是(几乎)任何内容:开发人员可以从 Client Structs 提供的 +API 中读取,调用游戏方法或任何必要的计算。 + +### 钩子函数 + +有时,每帧运行代码可能是不可取的。这可能是因为某些事情相对较少发生,或者没有很好 +的方法来轮询特定事件。在这种情况下,插件可以设置一个“钩子”。当插件针对游戏代码中 +的某个方法创建钩子时,该钩子将被调用,*而不是*游戏的原始函数,从而允许插件观察、 +改变甚至取消该方法的执行。 + +:::warning + +重要的是要注意,钩子是一种*高度侵入性*的操作!您正在用自己的代码替换游戏的代码, +这需要采取一定程度的注意。例如,如果您的钩子内部的代码引发异常,您很可能会使游戏 +崩溃。确保您正确处理/管理您的代码可能引发的异常。 + +在大多数情况下,钩子也是*阻塞的*,并且会阻止游戏执行,直到它们返回。确保钩子内部 +的任何代码都是合理的性能,并且不会导致不必要的延迟。 + +::: + +Dalamud 提供了一切必要的插件来创建钩子,使这个过程非常简单。例如,一个想要在任何 +宏更改时得到通知的插件可以钩住 RaptureMacroModule 的 `SetSavePendingFlag`: + +```csharp +public class MyHook : IDisposable { + private delegate void SetSavePendingDelegate(RaptureMacroModule* self, byte needsSave, uint set); + + private readonly Hook? _macroUpdateHook; + + public MyHook() { + var macroUpdateFPtr = RaptureMacroModule.Addresses.SetSavePendingFlag.Value; + this._macroUpdateHook = Hook.FromAddress((nint) macroUpdateFPtr, this.DetourSetSavePending); + this._macroUpdateHook.Enable(); + } + + public void Dispose() { + this._macroUpdateHook.Dispose(); + } + + private nint DetourSetSavePending(RaptureMacroModule* self, byte needsSave, uint set) { + PluginLog.Information("A macro save happened!"); + + try { + // your plugin logic goes here. + } catch (Exception ex) { + PluginLog.Error(ex, "An error occured when handling a macro save event."); + } + + return this._macroUpdateHook.Original(self, needsSave, set); + } +} +``` + +如果要挂钩的函数不在 Client Structs 中,则还可以通过 `SignatureHelper` 进行直接 +签名: + +```csharp +public class MySiggedHook : IDisposable { + private delegate nint SetSavePendingDelegate(RaptureMacroModule* self, byte needsSave, uint set); + + [Signature("45 85 C0 75 04 88 51 3D", DetourName = nameof(DetourSetSavePending))] + private Hook? _macroUpdateHook; + + public MyHook() { + this._macroUpdateHook?.Enable(); + } + + public void Dispose() { + this._macroUpdateHook?.Dispose(); + } + + private nint DetourSetSavePending(RaptureMacroModule* self, byte needsSave, uint set) { + PluginLog.Information("A macro save happened!"); + + try { + // your plugin logic goes here. + } catch (Exception ex) { + PluginLog.Error(ex, "An error occured when handling a macro save event."); + } + + return this._macroUpdateHook!.Original(self, needsSave, set); + } +} +``` + +这两个示例或多或少遵循相同的模式,只有在实际挂钩创建方式上有一些语义差异。但是, +在所有情况下,表示所讨论方法的 `delegate` 必须正确定义。此委托*必须*具有预期的返 +回类型,以及任何预期的参数,而且挂钩方法*必须*适当匹配委托。有关委托是什么以及它 +们如何工作的信息,请向上滚动。 + +与轮询一样,当不再需要时,必须正确处理挂钩。如果不这样做,挂钩函数将继续在挂钩函 +数的位置运行,并可能导致问题或混乱的行为。有许多情况,插件开发人员请求帮助,只是 +意识到他们的旧挂钩仍在生效! + +因为多个插件可能挂钩单个方法(或一个插件可能多次挂钩同一方法!),通常最好的做法 +是不修改参数或中断执行流程。虽然有许多有效的例外情况,但重要的是要意识到其他挂钩 +可能存在,并且可能在您创建的挂钩之前或之后运行。 diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/plugin-submission.md b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/plugin-submission.md new file mode 100644 index 0000000..204f20f --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/plugin-submission.md @@ -0,0 +1,89 @@ +# 插件提交和批准 + +本文介绍了决定哪些插件合并到官方存储库中以及如何确保进入的内容对您和我们都是安全 +的过程。需要知道的是,本文中描述的内容都不适用于自定义存储库 - 我们只能为正式提 +交其插件的开发人员执行此操作。 + +## 技术细节 + +首先,我们需要解释一下官方存储库中插件的提交流程是如何工作的。这有点简化,但这是 +批准流程的基础。 + +- 官方存储库中的所有插件都是开源的,不接受闭源插件。这意味着任何人都可以检查它们 + 的代码,如果他们愿意的话。 +- 插件开发人员通过提交“提交哈希”来提交他们的插件,这是一个指向他们源代码的特定版 + 本的加密哈希。在提交后更改其代码将导致新的哈希,这将要求他们重新提交并再次审核 + 其更改。[^1] +- 然后,云构建系统下载该源代码,构建插件,并输出“差异”,这是对插件进行的所有更改 + 的列表。构建系统没有直接的互联网访问权限,因此插件开发人员无法在构建插件时下载 + 其他代码。 +- 然后,[插件批准团队](#插件批准团队)的成员检查此差异。 + +所有这些结合在一起,使我们能够确保在用户的 PC 上运行的代码是我们批准的代码,并且 +在我们批准之前不会在用户的硬件上运行任何代码。该系统旨在方便开发人员和审核人员, +并且应该很少产生负担 - 开发人员只需要在其项目配置中使用一些特定的变量即可。 + +## 插件批准团队 + +插件批准团队是由 6 名志愿者组成的主观选择小组,他们具有技术、安全意识,自己也是 +插件开发人员,并且在 Dalamud 插件生态系统应该如何工作以及插件应该做什么和不应该 +做什么方面达成共识。 + +他们批准新的插件提交,并审查现有插件的建议更改。 + +## 新提交 + +当插件首次提交时,团队会检查它是否符合一组 +[指南](restrictions#我在插件中可以做什么) 和 +[技术标准](https://github.com/goatcorp/DalamudPluginsD17#approval-criteria)。然 +后,团队对每个新提交的插件进行投票 - 如果插件通过了 4 个赞成票,则它将获得批准并 +出现在存储库中。每个团队成员都可以否决插件,阻止其合并,直到问题得到解决。这种情 +况还没有发生过。 + +最重要的指南包括: + +- 插件不会以自动化的方式与游戏服务器交互,例如轮询数据或在没有用户直接交互的情况 + 下进行请求。 +- 插件不会超出规范范围,例如允许玩家以正常手段无法实现的方式向服务器提交内容。 +- 插件不会干扰战斗,除非它只提供有关您自己的团队成员的信息,而这些信息在其他情况 + 下也是可用的,但以不同的方式表示。 +- 请注意,存储库中有一些不遵守此规则的插件,但它们已被纳入老插件的范畴。我们认为 + 删除它们是愚蠢的,但是这些插件在 Dalamud 的早期阶段就被接受了,我们已经学到了 + 很多。如果它们现在提交,它们可能不会被接受。 +- 插件不会干扰 Square Enix 的经济利益(例如授予 Mog Station 物品的访问权限)。 + +这些指南的存在是因为我们希望改善 Square Enix 提供的体验,而不是损害它或任何其他 +玩家的游戏体验。我们认为它们为插件开发人员提供了很大的自由度,同时鼓励他们保持忠 +于这个目标。 + +技术标准包括彻底的代码审查,插件的正常工作以及它不会上传任何个人数据。所有这些都 +可能需要一段时间,因此新插件在队列中等待一周以上并不罕见 + +- 所有团队成员都是在业余时间做这件事情,因此可能在那之前无法处理它们。 + +我们还要求所有新插件在之前通过插件测试渠道,这将在将插件分发给所有使用 Dalamud +的人之前将其分发给测试用户。这有助于跟踪潜在的问题和错误。 + +## 插件更新 + +插件的更新只需要一个团队成员的批准,这有助于保持队列的小型化。更改的代码会经过仔 +细审核,然后更新的插件将被构建和分发。 + +![What a plugin approval team member sees when they are being called in to review a change to a plugin.](https://user-images.githubusercontent.com/16760685/217103831-de5c1af3-7244-438e-8e8e-7408d2545814.png) + +## 注意事项 + +虽然这一切都很好 - 它一直很好地为我们工作,没有发生任何事故 - 但所有这些工作都是 +由志愿者完成的,他们可能会错过或忽略一些事情。我们不能也不想给您一个 100% 的保证 +,保证一切都会很好,但我们相信我们可以给您一个相当好的保证。 + +由您决定信任谁! + +## 结语 + +我们希望这有助于澄清插件如何进入官方 Dalamud 插件列表。如果您有任何问题或认为这 +里的某些内容可以澄清,请随时联系我们。 + +[^1]: + 从技术上讲,这仍然是可能的,但您需要 NSA 级别的数据中心和大量时间(目前可能 + 需要数百年)来破解 Git 使用的哈希算法。 diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/restrictions.md b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/restrictions.md new file mode 100644 index 0000000..77dce35 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/restrictions.md @@ -0,0 +1,56 @@ +# 限制 + +## 我在插件中可以做什么? + +Dalamud 插件开发本质上干扰了游戏的运行并改变了 Square Enix 所期望的体验。这使得 +确保您的插件不会做任何人类玩家无法做到的事情非常重要;Dalamud 插件应该增强体验, +而不是彻底改变它。 + +请尽可能确保: + +- 您的插件不会以以下方式与游戏服务器交互: + - 自动化,例如轮询数据或在用户没有直接交互的情况下进行请求 + - 超出规范,例如允许玩家提交无法通过正常手段完成的事情 +- 您的插件不会增强、更改或干扰战斗,除非它仅提供有关您自己的队伍或联盟成员的信息 + ,而这些信息在其他情况下也是可用的,但以不同的方式表示。 + - 请注意,存储库中有一些不遵守此规则的插件,但它们已被纳入 grandfathered,类似 + 的插件将不被允许。 +- 您的插件不会干扰 Square Enix 的经济利益(例如授予访问 Mog Station 物品的权限) +- 您的插件不会提供解析、团队记录、DPS 米或类似的功能(即超出玩家传统可用的信息) +- 您的插件不会强依赖于违反插件指南的任何插件 + +如果您不确定您的插件是否会被允许,请在开始工作之前 _请_ 与我们联系。我们不想在您 +已经完成工作之后拒绝您! + +违反这些规则的插件将不会被接受到 Dalamud 插件存储库中,并且您将无法从 Dalamud 社 +区获得支持。 + +## 为什么您不鼓励某些类型的插件? + +> Dalamud 和 XIVLauncher 是我为了在我喜欢的游戏中做一些很酷的事情而制作的,同时 +> 也让其他人有机会这样做,同时使游戏本身更易于访问。我不想对游戏、社区或 Square +> Enix 造成伤害。我们作为一个集体所设定的“可接受”的定义之外的插件会产生分歧和争 +> 议,我们不想参与其中。 +> +> 随着 XIVLauncher 的普及,我的立场已经变得更加狭窄,您可以通过查看添加的一些最 +> 初的插件来注意到这一点。 +> +> 显然,这来自于道德的角度,可能与您的观点不同 - 我做出的规则和决定有时可能看起 +> 来没有理由 - 但我希望最小化 Square Enix 采取行动并拿走我们建立的东西的风险,同 +> 时降低他们的游戏的一般用户体验。 +> +> 我不能也不想控制任何基于我的工作制作免费软件的人,但我希望在创建依赖于 Dalamud +> 的软件时,请您考虑并同情我的观点。 + +- [goat](https://github.com/goaaats),XIVLauncher/Dalamud 的首席开发人员 + +## 有哪些性能限制需要注意? + +通常应该尽量不会对游戏性能产生太大影响,因为这可能会降低玩家的体验并引起其他问题 +。调试性能问题的好方法是通过插件统计信息窗口开始,该窗口可以通过开发菜单 +(`/xldev`)中的 Plugins > Open Plugin Stats 找到。 + +## 插件如何审核和批准? + +[此页面记录了我们的插件提交流程](plugin-submission),以及对每个新提交的插件应用 +的严格审核。如有需要,请随时加入 Discord 并询问更多细节。 diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/reverse-engineering.md b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/reverse-engineering.md new file mode 100644 index 0000000..426c855 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/plugin-development/reverse-engineering.md @@ -0,0 +1,120 @@ +# 逆向工程(针对插件开发人员) + +逆向工程一个应用程序是困难的。逆向工程像《最终幻想 XIV》这样的大型游戏则更加困难 +。有很多移动的部分,而且第一次打开反编译器甚至对经验丰富的开发人员来说都可能令人 +望而生畏。本文档的范围远远不足以教您如何逆向工程游戏,但它至少可以为您提供一些指 +导,让您自己找出问题。 + +## 入门 + +从根本上讲,《最终幻想 XIV》正在您的计算机上运行,这意味着它在本地不断运行游戏代 +码。这些代码负责与服务器通信、渲染场景、绘制 UI 元素、确定玩家处于什么状态以及各 +种其他事情。所有这些都发生在游戏的进程和内存空间中,使用在您的计算机上的 +`ffxiv_dx11.exe` 文件中找到的指令(通常是汇编语言)。 + +然而,这里有一个问题:我们没有《最终幻想 XIV》的源代码。他们提供给我们的最终程序 +非常简化,几乎没有有用的信息。为此,社区创建并维护了一个名为 +[FFXIVClientStructs](https://github.com/aers/FFXIVClientStructs) 的项目,它提供 +了有关游戏内部的一般信息,并提供了一组 C# 绑定,有效地允许插件将游戏用作库。然而 +,这份文档是不完整的,插件开发人员不可避免地需要逆向工程游戏本身来发现新事物。这 +使我们顺利地转到了下一个话题... + +## 如何逆向工程游戏? + +正如之前所提到的,编写一份完整的逆向工程游戏的指南是不可能的。逆向工程是一门非常 +复杂的学科,需要多年的时间来掌握。有先前使用 C/C++ 经验的开发人员可能会有一点优 +势,但最终仍然是一个难题。 + +在逆向工程中,至少有两种主要的方法可以与程序交互,以了解它们的运作方式:**静态分 +析**是指开发人员阅读游戏的反汇编或反编译源代码,通常使用专门设计用于此目的的工具 +;而 **动态分析** 则是使用调试器和内存编辑器创建断点,并在内存更改时进行通知。许 +多开发人员都会大量使用这两种机制,因为其中一种机制通常会为另一种机制提供上下文。 + +### 静态分析 + +静态分析是指阅读程序的反汇编代码,通常使用交互式反汇编器或反编译器。有许多工具可 +帮助完成此过程,例如 [Hex-Rays IDA][ida]、[Ghidra][ghidra] 和 [Binary +Ninja][binja],但还有其他工具。Dalamud 社区的绝大多数人都会使用 IDA 或 Ghidra 进 +行工作,并且大多数工具都是为这两个工具之一而构建的。这两个工具之间没有功能上的区 +别,因此真正取决于开发人员选择哪个更喜欢。 + +一旦您安装并使用了反汇编器(您肯定已经阅读了手册并找到了一些在线教程来了解如何导 +航它,对吧?),就可以加载 `ffxiv_dx11.exe` 并开始探索了。此时,大多数开发人员将 +加载[一些数据文件](https://github.com/aers/FFXIVClientStructs/tree/main/ida),并 +使用它来探索所涉及的程序。 + +[ida]: https://hex-rays.com/ +[ghidra]: https://github.com/NationalSecurityAgency/ghidra +[binja]: https://binary.ninja/ + +### 动态分析 + +与静态分析不同,动态分析是指检查代码的实时运行情况。这通常是像 [Cheat +Engine][cheat-engine]、[x64dbg][x64dbg] 和 [ReClass.NET][reclass-net] 这样的工具 +发挥作用的地方。开发人员通常会使用这些程序来查找有趣的内存地址或在已知数据结构上 +放置断点,以查看游戏代码如何影响内存中的某个位置。 + +某些工具,例如 +[pohky 的 XivReClassPlugin](https://github.com/pohky/XivReClassPlugin),还将一些 +动态分析工具与 ClientStructs 数据库绑定在一起,使开发人员可以更快地访问更多信息 +。 + +[cheat-engine]: https://www.cheatengine.org/ +[x64dbg]: https://x64dbg.com/ +[reclass-net]: https://github.com/ReClassNET/ReClass.NET + +## 函数、偏移和特征 + +正如之前所提到的,最终幻想 XIV 实际上是一个程序。程序通常会遵循从入口点(第一个 +被调用的函数)开始的执行流程,到达其他函数,跨越线程,响应用户输入以及各种其他使 +游戏实际可玩的事情。游戏将在特定的用户操作上调用某个函数,或使用函数来获取一些相 +关数据或执行某些计算,以在 UI 或类似位置中显示。所有这些都是为了说:函数可能是探 +索游戏代码的最关键概念之一。 + +函数存在于程序的内存空间中,从程序的基地址开始的特定偏移量。这些函数偏移量唯一地 +标识了特定的函数,并通常在反编译器和其他工具中表示为 +`ffxiv_dx11.exe+4BC200`[^1],尽管大多数开发人员在相互交流时会将其缩短为 +`4BC200`。但是,函数偏移量并不是固定的。游戏的每个版本都会更改所有函数偏移量,这 +意味着它们对于直接在插件中使用实际上是无用的。相反,开发人员将使用不同(且更稳定 +)的唯一标识符:特征。 + +_特征_ 是一系列特定的字节(表示为十六进制字符串),可唯一标识函数的开始(称为*直 +接特征*)或对特定函数的引用(_间接特征_)。例如,取特征 +`E8 ?? ?? ?? ?? 41 88 84 2C`。这个字符串在游戏的二进制文件中只存在一次,并唯一地 +标识了一个检查玩家是否完成了特定任务的函数。因为特征是指向二进制文件的一部分,所 +以它更加稳定 - 除非 Square Enix 更改了特征所代表的代码,或添加了生成相同特征的新 +代码,否则特征将不会失效。特征通常会持续多个主要补丁。特征可以由熟练的开发人员手 +动制作,也可以使用类似 +[Caraxi 的 SigMaker-x64](https://github.com/Caraxi/SigMaker-x64) 的工具自动生成 +。 + +### 使用游戏函数 + +开发人员可以使用两种主要方法来使用函数:创建一个钩子来拦截函数,或创建一个委托来 +使用该函数。 + +当开发人员想要拦截、修改、取消或以其他方式对函数调用进行操作时,他们将使用钩子。 +例如,一个想要知道用户何时切换状态的插件可能会钩取一个 `UpdateStatus` 函数,并采 +取一些自己的行动。同时,一个想要检查玩家是否完成任务的开发人员将创建一个指向 +`IsQuestCompleted` 函数的委托,以便他们可以随意调用它。 + +在这两种情况下,开发人员需要知道他们正在与之交互的函数的参数列表(和返回类型), +尽管静态分析工具将向开发人员公开此信息[^2]。在许多情况下,并非所有参数都是已知的 +(通常表示为 `a3` 或类似的内容),或者参数可能是指向特定(并且可能未知!)结构的 +指针。 + +## 关于结构和数据类型 + +结构只是 C 结构。我们有时将它们移植到 C# 中,并使用布局。指针数学也是一种技巧。 +我们经常使用 `intptr/nint`。 + +[^1]: + 有时,您还会看到 `1404BC200`。这是 `0x140000000` 的 `/BASE` 加上函数的偏移量 + ,其中基地址是 + [FFXIV 使用的编译器](https://learn.microsoft.com/en-us/cpp/build/reference/base-base-address?view=msvc-170) + 的属性。 + +[^2]: + 静态分析工具将使用 + [x64 调用约定](https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention) + 来确定参数的位置。 diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/versions/index.md b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/versions/index.md new file mode 100644 index 0000000..2ccb5b7 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/versions/index.md @@ -0,0 +1,90 @@ +--- +sidebar_position: 10 +--- + +# 版本和渠道 + +Dalamud 在版本控制方面有两个主要概念:**API 级别**和**渠道/通道**。 + +## 概述 + +| 渠道 | 分支 | API 级别 | 稳定性 | 推荐使用者 | +| ------- | -------- | -------- | ------ | ----------------------------------- | +| Release | `master` | 8 | 最高 | 自动分配给大多数用户 | +| Canary | `master` | 8 | 非常高 | 自动分配给少量用户 | +| Staging | `master` | 8 | 中等 | 核心/插件开发人员、倾向于测试的用户 | +| v9 | `v9` | 9 | 低 | 想要提前了解 v9 的核心/插件开发人员 | + +## API 级别 + +API 级别是一个数字,每当对 Dalamud API 进行破坏性更改时就会递增。这意味着针对旧 +API 级别编译的插件将无法与更新版本的 Dalamud 兼容。 + +### 历史 + +对于 Dalamud v9 及更高版本,API 级别将始终与主版本号匹配 - 即 Dalamud 9.0.0 将具 +有 API 级别 9,Dalamud 10.0.0 将具有 API 级别 10,以此类推。对于当前发布版本的 +Dalamud(7.x),当前的 API 级别为 8。 + +在 Dalamud v9 之前,API 级别与 Dalamud 版本没有直接关系。当对 Dalamud API 进行破 +坏性更改或进行主要游戏补丁时,API 级别会递增。 + +
+历史 API 级别 + +有兴趣回顾 Dalamud 的历史吗?以下是历史 API 级别的最佳表格。 + +:::note + +每个 API 级别的第一个提交/日期不一定是 API 升级发布的时间,而是第一个提交增加 +API 级别的时间。 + +::: + +| API 级别 | 第一个 Dalamud 版本 | 第一个游戏版本 | .NET 版本 | 第一个提交 | +| -------- | ------------------- | -------------- | ---------- | ------------------------------------------------------------------------------------------------- | +| 8 | 7.4.0.0 | Patch 6.3 | .NET 7.0 | [2023-01-10](https://github.com/goatcorp/Dalamud/commit/251359abe92ed805f1163f1a9da28a0aa4f891cb) | +| 7 | 7.0.0.0 | Patch 6.2 | .NET 6.0 | [2022-08-23](https://github.com/goatcorp/Dalamud/commit/6692d560296baab7758a372df10794cdf3717c17) | +| 6 | 6.4.0.0 | Patch 6.1 | .NET 5.0 | [2022-04-13](https://github.com/goatcorp/Dalamud/commit/d9f3800257fe1fa5621b9c13028c06911555889c) | +| 5 | 6.1.0.0 | Patch 6.0 | .NET 5.0 | [2021-12-04](https://github.com/goatcorp/Dalamud/commit/3f4400e67fd7c1a67f0fc86fb283a3ed3fc27304) | +| 4 | 6.0.0.17? | Patch 5.57hf? | .NET 5.0 | [2021-07-12](https://github.com/goatcorp/Dalamud/commit/0cb35619d2907d3cb65fce9be7dd08410fe31b7d) | +| 3 | 5.2.3.5? | Patch 5.45? | .NET 4.7.2 | [2021-04-01](https://github.com/goatcorp/Dalamud/commit/9751a9fed2e948cb4f114da107a7b55416c287bf) | +| 2 | 5.1.1.2? | Patch 5.4? | .NET 4.7.2 | [2020-12-08](https://github.com/goatcorp/Dalamud/commit/04b83f95336ec0ff006febf29b0af0afe2636a65) | +| 1 | 4.9.8.2[^1] | Patch 5.25? | .NET 4.7.2 | [2020-06-11](https://github.com/goatcorp/Dalamud/commit/ad93b6324f921b11c7e7dbd4565023697512c0bf) | + +[^1]: 这是第一个引入 `DALAMUD_API_LEVEL` 常量的提交。越多了解,就越好!✨ + +
+ +## 分支和渠道 + +分支和渠道是两个不同的概念,但它们密切相关。分支是 Dalamud 开发的实际 git 分支, +而渠道控制用户在其客户端上接收的更新。 + +### 渠道 + +Dalamud 有几个发布渠道,每个渠道都有自己的更新节奏和稳定性保证。XIVLauncher 将自 +动更新到您当前所在的渠道的最新版本,但您可以通过 `/xldev` > `Dalamud` 菜单中的“ +分支切换器”选项随时切换渠道。 + +- **Release**:默认渠道。该渠道会更新最新的 Dalamud 稳定版本。该渠道适用于大多数 + 用户。 +- **Canary**:新标记的 Dalamud 发布版本会推送到此渠道。Canary 会自动分配给 + Release 渠道的一小部分用户。该渠道应该与 Release 一样稳定,但其存在有助于我们 + 在新的稳定 Dalamud 发布版本到达全球所有用户之前捕捉到任何严重问题。 +- **Staging (`stg`)**:该渠道会更新到 `master` 的最新提交,而不是标记的发布版本 + 。新功能在移动到 Canary/Release 之前在此处提供。 +- **v9**:该渠道跟踪 `v9` 分支的最新提交。该分支是 Dalamud v9 的当前开发分支,仅 + 建议开发人员使用,因为它具有破坏性更改(包括 API 级别增加) + 。[您可以在此处查看有关 v9 的新内容信息。](v9) + +### 分支 + +渠道并不总是对应于单个分支。例如,Release、Canary 和 Staging 渠道都跟踪 `master` +分支,只是在不同的节奏上。也不是每个分支都有关联的渠道。 + +当前的主要分支包括: + +- `master`:Dalamud 的主要开发分支。该分支用于所有发布,并且是所有拉取请求的默认 + 分支。 +- `v9`:Dalamud v9 的开发分支。 diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/versions/v9.md b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/versions/v9.md new file mode 100644 index 0000000..e937810 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/versions/v9.md @@ -0,0 +1,397 @@ +# Dalamud v9 的新功能 + +Dalamud v9 是 Dalamud 的下一个主要版本,将与 6.5 补丁一起发布。这是变化的高级概 +述。您可以在 [此处](https://github.com/goatcorp/dalamud/compare/master...v9) 查 +看所有这些更改的代码差异。 + +## 关键信息 + +- **分支:** `v9` + ([在 GitHub 上查看](https://github.com/goatcorp/Dalamud/tree/v9)) +- **发布日期:** 目标是与 6.5 补丁一起发布(预计在 2023 年 9 月/10 月)。 +- **API 级别:** 9 +- **.NET 版本:** .NET 7.0 + - .NET 8 预计于 2023 年 11 月正式发布。由于发布时间,我们将针对 v9 使用 .NET + 7.0,并在未来的版本中升级到 .NET 8,以确认它在所有平台(Windows、Wine、OS + X)上的稳定性。 + +## 新功能 + +- 控制台(`/xllog`)已重做,现在可以创建过滤器以过滤到特定插件。 +- 添加了一个新的插件特定的日志类,您可以通过 `IPluginLog` 类型的服务实例接收它。 + - `PluginLog` 中的静态函数已被弃用,我们建议您转移到新的服务。它们很慢且不可靠 + ,因为需要通过反射查找调用插件,并且不支持改进的控制台中的新过滤功能。 + - 我们计划在未来的 API 周期中删除静态函数。 +- 扩展了 `DtrBarEntry`,现在可以允许工具提示和 OnClick 事件,为您的 DTR 条目添加 + 更多功能。 +- 添加了 `IAddonLifecycle` 和 `IAddonEventManager` 服务,大大简化了对插件的访问 + 。 +- 添加了全面的新服务,通过 `ITextureProvider` 从游戏图标/纹理获取 ImGui 纹理,详 + 见下文。 +- 添加了各种新功能,以在 `Dalamud.Interface.ColorHelpers` 中操作 UI 工作的颜色。 + +## 主要更改 + +### 通用 + +- 所有服务必须通过接口使用,它们的实现现在是私有的。 + - 这使我们能够创建每个插件的服务实现,这些实现将能够安全地处理插件特定的资源 - + 这是我们以前从未能够可靠地做到的 - 并打开了创建允许在游戏之外进行测试的模拟 + 的可能性。 + - 我们现在也可以构建依赖于检测使用服务的插件的功能,例如插件范围的日志和可靠的 + 命令列表。 + - 要迁移,您只需要将使用的类型更改为 `Dalamud.Plugin.Services` 命名空间中的接 + 口等效项(例如 `DataManager` => `IDataManager`)。 +- `IDataManager.ClientOpcodes` 和 `IDataManager.ServerOpcodes` 已被删除。直接使 + 用操作码的 Dalamud 系统现在将使用钩子。我们建议使用这些操作码的任何插件切换到 + 钩子。 + +### UI 和 ImGui + +- 添加了 `UiBuilder.OpenMainUi` 事件,您应该订阅它以打开您的插件的“主界面”,如果 + 适用的话 + - 这将显示为插件安装程序中的单独突出显示的按钮,除了已经存在的 `OpenConfigUi` + 按钮 +- 在 DataManager 上的与纹理和图标相关的函数已被删除,并且现在可在新服务 + `ITextureProvider` 中使用。 + - API 已经大大简化,并且现在正确支持高分辨率纹理 + - 使用此服务请求它们时,图标和纹理现在仅在全局加载一次,从而减少了内存使用 + - 如果未在 2 秒内绘制,则图标和纹理现在会自动卸载,并且如果插件再次访问它们, + 则会透明地重新加载 + - 添加了一种 API,允许插件替换 tex 文件的路径。这意味着您不再需要手动实现此功 + 能,以使您的插件与 UI mod 等兼容。 + - ITextureProvider 专为 ImGui 特定用例而设计。需要使用原始 tex 文件的插件应该 + 实现自己的逻辑。 +- 所有与纹理交互的 Dalamud API 现在都返回 `IDalamudTextureWrap`,而 + `IDalamudTextureWrap` 不再从 ImGuiScene 的 `TextureWrap` 类型继承。这样做是为 + 了将公共 API 与 ImGuiScene 解耦,并消除插件引用它的需要。 + - 要迁移,您只需要切换使用的类型。公开的 API 完全相同。 + - 添加了 `IDalamudTextureWrap.Size`(Vector2)。 +- IDisposable ImGui-helpers 已移动到主 Dalamud 组件中,并且现在可以通过 + `Dalamud.Interface.Utility` 命名空间访问 + - 我们建议对于**任何新的 UI**都使用它们,因为它们更安全,减少了 UI 代码不当行 + 为导致崩溃的机会 + +### 钩子 + +- 已删除 `Hook` 的过时构造函数和静态 `Hook.FromX()` 函数。 + - 请使用 `IGameInteropProvider` 服务中的等效函数 + - 添加了 `IGameInteropProvider.HookFromSignature()` +- 已删除 `SignatureHelper.Initialize()`。请使用 + `IGameInteropProvider.InitializeFromAttributes()`。 + +## 次要更改 + +- `Util.HttpClient` 已被删除,以允许插件管理自己的 HTTP 生命周期。 + - 您可以使用 `Dalamud.Networking.Http.HappyEyeballsCallback` 作为 + `SocketsHttpHandler.ConnectCallback`,以启用对双栈服务器的 IPv6 连接处理的改 + 进。 +- `SeStringManager` 已被删除。请改用 `SeStringBuilder`。 +- `ChatHandlers` 已被删除。 + - `ChatHandlers.IsAutoUpdateComplete` 现在可通过 + `DalamudPluginInterface.IsAutoUpdateComplete` 访问 + - `ChatHandlers.MakeItalics()` 可通过 `SeStringBuilder` 访问 +- `Util.CopyTo()` 已被删除,因为它已添加到标准库中作为 `Stream.CopyTo()`。 +- `DalamudPluginInterface.PluginNames` 和 `PluginInternalNames` 已被替换为 + `InstalledPlugins`,提供更多上下文。 +- `FontAwesomeIcon` 中的过时/非功能图标已被删除。 +- `DataManager.IsDataReady` 已被删除,因为在加载插件时它始终为 true。 +- `SeStringBuilder.AddItemLink()` 现在正确添加完整的物品链接,而不仅仅是添加 + `ItemPayload`。 +- `Util.IsLinux()` 已更改为 `Util.IsWine()`。添加了 `Util.GetHostPlatform()` 以 + 获取游戏实际运行的平台 - 这依赖于可能不在 Dalamud 可以运行的所有环境中都存在的 + 特殊 env 变量。 +- Serilog 属性 `SourceContext` 不再用于 Dalamud 系统。实现自己的日志系统的插件应 + 将其插件内部名称写入 `Dalamud.PluginName` 属性中,否则,**新控制台中的过滤将无 + 法工作**。 +- `UIBuilder.GposeActive` 已移至 `IClientState.IsGPosing`,现在更可靠/仅在用户实 + 际处于 GPose 中时才为 true。 +- `IDalamudPlugin.Name` 已被删除。在超过 2 年的时间里,它没有在任何地方显示过。 +- 没有清单的 Dev-Plugins 不再受支持。请拥有清单或使用 DalamudPackager! +- 大多数 `FlyTextKind` 枚举成员已重命名,请参见此提交 + [here](https://github.com/goatcorp/Dalamud/commit/4989e2b69b8ce23dbe01b8a6786267e6a0ed6ea2)。 + +## 勘误 + +这些更改是在官方稳定之后进行的。 + +- `SigScanner` 错误地被设置为 internal,使得插件无法创建自己的扫描器 + - `SigScanner` 以相同的 API 公开,并且内部服务实现已解耦 + +## 贡献者 + +我们要感谢以下人员在此补丁周期内对 Dalamud 的贡献: + +- MidoriKami +- Haselnussbomber +- kalilistic +- Soreepeong +- nebel +- Caraxi +- Ottermandias +- Aireil + +## FFXIVClientStructs 更改 + +这些是对 FFXIVClientStructs 进行的相关更改,供参考。我们要感谢 +aers、Pohky、WilldWolf 和其他 FFXIVClientStructs 贡献者的工作。 + +从 a593cb163e1c5d33b27d34df4d1ccc57d1e67643 开始,截至提交 +0af185ef155cf03f24c2dee8f50f3973a7d417aa: + +Client/Game/ActionManager.cs: + +- Changed UseAction to take a ulong instead of long for targetID +- Changed UseActionLocation to take a ulong instead of long for targetID +- Changed GetActionStatus to take a ulong instead of long for targetID + +Client/Game/ActionTimelineManager.cs + +- Added Parent character pointer +- Added GetHeightAdjustActionTimelineRowId + +Client/Game/Character/Character.cs: + +- Changed ActionRecipientsObjectIdArray to be ulong instead of long +- Added EmoteController +- Added CalculateHeight + +Client/Game/Character/CharacterManager.cs: + +- Changed LookupBattleCharaByObjectId to take a uint instead of int + +Client/Game/Control/TargetSystem.cs: + +- Changed GetCurrentTargetID to return ulong instead of uint +- Changed GameObjectArray.Objects to be ulong instead of long + +Client/Game/InstanceContent/PublicContentDirector.cs: + +- Added HandleEnterContentInfoPacket + +Client/Game/InventoryManager.cs: + +- Changed MoveItemSlot to use ushort instead of uint for slot + +Client/Game/Object/GameObject.cs: + +- Removed long operators + +Client/Game/QuestManager.cs: + +- Removed Obsolete fields and structs + +Client/Game/RetainerManager.cs: + +- Removed Obsolete fields and structs +- Changed RetainerList.Retainer to Retainer +- Changed functions from RetianerList.Retainer to Retainer + +Client/Game/UI/Map.cs: + +- Added MarkerInfo.ShouldRender field +- Removed Obsolete fields and structs + +Client/Graphics/Kernel/Notifier.cs: + +- Changed namespace to match file location + +Client/Graphics/Kernel/Texture.cs: + +- Changed namespace to match file location + +Client/Graphics/Scene/CharacterBase.cs: + +- Changed ColorSetTextures to ColorTableTextures +- Changed ColorSetTexturesSpan to ColorTableTexturesSpan + +Client/System/Framework/Framework.cs: + +- Added TaskManager field + +Client/System/Resource/Handle/ResourceHandle.cs: + +- Added Unknown0A field +- Added Expansion field +- Added UserData field +- Added LoadState field +- Added LoadIntoKernel method +- Added Load method +- Added GetUserData method + +Client/System/Resource/ResourceGraph.cs: + +- Changed ResourceCategory to be a ushort + +Client/UI/AddonLookingForGroupDetail.cs: + +- Added RelayPartyFinderInfoButton field +- Added CategoryImageNode field + +Client/UI/AddonRecipeNote.cs: + +- Changed many Unk fields to AtkTextNode\* + +Client/UI/AddonSalvageItemSelector.cs: + +- Changed ItemsData to Items with a fixed size array + +Client/UI/Agent/AgentContext.cs: + +- Changed ContextMenu.EventParams to be a fixed array + +Client/UI/Agent/AgentFriendList.cs: + +- Added SelectedPlayerName, SelectedContentId and SelectedIndex fields + +Client/UI/Agent/AgentHudLayout.cs: + +- Changed namespace to FFXIVClientStructs.FFXIV.Client.UI.Agent + +Client/UI/Agent/AgentReadyCheck.cs: + +- Changed ReadyCheckEntries from FixedArray to FixedSizeArray + +Client/UI/Agent/AgentRetainerList.cs: + +- Changed Retainers to fixed array + +Client/UI/Agent/AgentSalvage.cs: + +- Changed DesynthResult to DesynthResults and changed FixedArray to + FixedSizeArray + +Client/UI/Info/InfoProxyCommonList.cs: + +- Changed Data to CharData +- Changed ContentID to ulong +- Changed GetContentIDForEntry to return ulong +- Removed CharIndex field from InfoProxyCommonList +- Removed CharacterDict and CharacterArray from InfoProxyCommonList +- Moved OnlineStatus, MentorState, PartyStatus, DutyStatus to single + OnlineStatus enum linked to bitflag of OnlineStatus.exd +- Added Sort field +- Added ExtraFlags field + +Client/UI/Info/InfoProxyCatalogSearch.cs: + +- Was InfoProxyItemSearch + +Client/UI/Info/InfoProxyItemSearch.cs: + +- Was InfoProxy11 +- Changed SelectedItemId to SearchItemId +- Changed GlobalItemId to ListingId +- Added RetainerListings +- Added RetainerListingsCount +- Added PlayerRetainers + +Client/UI/Misc/CharaViewPortrait.cs: + +- Changed DirectionalLightingVerticalAngle and + DirectionalLightingHorizontalAngle to be signed + +Client/UI/Misc/ItemOrderModule.cs: + +- Changed RetainerSorter to be a `StdMap>` +- Removed RetainerSorterCount + +Client/UI/Misc/RaptureGearsetModule.cs: + +- Changed Gearset to be a fixed array named Entries +- Changed IsValidGearset to return bool instead of byte +- Changed CreateGearset to return sbyte instead of uint +- Changed HasLinkedGlamourPlate to return bool instead of byte +- Changed IsGearsetLinkedWithBanner to HasLinkedBanner +- Changed GetBannerIndexByGearsetIndex to GetBannerIndex +- Changed SetBannerIndexForGearsetIndex to SetBannerIndex +- Added FindGearsetIDByName +- Added GearsetItem.Flags field +- Changed GearsetEntry.RightLeft to be named RingLeft + +Client/UI/Misc/RaptureMacroModule.cs: + +- Changed Instance to be a method instead of a property +- Changed Individual and Shared to be fixed arrays +- Added RaptureTextModule and TextChecker fields + +Client/UI/Misc/RetainerCommentModule.cs: + +- Changed Retainers to fixed array +- Changed SetComment to return void instead of void\* + +Client/UI/Shell/RaptureShellModule.cs: + +- Changed Instance to be a method instead of a property + +Client/UI/UIModule.cs: + +- Changed GetUIInputData to return UIInputData\* + +Component/GUI/AtkComponentDragDrop.cs: + +- Added AtkDragDropInterface field + +Component/GUI/AtkComponentListItemRenderer.cs: + +- Added AtkDragDropInterface field + +Component/GUI/AtkDragDropManager.cs: + +- Added DragDrop1, DragDrop2 and DragDropS pointer fields +- Added IsDragging, ReclickToDrop, MouseMoved and IsNotDiscarding bool fields + +Component/GUI/AtkLinkedList.cs: + +- Obsoleted AtkLinkedList in favor of StdLinkedList + +Component/GUI/AtkModule.cs: + +- Added AtkTextureResourceManager field +- Changed DefaultTextureVersion to + AtkTextureResourceManager.DefaultTextureVersion +- Changed ExdModule to AtkTextureResourceManager.ExdModule + +Component/GUI/AtkStage.cs: + +- Added AtkTextureResourceManager field + +Component/GUI/AtkTextureResource.cs: + +- Changed Count_1 to Count +- Changed Count_2 to Version +- Added ShareCount field + +Component/GUI/AtkUnitBase.cs: + +- Added ShowHideFlags field +- Changed Show and Hide paramenters to match the game + +Component/GUI/AtkUnitList.cs: + +- Changed AtkUnitEntries to fixed array named Entries; +- Changed Count to ushort + +STD/Pair.cs: + +- Added Deconstruct method to StdPair + +New Files Added: + +- Component/GUI/AtkDragDropInterface.cs +- Component/GUI/AtkTextureResourceManager.cs +- Client/UI/UIInputData.cs +- Client/UI/Agent/AgentScenarioTree.cs +- Client/UI/Agent/AgentMycBattleAreaInfo.cs +- Client/UI/Agent/AgentMiragePrismPrismItemDetail.cs +- Client/UI/Agent/AgentItemDetail.cs +- Client/System/Framework/TaskManager.cs +- Client/System/Framework/Task.cs +- Client/System/Framework/RootTask.cs +- Application/Network/WorkDefinitions/EnterContentInfo.cs +- Client/Game/Control/EmoteController.cs +- Client/Game/Conditions.cs + +Changed All Agents to use the new AgentGettersGenerator + +Changed All VTable to vtbl diff --git a/i18n/zh-Hans/docusaurus-theme-classic/footer.json b/i18n/zh-Hans/docusaurus-theme-classic/footer.json new file mode 100644 index 0000000..7d0042b --- /dev/null +++ b/i18n/zh-Hans/docusaurus-theme-classic/footer.json @@ -0,0 +1,22 @@ +{ + "link.title.Community": { + "message": "Community", + "description": "The title of the footer links column with title=Community in the footer" + }, + "link.title.More": { + "message": "More", + "description": "The title of the footer links column with title=More in the footer" + }, + "link.item.label.Discord": { + "message": "Discord", + "description": "The label of footer link with label=Discord linking to https://discord.gg/holdshift" + }, + "link.item.label.GitHub": { + "message": "GitHub", + "description": "The label of footer link with label=GitHub linking to https://github.com/goatcorp/Dalamud" + }, + "copyright": { + "message": "Final Fantasy XIV © 2010-2023 SQUARE ENIX CO., LTD. All Rights Reserved. We are not affiliated with SQUARE ENIX CO., LTD. in any way.", + "description": "The footer copyright" + } +} diff --git a/i18n/zh-Hans/docusaurus-theme-classic/navbar.json b/i18n/zh-Hans/docusaurus-theme-classic/navbar.json new file mode 100644 index 0000000..39cd79d --- /dev/null +++ b/i18n/zh-Hans/docusaurus-theme-classic/navbar.json @@ -0,0 +1,22 @@ +{ + "title": { + "message": "Dalamud.dev", + "description": "The title in the navbar" + }, + "logo.alt": { + "message": "Dalamud logo", + "description": "The alt text of navbar logo" + }, + "item.label.Docs": { + "message": "Docs", + "description": "Navbar item with label Docs" + }, + "item.label.API": { + "message": "API", + "description": "Navbar item with label API" + }, + "item.label.News": { + "message": "News", + "description": "Navbar item with label News" + } +} diff --git a/package.json b/package.json index 19b2396..ca8ad03 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "devDependencies": { "@docusaurus/module-type-aliases": "3.0.0-alpha.0", "@docusaurus/tsconfig": "3.0.0-alpha.0", + "@docusaurus/types": "3.0.0-alpha.0", "@octokit/rest": "^19.0.13", "@swc/core": "^1.3.64", "@typescript-eslint/eslint-plugin": "^5.60.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5395f5..57db81b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,6 +34,9 @@ devDependencies: '@docusaurus/tsconfig': specifier: 3.0.0-alpha.0 version: 3.0.0-alpha.0 + '@docusaurus/types': + specifier: 3.0.0-alpha.0 + version: 3.0.0-alpha.0(@swc/core@1.3.64)(react-dom@18.0.0)(react@18.0.0) '@octokit/rest': specifier: ^19.0.13 version: 19.0.13