[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: stackotter\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Logs**\nIf applicable, obtain the logs at `~/Library/Application Support/dev.stackotter.delta-client/logs/delta-client.log`\nand attach the relevant part here (probably the last few lines). This is made easy by the `View > Logs`\nmenu bar item.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Extra information (please complete the following information**\n- OS: [e.g. macOS 11.5.1]\n- Hardware: [e.g. M1 MacBook Air]\n- Build: [e.g. commit 060e29e or Snapshot 9]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "# Description\n\nPlease include a summary of the change and which issue is fixed. Please also include relevant motivation and context.\n\nFixes # (issue) (delete if these changes aren't for fixing an issue)\n\n## Type of change\n\nPlease delete options that are not relevant.\n\n- [ ] Bug fix (non-breaking change which fixes an issue)\n- [ ] New feature (non-breaking change which adds functionality)\n- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)\n- [ ] This change requires a documentation update\n\n# Checklist:\n\n- [ ] My code follows the style guidelines of this project\n- [ ] I have performed a self-review of my own code\n- [ ] I have commented my code, particularly in hard-to-understand areas\n- [ ] I have made corresponding changes to the documentation comments\n- [ ] My changes generate no new warnings\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  build-macos:\n    runs-on: macOS-15\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: List Xcodes\n        run: ls /Applications\n      - name: Force Xcode 16.4\n        run: sudo xcode-select -switch /Applications/Xcode_16.4.app\n      - name: Version\n        run: swift --version\n\n      - name: Cache swift-bundler\n        id: cache-swift-bundler\n        uses: actions/cache@v5\n        with:\n          path: sbun\n          key: ${{ runner.os }}-swift-bundler\n      - name: Build swift-bundler\n        if: steps.cache-swift-bundler.outputs.cache-hit != 'true'\n        run: |\n          git clone https://github.com/moreSwift/swift-bundler\n          cd swift-bundler\n          git checkout 6d72c4f442cc2c57c2559f3df21b3777b1d0a917\n          swift build --product swift-bundler\n          cp .build/debug/swift-bundler ../sbun\n\n      - name: Build arm64\n        run: |\n          ./sbun bundle -c release --arch arm64\n          mv \"$(./sbun bundle --show-bundle-path)\" DeltaClient-arm64.app\n      - name: Build x86\n        run: |\n          ./sbun bundle -c release --arch x86_64\n          mv \"$(./sbun bundle --show-bundle-path)\" DeltaClient-x86_64.app\n      - name: Zip .app (arm64)\n        run: zip -r DeltaClient-arm64.zip DeltaClient-arm64.app\n      - name: Zip .app (x86_64)\n        run: zip -r DeltaClient-x86_64.zip DeltaClient-x86_64.app\n      - name: Upload artifact (arm64)\n        uses: actions/upload-artifact@v7\n        with:\n          name: DeltaClient-arm64\n          path: ./DeltaClient-arm64.zip\n      - name: Upload artifact (x86_64)\n        uses: actions/upload-artifact@v7\n        with:\n          name: DeltaClient-x86_64\n          path: ./DeltaClient-x86_64.zip\n  build-linux:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Setup Swift\n        uses: SwiftyLab/setup-swift@latest\n        with:\n          swift-version: \"5.9\"\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Build\n        run: |\n          cd Sources/Core\n          swift build\n"
  },
  {
    "path": ".github/workflows/swiftlint.yml",
    "content": "name: Lint\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  swift-lint:\n    runs-on: macOS-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Lint\n        run: |\n          URL=\"https://github.com/realm/SwiftLint/releases/download/0.50.3/portable_swiftlint.zip\"\n          curl -LO \"$URL\"\n          unzip portable_swiftlint -d ./swiftlint\n          rm -rf portable_swiftlint.zip\n          echo \"Linting with swiftlint $(./swiftlint/swiftlint version)\"\n          ./swiftlint/swiftlint lint --reporter github-actions-logging\n        shell: bash\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  test-macos:\n    runs-on: macOS-15\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Force Xcode 16.4\n        run: sudo xcode-select -switch /Applications/Xcode_16.4.app\n      - name: Version\n        run: swift --version\n      - name: Test\n        run: |\n          cd Sources/Core\n          swift test\n  test-linux:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Setup Swift\n        uses: SwiftyLab/setup-swift@latest\n        with:\n          swift-version: \"5.9\"\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Test\n        run: |\n          cd Sources/Core\n          swift test\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n.build\n.docc-build\n/Packages\n*.xcodeproj\nxcuserdata/\n*.xcworkspace\n/DeltaClient.app\n/.swiftpm\n/.vscode\n.swiftpm\n/assets\n/cache\n/registry\n"
  },
  {
    "path": ".swift-format",
    "content": "{\n    \"version\": 1,\n    \"indentation\": {\n        \"spaces\": 2\n    },\n    \"indentSwitchCaseLabels\": true\n}\n"
  },
  {
    "path": ".swiftlint.yml",
    "content": "excluded:\n- \"**/.build\"\n- \"Sources/Core/Sources/Cache/Protobuf/Generated/BlockRegistry.pb.swift\"\n- \"Sources/Core/Sources/Cache/Protobuf/Generated/BlockModelPalette.pb.swift\"\n# TODO: Reexclude once swiftlint github action is updated to support globs\n# - \"**/*.pb.swift\"\n\ndisabled_rules:\n- switch_case_alignment\n- trailing_whitespace\n- identifier_name\n- opening_brace # Has false positives in the code base\n\n# TODO: Re-enable these rules later once more warnings are fixed\n- nesting\n- todo\n\nline_length: 160\ntype_body_length: 300\nfunction_parameter_count: 8\nfile_length: 500\nfunction_body_length: 50\ncyclomatic_complexity:\n  error: 40 # TODO: reset once more occurrences are fixed\n\n# Custom rules\ncustom_rules:\n  comments_space: # from https://github.com/brandenr/swiftlintconfig\n    name: \"Space After Comment\"\n    regex: '(^ *//\\w+)'\n    message: \"There should be a space after //\"\n    severity: error\n\n# TODO: Once there are less warnings and violations, make the rules stricter and add some opt in ones\n"
  },
  {
    "path": "Bundler.toml",
    "content": "format_version = 2\n\n[apps.DeltaClient]\nproduct = 'DeltaClient'\nversion = 'v0.1.0-alpha.1'\nidentifier = 'dev.stackotter.delta-client'\ncategory = 'public.app-category.games'\nicon = 'AppIcon.icns'\n\n[apps.DeltaClient.plist]\n# Append the current commit hash to the user-facing version string\nCFBundleShortVersionString = \"$(VERSION), commit: $(COMMIT_HASH)\"\nGCSupportsControllerUserInteraction = \"True\"\nMetalCaptureEnabled = true\n"
  },
  {
    "path": "Contributing.md",
    "content": "# Contributing\n\nDelta Client is completely open source and I welcome contributions of all kinds. If you're\ninterested in contributing, you can checkout the [delta client issues](https://github.com/stackotter/delta-client/issues)\nand [delta client website issues](https://github.com/stackotter/delta-website) ( on GitHub for some\ntasks. Some of the tasks don't even require Swift! But before you get too excited, make sure that\nyou've read the contributing guidelines, and make sure that your contributions follow them. If you\nneed any help with a contribution, feel free to join the [Discord](https://discord.gg/xZPyDbmR6k)\nand chat :)\n\nNote: when you've decided on an issue to work on, please leave a comment on it so that everyone else\nknows not to work on it.\n\n## Guidelines\n\nIf your contributions follow these guidelines, they'll be much more likely to get accepted first try\n:thumbsup:\n\n1. Make sure your indent size matches the repository (in the case of delta-client that's 2 spaces).\n2. Be conscious of copyright and don't include any files distributed by Mojang in these\n   repositories.\n3. Be concise, only make changes that are required to achieve your end goal. The smaller your pull\n   request, the faster I'll get around to reviewing it.\n7. Add documentation comments for any methods or properties you create unless their usage is\n   completely self-evident. Be sure to also document potentially unexpected side-effects or\n   non-obvious requirements of methods.\n4. Remove file headers from all files you create, they're unnecessary. `swift-bundler` will\n   automatically remove them for you whenever you build.\n5. Use `log` for logging (not just print statements everywhere).\n6. If in doubt, consult [Google's Swift style guide](https://google.github.io/swift/#function-declarations)\n   because that's the one I try to follow.\n\n## Getting setup\n\n### Delta Client\n\n**Important**: Only Xcode 14+ is supported, Xcode 12 builds don't work because Delta Client uses new\nautomatic `Codable` conformance and there are some weird discrepancies between Xcode 12's swift\ncompiler and Xcode 14+'s swift compiler. Xcode 13 isn't supported because it causes some weird memory\ncorruption issues.\n\n[Delta Client](https://github.com/stackotter/delta-client) uses the\n[swift-bundler](https://github.com/stackotter/swift-bundler) build system so make sure you install\nthat.\n\nYou can use any ide you want to work on Delta Client (vscode and xcode are the best supported), but\nyou'll need Xcode installed anyway because sadly that's currently the only way to get Metal and\nSwiftUI build support.\n\nNext, fork Delta Client and then clone it.\n\n```sh\ngit clone [url of your delta-client fork]\ncd delta-client\n```\n\nTo run Delta Client, you can run `swift bundler run -c release`. If you are working on the UI you\ncan leave out the `-c release`, it is only required when working with computationally intense parts\nof the client such as rendering, because without optimizations those parts of the client are\nunusable. See the [swift-bundler repo](https://github.com/stackotter/swift-bundler) for more\ncommands and options.\n\nIf you are using Xcode as your IDE run `swift bundler generate-xcode-support` and then open\nPackage.swift with Xcode (`open Package.swift` should work unless you've changed your default\nprogram for opening swift files). **Make sure to choose the `DeltaClient` target in the top bar\ninstead of the `DeltaClient-Package` target.**\n\n**Note: Xcode puts DeltaCore in the dependencies section of the file navigator instead of at its\nphysical location in the directory structure. This is due to the way the plugin system had to be\nimplemented.**\n\nYou can now make changes and when you're done, just open a pull request on GitHub.\n\n### Website\n\nThe [website](https://delta.stackotter.dev) is built with svelte. Just follow these steps to get\nstarted;\n\n1. Fork and clone [delta-website](https://github.com/stackotter/delta-website) \n2. Run `npm i`\n3. Run `npm run dev` to start a development server. Whenever you save changes to a file, the page\n   will almost instantly reload with the changes, and most of the time it'll retain its state too\n   (pretty cool)\n"
  },
  {
    "path": "LICENSE",
    "content": "### GNU GENERAL PUBLIC LICENSE\n\nVersion 3, 29 June 2007\n\nCopyright (C) 2007 Free Software Foundation, Inc.\n<https://fsf.org/>\n\nEveryone is permitted to copy and distribute verbatim copies of this\nlicense document, but changing it is not allowed.\n\n### Preamble\n\nThe GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\nThe licenses for most software and other practical works are designed\nto take away your freedom to share and change the works. By contrast,\nthe GNU General Public License is intended to guarantee your freedom\nto share and change all versions of a program--to make sure it remains\nfree software for all its users. We, the Free Software Foundation, use\nthe GNU General Public License for most of our software; it applies\nalso to any other work released this way by its authors. You can apply\nit to your programs, too.\n\nWhen we speak of free software, we are referring to freedom, not\nprice. Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\nTo protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights. Therefore, you\nhave certain responsibilities if you distribute copies of the\nsoftware, or if you modify it: responsibilities to respect the freedom\nof others.\n\nFor example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received. You must make sure that they, too, receive\nor can get the source code. And you must show them these terms so they\nknow their rights.\n\nDevelopers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\nFor the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software. For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\nSome devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the\nmanufacturer can do so. This is fundamentally incompatible with the\naim of protecting users' freedom to change the software. The\nsystematic pattern of such abuse occurs in the area of products for\nindividuals to use, which is precisely where it is most unacceptable.\nTherefore, we have designed this version of the GPL to prohibit the\npractice for those products. If such problems arise substantially in\nother domains, we stand ready to extend this provision to those\ndomains in future versions of the GPL, as needed to protect the\nfreedom of users.\n\nFinally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish\nto avoid the special danger that patents applied to a free program\ncould make it effectively proprietary. To prevent this, the GPL\nassures that patents cannot be used to render the program non-free.\n\nThe precise terms and conditions for copying, distribution and\nmodification follow.\n\n### TERMS AND CONDITIONS\n\n#### 0. Definitions.\n\n\"This License\" refers to version 3 of the GNU General Public License.\n\n\"Copyright\" also means copyright-like laws that apply to other kinds\nof works, such as semiconductor masks.\n\n\"The Program\" refers to any copyrightable work licensed under this\nLicense. Each licensee is addressed as \"you\". \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\nTo \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of\nan exact copy. The resulting work is called a \"modified version\" of\nthe earlier work or a work \"based on\" the earlier work.\n\nA \"covered work\" means either the unmodified Program or a work based\non the Program.\n\nTo \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy. Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\nTo \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies. Mere interaction with a user\nthrough a computer network, with no transfer of a copy, is not\nconveying.\n\nAn interactive user interface displays \"Appropriate Legal Notices\" to\nthe extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License. If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n#### 1. Source Code.\n\nThe \"source code\" for a work means the preferred form of the work for\nmaking modifications to it. \"Object code\" means any non-source form of\na work.\n\nA \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\nThe \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form. A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\nThe \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities. However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work. For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\nThe Corresponding Source need not include anything that users can\nregenerate automatically from other parts of the Corresponding Source.\n\nThe Corresponding Source for a work in source code form is that same\nwork.\n\n#### 2. Basic Permissions.\n\nAll rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met. This License explicitly affirms your unlimited\npermission to run the unmodified Program. The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work. This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\nYou may make, run and propagate covered works that you do not convey,\nwithout conditions so long as your license otherwise remains in force.\nYou may convey covered works to others for the sole purpose of having\nthem make modifications exclusively for you, or provide you with\nfacilities for running those works, provided that you comply with the\nterms of this License in conveying all material for which you do not\ncontrol copyright. Those thus making or running the covered works for\nyou must do so exclusively on your behalf, under your direction and\ncontrol, on terms that prohibit them from making any copies of your\ncopyrighted material outside their relationship with you.\n\nConveying under any other circumstances is permitted solely under the\nconditions stated below. Sublicensing is not allowed; section 10 makes\nit unnecessary.\n\n#### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\nNo covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\nWhen you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such\ncircumvention is effected by exercising rights under this License with\nrespect to the covered work, and you disclaim any intention to limit\noperation or modification of the work as a means of enforcing, against\nthe work's users, your or third parties' legal rights to forbid\ncircumvention of technological measures.\n\n#### 4. Conveying Verbatim Copies.\n\nYou may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\nYou may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n#### 5. Conveying Modified Source Versions.\n\nYou may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these\nconditions:\n\n-   a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n-   b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under\n    section 7. This requirement modifies the requirement in section 4\n    to \"keep intact all notices\".\n-   c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy. This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged. This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n-   d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\nA compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit. Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n#### 6. Conveying Non-Source Forms.\n\nYou may convey a covered work in object code form under the terms of\nsections 4 and 5, provided that you also convey the machine-readable\nCorresponding Source under the terms of this License, in one of these\nways:\n\n-   a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n-   b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the Corresponding\n    Source from a network server at no charge.\n-   c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source. This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n-   d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge. You need not require recipients to copy the\n    Corresponding Source along with the object code. If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source. Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n-   e) Convey the object code using peer-to-peer transmission,\n    provided you inform other peers where the object code and\n    Corresponding Source of the work are being offered to the general\n    public at no charge under subsection 6d.\n\nA separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\nA \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal,\nfamily, or household purposes, or (2) anything designed or sold for\nincorporation into a dwelling. In determining whether a product is a\nconsumer product, doubtful cases shall be resolved in favor of\ncoverage. For a particular product received by a particular user,\n\"normally used\" refers to a typical or common use of that class of\nproduct, regardless of the status of the particular user or of the way\nin which the particular user actually uses, or expects or is expected\nto use, the product. A product is a consumer product regardless of\nwhether the product has substantial commercial, industrial or\nnon-consumer uses, unless such uses represent the only significant\nmode of use of the product.\n\n\"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to\ninstall and execute modified versions of a covered work in that User\nProduct from a modified version of its Corresponding Source. The\ninformation must suffice to ensure that the continued functioning of\nthe modified object code is in no case prevented or interfered with\nsolely because modification has been made.\n\nIf you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information. But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\nThe requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or\nupdates for a work that has been modified or installed by the\nrecipient, or for the User Product in which it has been modified or\ninstalled. Access to a network may be denied when the modification\nitself materially and adversely affects the operation of the network\nor violates the rules and protocols for communication across the\nnetwork.\n\nCorresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n#### 7. Additional Terms.\n\n\"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law. If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\nWhen you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit. (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.) You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\nNotwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders\nof that material) supplement the terms of this License with terms:\n\n-   a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n-   b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n-   c) Prohibiting misrepresentation of the origin of that material,\n    or requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n-   d) Limiting the use for publicity purposes of names of licensors\n    or authors of the material; or\n-   e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n-   f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions\n    of it) with contractual assumptions of liability to the recipient,\n    for any liability that these contractual assumptions directly\n    impose on those licensors and authors.\n\nAll other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10. If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term. If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\nIf you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\nAdditional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions; the\nabove requirements apply either way.\n\n#### 8. Termination.\n\nYou may not propagate or modify a covered work except as expressly\nprovided under this License. Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\nHowever, if you cease all violation of this License, then your license\nfrom a particular copyright holder is reinstated (a) provisionally,\nunless and until the copyright holder explicitly and finally\nterminates your license, and (b) permanently, if the copyright holder\nfails to notify you of the violation by some reasonable means prior to\n60 days after the cessation.\n\nMoreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\nTermination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License. If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n#### 9. Acceptance Not Required for Having Copies.\n\nYou are not required to accept this License in order to receive or run\na copy of the Program. Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance. However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work. These actions infringe copyright if you do\nnot accept this License. Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n#### 10. Automatic Licensing of Downstream Recipients.\n\nEach time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License. You are not responsible\nfor enforcing compliance by third parties with this License.\n\nAn \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations. If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\nYou may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License. For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n#### 11. Patents.\n\nA \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based. The\nwork thus licensed is called the contributor's \"contributor version\".\n\nA contributor's \"essential patent claims\" are all patent claims owned\nor controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version. For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\nEach contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\nIn the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement). To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\nIf you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients. \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\nIf, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\nA patent license is \"discriminatory\" if it does not include within the\nscope of its coverage, prohibits the exercise of, or is conditioned on\nthe non-exercise of one or more of the rights that are specifically\ngranted under this License. You may not convey a covered work if you\nare a party to an arrangement with a third party that is in the\nbusiness of distributing software, under which you make payment to the\nthird party based on the extent of your activity of conveying the\nwork, and under which the third party grants, to any of the parties\nwho would receive the covered work from you, a discriminatory patent\nlicense (a) in connection with copies of the covered work conveyed by\nyou (or copies made from those copies), or (b) primarily for and in\nconnection with specific products or compilations that contain the\ncovered work, unless you entered into that arrangement, or that patent\nlicense was granted, prior to 28 March 2007.\n\nNothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n#### 12. No Surrender of Others' Freedom.\n\nIf conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License. If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under\nthis License and any other pertinent obligations, then as a\nconsequence you may not convey it at all. For example, if you agree to\nterms that obligate you to collect a royalty for further conveying\nfrom those to whom you convey the Program, the only way you could\nsatisfy both those terms and this License would be to refrain entirely\nfrom conveying the Program.\n\n#### 13. Use with the GNU Affero General Public License.\n\nNotwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work. The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n#### 14. Revised Versions of this License.\n\nThe Free Software Foundation may publish revised and/or new versions\nof the GNU General Public License from time to time. Such new versions\nwill be similar in spirit to the present version, but may differ in\ndetail to address new problems or concerns.\n\nEach version is given a distinguishing version number. If the Program\nspecifies that a certain numbered version of the GNU General Public\nLicense \"or any later version\" applies to it, you have the option of\nfollowing the terms and conditions either of that numbered version or\nof any later version published by the Free Software Foundation. If the\nProgram does not specify a version number of the GNU General Public\nLicense, you may choose any version ever published by the Free\nSoftware Foundation.\n\nIf the Program specifies that a proxy can decide which future versions\nof the GNU General Public License can be used, that proxy's public\nstatement of acceptance of a version permanently authorizes you to\nchoose that version for the Program.\n\nLater license versions may give you additional or different\npermissions. However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n#### 15. Disclaimer of Warranty.\n\nTHERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT\nWARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND\nPERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE\nDEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR\nCORRECTION.\n\n#### 16. Limitation of Liability.\n\nIN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR\nCONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES\nARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT\nNOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR\nLOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM\nTO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER\nPARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n\n#### 17. Interpretation of Sections 15 and 16.\n\nIf the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\nEND OF TERMS AND CONDITIONS\n\n### How to Apply These Terms to Your New Programs\n\nIf you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these\nterms.\n\nTo do so, attach the following notices to the program. It is safest to\nattach them to the start of each source file to most effectively state\nthe exclusion of warranty; and each file should have at least the\n\"copyright\" line and a pointer to where the full notice is found.\n\n        <one line to give the program's name and a brief idea of what it does.>\n        Copyright (C) <year>  <name of author>\n\n        This program is free software: you can redistribute it and/or modify\n        it under the terms of the GNU General Public License as published by\n        the Free Software Foundation, either version 3 of the License, or\n        (at your option) any later version.\n\n        This program is distributed in the hope that it will be useful,\n        but WITHOUT ANY WARRANTY; without even the implied warranty of\n        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n        GNU General Public License for more details.\n\n        You should have received a copy of the GNU General Public License\n        along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper\nmail.\n\nIf the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n        <program>  Copyright (C) <year>  <name of author>\n        This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n        This is free software, and you are welcome to redistribute it\n        under certain conditions; type `show c' for details.\n\nThe hypothetical commands \\`show w' and \\`show c' should show the\nappropriate parts of the General Public License. Of course, your\nprogram's commands might be different; for a GUI interface, you would\nuse an \"about box\".\n\nYou should also get your employer (if you work as a programmer) or\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary. For more information on this, and how to apply and follow\nthe GNU GPL, see <https://www.gnu.org/licenses/>.\n\nThe GNU General Public License does not permit incorporating your\nprogram into proprietary programs. If your program is a subroutine\nlibrary, you may consider it more useful to permit linking proprietary\napplications with the library. If this is what you want to do, use the\nGNU Lesser General Public License instead of this License. But first,\nplease read <https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "Notes/Caching.md",
    "content": "# Caching\n\nDelta Client uses caching to speed up launches by orders of magnitude. The only thing I cache at the moment are block models because these can take up to 30 seconds to load from the JSON model descriptors with my current method (optimisation attempts are very welcome). To decide which serialization technique to use I created a test project and implemented multiple methods of caching the vanilla block model palette. Below is my interpretation of the [results](#the-numbers);\n\nThe first implementation was made using [Flatbuffers](https://github.com/google/flatbuffers) and the other was made using [Protobuf](https://github.com/protocolbuffers/protobuf). In release builds, Flatbuffers was faster at deserialization but slower than serialization. In this use case, deserialization performance is really the only thing we care about. To an end user only release build performance matters but when I'm developing the client, debug build performance greatly affects development speed. Interestingly, in debug builds, the fastest at serialization was Flatbuffers (by quite a bit), and Protobuf was over 3 times faster than Flatbuffers at deserialization.\n\nNow that I understood how Flatbuffers worked, I created an implementation that took a similar approach to my neater Protobuf-base implementation. This new Flatbuffers-based implementation was close enough to Protobuf deserialization speed in debug builds, and the serialization speed was pretty much the same as the original Flatbuffers-based implementation. However, in release builds deserialization speeds were now almost twice as slow as Protobuf and serialization speeds were also the slowest out of all three. Somehow, a change that almost doubled debug build performance significantly slowed down release performance?\n\nI have also previously tried out FastBinaryEncoding but it was quite a bit slower at deserialization (more like 2 seconds in release builds). To its credit, it did manage to encode it all into only 13mb which is pretty impressive. But that matters less than performance to me.\n\n## The verdict\n\nFor now I will be using Protobufs for caching because they are nicer to use and their performance is more consistent between debug and release builds. They also have the fastest debug build deserialization speed which is what I really care about right now, and the release build serialization speed wasn't too far off the fastest solution in the grand scheme of things. It also has the fastest release build serialization by far. It's only main downfall is that its debug build serialization speed is dismal. Oh, and It also had the smallest serialized size as an added bonus.\n\n## The numbers\n\n### Flatbuffers (with messy code)\n\nSerialized size: 21601644 bytes\n\n| Operation   | Debug   | Release |\n| ----------- | ------- | ------- |\n| Serialize   | 17676ms | 2185ms  |\n| Deserialize | 23190ms | 368ms   |\n\n### Flatbuffers, cleaner code\n\nSerialized size: 21601644 bytes\n\n| Operation   | Debug   | Release |\n| ----------- | ------- | ------- |\n| Serialize   | 18661ms | 2601ms  |\n| Deserialize | 11760ms | 972ms   |\n\n### Protobuf\n\nSerialized size: 16630993 bytes\n\n| Operation   | Debug   | Release |\n| ----------- | ------- | ------- |\n| Serialize   | 29436ms | 1219ms  |\n| Deserialize | 7490ms  | 551ms   |\n\n## Sticky encoding\n\n[Sticky encoding](https://github.com/stickytools/sticky-encoding) looks quite enticing too so I tried it out. All it requires is adding Codable conformance to the types that need to be serialized. However, I tried it out and it's awfully slow (likely because of limitations of Swift's Codable implementation). In debug builds, both serialize and deserialize took over 50 seconds, and in release builds, serialize took around 14 seconds and deserialize took around 12 seconds. It would be nice to use such a nice solution, but it just can't meet our performance requirements.\n\n## Custom binary serialization\n\nAs an experiment, I have started implementing a custom serialization and deserialization API that\nuses the Minecraft network protocol's binary format behind the scenes. The initial implementation\nwith no optimization was quite promisingsd. Given that I've gotten a new laptop and the block model\nformat has changed a lot, here are the initial results including the new Protobuf and Flatbuffer\ntimes. I've removed `Flatbuffers (with messy code)` because it would've been too much work to\nreimplement it for the newer block model palette format, and realistically it's not how I would\nimplement it in Delta Client because it's just too difficult to maintain. I will also not be testing\ndebug build performance because that's not important to me anymore now that my laptop can demolish\nrelease builds and run debug builds at a reasonable speed.\n\n| Method & Operation          | Release     |\n| --------------------------- | ----------- |\n| Flatbuffers serialization   | 470.42799ms |\n| Flatbuffers deserialization | 341.62700ms |\n| Protobuf serialization      | 342.27204ms |\n| Protobuf deserialization    | 181.43606ms |\n| Custom serialization        |  54.83699ms |\n| Custom deserialization      | 516.13605ms |\n\nThe serialization speed is almost 7x faster than Protobuf! The order of magnitude difference between\nserialization and deserialization tells me that deserialization should have some pretty easy\noptimizations to make.\n\n| Method      | Serialized size |\n| ----------- | --------------- |\n| Flatbuffers | 21472396 bytes  |\n| Protobuf    | 15105882 bytes  |\n| Custom      | 22136216 bytes  |\n\nAs you can see, the new custom method takes significantly more storage than Protobuf, and a tiny bit\nmore than Flatbuffers, but this isn't very important to me because it's still only 22ish megabytes\nwhich is completely acceptable for a cache.\n\n### Optimizing custom serialization and deserialization\n\nUsing Xcode instruments, I found that the `uvs` property of `BlockModelFace` is the most expensive\npart of deserializing the block model palette. Optimizing the float decoding code path (which uses\nthe fixed length integer decoding code path of `Buffer`) by rearranging code to avoid a call to\n`Array.reversed` managed to speed up deserialization by 1.7x (to 294.46900ms).\n\nBecause `uvs` is so performance critical, I ended up using some unsafe pointer stuff to avoid\nunnecessary copies and byte reordering (caused by MC protocol). I basically just store the Float\narray's raw bytes in the cache directly because it's a fixed length array. This increased the\ndeserialization speed by 2.09x (to 140.65397ms). This change also decreased the serialized size to\n20229880 bytes (almost a 9% decrease).\n\nAfter some further optimization of `Buffer.readInteger(size:endianness:)` I managed to get another\n2x deserialization speed improvement (down to 69.15092ms).\n\nI ended up deviating from the Minecraft protocol for integers to allow the use of unsafe pointers to\ndecode them which gave another 1.23x increase in deserialization speed (down to 56.47790ms).\nEssentially I just store integers by copying their raw bytes so that while deserializing I can just\nget a pointer into the reader's buffer and cast it to a pointer to an integer.\n\nThe next optimization gave a massive improvement by greatly simplifying the serialization and\ndeserialization process for bitwise copyable types. These types can simply just have their raw bytes\ncopied into the output and subsequently these bytes can be copied out as that type when\ndeserializing (using unsafe pointer tricks). This gave another 1.52x improvement in deserialization\nspeed (down to 37.13298ms). It also gave us our first big improvement in serialization speed of 1.3x\n(down to 38.87498ms).\n\nGiven that `BlockModelFace` is the most performance critical part of serializing/deserializing the\nblock model palette and it's technically a fixed amount of data, I decided to try making it a\nbitwise copyable type. All this involved was converting the fixed length array of `uvs` to a\ntuple-equivalent `struct`. I wasn't able to use a tuple because I needed `uvs` to be `Equatable` and\ntuples can't conform to protocols (how silly). After removing all use of indirection from\n`BlockModelFace` I was able to improve deserialization times by a factor of around 4.5 (to around\n8.6ms).\n\nAt this point, a large majority of the serialization time is allocations and appending to arrays. I\ntried using `Array.init(unsafeUninitializedCapacity:initializingWith:)` to avoid unnecessary\nallocations and append operations, however this ended up making the code slower. Either way, 8ms is\ndefinitely fast enough for this application :sweat_smile:.\n\n### Results\n\n| Method & Operation          | Release     |\n| --------------------------- | ----------- |\n| Flatbuffers serialization   | 470.42799ms |\n| Flatbuffers deserialization | 341.62700ms |\n| Protobuf serialization      | 343.40799ms |\n| Protobuf deserialization    | 181.43606ms |\n| Custom serialization        |  50.11296ms |\n| Custom deserialization      |   8.18300ms |\n\nSomewhere along the way I managed to reverse a majority of the progress that I made on serialization\nperformance, but this is fine for my caching needs because cache generation shouldn't happen very\noften at all.\n\n| Method      | Serialized size |\n| ----------- | --------------- |\n| Flatbuffers | 21472396 bytes  |\n| Protobuf    | 15105882 bytes  |\n| Custom      | 18839177 bytes  |\n\nI'm not quite sure exactly what optimizations ended up decreasing the serialized so much for the\ncustom serializer, but I guess it's a pleasant side effect!\n\n### Conclusion\n\nI managed to create a custom binary serialization solution that is 22.2x faster at deserialization\nthan Protobuf, 6.85x faster at serialization than Protobuf, and way more maintainable than an\nequivalent Protobuf-based caching system.\n\nAlthough I started out with the plan to use the Minecraft network protocol binary format to store\nthe cache, I quickly realised that the Minecraft network protocol just isn't built for high\nperformance serialization and deserialization. That's why I ended up just creating an approach that\nis essentially a fancy memdump that can handle indirection and complicated data structures.\n\n### Aftermath\n\nI've implemented binary caching for the item model palette, block registry, font palette, and\ntexture palettes (almost all of the expensive things to load). Overall this amounted to a 4x faster\napp launch time which is pretty amazing. The caching code is also way nicer than the old Protobuf\nstuff, and any optimization of BinarySerializable will basically have an effect on all of the\nexpensive start up tasks making it a very appealing target. However, startup time is now 160ms (when\nfiles have been cached in memory by previous launches; it's around 200ms when they haven't been), so\nI don't think I need to put in any more work for now :sweat_smile:.\n"
  },
  {
    "path": "Notes/ChunkPreparation.md",
    "content": "# Chunk preparation optimisation\n\nChunk mesh preparation is one of the prime areas of focus for optimisation efforts. Fast chunk loading is extremely important for that snappy feeling. There a few ideas for optimising chunk preparation that I have and here they are;\n\n## Initial benchmarks\n\nI have created a swiftpm executable that can download specified chunks from a server and then save them to allow repeated consistent tests. This is what I will be evaluating performance gains with. I have gathered data on the time spent in each of the main high level tasks required when meshing each block and ordered them by which requires more attention (which ones take the longest). Initial measurements were of delta-core commit c9f0d1c6dab773802fd69bedf3675d0255fadf13. The chunk section being prepared is section -20 0 -14 in seed -6243685378508790499.\n\n1. getCullingNeighbours: 0.0178ms, the longest task by far\n2. getBlockModels: 0.0026ms, this one is surprising given it's just a quick lookup, perhaps this is why getCullingNeighbours is so slow\n3. getNeighbourLightLevels: 0.0025ms\n4. adding the block models to the mesh: 0.0010ms\n\nTotal time: 102.8528ms\n\n## Improving getCullingNeighbours\n\ngetBlockModels was the main issue in this case, accessing the resources of a resource pack was super slow because of the use of a computed property called `vanillaResources` that took a stupidly large amount of time. First I tried changing the block models storage to use an array instead of a dictionary but there was no noticable performance difference. Fixing this issue made getCullingNeighbours 5x faster.\n\n1. getCullingNeighbours: 0.0038ms, still the longest task\n2. getNeighbourLightLevels: 0.0026ms\n3. adding the block models to the mesh: 0.0011ms\n4. getBlockModels: 0.0002ms, that's more like it\n\nNew total time: 28.0777ms (3.7x faster than original)\n\n## Improving getNeighbourLightLevels and getCullingNeighbours\n\nThe second biggest bottleneck was calculating neighbour indices, lots of branching could be eliminated from this function I think but it would greatly complicate the logic of the function and make it massive. So instead of optimising the function I just calculate neighbour indices once instead of twice and pass them to both functions that require them.\n\n1. getCullingNeighbours: 0.0025ms\n2. getNeighbourLightLevels: 0.0016ms\n3. getNeighbourIndices: 0.0012ms\n4. add block models: 0.0010ms\n\nNew total time: 22.8666ms (4.5x faster than original)\n\n## Improving getNeighbourIndices\n\nReserve capacity was the big winner here. Reserving a capacity of 6 won 0.0007ms (more than 2x faster). I also tried wrapping arithmetic (unchecked), but that didn't seem to make a noticable difference. Probably because the function is mostly bottlenecked by branching and collection operations.\n\n1. getCullingNeighbours: 0.00253ms\n2. getNeighbourLightLevels: 0.00160ms\n3. getNeighbourIndices: 0.00055ms (more than 2x faster)\n4. add block models: 0.00092ms\n\n## Improving getNeighbourLightLevels\n\nI tried reserve capacity for this getting light levels and culling faces too and it made light levels more than 2x faster as well! Not such a big gain for getCullingNeighbours, but still like a 20% reduction.\n\n1. getCullingNeighbours: 0.00208ms\n2. add block models: 0.00096ms\n3. getNeighbourLightLevels: 0.00084ms\n4. getNeighbourIndices: 0.00053ms\n\nNew total time: 15.8036 (6.5x faster than original, 1.45x faster than previous total time measurement)\n\n## Improving getNeighbourBlockStates and adding block models\n\nI also added reserve capacity to getNeighbourBlockStates which made getCullingNeighbours 1.6x faster. I then fixed the way texture info was accessed (same issue as getting block models had before). This made adding block models 1.8x faster.\n\n1. getCullingNeighbours: 0.00129ms, 1.6x faster than before\n2. getNeighbourLightLevels: 0.00080ms\n3. add block models: 0.00051ms, 1.8x faster than before\n4. getNeighbourIndices: 0.00049ms\n\nNew total time: 11.6262 (8.8x faster than original, 1.35x faster than previous)\n\n## Improving everything\n\nI moved some work from mesh preparation to resource pack loading time, just flattened some information and added a list of cullable and noncullable faces too. This information also makes it easier to detect that a block isn't visible earlier. I also now only do light level lookups for required faces\n\nFrom now on times will be as total time spent in that task over the course of preparing the whole section because the flow of what tasks are done for each block is more complex now. There will be discrepancies between the sum of the measurements and the 'New total time' because the timer has some overhead.\n\n1. calculate face visibility: 7.65150ms\n2. get block models: 1.37812ms\n3. get neighbour indices: 1.01480ms\n4. add block models: 0.46374ms\n\nNew total time: 7.5382ms (13.4x faster than original, 1.54x faster than previous)\n\n## Improving face visibility calculation\n\nI added two ifs to early exit the calculation for most blocks, and use arrays and iteration instead of a dictionary for neighbour indices.\n\n1. get culling neighbours: 4.48527ms\n2. get block models: 1.41321ms\n3. calculate face visibility: 0.98564ms\n4. get neighbour indices: 0.86533ms\n\nNew total time: 5.7885ms\n\n## Improving block model getting\n\nI now store block models in an array instead of a dictionary because dictionaries are slow.\n\n1. get culling neighbours: 4.12294ms\n2. calculate face visibility: 1.03458ms\n3. get neighbour indices: 0.96912ms\n4. get block models: 0.87172ms\n5. add block models: 0.49894ms\n\nNew total time: 4.9672ms\n\nIt now takes 8-9 seconds to prepare all chunks within 10 render distance.\n\n## Replacing Set<Direction> with DirectionSet (a bitset-based implementation)\n\nThis is just overall a much better data structure for the situation. It also ended up improving the\nblock model cache loading time by a bit over 20% because the data structure is much better suited to\nbinary caching (fixed size and just a single integer).\n\nOriginal total time: 6.03ms (not sure whether my computer was slower or the mesh builder slowly got\nslower)\n\nNew total time: 3.35ms (1.8x faster than with Set<Direction>)\n"
  },
  {
    "path": "Notes/DebuggingDeadlocks.md",
    "content": "# Debugging deadlocks \n\nThe more high performance parts of Delta Client use locks to ensure threadsafety (instead of higher\nlevel and slower concepts such as dispatch queues). This is all well and good until a subtle bug\ncauses a deadlock and ruins your day. This document will hopefully help you find the cause faster.\n\n## The basics\n\nWhere applicable, many methods have an `acquireLock` parameter to allow callers to have more control\nover how locking occurs. The most common use case for this is calling a locking function from within\nanother locking function. If either of the functions requires a write lock, you're in trouble, and\nthe easiest way to avoid this issue is to call the inner function with `acquireLock: false`. Make\nsure that the outer function acquires a lock that is at least as permissive as the inner function\nrequires. For example, if the inner function normally acquires a write lock, the outer function must\nacquire a write lock too.\n\n## Pay attention to lock levels\n\nOften locks are arranged into levels, `World` has `terrainLock` for accessing chunks from the chunk\nstorage, and then `Chunk` has `lock` for accessing its stored information. If some code has a chunk\nlock and wants to acquire a terrain lock, but some other code already has a terrain lock and is\nwaiting for a chunk lock, you will get a deadlock. This can be a lot more subtle than a regular\ndeadlock. The rule of thumb here is to only get more and more specific locks when locking and avoid\nhaving a specific lock while getting a general lock. If impossible (or tedious) to obey this rule in\na certain bit of code, it is also possible to use caution and carefully ensure that one of the\noffending bits of code only takes one lock at a time (as seen in commit\n[17fb74bc36ad5b6619d6a6066ebf47073ff22659](https://github.com/stackotter/delta-client/commit/17fb74bc36ad5b6619d6a6066ebf47073ff22659)).\n\n## `ClientboundEntityPacket`'s\n\nWhen implementing the `handle` method of a `ClientboundEntityPacket`, ensure that you do not attempt\nto acquire any locks on the ECS nexus. This is because these packets are handled in the\n`EntityPacketSystem` which is run during the game tick, and the tick scheduler always acquires a\nwrite lock on the nexus before running any systems.\n\n## Finding the offending code (in trickier cases)\n\nIf you are struggling to identify a deadlock cause, try defining the `DEBUG_LOCKS` flag. This\nenabled the `lastLockedBy` property on `ReadWriteLock` which stores the file, line and column where\nthe lock was most recently acquired. Use the [`swiftSettings`](https://github.com/apple/swift-package-manager/blob/11ae0a7bbfaab580c5695eea2c76db9ab092b8a4/Documentation/PackageDescription.md#methods-9)\nproperty of the Swift package target you are building to define the flag.\n\nAfter defining the `DEBUG_LOCKS` flag, run the program under lldb or the Xcode debugger and wait for\nthe deadlock to occur. Find a thread that is stuck waiting for a lock and inspect the lock's\n`lastLockedBy` property. This will hopefully help you find the code path that either forgot to unlock\nthe lock or is acquiring a lock twice.\n"
  },
  {
    "path": "Notes/GUIRendering.md",
    "content": "# GUI rendering\n\n## Optimisation\n\nGUI rendering should be a relatively cheap operation in the Delta Client pipeline, but at the moment\nit isn't (at least compared to what it should be). It takes close to 4ms per frame on my Intel\nMacBook Air (on a superflat world on low render distance that's 8 times higher than world rendering).\n\nIf GUI rendering were put on a scale of optimisability, it would probably be pretty high because\nmost GUI elements don't change from frame to frame.\n\nThe main source of slow downs is that no mesh buffers are being reused at all. A new\nbuffer is created for each mesh each frame.\n\n### Avoid updating uniforms unless they have changed\n\nThis made almost no difference to CPU or GPU time, but it's good practice.\n\n### Combine meshes that use the same array texture where possible\n\nThe only measurement that is affected by this improvement is `gui.encode`. The differences in other\nmeasurements are just due to external factors. Overall it seems to be a pretty fair comparison: some\nmeasurements that should be constant are slightly more and some are slightly less by they seem to be\nrelatively consistent.\n\nThis improvement gave a 2.36x reduction in encode time which is pretty great.\n\nThis improvement isn't foolproof, it should be conservative enough to always retain ordering when required,\nbut it doesn't seem to always combine meshes when they can be combined. This can be worked around by\nrefactoring mesh generation to group by array texture when possible. I had to do this with\n`GUIList`'s row background generation (by putting all the backgrounds first) and it worked a charm.\nI don't quite know why it doesn't manage to combine the meshes in these cases, the text is probably\noverlapping in the transparent parts or something. This could be investigated by adding a mesh\nbounding box rendering feature to the GUI.\n\n```\nwaitForRenderPassDescriptor 8.21971ms\nupdateCamera                0.01754ms\ncreateRenderCommandEncoder  0.07330ms\nworld                       0.53691ms\nentities                    0.06140ms\ngui                         3.30120ms\n  updateUniforms 0.03868ms\n  updateContent  0.52186ms\n  createMeshes   1.07536ms\n  encode         1.63316ms\ncommitToGPU                 0.07620ms\n```\nFigure 1: *before*\n\n```\n=== Start profiler summary ===\nwaitForRenderPassDescriptor 13.06693ms\nupdateCamera                0.02092ms\ncreateRenderCommandEncoder  0.07628ms\nworld                       0.60788ms\nentities                    0.03953ms\ngui                         2.37221ms\n  updateUniforms 0.03809ms\n  updateContent  0.64521ms\n  createMeshes   0.95568ms\n  encode         0.69374ms\ncommitToGPU                 0.05965ms\n===  End profiler summary  ===\n```\nFigure 2: *after*\n\n### Reuse vertex buffers\n\nThis was implemented simply by keeping an array of previous meshes and then when rendering a mesh,\nuse the previous mesh at the same index if one exists. This means that in general a mesh will mostly\nreuse itself meaning that the size should be similar and the creation of a new vertex buffer can be\navoided. This also means that as long as there aren't more meshes than there were in a previous\nframe (true most of the time), no uniforms buffers need to be created.\n\nVertex buffers are created with enough head room to fit 20 more quads which reduced the number of\nnew buffers created by my repeatable GUI test from 91 to 17 (the minimum possible being 10 which is\nthe number of meshes the GUI uses with chat open, the number of items in the hotbar of the account I\nwas using and the debug overlay activated).\n\nThis cut down the `gui.encode` measurement by 2.5x (for a total 6x improvement so far).\n\n```\n=== Start profiler summary ===\nwaitForRenderPassDescriptor 13.51036ms\nupdateCamera                0.01717ms\ncreateRenderCommandEncoder  0.07296ms\nworld                       0.57931ms\nentities                    0.04536ms\ngui                         2.22940ms\n  updateUniforms 0.04108ms\n  updateContent  0.81972ms\n  createMeshes   1.07331ms\n  encode         0.27231ms\ncommitToGPU                 0.06338ms\n===  End profiler summary  ===\n```\nFigure 3: *after*\n\n### Micro optimization time\n\nAt the moment I can't spot any other obvious causes for slow down so I just opened up Instruments\nand started optimizing slow codepaths. By using some unsafe pointers, smarter algorithms and tuples\ninstead of arrays when size is fixed I managed to get quite a big improvement. I think this\nperformance is finally getting pretty reasonable. Total CPU time for GUI rendering is now 2.5x lower\nthan before I started optimizing.\n\n```\n=== Start profiler summary ===\nwaitForRenderPassDescriptor 14.55348ms\nupdateCamera                0.01872ms\ncreateRenderCommandEncoder  0.07736ms\nworld                       0.66486ms\nentities                    0.03725ms\ngui                         1.35770ms\n  updateUniforms 0.03743ms\n  updateContent  0.43399ms\n  createMeshes   0.51876ms\n  encode         0.34344ms\ncommitToGPU                 0.05536ms\n===  End profiler summary  ===\n```\n"
  },
  {
    "path": "Notes/PluginSystem.md",
    "content": "# Plugin System\n\nThis document explains how the plugin system is designed and implemented.\n\n## `PluginEnvironment`\n\nThe `PluginEnvironment` manages loading, unloading and reloading of plugins. All errors to do with\nplugin loading are added to `PluginEnvironment.errors` so that they can be accessed at a later time\n(e.g. in the plugin settings view). `PluginEnvironment` also has a method called `addEventBus` which\nis used to add all loaded plugins to an event bus as listeners. And a method `handleWillJoinServer`\nwhich should be replaced by an event at a later date when the events part is cleaned up.\n\n## Dynamic linking\n\nAll of a plugin's code is contained within a dynamic library (`libPlugin.dylib`). The dynamic\nlibrary exposes a function called `buildPlugin` (exported using `@_cdecl(\"buildPlugin\")`) which is\ncalled by the client to get a reference to a `PluginBuilder`. The plugin builder is then used to\ncreate an instance of the plugin and the plugin is installed into the main `PluginEnvironment`\n(`DeltaClientApp.pluginEnvironment`).\n\nFor typecasting to work correctly in plugins, both the client and the plugin have to be using the\nsame copies of types. This means that both the client and plugin have to be dynamically linked\nagainst `DeltaCore` (which contains the common code that both plugins and the client have access\nto). Due to limitations of Swift Package Manager, this means that `DeltaCore` has to be its own\nswift package which has a dynamic library product (see `Sources/Core/Package.swift`). The root swift\npackage includes `Sources/Core` as a package dependency and the `DeltaClient` target in the root\npackage has this as a dependency.\n\nTo allow use of `DeltaCore` outside of `DeltaClient` (i.e. in plugins), the root package exposes two\nproducts: `DynamicShim` and `StaticShim`. `DynamicShim` re-exports the `DeltaCore` dynamic library\nfor use in plugins (which must dynamically link to `DeltaCore`). However, not all projects require\n`DeltaCore` to be dynamically link (e.g. a simple bot client), therefore the `StaticShim` product\nwas also exposed which re-exports a statically linked version of `DeltaCore`.\n\n## Loading order\n\nIf a feature only supports one plugin using it at a time (such as custom render coordinators), the\nclient will use the first plugin that uses the feature. Until proper sorting support is added this\ncan be worked around by unloading all and manually loading the plugins in the order you want (will\nnot persist across launches).\n\n## Manifest files\n\nA plugin's manifest file is just a JSON file containing basic information about the client such as\nits identifier, description and display name. The identifier is used to uniquely identify each\nplugin.\n\n## Directory structure\n\nPlugins are just directories with the `deltaplugin` extension. They currently only contain a\n`manifest.json` and a `libPlugin.dylib`.\n"
  },
  {
    "path": "Notes/Readme.md",
    "content": "# Notes\n\nIn this directory I will be documenting the reasoning behind certain design decisions. I will also be documenting the results of investigations I identify the most efficient solutions to certain bottlenecking problems. Hopefully this documentation will help you understand the design of Delta Core/Client and make better contributions. \n"
  },
  {
    "path": "Notes/RenderPipeline.md",
    "content": "# Render pipeline optimisation\n\nThe CPU usage of the render pipeline is currently bottlenecking fps much more than the actual GPU usage. This document will contain my findings as I try to optimise the CPU part of the render pipeline.\n\nAll measurements of pipeline performance will be measured at -312 140 -216 with yaw 0 and pitch 90 (looking straight down) in seed -6243685378508790499 with render distance 5. It will always be measured in full screen on my laptop's screen not my monitor's.\n\n## Initial measurements\n\n- WorldRenderer.draw: ~25ms\n\nJust did a bunch of cleanup and now it's a bunch faster somehow;\n\n- WorldRenderer.draw: ~7ms\n\nI don't quite believe that it sped up that much so i probably measured it incorrectly\n"
  },
  {
    "path": "Package.resolved",
    "content": "{\n  \"pins\" : [\n    {\n      \"identity\" : \"asn1parser\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/ASN1Parser\",\n      \"state\" : {\n        \"branch\" : \"main\",\n        \"revision\" : \"f92a5b26f5c92d38ae858a5a77048e9af82331a3\"\n      }\n    },\n    {\n      \"identity\" : \"async-http-client\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swift-server/async-http-client.git\",\n      \"state\" : {\n        \"revision\" : \"037b70291941fe43de668066eb6fb802c5e181d2\",\n        \"version\" : \"1.1.1\"\n      }\n    },\n    {\n      \"identity\" : \"bigint\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/attaswift/BigInt.git\",\n      \"state\" : {\n        \"revision\" : \"e07e00fa1fd435143a2dcf8b7eec9a7710b2fdfe\",\n        \"version\" : \"5.7.0\"\n      }\n    },\n    {\n      \"identity\" : \"bluesocket\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/BlueSocket.git\",\n      \"state\" : {\n        \"branch\" : \"master\",\n        \"revision\" : \"31e92ab743a9ffc2a4a6e7e2f1043f5fe1d97e80\"\n      }\n    },\n    {\n      \"identity\" : \"circuitbreaker\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/Kitura/CircuitBreaker.git\",\n      \"state\" : {\n        \"revision\" : \"bd4255762e48cc3748a448d197f1297a4ba705f7\",\n        \"version\" : \"5.1.0\"\n      }\n    },\n    {\n      \"identity\" : \"cryptoswift\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/krzyzanowskim/CryptoSwift\",\n      \"state\" : {\n        \"revision\" : \"e45a26384239e028ec87fbcc788f513b67e10d8f\",\n        \"version\" : \"1.9.0\"\n      }\n    },\n    {\n      \"identity\" : \"ecs\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/ecs.git\",\n      \"state\" : {\n        \"branch\" : \"master\",\n        \"revision\" : \"c7660bcd24e31ef2fc3457f56a2bf4a58c3ad6ee\"\n      }\n    },\n    {\n      \"identity\" : \"fireblade-math\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/fireblade-math.git\",\n      \"state\" : {\n        \"branch\" : \"matrix2x2\",\n        \"revision\" : \"750239647673b07457bea80b39438a6db52198f1\"\n      }\n    },\n    {\n      \"identity\" : \"jjliso8601dateformatter\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/michaeleisel/JJLISO8601DateFormatter\",\n      \"state\" : {\n        \"revision\" : \"50d5ea26ffd3f82e3db8516d939d80b745e168cf\",\n        \"version\" : \"0.2.0\"\n      }\n    },\n    {\n      \"identity\" : \"jpeg\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/jpeg\",\n      \"state\" : {\n        \"revision\" : \"a27e47f49479993b2541bc5f5c95d9354ed567a0\",\n        \"version\" : \"1.0.2\"\n      }\n    },\n    {\n      \"identity\" : \"libpng\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/the-swift-collective/libpng\",\n      \"state\" : {\n        \"revision\" : \"0eff23aca92a086b7892831f5cb4f58e15be9449\",\n        \"version\" : \"1.6.45\"\n      }\n    },\n    {\n      \"identity\" : \"libwebp\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/the-swift-collective/libwebp\",\n      \"state\" : {\n        \"revision\" : \"5f745a17b9a5c2a4283f17c2cde4517610ab5f99\",\n        \"version\" : \"1.4.1\"\n      }\n    },\n    {\n      \"identity\" : \"loggerapi\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/Kitura/LoggerAPI.git\",\n      \"state\" : {\n        \"revision\" : \"e82d34eab3f0b05391082b11ea07d3b70d2f65bb\",\n        \"version\" : \"1.9.200\"\n      }\n    },\n    {\n      \"identity\" : \"opencombine\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/OpenCombine/OpenCombine.git\",\n      \"state\" : {\n        \"revision\" : \"8576f0d579b27020beccbccc3ea6844f3ddfc2c2\",\n        \"version\" : \"0.14.0\"\n      }\n    },\n    {\n      \"identity\" : \"puppy\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/sushichop/Puppy\",\n      \"state\" : {\n        \"revision\" : \"80ebe6ab25fb7050904647cb96f6ad7bee77bad6\",\n        \"version\" : \"0.9.0\"\n      }\n    },\n    {\n      \"identity\" : \"rainbow\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/onevcat/Rainbow\",\n      \"state\" : {\n        \"revision\" : \"cdf146ae671b2624917648b61c908d1244b98ca1\",\n        \"version\" : \"4.2.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-algorithms\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-algorithms.git\",\n      \"state\" : {\n        \"revision\" : \"87e50f483c54e6efd60e885f7f5aa946cee68023\",\n        \"version\" : \"1.2.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-argument-parser\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-argument-parser\",\n      \"state\" : {\n        \"revision\" : \"626b5b7b2f45e1b0b1c6f4a309296d1d21d7311b\",\n        \"version\" : \"1.7.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-asn1\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-asn1.git\",\n      \"state\" : {\n        \"revision\" : \"9f542610331815e29cc3821d3b6f488db8715517\",\n        \"version\" : \"1.6.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-async-algorithms\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-async-algorithms.git\",\n      \"state\" : {\n        \"revision\" : \"9d349bcc328ac3c31ce40e746b5882742a0d1272\",\n        \"version\" : \"1.1.3\"\n      }\n    },\n    {\n      \"identity\" : \"swift-async-dns-resolver\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-async-dns-resolver.git\",\n      \"state\" : {\n        \"revision\" : \"36eedf108f9eda4feeaf47f3dfce657a88ef19aa\",\n        \"version\" : \"0.6.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-atomics\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-atomics.git\",\n      \"state\" : {\n        \"revision\" : \"b601256eab081c0f92f059e12818ac1d4f178ff7\",\n        \"version\" : \"1.3.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-case-paths\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/pointfreeco/swift-case-paths\",\n      \"state\" : {\n        \"revision\" : \"206cbce3882b4de9aee19ce62ac5b7306cadd45b\",\n        \"version\" : \"1.7.3\"\n      }\n    },\n    {\n      \"identity\" : \"swift-certificates\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-certificates.git\",\n      \"state\" : {\n        \"revision\" : \"24ccdeeeed4dfaae7955fcac9dbf5489ed4f1a25\",\n        \"version\" : \"1.18.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-collections\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-collections.git\",\n      \"state\" : {\n        \"revision\" : \"6675bc0ff86e61436e615df6fc5174e043e57924\",\n        \"version\" : \"1.4.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-cross-ui\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/swift-cross-ui\",\n      \"state\" : {\n        \"revision\" : \"835c9ea90a3b1d2dc20436eb2c87b00037b4eb9c\",\n        \"version\" : \"0.4.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-crypto\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-crypto.git\",\n      \"state\" : {\n        \"revision\" : \"bb4ba815dab96d4edc1e0b86d7b9acf9ff973a84\",\n        \"version\" : \"4.3.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-cwinrt\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/moreSwift/swift-cwinrt\",\n      \"state\" : {\n        \"revision\" : \"601287a2a89e8e56e8aa53782b6054db8be0bce8\",\n        \"version\" : \"0.1.2\"\n      }\n    },\n    {\n      \"identity\" : \"swift-http-structured-headers\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-http-structured-headers.git\",\n      \"state\" : {\n        \"revision\" : \"76d7627bd88b47bf5a0f8497dd244885960dde0b\",\n        \"version\" : \"1.6.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-http-types\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-http-types.git\",\n      \"state\" : {\n        \"revision\" : \"45eb0224913ea070ec4fba17291b9e7ecf4749ca\",\n        \"version\" : \"1.5.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-image\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/swift-image.git\",\n      \"state\" : {\n        \"branch\" : \"master\",\n        \"revision\" : \"7160e2d8799f8b182498ab699aa695cb66cf4ea6\"\n      }\n    },\n    {\n      \"identity\" : \"swift-image-formats\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/swift-image-formats\",\n      \"state\" : {\n        \"revision\" : \"2e5dc1ead747afab9fd517d81316d961969e3610\",\n        \"version\" : \"0.3.3\"\n      }\n    },\n    {\n      \"identity\" : \"swift-log\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-log.git\",\n      \"state\" : {\n        \"revision\" : \"ce592ae52f982c847a4efc0dd881cc9eb32d29f2\",\n        \"version\" : \"1.6.4\"\n      }\n    },\n    {\n      \"identity\" : \"swift-macro-toolkit\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/swift-macro-toolkit\",\n      \"state\" : {\n        \"revision\" : \"d319bcc2586f7dbc6a09c05792105078263f1ed3\",\n        \"version\" : \"0.7.2\"\n      }\n    },\n    {\n      \"identity\" : \"swift-mutex\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swhitty/swift-mutex\",\n      \"state\" : {\n        \"revision\" : \"1770152df756b54c28ef1787df1e957d93cc62d5\",\n        \"version\" : \"0.0.6\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio.git\",\n      \"state\" : {\n        \"revision\" : \"558f24a4647193b5a0e2104031b71c55d31ff83a\",\n        \"version\" : \"2.97.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-extras\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-extras.git\",\n      \"state\" : {\n        \"revision\" : \"abcf5312eb8ed2fb11916078aef7c46b06f20813\",\n        \"version\" : \"1.33.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-http2\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-http2.git\",\n      \"state\" : {\n        \"revision\" : \"6d8d596f0a9bfebb925733003731fe2d749b7e02\",\n        \"version\" : \"1.42.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-ssl\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-ssl.git\",\n      \"state\" : {\n        \"revision\" : \"df9c3406028e3297246e6e7081977a167318b692\",\n        \"version\" : \"2.36.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-numerics\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-numerics.git\",\n      \"state\" : {\n        \"revision\" : \"0c0290ff6b24942dadb83a929ffaaa1481df04a2\",\n        \"version\" : \"1.1.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-package-zlib\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/fourplusone/swift-package-zlib\",\n      \"state\" : {\n        \"revision\" : \"03ecd41814d8929362f7439529f9682536a8de13\",\n        \"version\" : \"1.2.11\"\n      }\n    },\n    {\n      \"identity\" : \"swift-parsing\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/pointfreeco/swift-parsing\",\n      \"state\" : {\n        \"revision\" : \"3432cb81164dd3d69a75d0d63205be5fbae2c34b\",\n        \"version\" : \"0.14.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-png\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/swift-png\",\n      \"state\" : {\n        \"revision\" : \"b68a5662ef9887c8f375854720b3621f772bf8c5\"\n      }\n    },\n    {\n      \"identity\" : \"swift-service-lifecycle\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swift-server/swift-service-lifecycle.git\",\n      \"state\" : {\n        \"revision\" : \"9829955b385e5bb88128b73f1b8389e9b9c3191a\",\n        \"version\" : \"2.11.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-syntax\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swiftlang/swift-syntax.git\",\n      \"state\" : {\n        \"revision\" : \"f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2\",\n        \"version\" : \"601.0.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-system\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-system.git\",\n      \"state\" : {\n        \"revision\" : \"7c6ad0fc39d0763e0b699210e4124afd5041c5df\",\n        \"version\" : \"1.6.4\"\n      }\n    },\n    {\n      \"identity\" : \"swift-uwp\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/moreSwift/swift-uwp\",\n      \"state\" : {\n        \"revision\" : \"a0a86467514eaa63272eae848c70d49e6874ede4\",\n        \"version\" : \"0.1.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-webview2core\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/moreSwift/swift-webview2core\",\n      \"state\" : {\n        \"revision\" : \"a8ed52a57f6d81ee06c8db692f8ee0431174379a\",\n        \"version\" : \"0.1.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-windowsappsdk\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/moreSwift/swift-windowsappsdk\",\n      \"state\" : {\n        \"revision\" : \"3c06cb36da9711e24a76c35861e0b97702448015\",\n        \"version\" : \"0.1.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-windowsfoundation\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/moreSwift/swift-windowsfoundation\",\n      \"state\" : {\n        \"revision\" : \"94d56a5aea472672bc5d041c091f6cdf72e3cc44\",\n        \"version\" : \"0.1.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-winui\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/moreSwift/swift-winui\",\n      \"state\" : {\n        \"revision\" : \"73d5d19c39c523c92355027dd13aee445fc627a7\",\n        \"version\" : \"0.1.1\"\n      }\n    },\n    {\n      \"identity\" : \"swiftcpudetect\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/JWhitmore1/SwiftCPUDetect\",\n      \"state\" : {\n        \"branch\" : \"main\",\n        \"revision\" : \"5ca694c6ad7eef1199d69463fa956c24c202465f\"\n      }\n    },\n    {\n      \"identity\" : \"swiftpackagesbase\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/ITzTravelInTime/SwiftPackagesBase\",\n      \"state\" : {\n        \"revision\" : \"79477622b5dbacc3722d485c5060c46a90740016\",\n        \"version\" : \"0.0.23\"\n      }\n    },\n    {\n      \"identity\" : \"swiftyrequest\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/Kitura/SwiftyRequest.git\",\n      \"state\" : {\n        \"revision\" : \"2c543777a5088bed811503a68551a4b4eceac198\",\n        \"version\" : \"3.2.200\"\n      }\n    },\n    {\n      \"identity\" : \"swordrpc\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/SwordRPC\",\n      \"state\" : {\n        \"revision\" : \"3ddf125eeb3d83cb17a6e4cda685f9c80e0d4bed\"\n      }\n    },\n    {\n      \"identity\" : \"xctest-dynamic-overlay\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/pointfreeco/xctest-dynamic-overlay\",\n      \"state\" : {\n        \"revision\" : \"dfd70507def84cb5fb821278448a262c6ff2bbad\",\n        \"version\" : \"1.9.0\"\n      }\n    },\n    {\n      \"identity\" : \"zipfoundation\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/weichsel/ZIPFoundation.git\",\n      \"state\" : {\n        \"revision\" : \"22787ffb59de99e5dc1fbfe80b19c97a904ad48d\",\n        \"version\" : \"0.9.20\"\n      }\n    },\n    {\n      \"identity\" : \"zippyjson\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/michaeleisel/ZippyJSON\",\n      \"state\" : {\n        \"revision\" : \"ddbbc024ba5a826f9676035a0a090a0bc2d40755\",\n        \"version\" : \"1.2.15\"\n      }\n    },\n    {\n      \"identity\" : \"zippyjsoncfamily\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/michaeleisel/ZippyJSONCFamily\",\n      \"state\" : {\n        \"revision\" : \"c1c0f88977359ea85b81e128b2d988e8250dfdae\",\n        \"version\" : \"1.2.14\"\n      }\n    },\n    {\n      \"identity\" : \"zlib\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/the-swift-collective/zlib.git\",\n      \"state\" : {\n        \"revision\" : \"f1d153b90420f9fcc6ef916cd67ea96f0e68d137\",\n        \"version\" : \"1.3.2\"\n      }\n    }\n  ],\n  \"version\" : 2\n}\n"
  },
  {
    "path": "Package.swift",
    "content": "// swift-tools-version:5.9\n\nimport PackageDescription\n\nvar products: [Product] = [\n  .executable(\n    name: \"DeltaClientGtk\",\n    targets: [\"DeltaClientGtk\"]\n  ),\n\n  // Importing DynamicShim as a dependency in your own project will in effect just import\n  // DeltaCore using dynamic linking\n  .library(\n    name: \"DynamicShim\",\n    targets: [\"DynamicShim\"]\n  ),\n\n  // Importing StaticShim as a dependency in your own project will just import DeltaCore\n  // using static linking\n  .library(\n    name: \"StaticShim\",\n    targets: [\"StaticShim\"]\n  ),\n]\n\n#if canImport(Darwin)\n  products.append(\n    .executable(\n      name: \"DeltaClient\",\n      targets: [\"DeltaClient\"]\n    )\n  )\n#endif\n\nvar targets: [Target] = [\n  .executableTarget(\n    name: \"DeltaClientGtk\",\n    dependencies: [\n      .product(name: \"DeltaCore\", package: \"DeltaCore\"),\n      .product(name: \"SwiftCrossUI\", package: \"swift-cross-ui\"),\n      .product(name: \"GtkBackend\", package: \"swift-cross-ui\"),\n    ],\n    path: \"Sources/ClientGtk\"\n  ),\n\n  .target(\n    name: \"DynamicShim\",\n    dependencies: [\n      .product(name: \"DeltaCore\", package: \"DeltaCore\")\n    ],\n    path: \"Sources/Exporters/DynamicShim\"\n  ),\n\n  .target(\n    name: \"StaticShim\",\n    dependencies: [\n      .product(name: \"StaticDeltaCore\", package: \"DeltaCore\")\n    ],\n    path: \"Sources/Exporters/StaticShim\"\n  ),\n]\n\n#if canImport(Darwin)\n  targets.append(\n    .executableTarget(\n      name: \"DeltaClient\",\n      dependencies: [\n        \"DynamicShim\",\n        .product(name: \"SwordRPC\", package: \"SwordRPC\", condition: .when(platforms: [.macOS])),\n        .product(name: \"ArgumentParser\", package: \"swift-argument-parser\"),\n      ],\n      path: \"Sources/Client\"\n    )\n  )\n#endif\n\nlet package = Package(\n  name: \"DeltaClient\",\n  platforms: [.macOS(.v11), .iOS(.v15), .tvOS(.v15)],\n  products: products,\n  dependencies: [\n    // See Notes/PluginSystem.md for more details on the architecture of the project in regards to dependencies, targets and linking\n    // In short, the dependencies for DeltaCore can be found in Sources/Core/Package.swift\n    .package(name: \"DeltaCore\", path: \"./Sources/Core\"),\n    .package(url: \"https://github.com/apple/swift-argument-parser\", from: \"1.0.0\"),\n    .package(\n      url: \"https://github.com/stackotter/SwordRPC\",\n      revision: \"3ddf125eeb3d83cb17a6e4cda685f9c80e0d4bed\"\n    ),\n    .package(\n      url: \"https://github.com/stackotter/swift-cross-ui\",\n      .upToNextMinor(from: \"0.4.0\")\n    ),\n  ],\n  targets: targets\n)\n"
  },
  {
    "path": "Readme.md",
    "content": "# Delta Client - Changing the meaning of speed\r\n\r\n[![Discord](https://img.shields.io/discord/851058836776419368.svg?label=&logo=discord&logoColor=ffffff&color=5C5C5C&labelColor=6A7EC2)](https://deltaclient.app/discord)\r\n\r\nAn open source rewrite of the *Minecraft: Java Edition* client, written in Swift for macOS and iOS (experimental). Currently Delta Client only supports connecting to **1.16.1** servers.\r\n\r\n## Disclaimers\r\n\r\nThis client is not finished yet. If you're looking for a client to use to play Minecraft today, then this is not for you. **I am NOT responsible for anti-cheat bans, the client has not been thoroughly tested yet and is still deep in development.**\r\n\r\n**This software is not affiliated with Mojang AB, the original developer of Minecraft.**\r\n\r\n## Overview\r\n\r\nThe main focus of this project is to create a highly efficient Java Edition compatible client written in Swift for macOS. The client also has experimental support for iOS, and is in the process of getting ported to Linux using SwiftCrossUI and eventually Kinzoku (once it is ready).\r\n\r\nIf you want to have a say in the development of the client or have any questions, feel free to join the community on [Discord](https://deltaclient.app/discord).\r\n\r\n![Playing with Delta Client in a Hypixel lobby](Screenshots/hypixel-3.png)\r\n\r\n## Performance\r\n\r\nOne of the biggest advantages of Delta Client is its performance. Epic comparison graphs are in progress, but for now have some dot-points.\r\n\r\n- Start-up time:\r\n  - M2 MacBook Air: 0.9 seconds (vanilla takes 43 seconds)\r\n  - i5 MacBook Air: 2.7 seconds (vanilla takes ~60 seconds)\r\n- FPS on 10 render distance in the mountains (bad for fps):\r\n  - M2 MacBook Air (on 1080p monitor): ~120 fps (vanilla gets ~75 fps)\r\n  - i5 MacBook Air: ~70 fps (vanilla gets ~35fps)\r\n\r\n## Installation\r\n\r\n### Prebuilt\r\n\r\n1. Visit [Delta Client's downloads page](https://delta.stackotter.dev/downloads) and download the\r\n   latest build from the `main` branch.\r\n2. Unzip the downloaded zip archive and open the app inside\r\n3. You will get a security alert, click ok\r\n4. Right click the app in finder and select open\r\n5. You should get another pop-up, click 'Open'\r\n6. Delta Client will now open and start downloading the required assets (this only has to happen once and should take around 40s with a mediocre internet speed)\r\n7. You can move Delta Client to your Applications folder for ease of use if you want\r\n\r\nTo view application logs, click `View > Logs` in the menu bar while Delta Client is open.\r\n\r\n### Building from source\r\n\r\nTo build Delta Client you'll first need to install Xcode 14+ and the latest version of [Swift Bundler](https://github.com/stackotter/swift-bundler). Please note that using Xcode 13 is ok but you may run into some weird memory corruption issues, so test with Xcode 14+ before assuming that it's a Delta Client bug. Once you've installed the requirements, run the following commands in terminal;\r\n\r\n```sh\r\n# Clone Delta Client\r\ngit clone https://github.com/stackotter/delta-client\r\ncd delta-client\r\n\r\n# Perform a release build and output the bundled app to the current directory\r\nsh ./build.sh\r\n\r\n# If you want to develop Delta Client using Xcode, run the following command\r\nswift bundler generate-xcode-support\r\n# And then open Package.swift with Xcode and you'll be able to build it from Xcode too\r\n```\r\n\r\n## Minecraft version support\r\n\r\nAt the moment the client only supports joining **1.16.1** servers. In the future I plan to support more versions. \r\n\r\nNot every version will be perfectly supported but I will try and have the most polished support for the following versions;\r\n\r\n- 1.8.9\r\n- the latest speedrunning version (currently 1.16.1)\r\n- the latest stable version\r\n\r\n## Features\r\n\r\n- [x] Networking\r\n  - [x] Basic networking\r\n  - [x] Server list ping\r\n  - [x] Encryption (for non-offline mode servers)\r\n    - [x] Mojang accounts\r\n    - [x] Microsoft accounts\r\n  - [x] LAN server detection\r\n- [x] Basic config system\r\n  - [x] Multi-accounting\r\n- [ ] Rendering\r\n  - [x] World\r\n    - [x] Basic block rendering\r\n    - [x] Basic chunk rendering\r\n    - [x] Block culling\r\n    - [x] Block models\r\n    - [x] Multipart structures (e.g. fences)\r\n    - [x] Multiple chunks\r\n    - [x] Lighting\r\n    - [x] Animated textures (e.g. lava)\r\n    - [x] Translucency\r\n    - [x] Fluids (lava and water)\r\n    - [x] Chunk frustum culling\r\n    - [x] Biome blending (mostly)\r\n    - [x] [Sky box](https://github.com/stackotter/delta-client/pull/188)\r\n  - [ ] Entities\r\n    - [x] Basic entity rendering (just coloured cubes)\r\n    - [x] Render entity models\r\n    - [ ] Block entities (e.g. chests)\r\n    - [ ] Item entities\r\n    - [ ] Entity animations\r\n  - [ ] GUI\r\n    - [x] Chat\r\n    - [x] F3-style stuff\r\n    - [x] Bossbars\r\n    - [ ] Scoreboard\r\n    - [x] Tab list\r\n    - [x] Health, hunger and experience\r\n    - [x] Hotbar\r\n    - [ ] Inventory\r\n      - [x] Basic inventory\r\n      - [x] Basic crafting\r\n      - [x] Inventory actions\r\n      - [ ] Using recipe blocks (like crafting tables and stuff)\r\n      - [ ] Creative inventory\r\n- [ ] Sound\r\n  - [ ] Basic sounds system\r\n- [x] Physics\r\n  - [x] Physics loop\r\n  - [x] Input system\r\n  - [x] Collision system\r\n- [x] Interaction\r\n  - [x] Block placing\r\n  - [x] Block breaking\r\n  - [x] Block entity interaction\r\n  - [x] Entity interaction\r\n- [ ] Particles\r\n  - [ ] Basic particle system\r\n  - [ ] Block break particles\r\n  - [ ] Ambient particles\r\n  - [ ] Hit particles\r\n  - [ ] Particles from server\r\n\r\n## Contributing\r\n\r\nFirst, please check out the [contributing guidelines](Contributing.md). Then you can checkout the [issues](https://github.com/stackotter/delta-client/issues) for a place to get started. Make sure to leave a comment on the issue you choose so that people know that someone's already working on it.\r\n\r\n## Servers\r\n\r\nWe now have an official testing server (`play.deltaclient.app`)! However, if you want to mess around to your hearts content you can run a server on your computer for full control (see below).\r\n\r\nTo start a test server, download a 1.16.1 server jar from [here](https://mcversions.net/download/1.16.1). Then in Terminal type `java -jar ` and then drag the downloaded .jar file onto the terminal window and then hit enter. Wait for the server to start up. Now add a new server with the address `localhost` in Delta Client and you should be able to connect to it. Keep in mind the server may use a significant amount of resources and slow down Delta Client.\r\n\r\n## More screenshots\r\n\r\n![Delta Client being used to view a survival world from a vantage point on a mountain](Screenshots/survival.png)\r\n\r\n![Delta Client's server selection UI](Screenshots/ui.png)\r\n"
  },
  {
    "path": "Roadmap.md",
    "content": "# **This is outdated, please ignore for now**\n\n# Demo Versions\n\nThe demo versions are just proof-of-concepts.\n\n## Demo 1 - Efficient Launch, Basic Rendering & All Networking *(Released)*\n\n### Features\n\n- smooth first launch flow (downloading assets and creating default config and stuff)\n- login flow for mojang accounts\n- fast subsequent startups\n- rendering player's current chunk\n  - the view can be moved around using ```tp``` commands from another player\n- basic config screens (account settings and server settings)\n\n### Todo\n\n- [x] refactor networking\n- [x] add compression support\n- [x] add encryption support\n- [x] basic config system\n- [x] sign in screen on first launch\n- [x] config screen for changing account details (used a logout button for now instead)\n- [x] remove need for minecraft to be already installed\n- [x] add basic way to exit game and get back to server list\n- [x] prettyprint config json\n- [x] way to re-order servers\n- [x] move some things to menu bar instead of tool bar (like account stuff)\n\n## Demo 2 - Critical Bug Fixes for Demo 1 *(Released)*\n\n### New Features\n\n- none, just fixes some critical bugs from demo 1\n\n### Todo\n\n- [x] refresh access token before each server join\n- [x] fix first time server join\n\n# Alpha Versions\n\nThe demo versions were just proof-of-concepts. The alpha versions will still be far from complete but will be looking a lot more promising.\n\n## Alpha 1 - Basically Spectator Mode *(WIP)*\n\n### New Features\n\n- rendering\n  - proper lighting\n  - multipart structures (e.g. fences)\n  - random block rotations\n  - multichunk rendering\n  - complete redesigned rendering system\n  - animated textures\n  - frustum culling\n  - mip maps\n  - proper tints (redstone, leaves, water and grass etc.)\n  - fluids\n  - proper transparency and translucency rendering\n- movements\n  - basic input system\n  - basic spectator mode movement (freecam)\n- ui\n  - cleaner ui and ui code\n  - edit server list screen\n  - accounts screen\n- multi-accounting (allow easily switching between accounts)\n- improved memory usage\n\n### Todo\n\n- [x] multipart structure reading (from pixlyzer)\n- [x] multipart structure rendering\n- [x] parallelise generating chunk meshes\n- [x] order chunks based on distance from player when generating meshes\n- [x] order chunks based off frustum as well\n- [x] basic shading (face normal based) (bake light levels into block models)\n- [x] mip maps\n- [x] rewrite block model loading system\n- [x] lighting\n- [x] animated textures\n- [x] fix gpu ram usage (clear buffers when not being used, possibly use private mode buffers, possibly use a big default size buffer and transfer things through that)\n- [x] fix indices (either use 6 vertices to a face or do the fancy shader thing from before (adjusting the vertex indices))\n- [x] optimise chunk mesh preparation\n- [x] proper transparent and translucent rendering\n- [x] non-transparent leaves for now (better performance)\n- [x] reduce cpu time for frames (gpu frame time is well below 16ms, cpu is taking up the majority of the time each frame)\n- [x] possibly use section based rendering instead of chunk based\n- [x] basic multichunk rendering\n- [x] fix hypixel chunk loading\n- [x] fix grass block sides\n- [x] implement rescale\n- [x] random block variants (that match vanilla)\n- [x] frustum culling for chunks\n- [x] create an input system\n  - [x] keyboard\n  - [x] mouse\n- [x] create a physics loop\n  - [x] make a loop that runs at a consistent interval\n  - [x] add basic physics simulation\n- [x] add basis for multi-accounting\n  - [x] config\n  - [x] login code\n- [x] add offline account support\n- [x] ui for switching accounts\n- [x] fix memory usage errors (when leaving a server or starting the app for the first time a lot of memory doesn't get freed for some reason)\n- [x] fluids\n- [x] waterlogging\n- [x] block id refactor (issue #25)\n\n## Alpha 2 - Creative Mode Without Inventory\n\n### New Features\n\n- collision system\n- auto-jump\n- gravity\n\n### Todo\n\n- [x] load hitboxes\n- [x] wireframe hitboxes\n- [x] detect player-block collisions\n- [x] handle collisions\n- [ ] auto-jump\n- [x] add gravity to physics\n\n## Alpha 3 - Basic Survival Mode\n\n### New Features - Basic HUD & Inventory\n\n- f3\n- hotbar\n- health\n- xp bar\n- bubbles (the indicator for drowning stuff)\n- basic inventory view\n\n### Todo\n\n- [x] font rendering\n- [x] modular hud system\n- [x] bars\n  - [x] health\n  - [x] hunger\n  - [x] xp\n  - [ ] bubbles\n- [x] item rendering\n- [x] hotbar\n- [x] basic inventory (just for viewing)\n"
  },
  {
    "path": "Security.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nOnly bugs present in the latest commit on a branch will be acted on (because all others have clearly been fixed). However, if there's a security bug in one of the GitHub releases, a security advisory will be added to release's description. And if it's the latest release, a new release will be made once the bug is fixed.\n\n## Reporting a Vulnerability\n\nCreate an issue for the vulnerability unless publicising the issue before it is fixed would put users at risk. In the case that creating a public GitHub issue is not sensible, contact me directly on Discord (stackotter#3100). If the bug does turn out to be serious after further investigation, it will be prioritised and fixed as soon as possible. If it's not serious, it will have the same priority as any other issue.\n"
  },
  {
    "path": "Sources/Client/CommandLineArguments.swift",
    "content": "import ArgumentParser\nimport Foundation\nimport Logging\n\n/// An error thrown while parsing ``CommandLineArguments``.\nenum CommandLineArgumentsError: LocalizedError {\n  case invalidLogLevel(String)\n\n  var errorDescription: String? {\n    switch self {\n      case .invalidLogLevel(let level):\n        return \"Invalid log level '\\(level)'. Must be one of \\(Logger.Level.allCases.map(\\.rawValue))\"\n    }\n  }\n}\n\n/// The command line arguments for Delta Client.\nstruct CommandLineArguments: ParsableCommand {\n  static let configuration = CommandConfiguration(commandName: \"DeltaClient\")\n\n  /// A replacement for the default plugins directory.\n  @Option(\n    name: .customLong(\"plugins-dir\"),\n    help: \"A directory to load plugins from instead of the default plugins directory.\",\n    transform: URL.init(fileURLWithPath:))\n  var pluginsDirectory: URL?\n\n  /// The minimum log level to output to stdout.\n  @Option(\n    help: \"The minimum log level to output to stdout.\",\n    transform: { string in\n      switch string {\n        case \"trace\":\n          return .trace\n        case \"verbose\":\n          return .verbose\n        case \"debug\":\n          return .debug\n        case \"info\":\n          return .info\n        case \"notice\":\n          return .notice\n        case \"warning\":\n          return .warning\n        case \"error\":\n          return .error\n        case \"critical\":\n          return .critical\n        default:\n          throw CommandLineArgumentsError.invalidLogLevel(string)\n      }\n    })\n  var logLevel = LogLevel.info\n\n  /// Xcode passes the `-NSDocumentRevisionsDebugMode` flag when running applications (no clue why).\n  /// It needs to be defined here because otherwise it throws an error due to strict parsing.\n  @Option(\n    name: .customLong(\"NSDocumentRevisionsDebugMode\", withSingleDash: true),\n    help: .init(\"Ignore this, just Xcode being weird.\", visibility: .hidden))\n  var nsDocumentRevisionsDebugMode: String?\n}\n"
  },
  {
    "path": "Sources/Client/Components/AddressField.swift",
    "content": "import SwiftUI\nimport Combine\n\nstruct AddressField: View {\n  // swiftlint:disable:next force_try\n  static private let ipRegex = try! NSRegularExpression(\n    pattern: \"^((25[0-5]|(2[0-4]|1\\\\d|[1-9]|)\\\\d)(\\\\.(?!$)|$)){4}$\"\n  )\n\n  // swiftlint:disable:next force_try\n  static private let domainRegex = try! NSRegularExpression(\n    pattern: \"^[A-Za-z0-9-]+(\\\\.[A-Za-z0-9-]+)*$\"\n  )\n\n  let title: String\n\n  @State private var string: String\n  @Binding private var host: String\n  @Binding private var port: UInt16?\n\n  @Binding private var isValid: Bool\n\n  init(_ title: String, host: Binding<String>, port: Binding<UInt16?>, isValid: Binding<Bool>) {\n    self.title = title\n    _host = host\n    _port = port\n\n    if let port = port.wrappedValue {\n      _string = State(initialValue: \"\\(host.wrappedValue):\\(port)\")\n    } else {\n      _string = State(initialValue: host.wrappedValue)\n    }\n\n    _isValid = isValid\n  }\n\n  private func update(_ newValue: String) {\n    let components = newValue.split(separator: \":\")\n    if components.count == 0 {\n      log.trace(\"Invalid ip, empty string\")\n      isValid = false\n    } else if components.count > 2 {\n      log.trace(\"Invalid ip, too many components: '\\(newValue)'\")\n      isValid = false\n    } else if newValue.hasSuffix(\":\") {\n      log.trace(\"Invalid ip, empty port: '\\(newValue)'\")\n      isValid = false\n    }\n\n    // Check host component\n    if components.count > 0 {\n      let hostString = String(components[0])\n      let range = NSRange(location: 0, length: hostString.utf16.count)\n      let isIp = Self.ipRegex.firstMatch(in: hostString, options: [], range: range) != nil\n      let isDomain = Self.domainRegex.firstMatch(in: hostString, options: [], range: range) != nil\n      if isIp || isDomain {\n        host = hostString\n        isValid = true\n      } else {\n        log.trace(\"Invalid host component: '\\(hostString)'\")\n        isValid = false\n      }\n    }\n\n    // Check port component\n    if components.count == 2 {\n      let portString = components[1]\n      if let port = UInt16(portString) {\n        self.port = port\n        isValid = true\n      } else {\n        log.trace(\"Invalid port component: '\\(portString)'\")\n        isValid = false\n      }\n    } else {\n      port = nil\n    }\n  }\n\n  var body: some View {\n    TextField(title, text: $string)\n      .onReceive(Just(string), perform: update)\n      .onAppear {\n        update(string)\n      }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Components/ButtonStyles/DisabledButtonStyle.swift",
    "content": "import SwiftUI\n\nstruct DisabledButtonStyle: ButtonStyle {\n  func makeBody(configuration: Configuration) -> some View {\n    HStack {\n      Spacer()\n      configuration.label.foregroundColor(.gray)\n      Spacer()\n    }\n    .padding(6)\n    .background(Color.secondary.brightness(-0.4).cornerRadius(4))\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Components/ButtonStyles/PrimaryButtonStyle.swift",
    "content": "import SwiftUI\n\n#if os(tvOS)\n  typealias PrimaryButtonStyle = DefaultButtonStyle\n#else\n  struct PrimaryButtonStyle: ButtonStyle {\n    func makeBody(configuration: Configuration) -> some View {\n      HStack {\n        Spacer()\n        configuration.label.foregroundColor(.white)\n        Spacer()\n      }\n      .padding(6)\n      .background(Color.accentColor.brightness(configuration.isPressed ? 0.15 : 0).cornerRadius(4))\n    }\n  }\n#endif\n"
  },
  {
    "path": "Sources/Client/Components/ButtonStyles/SecondaryButtonStyle.swift",
    "content": "import SwiftUI\n\n#if os(tvOS)\n  typealias SecondaryButtonStyle = DefaultButtonStyle\n#else\n  struct SecondaryButtonStyle: ButtonStyle {\n    func makeBody(configuration: Configuration) -> some View {\n      HStack {\n        Spacer()\n        configuration.label.foregroundColor(.white)\n        Spacer()\n      }\n      .padding(6)\n      .background(Color.secondary.brightness(configuration.isPressed ? 0 : -0.15).cornerRadius(4))\n    }\n  }\n#endif\n"
  },
  {
    "path": "Sources/Client/Components/EditableList/EditableList.swift",
    "content": "import SwiftUI\n\nenum EditableListAction {\n  case delete\n  case edit\n  case moveUp\n  case moveDown\n  case select\n}\n\nenum EditableListState {\n  case list\n  case addItem\n  case editItem(Int)\n}\n\nstruct EditableList<Row: View, ItemEditor: EditorView>: View {\n  @State var state: EditableListState = .list\n\n  #if os(tvOS)\n  @Namespace var focusNamespace\n  #endif\n\n  @Binding var items: [ItemEditor.Item]\n  @Binding var selected: Int?\n\n  let itemEditor: ItemEditor.Type\n  let row: (\n    _ item: ItemEditor.Item,\n    _ selected: Bool,\n    _ isFirst: Bool,\n    _ isLast: Bool,\n    _ handler: @escaping (EditableListAction) -> Void\n  ) -> Row\n\n  let save: (() -> Void)?\n  let cancel: (() -> Void)?\n\n  /// Message to display when the list is empty.\n  let emptyMessage: String\n\n  init(\n    _ items: Binding<[ItemEditor.Item]>,\n    selected: Binding<Int?> = Binding<Int?>(get: { nil }, set: { _ in }),\n    itemEditor: ItemEditor.Type,\n    @ViewBuilder row: @escaping (\n      _ item: ItemEditor.Item,\n      _ selected: Bool,\n      _ isFirst: Bool,\n      _ isLast: Bool,\n      _ handler: @escaping (EditableListAction) -> Void\n    ) -> Row,\n    saveAction: (() -> Void)?,\n    cancelAction: (() -> Void)?,\n    emptyMessage: String = \"No items\",\n    forceShowCreation: Bool = false\n  ) {\n    self._items = items\n    self._selected = selected\n    self.itemEditor = itemEditor\n    self.row = row\n    self.emptyMessage = emptyMessage\n    save = saveAction\n    cancel = cancelAction\n\n    if forceShowCreation {\n      _state = State(wrappedValue: .addItem)\n    }\n  }\n\n  func handleItemEditableListAction(_ index: Int, _ action: EditableListAction) {\n    switch action {\n      case .delete:\n        if selected == index {\n          if items.count == 1 {\n            selected = nil\n          } else {\n            selected = 0\n          }\n        } else if let selectedIndex = selected, selectedIndex > index {\n          selected = selectedIndex - 1\n        }\n        items.remove(at: index)\n      case .edit:\n        state = .editItem(index)\n      case .moveUp:\n        if index != 0 {\n          let item = items.remove(at: index)\n          items.insert(item, at: index - 1)\n        }\n      case .moveDown:\n        if index != items.count - 1 {\n          let item = items.remove(at: index)\n          items.insert(item, at: index + 1)\n        }\n      case .select:\n        selected = index\n    }\n  }\n\n  var body: some View {\n    Group {\n      switch state {\n        case .list:\n          VStack(alignment: .center, spacing: 16) {\n            if items.count == 0 {\n              Text(emptyMessage).italic()\n            }\n\n            ScrollView(showsIndicators: true) {\n              ForEach(items.indices, id: \\.self) { index in\n                VStack(alignment: .leading) {\n                  Divider()\n\n                  let isFirst = index == 0\n                  let isLast = index == items.count - 1\n                  row(items[index], selected == index, isFirst, isLast, { action in\n                    handleItemEditableListAction(index, action)\n                  })\n\n                  if index == items.count - 1 {\n                    Divider()\n                  }\n                }\n              }\n            }\n            #if os(tvOS)\n            .focusSection()\n            #endif\n\n            VStack {\n              Button(\"Add\") {\n                state = .addItem\n              }\n              #if os(tvOS)\n              .prefersDefaultFocus(in: focusNamespace)\n              #endif\n              .buttonStyle(SecondaryButtonStyle())\n\n              if save != nil || cancel != nil {\n                HStack {\n                  if let cancel = cancel {\n                    Button(\"Cancel\", action: cancel)\n                      .buttonStyle(SecondaryButtonStyle())\n                  }\n                  if let save = save {\n                    Button(\"Save\", action: save)\n                      .buttonStyle(PrimaryButtonStyle())\n                  }\n                }\n              }\n            }\n            #if !os(tvOS)\n            .frame(width: 200)\n            #else\n            .focusSection()\n            #endif\n          }\n          #if os(tvOS)\n          .focusSection()\n          #endif\n        case .addItem:\n          itemEditor.init(nil, completion: { newItem in\n            items.append(newItem)\n            selected = items.count - 1\n            state = .list\n          }, cancelation: {\n            state = .list\n          })\n        case let .editItem(index):\n          itemEditor.init(items[index], completion: { editedItem in\n            items[index] = editedItem\n            state = .list\n          }, cancelation: {\n            state = .list\n          })\n      }\n    }\n    #if !os(tvOS)\n    .frame(width: 400)\n    #else\n    .focusScope(focusNamespace)\n    #endif\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Components/EditableList/EditorView.swift",
    "content": "import SwiftUI\n\nprotocol EditorView: View {\n  associatedtype Item\n  \n  init(_ item: Item?, completion: @escaping (Item) -> Void, cancelation: (() -> Void)?)\n}\n"
  },
  {
    "path": "Sources/Client/Components/EmailField.swift",
    "content": "import SwiftUI\nimport Combine\n\nstruct EmailField: View {\n  // swiftlint:disable:next force_try\n  static private let regex = try! NSRegularExpression(\n    pattern: \"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\\\.[A-Za-z]{2,64}\"\n  )\n  \n  private let title: String\n  \n  @Binding private var email: String\n  @Binding private var isValid: Bool\n  \n  init(\n    _ title: String,\n    email: Binding<String>,\n    isValid: Binding<Bool> = Binding<Bool>(get: { false }, set: { _ in })\n  ) {\n    self.title = title\n    _email = email\n    _isValid = isValid\n  }\n  \n  private func validate(_ email: String) {\n    let range = NSRange(location: 0, length: email.utf16.count)\n    isValid = Self.regex.firstMatch(in: email, options: [], range: range) != nil\n  }\n  \n  var body: some View {\n    TextField(title, text: $email)\n      .onReceive(Just(email), perform: validate)\n      .onAppear {\n        validate(email)\n      }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Components/IconButton.swift",
    "content": "import SwiftUI\n\nstruct IconButton: View {\n  let icon: String\n  let isDisabled: Bool\n  let action: () -> Void\n  \n  init(_ icon: String, isDisabled: Bool = false, action: @escaping () -> Void) {\n    self.icon = icon\n    self.isDisabled = isDisabled\n    self.action = action\n  }\n  \n  var body: some View {\n    Button(action: action, label: {\n      Image(systemName: icon)\n    })\n    #if !os(tvOS)\n    .buttonStyle(BorderlessButtonStyle())\n    #endif\n    .disabled(isDisabled)\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Components/LegacyFormattedTextView.swift",
    "content": "import Foundation\nimport DeltaCore\nimport SwiftUI\n\n/// SwiftUI view for displaying text that is formatted using Legacy formatting codes.\n///\n/// See ``LegacyTextFormatter``.\nstruct LegacyFormattedTextView: View {\n  /// The legacy formatted string.\n  var string: String\n  /// The formatted text.\n  var attributedString = NSAttributedString(string: \"\")\n  /// The font size.\n  var fontSize: CGFloat\n  /// The alignment of the text.\n  var alignment: NSTextAlignment = .center\n  \n  init(legacyString: String, fontSize: CGFloat, alignment: NSTextAlignment = .center) {\n    self.string = legacyString\n    self.attributedString = LegacyFormattedText(legacyString).attributedString(fontSize: fontSize)\n    self.fontSize = fontSize\n    self.alignment = alignment\n  }\n  \n  var body: some View {\n    #if os(tvOS)\n      Text(attributedString.string)\n    #else\n      NSAttributedTextView(\n        attributedString: attributedString,\n        alignment: alignment\n      ).frame(width: attributedString.size().width)\n    #endif\n  }\n}\n\n#if canImport(AppKit)\nstruct NSAttributedTextView: NSViewRepresentable {\n  var attributedString: NSAttributedString\n  var alignment: NSTextAlignment\n  \n  func makeNSView(context: Context) -> some NSView {\n    let label = NSTextField()\n    label.backgroundColor = .clear\n    label.isBezeled = false\n    label.isEditable = false\n    return label\n  }\n  \n  func updateNSView(_ nsView: NSViewType, context: Context) {\n    guard let label = nsView as? NSTextField else { return }\n    label.attributedStringValue = attributedString\n    label.alignment = .left\n  }\n}\n#elseif canImport(UIKit)\nstruct NSAttributedTextView: UIViewRepresentable {\n  var attributedString: NSAttributedString\n  var alignment: NSTextAlignment\n  \n  func makeUIView(context: Context) -> some UIView {\n    let label = UILabel()\n    label.backgroundColor = .clear\n    label.autoresizingMask = [.flexibleWidth, .flexibleHeight]\n    return label\n  }\n  \n  func updateUIView(_ uiView: UIViewType, context: Context) {\n    guard let label = uiView as? UILabel else { return }\n    label.attributedText = attributedString\n  }\n}\n#else\n#error(\"Unsupported platform, no NSAttributedTextView\")\n#endif\n"
  },
  {
    "path": "Sources/Client/Components/OptionalNumberField.swift",
    "content": "import SwiftUI\nimport Combine\n\nstruct OptionalNumberField<Number: FixedWidthInteger>: View {\n  let title: String\n  \n  @State private var string: String\n  @Binding private var number: Number?\n  \n  @Binding private var isValid: Bool\n  \n  init(_ title: String, number: Binding<Number?>, isValid: Binding<Bool>) {\n    self.title = title\n    \n    _number = number\n    _string = State(initialValue: number.wrappedValue?.description ?? \"\")\n    _isValid = isValid\n  }\n  \n  var body: some View {\n    TextField(title, text: $string)\n      .onReceive(Just(string)) { newValue in\n        if newValue == \"\" {\n          number = nil\n          isValid = false\n        }\n        \n        if let number = Number(newValue) {\n          self.number = number\n          isValid = true\n        } else {\n          isValid = false\n        }\n      }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Components/PingIndicator.swift",
    "content": "import SwiftUI\n\nstruct PingIndicator: View {\n  let color: Color\n  \n  var body: some View {\n    Circle()\n      .foregroundColor(color)\n      .fixedSize()\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Components/PixellatedBorder.swift",
    "content": "import SwiftUI\n\nstruct PixellatedBorder: InsettableShape {\n  public var insetAmount: CGFloat = 0\n  \n  private let borderCorner: CGFloat = 4\n\n  func path(in rect: CGRect) -> Path {\n    var path = Path()\n\n    // Top\n    path.move(to: CGPoint(x: borderCorner + insetAmount, y: borderCorner / 2 + insetAmount))\n    path.addLine(to: CGPoint(x: rect.width - borderCorner - insetAmount, y: borderCorner / 2 + insetAmount))\n\n    // Right\n    path.move(to: CGPoint(x: rect.width - borderCorner / 2 - insetAmount, y: borderCorner + insetAmount))\n    path.addLine(to: CGPoint(x: rect.width - borderCorner/2 - insetAmount, y: rect.height - borderCorner - insetAmount))\n\n    // Bottom\n    path.move(to: CGPoint(x: rect.width - borderCorner - insetAmount, y: rect.height - borderCorner / 2 - insetAmount))\n    path.addLine(to: CGPoint(x: borderCorner + insetAmount, y: rect.height - borderCorner / 2 - insetAmount))\n\n    // Left\n    path.move(to: CGPoint(x: borderCorner / 2 + insetAmount, y: rect.height - borderCorner - insetAmount))\n    path.addLine(to: CGPoint(x: borderCorner / 2 + insetAmount, y: borderCorner + insetAmount))\n\n    return path\n  }\n  \n  func inset(by amount: CGFloat) -> some InsettableShape {\n    var border = self\n    border.insetAmount += amount\n    return border\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Config/Config.swift",
    "content": "import Foundation\nimport DeltaCore\nimport OrderedCollections\n\n/// The client's configuration. Usually stored in a JSON file.\nstruct Config: Codable, ClientConfiguration {\n  /// The current config version used by the client.\n  static let currentVersion: Float = 0\n\n  /// The config format version. Used to detect outdated config files (and maybe\n  /// in future to migrate them). Completely independent from the actual Delta\n  /// Client version.\n  var version: Float = currentVersion\n\n  /// The random token used to identify ourselves to Mojang's API\n  var clientToken = UUID().uuidString\n  /// The id of the currently selected account.\n  var selectedAccountId: String?\n  /// The dictionary containing all of the user's accounts.\n  var accounts: [String: Account] = [:]\n  /// The user's server list.\n  var servers: [ServerDescriptor] = []\n  /// Rendering related configuration.\n  var render = RenderConfiguration(enableOrderIndependentTransparency: true)\n  /// Plugins that the user has explicitly unloaded.\n  var unloadedPlugins: [String] = []\n  /// The user's keymap.\n  var keymap = Keymap.default\n  /// Whether to use the sprint key as a toggle.\n  var toggleSprint = false\n  /// Whether to use the sneak key as a toggle.\n  var toggleSneak = false\n  /// The in game mouse sensitivity\n  var mouseSensitivity: Float = 1\n\n  /// The user's accounts, ordered by username.\n  var orderedAccounts: [Account] {\n    accounts.values.sorted { $0.username <= $1.username }\n  }\n\n  /// The account the user has currently selected.\n  var selectedAccount: Account? {\n    if let id = selectedAccountId {\n      return accounts[id]\n    } else {\n      return nil\n    }\n  }\n\n  /// Creates the default config.\n  init() {}\n\n  /// Loads a configuration from a JSON configuration file.\n  static func load(from file: URL) throws -> Config {\n    let data = try Data(contentsOf: file)\n    let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]\n\n    guard let version = json?[\"version\"] as? Float else {\n      throw ConfigError.missingVersion\n    }\n\n    guard version == currentVersion else {\n      throw ConfigError.unsupportedVersion(version)\n    }\n\n    return try JSONDecoder().decode(Self.self, from: data)\n  }\n\n  /// Saves the configuration to a JSON file.\n  func save(to file: URL) throws {\n    let encoder = JSONEncoder()\n    encoder.outputFormatting = [.prettyPrinted, .sortedKeys]\n    let data = try encoder.encode(self)\n    try data.write(to: file)\n  }\n\n  /// Adds an account to the configuration. If an account with the same id already\n  /// exists, it gets replaced.\n  mutating func addAccount(_ account: Account) {\n    accounts[account.id] = account\n  }\n\n  /// Sets the selected account id. If the `id` is `nil`, then no account is selected.\n  mutating func selectAccount(withId id: String?) {\n    selectedAccountId = id\n  }\n\n  /// Adds a collection of accounts to the configuration. Any existing accounts with\n  /// overlapping ids are replaced.\n  mutating func addAccounts(_ accounts: [Account]) {\n    for account in accounts {\n      addAccount(account)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Config/ConfigError.swift",
    "content": "import Foundation\n\npublic enum ConfigError: LocalizedError {\n  /// The config file is missing a version.\n  case missingVersion\n  /// The config file is using an unsupported version.\n  case unsupportedVersion(Float)\n  /// The account in question is of an invalid type.\n  case invalidAccountType\n  /// No account exists with the given id.\n  case invalidAccountId\n  /// The currently selected account is of an invalid type.\n  case invalidSelectedAccountType\n  /// Failed to refresh an account.\n  case accountRefreshFailed\n  /// Not account has been selected.\n  case noAccountSelected\n  \n  public var errorDescription: String? {\n    switch self {\n      case .missingVersion:\n        return \"Your config file is missing a 'version' field.\"\n      case let .unsupportedVersion(version):\n        return \"Your config file version (\\(version)) is unsupported. \" +\n          \"\\(Config.currentVersion) is the current supported version.\"\n      case .invalidAccountType:\n        return \"The account in question is of an invalid type.\"\n      case .invalidAccountId:\n        return \"No such account.\"\n      case .invalidSelectedAccountType:\n        return \"The currently selected account is of an invalid type.\"\n      case .accountRefreshFailed:\n        return \"Failed to refresh the given account.\"\n      case .noAccountSelected:\n        return \"No account has been selected.\"\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Config/ManagedConfig.swift",
    "content": "import Foundation\nimport DeltaCore\n\n/// Manages the config stored in a config file.\n@dynamicMemberLookup\nfinal class ManagedConfig: ObservableObject {\n  /// The current config.\n  var config: Config {\n    willSet {\n      objectWillChange.send()\n    }\n    didSet {\n      do {\n        // TODO: Reduce the number of writes to disk, perhaps by asynchronously\n        //   debouncing. Often multiple config changes will happen in very quick\n        //   succession.\n        try config.save(to: file)\n      } catch {\n        saveErrorHandler?(error)\n      }\n    }\n  }\n\n  subscript<T>(dynamicMember member: WritableKeyPath<Config, T>) -> T {\n    get {\n      config[keyPath: member]\n    }\n    set {\n      config[keyPath: member] = newValue\n    }\n  }\n\n  /// The file the configuration is stored in.\n  let file: URL\n  /// A handler for errors which occur when attempting to save the configuration\n  /// to disk in the background.\n  let saveErrorHandler: ((any Error) -> Void)?\n\n  /// Manages a configuration backed by a given file.\n  init(_ config: Config, backedBy file: URL, saveErrorHandler: ((any Error) -> Void)?) {\n    self.file = file\n    self.config = config\n    self.saveErrorHandler = saveErrorHandler\n  }\n\n  /// Resets the configuration to defaults.\n  func reset() throws {\n    config = Config()\n  }\n\n  /// Refreshes the specified account if necessary, saves it, and returns it (if\n  /// an account exists with the given id). \n  func refreshAccount(withId id: String) async throws -> Account {\n    // Takes an id instead of a whole account to make it clear that it's\n    // operating in-place on the config (taking an account would make it\n    // unclear whether the account must be in the underlying config and\n    // whether it gets saved after getting refreshed).\n    guard let account = config.accounts[id] else {\n      throw ConfigError.invalidAccountId.with(\"Id\", id)\n    }\n\n    guard account.online?.accessToken.hasExpired == true else {\n      return account\n    }\n    \n    do {\n      let refreshedAccount = try await account.refreshed(withClientToken: config.clientToken)\n      config.addAccount(refreshedAccount)\n      return refreshedAccount\n    } catch {\n      throw ConfigError.accountRefreshFailed\n        .with(\"Username\", account.username)\n        .becauseOf(error)\n    }\n  }\n\n  /// Gets the currently selected account (throws if no account is selected),\n  /// and refreshes the account if necessary. May seem like the function is\n  /// doing too much, but it's beneficial for usage of this to be concise.\n  func selectedAccountRefreshedIfNecessary() async throws -> Account {\n    guard let account = config.selectedAccount else {\n      throw ConfigError.noAccountSelected\n    }\n\n    return try await refreshAccount(withId: account.id)\n  }\n\n  // TODO: Maybe we just shouldn't even refresh at app start, we could just refresh at time of\n  //   use.\n  /// Refreshes all accounts which are close to expiring or have expired.\n  /// - Returns: Any errors which occurred during account refreshing.\n  func refreshAccounts() async -> [any Error] {\n    var errors: [any Error] = []\n    for account in config.accounts.values {\n      do {\n        _ = try await refreshAccount(withId: account.id)\n      } catch {\n        errors.append(error)\n      }\n    }\n    return errors\n  }\n}\n\nextension ManagedConfig: ClientConfiguration {\n  var render: RenderConfiguration {\n    get {\n      config.render\n    }\n    set {\n      config.render = newValue\n    }\n  }\n\n  var keymap: Keymap {\n    get {\n      config.keymap\n    }\n    set {\n      config.keymap = newValue\n    }\n  }\n\n  var toggleSprint: Bool {\n    get {\n      config.toggleSprint\n    }\n    set {\n      config.toggleSprint = newValue\n    }\n  }\n\n  var toggleSneak: Bool {\n    get {\n      config.toggleSneak\n    }\n    set {\n      config.toggleSneak = newValue\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/DeltaClientApp.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\n/// The entry-point for Delta Client.\nstruct DeltaClientApp: App {\n  @StateObject var appState = StateWrapper<AppState>(initial: .serverList)\n  @StateObject var pluginEnvironment = PluginEnvironment()\n  @StateObject var modal = Modal()\n  @StateObject var controllerHub = ControllerHub()\n\n  @State var storage: StorageDirectory?\n\n  @State var hasLoaded = false\n\n  let arguments: CommandLineArguments\n\n  /// A Delta Client version.\n  enum Version {\n    /// A version such as `1.0.0`.\n    case semantic(String)\n    /// A nightly build's version (e.g. `ae348f8...`).\n    case commit(String)\n  }\n\n  /// Gets the current client's version. `nil` if the app doesn't have an `Info.plist` or\n  /// the `CFBundleShortVersionString` key is missing.\n  static var version: Version? {\n    guard let versionString = Bundle.main.object(forInfoDictionaryKey: \"CFBundleShortVersionString\") as? String else {\n      return nil\n    }\n\n    if versionString.hasPrefix(\"commit: \") {\n      return .commit(String(versionString.dropFirst(\"commit: \".count)))\n    } else {\n      return .semantic(versionString)\n    }\n  }\n\n  init() {\n    arguments = CommandLineArguments.parseOrExit()\n    setConsoleLogLevel(arguments.logLevel)\n\n    DiscordManager.shared.updateRichPresence(to: .menu)\n  }\n\n  var body: some Scene {\n    WindowGroup {\n      LoadAndThen(arguments, $hasLoaded, $storage) { managedConfig, resourcePack, pluginEnvironment in\n        RouterView()\n          .environmentObject(resourcePack)\n          .environmentObject(pluginEnvironment)\n          .environmentObject(managedConfig)\n          .onAppear {\n            // TODO: Make a nice clean onboarding experience\n            if managedConfig.selectedAccountId == nil {\n              appState.update(to: .login)\n            }\n          }\n      }\n        .environment(\\.storage, storage ?? StorageDirectoryEnvironmentKey.defaultValue)\n        .environmentObject(modal)\n        .environmentObject(appState)\n        .environmentObject(controllerHub)\n        .frame(maxWidth: .infinity, maxHeight: .infinity)\n        .navigationTitle(\"Delta Client\")\n        .alert(isPresented: modal.isPresented) {\n          Alert(title: Text(\"Error\"), message: (modal.content?.message).map(Text.init), dismissButton: Alert.Button.default(Text(\"OK\")))\n        }\n    }\n    #if os(macOS)\n      .commands {\n        // Add preferences menu item and shortcut (cmd+,)\n        CommandGroup(after: .appSettings, addition: {\n          Button(\"Preferences\") {\n            guard hasLoaded, modal.content == nil else {\n              return\n            }\n\n            switch appState.current {\n              case .serverList, .editServerList, .accounts, .login, .directConnect:\n                appState.update(to: .settings(nil))\n              case .playServer, .settings:\n                break\n            }\n          }\n          .keyboardShortcut(KeyboardShortcut(KeyEquivalent(\",\"), modifiers: [.command]))\n        })\n        CommandGroup(after: .toolbar, addition: {\n          Button(\"Logs\") {\n            guard let file = storage?.currentLogFile else {\n              modal.error(\"File logging not enabled yet\")\n              return\n            }\n            NSWorkspace.shared.open(file)\n          }\n        })\n        CommandGroup(after: .windowSize, addition: {\n          Button(\"Toggle Full Screen\") {\n            NSApp?.windows.first?.toggleFullScreen(nil)\n          }\n          .keyboardShortcut(KeyboardShortcut(KeyEquivalent(\"f\"), modifiers: [.control, .command]))\n        })\n      }\n    #endif\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Discord/DiscordManager.swift",
    "content": "import Foundation\n\n#if os(macOS)\nimport SwordRPC\n#endif\n\n/// Manages discord interactions\nfinal class DiscordManager {\n  /// ``DiscordManager`` shared instance.\n  public static let shared = DiscordManager()\n  \n  /// The discord rich presence currently being displayed on the user's profile.\n  private var currentPresenceState: RichPresenceState?\n  \n  #if os(macOS)\n  /// Manages discord rich presence.\n  private let richPresenceManager = SwordRPC(appId: \"907736809990148107\")\n  #endif\n  \n  private init() {\n    #if os(macOS)\n    _ = richPresenceManager.connect()\n    #endif\n  }\n  \n  /// Updates the user's discord rich presence state for Delta Client if the current OS is supported.\n  /// - Parameter state: the preset to be used to configure the rich presence.\n  public func updateRichPresence(to state: RichPresenceState) {\n    #if os(macOS)\n    guard currentPresenceState != state else {\n      return\n    }\n    \n    var presence = RichPresence()\n    presence.assets.largeImage = \"dark\"\n    presence.details = state.title\n    presence.state = state.subtitle\n    presence.timestamps.start = Date()\n    richPresenceManager.setPresence(presence)\n    currentPresenceState = state\n    #endif\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Discord/DiscordPresenceState.swift",
    "content": "import Foundation\n\nextension DiscordManager {\n  /// State of Discord rich presence.\n  enum RichPresenceState: Equatable {\n    /// The user is not currently connected to a server.\n    case menu\n    /// The user is playing on a server.\n    case game(server: String?)\n    \n    /// Shown as the state of the game in Discord. Displayed under the name 'Delta Client'.\n    var title: String {\n      switch self {\n        case .menu:\n          return \"Main menu\"\n        case .game:\n          return \"In game\"\n      }\n    }\n    \n    /// Shown under `title` by Discord.\n    var subtitle: String? {\n      switch self {\n        case .menu:\n          return nil\n        case .game(let server):\n          if let server = server {\n            return \"Playing on '\\(server)'\"\n          }\n          return nil\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Extensions/Binding+onChange.swift",
    "content": "import Foundation\nimport SwiftUI\n\n// This excellent solution is from https://www.hackingwithswift.com/quick-start/swiftui/how-to-run-some-code-when-state-changes-using-onchange\n\nextension Binding {\n  func onChange(_ handler: @escaping (Value) -> Void) -> Binding<Value> {\n    Binding(\n      get: { self.wrappedValue },\n      set: { newValue in\n        self.wrappedValue = newValue\n        handler(newValue)\n      }\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Extensions/Box+ObservableObject.swift",
    "content": "import Combine\nimport DeltaCore\n\nextension Box: ObservableObject {\n  public var objectWillChange: ObservableObjectPublisher {\n    ObservableObjectPublisher()\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Extensions/TaskProgress+ObservableObject.swift",
    "content": "import Combine\nimport DeltaCore\n\nextension TaskProgress: ObservableObject {\n  public var objectWillChange: ObservableObjectPublisher {\n    let publisher = ObservableObjectPublisher()\n    onChange { _ in\n      ThreadUtil.runInMain {\n        publisher.send()\n      }\n    }\n    return publisher\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Extensions/View.swift",
    "content": "import SwiftUI\n\nextension View {\n  /// Returns a copy of self with the specified property set to the given value.\n  /// Useful for implementing custom view modifiers.\n  func with<T>(_ keyPath: WritableKeyPath<Self, T>, _ value: T) -> Self {\n    var view = self\n    view[keyPath: keyPath] = value\n    return view\n  }\n\n  /// Appends an action to an action stored property. Useful for implementing custom\n  /// view modifiers such as `onClick` etc. Allows the modifier to be called multiple\n  /// times without overwriting previous actions. If the stored action is nil, the\n  /// given action becomes the stored action, otherwise the new action is appended to\n  /// the existing action.\n  func appendingAction<T>(\n    to keyPath: WritableKeyPath<Self, ((T) -> Void)?>,\n    _ action: @escaping (T) -> Void\n  ) -> Self {\n    with(keyPath) { argument in\n      self[keyPath: keyPath]?(argument)\n      action(argument)\n    }\n  }\n\n  /// Appends an action to an action stored property. Useful for implementing custom\n  /// view modifiers such as `onClick` etc. Allows the modifier to be called multiple\n  /// times without overwriting previous actions. If the stored action is nil, the\n  /// given action becomes the stored action, otherwise the new action is appended to\n  /// the existing action.\n  func appendingAction<T, U>(\n    to keyPath: WritableKeyPath<Self, ((T, U) -> Void)?>,\n    _ action: @escaping (T, U) -> Void\n  ) -> Self {\n    with(keyPath) { argument1, argument2 in\n      self[keyPath: keyPath]?(argument1, argument2)\n      action(argument1, argument2)\n    }\n  }\n\n  /// Appends an action to an action stored property. Useful for implementing custom\n  /// view modifiers such as `onClick` etc. Allows the modifier to be called multiple\n  /// times without overwriting previous actions. If the stored action is nil, the\n  /// given action becomes the stored action, otherwise the new action is appended to\n  /// the existing action.\n  func appendingAction<T, U, V>(\n    to keyPath: WritableKeyPath<Self, ((T, U, V) -> Void)?>,\n    _ action: @escaping (T, U, V) -> Void\n  ) -> Self {\n    with(keyPath) { argument1, argument2, argument3 in\n      self[keyPath: keyPath]?(argument1, argument2, argument3)\n      action(argument1, argument2, argument3)\n    }\n  }\n\n  /// Appends an action to an action stored property. Useful for implementing custom\n  /// view modifiers such as `onClick` etc. Allows the modifier to be called multiple\n  /// times without overwriting previous actions. If the stored action is nil, the\n  /// given action becomes the stored action, otherwise the new action is appended to\n  /// the existing action.\n  func appendingAction<T, U, V, W>(\n    to keyPath: WritableKeyPath<Self, ((T, U, V, W) -> Void)?>,\n    _ action: @escaping (T, U, V, W) -> Void\n  ) -> Self {\n    with(keyPath) { argument1, argument2, argument3, argument4 in\n      self[keyPath: keyPath]?(argument1, argument2, argument3, argument4)\n      action(argument1, argument2, argument3, argument4)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/GitHub/GitHubBranch.swift",
    "content": "import Foundation\n\nstruct GitHubBranch: Decodable {\n  var name: String\n  var commit: GitHubCommit\n}\n"
  },
  {
    "path": "Sources/Client/GitHub/GitHubCommit.swift",
    "content": "import Foundation\n\nstruct GitHubCommit: Decodable {\n  var sha: String\n  var url: String\n}\n"
  },
  {
    "path": "Sources/Client/GitHub/GitHubComparison.swift",
    "content": "import Foundation\n\n/// Represents a comparison between GitHub branches and/or commits\nstruct GitHubComparison: Decodable {\n  /// The position difference between the compared objects in the tree\n  enum Status: String, Decodable {\n    case diverged\n    case ahead\n    case behind\n    case identical\n  }\n  \n  var status: Status\n}\n"
  },
  {
    "path": "Sources/Client/GitHub/GitHubReleasesAPIResponse.swift",
    "content": "import Foundation\n\nstruct GitHubReleasesAPIResponse: Codable {\n  var tagName: String\n  var assets: [Asset]\n  \n  struct Asset: Codable {\n    var browserDownloadURL: String\n\n    enum CodingKeys: String, CodingKey {\n      case browserDownloadURL = \"browserDownloadUrl\"\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Input/Controller.swift",
    "content": "import GameController\nimport Combine\n\n/// A control on a controller (a button, a thumbstick, a trigger, etc).\nprotocol Control {\n  associatedtype Element: GCControllerElement\n\n  mutating func update(with element: Element)\n}\n\n/// A game controller (e.g. a PlayStation 5 controller).\nclass Controller: ObservableObject {\n  /// A display name for the controller.\n  var name: String {\n    var name = gcController.productCategory\n    if gcController.playerIndex != GCControllerPlayerIndex.indexUnset {\n      name = \"\\(name) (player \\(gcController.playerIndex.rawValue + 1))\"\n    }\n    return name\n  }\n\n  /// The underlying controller getting wrapped. I usually wouldn't prefix\n  /// a property like that to match its type, but ``ControllerHub`` was getting\n  /// so confusing with the two different types of controllers both getting\n  /// referred to as `controller` and `controller.controller` and stuff.\n  let gcController: GCController\n\n  /// A publisher for subscribing to controller input events.\n  var eventPublisher: AnyPublisher<Event, Never> {\n    eventSubject.eraseToAnyPublisher()\n  }\n\n  /// The states of all buttons, indexed by ``Button/rawValue``. Assumes that the\n  /// raw values form a contiguous range starting from 0.\n  private var buttonStates = Array(repeating: false, count: Button.allCases.count)\n  /// The states of all thumbsticks, indexed by ``Thumbstick/rawValue``. Assumes that the\n  /// raw values form a contiguous range starting from 0.\n  private var thumbstickStates = Array(\n    repeating: ThumbstickState(),\n    count: Thumbstick.allCases.count\n  )\n\n  /// Private to prevent users from publishing their own events and messing things up.\n  private var eventSubject = PassthroughSubject<Event, Never>()\n\n  /// Wraps a controller to make it easily observable. Returns `nil` if the\n  /// controller isn't supported (doesn't have an extended gamepad).\n  init?(for gcController: GCController) {\n    guard let pad = gcController.extendedGamepad else {\n      return nil\n    }\n\n    self.gcController = gcController\n\n    pad.valueChangedHandler = { [weak self] pad, element in\n      guard let self = self else { return }\n\n      // We could use `element` to skip buttons which haven't been updated,\n      // but that wouldn't work for the dpad buttons, because in that case\n      // the element is the dpad instead of the invidual dpad button that\n      // changed.\n      for button in Button.allCases {\n        // Get the button's new state, skipping buttons which the controller doesn't have.\n        let newState: Bool\n        switch button.keyPath {\n          case let .required(keyPath):\n            newState = pad[keyPath: keyPath].isPressed\n          case let .optional(keyPath):\n            guard let buttonElement = pad[keyPath: keyPath] else {\n              continue\n            }\n            newState = buttonElement.isPressed\n        }\n\n        // Some buttons set multiple updates when changing states because they're\n        // analog (like the triggers on the PS5 controller), so we need to filter\n        // those updates out.\n        guard newState != self.buttonStates[button.rawValue] else {\n          continue\n        }\n\n        self.buttonStates[button.rawValue] = newState\n        if newState {\n          self.eventSubject.send(.buttonPressed(button))\n        } else {\n          self.eventSubject.send(.buttonReleased(button))\n        }\n      }\n\n      for thumbstick in Thumbstick.allCases {\n        let thumbstickElement = pad[keyPath: thumbstick.keyPath]\n\n        // Ignore updates which don't affect this thumbstick\n        guard thumbstickElement == element else {\n          continue\n        }\n\n        let x = thumbstickElement.xAxis.value\n        let y = thumbstickElement.yAxis.value\n        self.thumbstickStates[thumbstick.rawValue].x = x\n        self.thumbstickStates[thumbstick.rawValue].y = y\n        self.eventSubject.send(.thumbstickMoved(thumbstick, x: x, y: y))\n      }\n    }\n\n    NotificationCenter.default.addObserver(\n      self,\n      selector: #selector(handlePossibleDisconnect),\n      name: NSNotification.Name.GCControllerDidDisconnect,\n      object: nil\n    )\n  }\n\n  /// Due to how vague the NotificationCenter observation API is, we just know\n  /// that *a* controller was disconnected. If it was this one, do something\n  /// about it.\n  @objc private func handlePossibleDisconnect() {\n    guard !GCController.controllers().contains(gcController) else {\n      // We survive another day, do nothing\n      return\n    }\n\n    // TODO: Do something when a controller disconnects\n  }\n}\n\nextension Controller {\n  enum Event {\n    /// A specific button was pressed.\n    case buttonPressed(Button)\n    /// A specific button was released.\n    case buttonReleased(Button)\n    /// The thumbstick moved to a new position `(x, y)`.\n    case thumbstickMoved(Thumbstick, x: Float, y: Float)\n  }\n\n  /// `GCControllerButtonInput`s are sometimes optional, but we want to treat\n  /// them all the same way so we need an enum.\n  enum GCControllerButtonKeyPath {\n    case required(KeyPath<GCExtendedGamepad, GCControllerButtonInput>)\n    case optional(KeyPath<GCExtendedGamepad, GCControllerButtonInput?>)\n  }\n\n  /// A controller button (includes triggers, shoulders, thumbstick buttons, etc). The\n  /// raw value is used as an index when storing buttons in arrays.\n  enum Button: Int, CaseIterable {\n    case leftTrigger\n    case rightTrigger\n    case leftShoulder\n    case rightShoulder\n\n    case leftThumbstickButton\n    case rightThumbstickButton\n\n    case buttonA\n    case buttonB\n    case buttonX\n    case buttonY\n\n    case dpadUp\n    case dpadDown\n    case dpadLeft\n    case dpadRight\n\n    var keyPath: GCControllerButtonKeyPath {\n      switch self {\n        case .leftTrigger:\n          return .required(\\.leftTrigger)\n        case .rightTrigger:\n          return .required(\\.rightTrigger)\n        case .leftShoulder:\n          return .required(\\.leftShoulder)\n        case .rightShoulder:\n          return .required(\\.rightShoulder)\n        case .leftThumbstickButton:\n          return .optional(\\.leftThumbstickButton)\n        case .rightThumbstickButton:\n          return .optional(\\.rightThumbstickButton)\n        case .buttonA:\n          return .required(\\.buttonA)\n        case .buttonB:\n          return .required(\\.buttonB)\n        case .buttonX:\n          return .required(\\.buttonX)\n        case .buttonY:\n          return .required(\\.buttonY)\n        case .dpadUp:\n          return .required(\\.dpad.up)\n        case .dpadDown:\n          return .required(\\.dpad.down)\n        case .dpadLeft:\n          return .required(\\.dpad.left)\n        case .dpadRight:\n          return .required(\\.dpad.right)\n      }\n    }\n  }\n\n  /// A controller thumbstick (often called a joystick). The raw value is used as\n  /// an index when storing thumbsticks in arrays.\n  enum Thumbstick: Int, CaseIterable {\n    case left\n    case right\n\n    var keyPath: KeyPath<GCExtendedGamepad, GCControllerDirectionPad> {\n      switch self {\n        case .left:\n          return \\.leftThumbstick\n        case .right:\n          return \\.rightThumbstick\n      }\n    }\n  }\n\n  /// The current state of a thumbstick.\n  struct ThumbstickState {\n    var x: Float = 0\n    var y: Float = 0\n  }\n}\n\nextension Controller: Equatable {\n  static func ==(_ lhs: Controller, _ rhs: Controller) -> Bool {\n    lhs.gcController == rhs.gcController\n  }\n}\n\nextension Controller: Hashable {\n  func hash(into hasher: inout Hasher) {\n    hasher.combine(ObjectIdentifier(gcController))\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Input/ControllerHub.swift",
    "content": "import GameController\nimport DeltaCore\n\nclass ControllerHub: ObservableObject {\n  @Published var controllers: [Controller] = []\n  @Published var currentController: Controller?\n\n  init() {\n    // Register notification observers.\n    observe(NSNotification.Name.GCControllerDidConnect)\n    observe(NSNotification.Name.GCControllerDidDisconnect)\n    observe(NSNotification.Name.GCControllerDidBecomeCurrent)\n    observe(NSNotification.Name.GCControllerDidStopBeingCurrent)\n\n    // Check for controllers that might already be connected.\n    updateControllers()\n  }\n\n  /// Registers an observer to update all controllers when a given event occurs.\n  private func observe(_ event: NSNotification.Name) {\n    NotificationCenter.default.addObserver(\n      self,\n      selector: #selector(updateControllers),\n      name: event,\n      object: nil\n    )\n  }\n\n  @objc private func updateControllers() {\n    // If we jump to the main thread synchronously then some sort of internal GCController\n    // lock never gets dropped and `GCController.current` causes a deadlock (strange).\n    DispatchQueue.main.async {\n      let gcControllers = GCController.controllers()\n\n      // Handle newly connected controllers\n      for gcController in gcControllers {\n        guard\n          !self.controllers.contains(where: { $0.gcController == gcController }),\n          let controller = Controller(for: gcController)\n        else {\n          continue\n        }\n\n        gcController.playerIndex = GCControllerPlayerIndex(rawValue: self.controllers.count) ?? .indexUnset\n        self.controllers.append(controller)\n        log.info(\"Connected \\(controller.name) controller\")\n      }\n\n      // Handle newly disconnected controllers\n      for (i, controller) in self.controllers.enumerated() {\n        if !gcControllers.contains(controller.gcController) {\n          self.controllers.remove(at: i)\n          log.info(\"Disconnected \\(controller.name) controller\")\n        }\n      }\n\n      // Update the current controller (last used)\n      var current: Controller? = nil\n      for controller in self.controllers {\n        if controller.gcController == GCController.current {\n          current = controller\n        }\n      }\n\n      if self.currentController != current {\n        self.currentController = current\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Input/InputMethod.swift",
    "content": "enum InputMethod: Hashable {\n  case keyboardAndMouse\n  case controller(Controller)    \n\n  var name: String {\n    switch self {\n      case .keyboardAndMouse:\n        return \"Keyboard and mouse\"\n      case let .controller(controller):\n        return controller.gcController.productCategory\n    }\n  }\n\n  var detail: String? {\n    switch self {\n      case .keyboardAndMouse:\n        return nil\n      case let .controller(controller):\n        let player = controller.gcController.playerIndex\n        if player != .indexUnset {\n          return \"player \\(player.rawValue + 1)\"\n        } else {\n          return nil\n        }\n    }\n  }\n\n  var isController: Bool {\n    switch self {\n      case .keyboardAndMouse:\n        return false\n      case .controller:\n        return true\n    }\n  }\n\n  var controller: Controller? {\n    switch self {\n      case .keyboardAndMouse:\n        return nil\n      case let .controller(controller):\n        return controller\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Logger.swift",
    "content": "@_exported import DeltaLogger\n"
  },
  {
    "path": "Sources/Client/Modal.swift",
    "content": "import DeltaCore\nimport SwiftUI\n\nclass Modal: ObservableObject {\n  enum Content {\n    case warning(String)\n    case errorMessage(String)\n    case error(Error)\n\n    var message: String {\n      switch self {\n        case let .warning(message):\n          return message\n        case let .errorMessage(message):\n          return message\n        case let .error(error):\n          if let richError = error as? RichError {\n            return richError.richDescription\n          } else {\n            return error.localizedDescription\n          }\n      }\n    }\n  }\n\n  var isPresented: Binding<Bool> {\n    Binding {\n      self.content != nil\n    } set: { newValue in\n      if !newValue {\n        self.content = nil\n        self.dismissHandler?()\n      }\n    }\n  }\n\n  @Published var content: Content?\n\n  private var dismissHandler: (() -> Void)?\n  /// Handlers to call when an error occurs. Guaranteed to be called on the\n  /// main thread.\n  private var errorHandlers: [(Error) -> Void] = []\n\n  func onError(_ action: @escaping (Error) -> Void) {\n    errorHandlers.append(action)\n  }\n\n  func warning(_ message: String, onDismiss dismissHandler: (() -> Void)? = nil) {\n    log.warning(message)\n    ThreadUtil.runInMain {\n      content = .warning(message)\n      self.dismissHandler = dismissHandler\n    }\n  }\n\n  func error(_ message: String, onDismiss dismissHandler: (() -> Void)? = nil) {\n    log.error(message)\n    ThreadUtil.runInMain {\n      content = .errorMessage(message)\n      self.dismissHandler = dismissHandler\n      for errorHandler in errorHandlers {\n        errorHandler(RichError(message))\n      }\n    }\n  }\n\n  func error(_ error: Error, onDismiss dismissHandler: (() -> Void)? = nil) {\n    log.error(error.localizedDescription)\n    ThreadUtil.runInMain {\n      content = .error(error)\n      self.dismissHandler = dismissHandler\n      for errorHandler in errorHandlers {\n        errorHandler(error)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/State/AppState.swift",
    "content": "import Foundation\nimport DeltaCore\n\n/// App states\nindirect enum AppState: Equatable {\n  case serverList\n  case editServerList\n  case accounts\n  case login\n  case directConnect\n  case playServer(ServerDescriptor, paneCount: Int)\n  case settings(SettingsState?)\n}\n"
  },
  {
    "path": "Sources/Client/State/StateWrapper.swift",
    "content": "import Foundation\nimport DeltaCore\n\n/// An observable wrapper around a state enum for use with SwiftUI\nfinal class StateWrapper<State>: ObservableObject {\n  @Published private(set) var current: State\n  private var history: [State] = []\n  \n  /// Create a new state wrapper with the specified initial state\n  init(initial: State) {\n    current = initial\n  }\n  \n  /// Update the app state to the state specified, on the main thread.\n  func update(to newState: State) {\n    ThreadUtil.runInMain {\n      // Update state\n      let current = self.current\n      self.current = newState\n      \n      // Simplify state history\n      if let index = history.firstIndex(where: { name(of: $0) == name(of: current) }) {\n        history.removeLast(history.count - index)\n      }\n      if let index = history.firstIndex(where: { name(of: $0) == name(of: newState) }) {\n        history.removeLast(history.count - index)\n      }\n      \n      // Update state history\n      history.append(current)\n    }\n  }\n  \n  /// Return to the previous app state\n  func pop() {\n    ThreadUtil.runInMain {\n      if !history.isEmpty {\n        let previousState = history.removeLast()\n        update(to: previousState)\n      } else {\n        log.warning(\"failed to pop app state, no previous state to return to\")\n      }\n    }\n  }\n  \n  private func name(of state: State) -> String {\n    return String(describing: state)\n  }\n}\n"
  },
  {
    "path": "Sources/Client/StorageDirectory.swift",
    "content": "import Foundation\nimport SwiftUI\nimport DeltaCore\n\n/// An error thrown by ``StorageDirectory``.\nenum StorageDirectoryError: LocalizedError {\n  case failedToCreateBackup\n\n  var errorDescription: String? {\n    switch self {\n      case .failedToCreateBackup:\n        return \"Failed to create backup of storage directory.\"\n    }\n  }\n}\n\nstruct StorageDirectoryEnvironmentKey: EnvironmentKey {\n  static let defaultValue: StorageDirectory = StorageDirectory(URL(fileURLWithPath: \".\"))\n}\n\nextension EnvironmentValues {\n  var storage: StorageDirectory {\n    get { self[StorageDirectoryEnvironmentKey.self] }\n    set { self[StorageDirectoryEnvironmentKey.self] = newValue }\n  }\n}\n\n// TODO: Find if there's a way to just have this as a struct (it's just immutable data, but needs\n//   to be a class).\n/// A wrapper around Delta Client's storage directory URL. Used to define a consistent structure,\n/// and house useful helper methods. Never guarantees that the described directories exist.\nstruct StorageDirectory {\n  /// The default storage directory for the current platform.\n  static var platformDefault: StorageDirectory? {\n    #if os(macOS) || os(iOS)\n      let options = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)\n      let base = options.first?.appendingPathComponent(\"dev.stackotter.delta-client\")\n      return base.map(StorageDirectory.init)\n    #elseif os(tvOS)\n      let base = FileManager.default.temporaryDirectory.appendingPathComponent(\"dev.stackotter.delta-client\")\n      try? FileManager.default.createDirectory(at: base, withIntermediateDirectories: true)\n      return StorageDirectory(base)\n    #elseif os(Linux)\n      #error(\"Linux storage directory not implemented\")\n    #endif\n  }\n\n  /// The root of the storage directory.\n  private(set) var baseDirectory: URL\n\n  /// A custom plugin directory which overrides the default plugin directory\n  /// location. Set to `nil` for ``StorageDirectory/pluginDirectory`` to go\n  /// back to the default.\n  var pluginDirectoryOverride: URL?\n\n  /// The directory for assets (e.g. the vanilla resource pack).\n  var assetDirectory: URL {\n    baseDirectory.appendingPathComponent(\"assets\")\n  }\n\n  /// The directory for registries (e.g. the block registry and item registry).\n  var registryDirectory: URL {\n    baseDirectory.appendingPathComponent(\"registries\")\n  }\n\n  /// The directory for user-installed plugins.\n  var pluginDirectory: URL {\n    pluginDirectoryOverride ?? baseDirectory.appendingPathComponent(\"plugins\")\n  }\n\n  /// The directory for performance-enhancing caches.\n  var cacheDirectory: URL {\n    baseDirectory.appendingPathComponent(\"cache\")\n  }\n\n  /// The directory for GPU captures (traces).\n  var gpuCaptureDirectory: URL {\n    baseDirectory.appendingPathComponent(\"captures\")\n  }\n\n  /// The directory to store log files.\n  var logDirectory: URL {\n    baseDirectory.appendingPathComponent(\"logs\")\n  }\n\n  /// The current log file.\n  var currentLogFile: URL {\n    logDirectory.appendingPathComponent(\"delta-client.log\")\n  }\n\n  /// The client configuration file.\n  var configFile: URL {\n    baseDirectory.appendingPathComponent(\"config.json\")\n  }\n\n  /// Initializes a storage directory (without guaranteeing that it exists).\n  init(_ base: URL) {\n    baseDirectory = base\n  }\n\n  /// Creates a unique GPU capture output file path of the form\n  /// `capture_dd-MM-yyyy_HH-mm-ss.gputrace`.\n  func uniqueGPUCaptureFile() -> URL {\n    let date = Date()\n    let formatter = DateFormatter()\n    formatter.dateFormat = \"dd-MM-yyyy_HH-mm-ss\"\n\n    let fileName = \"capture_\\(formatter.string(from: date)).gputrace\"\n    return gpuCaptureDirectory.appendingPathComponent(fileName)\n  }\n\n  /// Creates the storage directory if it doesn't already exist.\n  func ensureCreated() throws {\n    guard !FileSystem.itemExists(baseDirectory) else {\n      return\n    }\n\n    try FileSystem.createDirectory(baseDirectory)\n  }\n\n  /// Gets the location of the resource pack cache for the pack with the given name.\n  func cache(forResourcePackNamed name: String) -> URL {\n    cacheDirectory.appendingPathComponent(\"\\(name).rpcache\")\n  }\n\n  /// Gets the URL to a file with the given name in the storage directory.\n  func file(_ name: String) -> URL {\n    baseDirectory.appendingPathComponent(name)\n  }\n\n  /// Creates a backup of the storage directory. If an output file isn't supplied, a\n  /// filename is autogenerated from the current datetime, and the backup is stored\n  /// in the base of the storage directory.\n  ///\n  /// If `zipFile` is provided, and the file already exists, its existing contents\n  /// get overwritten.\n  func backup(to zipFile: URL? = nil) throws {\n    let backupFile: URL\n    if let zipFile = zipFile {\n      backupFile = zipFile\n    } else {\n      let date = Date()\n      let formatter = DateFormatter()\n      formatter.dateFormat = \"dd-MM-yyyy_HH-mm-ss\"\n\n      let backupName = \"backup_\\(formatter.string(from: date))\"\n      backupFile = file(\"\\(backupName).zip\")\n    }\n\n    do {\n      try FileManager.default.zipItem(at: baseDirectory, to: backupFile)\n    } catch {\n      throw StorageDirectoryError.failedToCreateBackup.becauseOf(error)\n    }\n  }\n\n  /// Resets the contents of the storage directory. If `keepBackups` is true, then\n  /// any files which begin with `backup` won't get deleted.\n  func reset(keepBackups: Bool = true) throws {\n    let contents = try FileSystem.children(of: baseDirectory)\n    for item in contents {\n      // Remove item if it is not a backup\n      if !(keepBackups && item.lastPathComponent.hasPrefix(\"backup\")) {\n        try FileSystem.remove(item)\n      }\n    }\n  }\n\n  /// Removes all caches from the storage directory.\n  func removeCache() throws {\n    try FileSystem.remove(cacheDirectory)\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Utility/Clipboard.swift",
    "content": "#if canImport(AppKit)\nimport AppKit\n#elseif canImport(UIKit)\nimport UIKit\n#endif\n\n/// A simple helper for managing the user's clipboard.\nenum Clipboard {\n  /// Sets the contents of the user's clipboard to a given string.\n  static func copy(_ contents: String) {\n    #if canImport(AppKit)\n      NSPasteboard.general.clearContents()\n      NSPasteboard.general.setString(contents, forType: .string)\n    #elseif os(iOS)\n      UIPasteboard.general.string = contents\n    #elseif os(tvOS)\n      #warning(\"Remove dependence on copy on tvOS\")\n    #else\n      #error(\"Unsupported platform, unknown clipboard implementation\")\n    #endif\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Utility/Updater.swift",
    "content": "#if os(macOS)\nimport SwiftUI\nimport DeltaCore\n\n/// An error thrown by ``Updater``.\nenum UpdaterError: LocalizedError {\n  case failedToGetDownloadURL\n  case failedToGetDownloadURLFromGitHubReleases\n  case alreadyUpToDate\n  case failedToGetBranches\n  case failedToGetGitHubAPIResponse\n  case failedToDownloadUpdate\n  case failedToSaveDownload\n  case failedToUnzipUpdate\n  case failedToDeleteCache\n  case invalidReleaseURL\n  case invalidNightlyURL\n  case invalidGitHubComparisonURL\n  \n  var errorDescription: String? {\n    switch self {\n      case .failedToGetDownloadURL:\n        return \"Failed to get download URL.\"\n      case .failedToGetDownloadURLFromGitHubReleases:\n        return \"Failed to get download URL from GitHub Releases.\"\n      case .alreadyUpToDate:\n        return \"You are already up to date.\"\n      case .failedToGetBranches:\n        return \"Failed to get branches.\"\n      case .failedToGetGitHubAPIResponse:\n        return \"Failed to get GitHub API response.\"\n      case .failedToDownloadUpdate:\n        return \"Failed to download update.\"\n      case .failedToSaveDownload:\n        return \"Failed to save download to disk.\"\n      case .failedToUnzipUpdate:\n        return \"Failed to unzip update.\"\n      case .failedToDeleteCache:\n        return \"Failed to delete cache.\"\n      case .invalidReleaseURL:\n        return \"Invalid release URL.\"\n      case .invalidNightlyURL:\n        return \"Invalid nightly URL.\"\n      case .invalidGitHubComparisonURL:\n        return \"Invalid GitHub comparison URL.\"\n    }\n  }\n}\n\n/// An update to perform.\nenum Update {\n  /// Update from GitHub releases.\n  case latestRelease\n  /// Update from latest CI build.\n  case nightly(branch: String)\n\n  /// Whether the update is nightly or not.\n  var isNightly: Bool {\n    switch self {\n      case .latestRelease:\n        return false\n      case .nightly:\n        return true\n    }\n  }\n}\n\n/// A download for an update.\nstruct Download {\n  /// The download's URL.\n  var url: URL\n  /// The version to display.\n  var version: String\n}\n\n/// Used to update the client to either the latest successful CI build or the latest GitHub release.\nenum Updater {\n  /// A step in an update. Used to track progress.\n  enum UpdateStep: TaskStep {\n    case downloadUpdate\n    case unzipUpdate\n    case unzipUpdateSecondLayer\n    case deleteCache\n    case tMinus3\n    case tMinus2\n    case tMinus1\n\n    var message: String {\n      switch self {\n        case .downloadUpdate:\n          return \"Downloading update\"\n        case .unzipUpdate:\n          return \"Unzipping update\"\n        case .unzipUpdateSecondLayer:\n          return \"Unzipping second layer of update\"\n        case .deleteCache:\n          return \"Deleting cache\"\n        case .tMinus3:\n          return \"Restarting app in 3\"\n        case .tMinus2:\n          return \"Restarting app in 2\"\n        case .tMinus1:\n          return \"Restarting app in 1\"\n      }\n    }\n\n    var relativeDuration: Double {\n      switch self {\n        case .downloadUpdate:\n          return 10\n        case .unzipUpdate:\n          return 2\n        case .unzipUpdateSecondLayer:\n          return 2\n        case .deleteCache:\n          return 1\n        case .tMinus3:\n          return 1\n        case .tMinus2:\n          return 1\n        case .tMinus1:\n          return 1\n      }\n    }\n  }\n\n  /// Performs a given update. Once the update's version string is known, `displayVersion`\n  /// is updated (allowing a UI to display the version before the update has finished).\n  /// `storage` is used to delete the cache before restarting the app.\n  ///\n  /// Never returns (unless an error occurs) because it restarts the app to allow the\n  /// update to take effect.\n  static func performUpdate(download: Download, isNightly: Bool, storage: StorageDirectory, progress: TaskProgress<UpdateStep>? = nil) async throws -> Never {\n    let progress = progress ?? TaskProgress()\n    // Download the release\n    let data = try await progress.perform(.downloadUpdate) { observeStepProgress in\n      try await withCheckedThrowingContinuation { continuation in\n        let task = URLSession.shared.dataTask(with: download.url) { data, response, error in\n          if let data = data {\n          continuation.resume(returning: data)\n          } else {\n            var richError: any LocalizedError = UpdaterError.failedToDownloadUpdate\n            if let error = error {\n              richError = richError.becauseOf(error)\n            }\n            continuation.resume(throwing: richError)\n          }\n        }\n        observeStepProgress(task.progress)\n        task.resume()\n      }\n    }\n\n    try Task.checkCancellation()\n\n    let temp = FileManager.default.temporaryDirectory\n    let zipFile = temp.appendingPathComponent(\"DeltaClient.zip\")\n    let outputDirectory = temp.appendingPathComponent(\"DeltaClient-\\(UUID().uuidString)\")\n\n    do {\n      try data.write(to: zipFile)\n    } catch {\n      throw UpdaterError.failedToSaveDownload.becauseOf(error)\n    }\n\n    try Task.checkCancellation()\n\n    try await progress.perform(.unzipUpdate) { observeStepProgress in\n      do {\n        let progress = Progress()\n        observeStepProgress(progress)\n        try FileManager.default.unzipItem(at: zipFile, to: outputDirectory, skipCRC32: true, progress: progress)\n      } catch {\n        throw UpdaterError.failedToUnzipUpdate.becauseOf(error)\n      }\n    }\n\n    try Task.checkCancellation()\n\n    // Nightly builds have two layers of zip for whatever reason\n    try await progress.perform(.unzipUpdateSecondLayer, if: isNightly) { observeStepProgress in\n      let progress = Progress()\n      observeStepProgress(progress)\n\n      try FileManager.default.unzipItem(\n        at: outputDirectory.appendingPathComponent(\"DeltaClient.zip\"),\n        to: outputDirectory,\n        skipCRC32: true,\n        progress: progress\n      )\n    }\n\n    try Task.checkCancellation()\n\n    // Delete the cache to avoid common issues which can occur after updating\n    try progress.perform(.deleteCache) {\n      do {\n        try FileSystem.remove(storage.cacheDirectory)\n      } catch {\n        throw UpdaterError.failedToDeleteCache.becauseOf(error)\n      }\n    }\n\n    try Task.checkCancellation()\n    progress.update(to: .tMinus3)\n    try await Task.sleep(nanoseconds: 1_000_000_000)\n    try Task.checkCancellation()\n    progress.update(to: .tMinus2)\n    try await Task.sleep(nanoseconds: 1_000_000_000)\n    try Task.checkCancellation()\n    progress.update(to: .tMinus1)\n    try await Task.sleep(nanoseconds: 1_000_000_000)\n    try Task.checkCancellation()\n\n    // Create a background task to replace the app and relaunch\n    let newApp = outputDirectory\n      .appendingPathComponent(\"DeltaClient.app\").path\n      .replacingOccurrences(of: \" \", with: \"\\\\ \")\n    let currentApp = Bundle.main.bundlePath.replacingOccurrences(of: \" \", with: \"\\\\ \")\n    let logFile = storage.baseDirectory\n      .appendingPathComponent(\"output.log\").path\n      .replacingOccurrences(of: \" \", with: \"\\\\ \")\n\n    // TODO: Refactor to avoid potential for command injection attacks (relatively low impact anyway,\n    //   Delta Client doesn't run with elevated privileges, and this requires user interaction).\n    Utils.shell(\n      #\"nohup sh -c 'sleep 3; rm -rf \\#(currentApp); mv \\#(newApp) \\#(currentApp); open \\#(currentApp); open \\#(currentApp)' >\\#(logFile) 2>&1 &\"#\n    )\n\n    Foundation.exit(0)\n  }\n\n  /// Gets the download for a given update.\n  static func getDownload(for update: Update) throws -> Download {\n    switch update {\n      case .latestRelease:\n        return try getLatestReleaseDownload()\n      case .nightly(let branch):\n        return try getLatestNightlyDownload(branch: branch)\n    }\n  }\n\n  /// Gets the download for the latest GitHub release.\n  static func getLatestReleaseDownload() throws -> Download {\n    var decoder = CustomJSONDecoder()\n    decoder.keyDecodingStrategy = .convertFromSnakeCase\n\n    let apiURL = URL(string: \"https://api.github.com/repos/stackotter/delta-client/releases\")!\n\n    let data: Data\n    let response: [GitHubReleasesAPIResponse]\n    do {\n      data = try Data(contentsOf: apiURL)\n      response = try decoder.decode([GitHubReleasesAPIResponse].self, from: data)\n    } catch {\n      throw UpdaterError.failedToGetGitHubAPIResponse.becauseOf(error)\n    }\n\n    guard\n      let tagName = response.first?.tagName,\n      let downloadURL = response.first?.assets.first?.browserDownloadURL\n    else {\n      throw UpdaterError.failedToGetDownloadURLFromGitHubReleases\n    }\n\n    guard let url = URL(string: downloadURL) else {\n      throw UpdaterError.invalidReleaseURL.with(\"URL\", downloadURL)\n    }\n    return Download(url: url, version: tagName)\n  }\n\n  /// Gets the download for the artifact uploaded by the latest successful GitHub action run\n  /// on a given branch.\n  static func getLatestNightlyDownload(branch: String) throws -> Download {\n    let branches = try getBranches()\n    guard let commit = (branches.filter { $0.name == branch }.first?.commit) else {\n      throw UpdaterError.failedToGetDownloadURL.with(\"Reason\", \"Branch '\\(branch)' doesn't exist.\")\n    }\n\n    if case let .commit(currentCommit) = DeltaClientApp.version {\n      guard currentCommit != commit.sha else {\n        throw UpdaterError.alreadyUpToDate.with(\"Current commit\", currentCommit)\n      }\n    }\n\n    let hash = commit.sha.prefix(7)\n    let url = \"https://backend.deltaclient.app/download/\\(branch)/latest/DeltaClient.app.zip\"\n    guard let url = URL(string: url) else {\n      throw UpdaterError.invalidNightlyURL.with(\"URL\", url)\n    }\n\n    return Download(url: url, version: \"commit \\(hash) (latest)\")\n  }\n\n  /// Gets basic information about all branches of the Delta Client GitHub repository.\n  static func getBranches() throws -> [GitHubBranch] {\n    let url = URL(string: \"https://api.github.com/repos/stackotter/delta-client/branches\")!\n    do {\n      let data = try Data(contentsOf: url)\n      let response = try CustomJSONDecoder().decode([GitHubBranch].self, from: data)\n      return response\n    } catch {\n      throw UpdaterError.failedToGetBranches.becauseOf(error)\n    }\n  }\n\n  /// Compares two gitrefs in the Delta Client GitHub repository.\n  static func compareGitRefs(_ first: String, _ second: String) throws -> GitHubComparison.Status {\n    let url = \"https://api.github.com/repos/stackotter/delta-client/compare/\\(first)...\\(second)\"\n    guard let url = URL(string: url) else {\n      throw UpdaterError.invalidGitHubComparisonURL.with(\"URL\", url)\n    }\n    let data = try Data(contentsOf: url)\n    return try CustomJSONDecoder().decode(GitHubComparison.self, from: data).status\n  }\n  \n  /// Checks if the user is on the main branch and a newer commit is available.\n  static func isUpdateAvailable() throws -> Bool {\n    guard case let .commit(commit) = DeltaClientApp.version else {\n      return false\n    }\n\n    // TODO: When releases are supported again, check for newer releases if the user if on a release build.\n    let status = try compareGitRefs(commit, \"main\")\n    return status == .behind\n  }\n}\n#endif\n"
  },
  {
    "path": "Sources/Client/Utility/Utils.swift",
    "content": "import Foundation\n\nenum Utils {\n  #if os(macOS)\n  /// Runs a shell command.\n  static func shell(_ command: String) {\n    let task = Process()\n    task.arguments = [\"-c\", command]\n    task.launchPath = \"/bin/bash\"\n    task.launch()\n  }\n  \n  /// Relaunches the application.\n  static func relaunch() {\n    log.info(\"Relaunching Delta Client\")\n    let url = URL(fileURLWithPath: Bundle.main.resourcePath!)\n    let path = url.deletingLastPathComponent().deletingLastPathComponent().absoluteString\n    let task = Process()\n    task.launchPath = \"/usr/bin/open\"\n    task.arguments = [path]\n    task.launch()\n    exit(0)\n  }\n  #endif\n}\n"
  },
  {
    "path": "Sources/Client/Views/Play/DirectConnectView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nstruct DirectConnectView: View {\n  @EnvironmentObject var appState: StateWrapper<AppState>\n  \n  @State var host: String = \"\"\n  @State var port: UInt16? = nil\n  \n  @State var errorMessage: String? = nil\n  @State var isAddressValid = false\n  \n  private func verify() -> Bool {\n    if !isAddressValid {\n      errorMessage = \"Invalid IP\"\n    } else {\n      return true\n    }\n    return false\n  }\n  \n  var body: some View {\n    VStack {\n      AddressField(\"Server address\", host: $host, port: $port, isValid: $isAddressValid)\n      \n      if let message = errorMessage {\n        Text(message)\n          .bold()\n      }\n      \n      HStack {\n        Button(\"Cancel\") {\n          appState.update(to: .serverList)\n        }\n        .buttonStyle(SecondaryButtonStyle())\n        \n        Button(\"Connect\") {\n          if verify() {\n            let descriptor = ServerDescriptor(name: \"Direct Connect\", host: host, port: port)\n            appState.update(to: .playServer(descriptor, paneCount: 1))\n          }\n        }\n        .buttonStyle(PrimaryButtonStyle())\n      }\n      .padding(.top, 16)\n    }\n    #if !os(tvOS)\n    .frame(width: 200)\n    #endif\n    #if !os(iOS)\n    .onExitCommand {\n      appState.update(to: .serverList)\n    }\n    #endif\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Play/GameView.swift",
    "content": "import SwiftUI\nimport DeltaCore\nimport DeltaRenderer\nimport Combine\n\n/// Where the real Minecraft stuff happens. This renders the actual game itself and\n/// also handles user input.\nstruct GameView: View {\n  enum GameState {\n    case playing\n    case gpuFrameCaptureComplete(file: URL)\n  }\n\n  @EnvironmentObject var appState: StateWrapper<AppState>\n  @EnvironmentObject var managedConfig: ManagedConfig\n  @EnvironmentObject var modal: Modal\n  @Environment(\\.storage) var storage: StorageDirectory\n\n  @State var state: GameState = .playing\n  @State var inputCaptured = true\n  @State var cursorCaptured = true\n\n  @Binding var inGameMenuPresented: Bool\n  \n  var serverDescriptor: ServerDescriptor\n  var account: Account\n  var controller: Controller?\n  var controllerOnly: Bool\n\n  init(\n    connectingTo serverDescriptor: ServerDescriptor,\n    with account: Account,\n    controller: Controller?,\n    controllerOnly: Bool,\n    inGameMenuPresented: Binding<Bool>\n  ) {\n    self.serverDescriptor = serverDescriptor\n    self.account = account\n    self.controller = controller\n    self.controllerOnly = controllerOnly\n    _inGameMenuPresented = inGameMenuPresented\n  }\n\n  var body: some View {\n    JoinServerAndThen(serverDescriptor, with: account) { client in\n      WithRenderCoordinator(for: client) { renderCoordinator in\n        VStack {\n          switch state {\n            case .playing:\n              ZStack {\n                WithController(controller, listening: $inputCaptured) {\n                  if controllerOnly {\n                    gameView(client: client, renderCoordinator: renderCoordinator)\n                  } else {\n                    InputView(listening: $inputCaptured, cursorCaptured: !inGameMenuPresented && cursorCaptured) {\n                      gameView(client: client, renderCoordinator: renderCoordinator)\n                    }\n                    .onKeyPress { [weak client] key, characters in\n                      client?.press(key, characters)\n                    }\n                    .onKeyRelease { [weak client] key in\n                      client?.release(key)\n                    }\n                    .onMouseMove { [weak client] x, y, deltaX, deltaY in\n                      // TODO: Formalise this adjustment factor somewhere\n                      let sensitivityAdjustmentFactor: Float = 0.004\n                      let sensitivity = sensitivityAdjustmentFactor * managedConfig.mouseSensitivity\n                      client?.moveMouse(x: x, y: y, deltaX: sensitivity * deltaX, deltaY: sensitivity * deltaY)\n                    }\n                    .passthroughClicks(!cursorCaptured)\n                  }\n                }\n                .onButtonPress { [weak client] button in\n                  guard let input = input(for: button) else {\n                    return\n                  }\n                  client?.press(input)\n                }\n                .onButtonRelease { [weak client] button in\n                  guard let input = input(for: button) else {\n                    return\n                  }\n                  client?.release(input)\n                }\n                .onThumbstickMove { [weak client] thumbstick, x, y in\n                  switch thumbstick {\n                    case .left:\n                      client?.moveLeftThumbstick(x, y)\n                    case .right:\n                      client?.moveRightThumbstick(x, y)\n                  }\n                }\n                #if os(tvOS)\n                .focusable(!inGameMenuPresented)\n                .onExitCommand {\n                  inGameMenuPresented = true\n                }\n                #endif\n              }\n            case .gpuFrameCaptureComplete(let file):\n              frameCaptureResult(file)\n                .onAppear {\n                  cursorCaptured = false\n                  inputCaptured = false\n                }\n          }\n        }\n        .onAppear {\n          modal.onError { [weak client] _ in\n            client?.game.tickScheduler.cancel()\n            cursorCaptured = false\n            inputCaptured = false\n          }\n\n          registerEventHandler(client, renderCoordinator)\n        }\n      } cancellationHandler: {\n        appState.update(to: .serverList)\n      }\n    } cancellationHandler: {\n      appState.update(to: .serverList)\n    }\n    .onChange(of: inGameMenuPresented) { presented in\n      if presented {\n        cursorCaptured = false\n        inputCaptured = false\n      } else {\n        cursorCaptured = true\n        inputCaptured = true\n      }\n    }\n  }\n\n  func registerEventHandler(_ client: Client, _ renderCoordinator: RenderCoordinator) {\n    client.eventBus.registerHandler { event in\n      switch event {\n        case _ as OpenInGameMenuEvent:\n          inGameMenuPresented = true\n          client.releaseAllInputs()\n        case _ as ReleaseCursorEvent:\n          cursorCaptured = false\n        case _ as CaptureCursorEvent:\n          cursorCaptured = true\n        case let event as KeyPressEvent where event.input == .performGPUFrameCapture:\n          let outputFile = storage.uniqueGPUCaptureFile()\n          do {\n            try renderCoordinator.captureFrames(count: 10, to: outputFile)\n          } catch {\n            modal.error(RichError(\"Failed to start frame capture\").becauseOf(error))\n          }\n        case let event as FinishFrameCaptureEvent:\n          ThreadUtil.runInMain {\n            state = .gpuFrameCaptureComplete(file: event.file)\n          }\n        default:\n          break\n      }\n    }\n  }\n\n  func gameView(client: Client, renderCoordinator: RenderCoordinator) -> some View {\n    ZStack {\n      if #available(macOS 13, iOS 16, *) {\n        MetalView(renderCoordinator: renderCoordinator)\n          .onAppear {\n            cursorCaptured = true\n            inputCaptured = true\n          }\n      } else {\n        MetalViewClass(renderCoordinator: renderCoordinator)\n          .onAppear {\n            cursorCaptured = true\n            inputCaptured = true\n          }\n      }\n\n      #if os(iOS)\n      movementControls(client)\n      #endif\n    }\n  }\n\n  /// Gets the input associated with a particular controller button.\n  func input(for button: Controller.Button) -> Input? {\n    switch button {\n      case .buttonA:\n        return .jump\n      case .leftTrigger:\n        return .place\n      case .rightTrigger:\n        return .destroy\n      case .leftShoulder:\n        return .previousSlot\n      case .rightShoulder:\n        return .nextSlot\n      case .leftThumbstickButton:\n        return .sprint\n      case .buttonB:\n        return .sneak\n      case .dpadUp:\n        return .changePerspective\n      case .dpadRight:\n        return .openChat\n      default:\n        return nil\n    }\n  }\n\n  func frameCaptureResult(_ file: URL) -> some View {\n    VStack {\n      Text(\"GPU frame capture complete\")\n\n      Group {\n        #if os(macOS)\n          Button(\"Show in finder\") {\n            NSWorkspace.shared.activateFileViewerSelecting([file])\n          }.buttonStyle(SecondaryButtonStyle())\n        #else\n          // TODO: Add a file sharing menu for iOS and tvOS\n          Text(\"I have no clue how to get hold of the file\")\n        #endif\n\n        Button(\"OK\") {\n          state = .playing\n        }.buttonStyle(PrimaryButtonStyle())\n      }.frame(width: 200)\n    }\n  }\n\n  #if os(iOS)\n  func movementControls(_ client: Client) -> some View {\n    VStack {\n      Spacer()\n      HStack {\n        HStack(alignment: .bottom) {\n          movementControl(\"a\", .strafeLeft, client)\n          VStack {\n            movementControl(\"w\", .moveForward, client)\n            movementControl(\"s\", .moveBackward, client)\n          }\n          movementControl(\"d\", .strafeRight, client)\n        }\n        Spacer()\n        VStack {\n          movementControl(\"*\", .jump, client)\n          movementControl(\"_\", .sneak, client)\n        }\n      }\n    }\n  }\n\n  func movementControl(_ label: String, _ input: Input, _ client: Client) -> some View {\n    return ZStack {\n      Color.blue.frame(width: 50, height: 50)\n      Text(label)\n    }.onLongPressGesture(\n      minimumDuration: 100000000000,\n      maximumDistance: 50,\n      perform: { return },\n      onPressingChanged: { [weak client] isPressing in\n        if isPressing {\n          client?.press(input)\n        } else {\n          client?.release(input)\n        }\n      }\n    )\n  }\n  #endif\n}\n"
  },
  {
    "path": "Sources/Client/Views/Play/InGameMenu.swift",
    "content": "import SwiftUI\n\nstruct InGameMenu: View {\n  enum InGameMenuState {\n    case menu\n    case settings\n  }\n\n  @EnvironmentObject var appState: StateWrapper<AppState>\n\n  @Binding var presented: Bool\n  @State var state: InGameMenuState = .menu\n\n  #if os(tvOS)\n  @FocusState var focusState: FocusElements?\n\n  func moveFocus(_ direction: MoveCommandDirection) {\n    if let focusState = focusState {\n      let step: Int\n      switch direction {\n        case .down:\n          step = 1\n        case .up:\n          step = -1\n        case .left, .right:\n          return\n      }\n      let count = FocusElements.allCases.count\n      // Add an extra count before taking the modulo cause Swift's mod operator isn't\n      // the real mathematical modulo.\n      let index = (focusState.rawValue + step + count) % count\n      self.focusState = FocusElements.allCases[index]\n    }\n  }\n\n  enum FocusElements: Int, CaseIterable {\n    case backToGame\n    case settings\n    case disconnect\n  }\n  #endif\n\n  init(presented: Binding<Bool>) {\n    _presented = presented\n  }\n  \n  var body: some View {\n    if presented {    \n      GeometryReader { geometry in\n        VStack {\n          switch state {\n            case .menu:\n              VStack {\n                Button(\"Back to game\") {\n                  presented = false\n                }\n                  #if os(tvOS)\n                  .focused($focusState, equals: .backToGame)\n                  #else\n                  .buttonStyle(PrimaryButtonStyle())\n                  .keyboardShortcut(.escape, modifiers: [])\n                  #endif\n\n                Button(\"Settings\") { \n                  state = .settings\n                }\n                  #if os(tvOS)\n                  .focused($focusState, equals: .settings)\n                  #endif\n                  .buttonStyle(SecondaryButtonStyle())\n\n                Button(\"Disconnect\") {\n                  appState.update(to: .serverList)\n                }\n                  #if os(tvOS)\n                  .focused($focusState, equals: .disconnect)\n                  #endif\n                  .buttonStyle(SecondaryButtonStyle())\n              }\n              #if !os(tvOS)\n              .frame(width: 200)\n              #endif\n            case .settings:\n              SettingsView(isInGame: true) {\n                state = .menu\n              }\n          }\n        }\n          .frame(width: geometry.size.width, height: geometry.size.height)\n          .background(Color.black.opacity(0.702), alignment: .center)\n          #if os(tvOS)\n          .onAppear {\n            focusState = .backToGame\n          }\n          .focusSection()\n          #endif\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Play/InputView.swift",
    "content": "import SwiftUI\nimport DeltaCore\nimport DeltaRenderer\n\nstruct InputView<Content: View>: View {\n  @EnvironmentObject var modal: Modal\n  @EnvironmentObject var appState: StateWrapper<AppState>\n\n  @State var monitorsAdded = false\n  @State var scrollWheelDeltaY: Float = 0\n\n  #if os(macOS)\n  @State var previousModifierFlags: NSEvent.ModifierFlags?\n  #endif\n\n  @Binding var listening: Bool\n\n  private var content: () -> Content\n\n  private var handleKeyRelease: ((Key) -> Void)?\n  private var handleKeyPress: ((Key, [Character]) -> Void)?\n  private var handleMouseMove: ((\n    _ x: Float,\n    _ y: Float,\n    _ deltaX: Float,\n    _ deltaY: Float\n  ) -> Void)?\n  private var handleScroll: ((_ deltaY: Float) -> Void)?\n  private var shouldPassthroughClicks = false\n  private var shouldAvoidGeometryReader = false\n\n  init(\n    listening: Binding<Bool>,\n    cursorCaptured: Bool,\n    @ViewBuilder _ content: @escaping () -> Content\n  ) {\n    _listening = listening\n    self.content = content\n\n    if cursorCaptured {\n      Self.captureCursor()\n    } else {\n      Self.releaseCursor()\n    }\n  }\n\n  /// Captures the cursor (locks it in place and makes it invisible).\n  private static func captureCursor() {\n    #if os(macOS)\n      CGAssociateMouseAndMouseCursorPosition(0)\n      NSCursor.hide()\n    #endif\n  }\n\n  /// Releases the cursor, making it visible and able to move around.\n  private static func releaseCursor() {\n    #if os(macOS)\n      CGAssociateMouseAndMouseCursorPosition(1)\n      NSCursor.unhide()\n    #endif\n  }\n\n  /// If listening, the view will still process clicks, but the click will\n  /// get passed through for the underlying view to process as well.\n  func passthroughClicks(_ passthroughClicks: Bool = true) -> Self {\n    with(\\.shouldPassthroughClicks, passthroughClicks)\n  }\n\n  /// When `true`, `GeometryReader` won't be used, but mouse movements\n  /// won't be tracked. This can be used when mouse movements aren't\n  /// required but the `GeometryReader` is messing with layouts.\n  func avoidGeometryReader(_ avoidGeometryReader: Bool = true) -> Self {\n    with(\\.shouldAvoidGeometryReader, avoidGeometryReader)\n  }\n\n  /// Adds an action to run when a key is released.\n  func onKeyRelease(_ action: @escaping (Key) -> Void) -> Self {\n    appendingAction(to: \\.handleKeyRelease, action)\n  }\n\n  /// Adds an action to run when a key is pressed.\n  func onKeyPress(_ action: @escaping (Key, [Character]) -> Void) -> Self {\n    appendingAction(to: \\.handleKeyPress, action)\n  }\n\n  /// Adds an action to run when the mouse is moved.\n  func onMouseMove(_ action: @escaping (_ x: Float, _ y: Float, _ deltaX: Float, _ deltaY: Float) -> Void) -> Self {\n    appendingAction(to: \\.handleMouseMove, action)\n  }\n\n  /// Adds an action to run when scrolling occurs.\n  func onScroll(_ action: @escaping (_ deltaY: Float) -> Void) -> Self {\n    appendingAction(to: \\.handleScroll, action)\n  }\n\n  #if os(macOS)\n    func mousePositionInView(with geometry: GeometryProxy) -> (x: Float, y: Float)? {\n      // This assumes that there's only one window and that this is only called once\n      // the view's body has been evaluated at least once.\n      guard let window = NSApplication.shared.orderedWindows.first else {\n        return nil\n      }\n\n      let viewFrame = geometry.frame(in: .global)\n      let x = (NSEvent.mouseLocation.x - window.frame.minX) - viewFrame.minX\n      let y = window.frame.maxY - NSEvent.mouseLocation.y - viewFrame.minY\n\n      // AppKit gives us the position scaled by the screen's scaling factor, so we\n      // adjust it back to get the position in terms of true pixels.\n      let scalingFactor = CGFloat(GUIRenderer.screenScalingFactor())\n      return (Float(x * scalingFactor), Float(y * scalingFactor))\n    }\n  #endif\n\n  var body: some View {\n    VStack {\n      if shouldAvoidGeometryReader {\n        contentWithEventListeners()\n      } else {\n        GeometryReader { geometry in\n          contentWithEventListeners(geometry)\n        }\n      }\n    }\n  }\n\n  func contentWithEventListeners(_ geometry: GeometryProxy? = nil) -> some View {\n    // Make sure that the latest position is known to any observers (e.g. if\n    // listening was disabled and now isn't, observers won't have been told\n    // about any changes that occured during the period in which listening\n    // was disabled).\n    #if os(macOS)\n      if let geometry = geometry {\n        if let mousePosition = mousePositionInView(with: geometry) {\n          handleMouseMove?(mousePosition.x, mousePosition.y, 0, 0)\n        } else {\n          modal.error(\"Failed to get mouse position (on demand)\") {\n            appState.update(to: .serverList)\n          }\n        }\n      }\n    #endif\n\n    return content()\n      #if os(iOS)\n      .gesture(TapGesture(count: 2).onEnded { _ in\n        handleKeyPress?(.escape, [])\n      })\n      .gesture(LongPressGesture(minimumDuration: 2, maximumDistance: 9).onEnded { _ in\n        handleKeyPress?(.f3, [])\n      })\n      .gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global).onChanged { value in\n        // TODO: Implement absolute\n        handleMouseMove?(\n          Float(value.location.x),\n          Float(value.location.y),\n          Float(value.translation.width),\n          Float(value.translation.height)\n        )\n      })\n      #endif\n      #if os(macOS)\n      .onDisappear {\n        Self.releaseCursor()\n      }\n      .onAppear {\n        if !monitorsAdded {\n          NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved, .leftMouseDragged, .rightMouseDragged, .otherMouseDragged], handler: { event in\n            guard listening, let geometry = geometry else {\n              return event\n            }\n\n            guard let mousePosition = mousePositionInView(with: geometry) else {\n              modal.error(\"Failed to get mouse position\") {\n                appState.update(to: .serverList)\n              }\n              return event\n            }\n\n            let x = mousePosition.x\n            let y = mousePosition.y\n\n            let deltaX = Float(event.deltaX)\n            let deltaY = Float(event.deltaY)\n\n            handleMouseMove?(x, y, deltaX, deltaY)\n\n            return event\n          })\n\n          NSEvent.addLocalMonitorForEvents(matching: [.scrollWheel], handler: { event in\n            if !listening {\n              return event\n            }\n\n            let deltaY = Float(event.scrollingDeltaY)\n            handleScroll?(deltaY)\n\n            scrollWheelDeltaY += deltaY\n\n            // TODO: Implement a scroll wheel sensitivity setting\n            let threshold: Float = 0.5\n            let key: Key\n            if scrollWheelDeltaY >= threshold {\n              key = .scrollUp\n            } else if deltaY <= -threshold {\n              key = .scrollDown\n            } else {\n              return nil\n            }\n\n            scrollWheelDeltaY = 0\n\n            handleKeyPress?(key, [])\n            handleKeyRelease?(key)\n\n            return nil\n          })\n\n          NSEvent.addLocalMonitorForEvents(matching: [.rightMouseDown, .leftMouseDown, .otherMouseDown], handler: { event in\n            if !listening {\n              return event\n            }\n\n            if event.associatedEventsMask.contains(.leftMouseDown) {\n              handleKeyPress?(.leftMouseButton, [])\n            }\n            if event.associatedEventsMask.contains(.rightMouseDown) {\n              handleKeyPress?(.rightMouseButton, [])\n            }\n            if event.associatedEventsMask.contains(.otherMouseDown) {\n              handleKeyPress?(.otherMouseButton(event.buttonNumber), [])\n            }\n\n            return shouldPassthroughClicks ? event : nil\n          })\n\n          NSEvent.addLocalMonitorForEvents(matching: [.rightMouseUp, .leftMouseUp, .otherMouseUp], handler: { event in\n            if !listening {\n              return event\n            }\n\n            if event.associatedEventsMask.contains(.leftMouseUp) {\n              handleKeyRelease?(.leftMouseButton)\n            }\n            if event.associatedEventsMask.contains(.rightMouseUp) {\n              handleKeyRelease?(.rightMouseButton)\n            }\n            if event.associatedEventsMask.contains(.otherMouseUp) {\n              handleKeyRelease?(.otherMouseButton(event.buttonNumber))\n            }\n\n            return shouldPassthroughClicks ? event : nil\n          })\n\n          NSEvent.addLocalMonitorForEvents(matching: [.keyDown], handler: { event in\n            if !listening {\n              return event\n            }\n\n            if let key = Key(keyCode: event.keyCode) {\n              handleKeyPress?(key, Array(event.characters ?? \"\"))\n\n              if key == .q && event.modifierFlags.contains(.command) {\n                // Pass through quit command\n                return event\n              }\n\n              if key == .f && event.modifierFlags.contains(.command) && event.modifierFlags.contains(.control) {\n                // Pass through full screen command\n                return event\n              }\n            }\n\n            return nil\n          })\n\n          NSEvent.addLocalMonitorForEvents(matching: [.keyUp], handler: { event in\n            if !listening {\n              return event\n            }\n\n            if let key = Key(keyCode: event.keyCode) {\n              handleKeyRelease?(key)\n            }\n            return event\n          })\n\n          NSEvent.addLocalMonitorForEvents(matching: [.flagsChanged], handler: { event in\n            if !listening {\n              return event\n            }\n\n            let raw = Int32(event.modifierFlags.rawValue)\n            let previousRaw = Int32(previousModifierFlags?.rawValue ?? 0)\n\n            func check(_ key: Key, mask: Int32) {\n              if raw & mask != 0 && previousRaw & mask == 0 {\n                handleKeyPress?(key, [])\n              } else if raw & mask == 0 && previousRaw & mask != 0 {\n                handleKeyRelease?(key)\n              }\n            }\n\n            check(.leftOption, mask: NX_DEVICELALTKEYMASK)\n            check(.leftCommand, mask: NX_DEVICELCMDKEYMASK)\n            check(.leftControl, mask: NX_DEVICELCTLKEYMASK)\n            check(.leftShift, mask: NX_DEVICELSHIFTKEYMASK)\n            check(.rightOption, mask: NX_DEVICERALTKEYMASK)\n            check(.rightCommand, mask: NX_DEVICERCMDKEYMASK)\n            check(.rightControl, mask: NX_DEVICERCTLKEYMASK)\n            check(.rightShift, mask: NX_DEVICERSHIFTKEYMASK)\n\n            previousModifierFlags = event.modifierFlags\n\n            return event\n          })\n        }\n        monitorsAdded = true\n      }\n      #endif\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Play/JoinServerAndThen.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nenum ServerJoinState {\n  case connecting\n  case loggingIn\n  case downloadingChunks(received: Int, total: Int)\n  case joined\n\n  var message: String {\n    switch self {\n      case .connecting:\n        return \"Establishing connection...\"\n      case .loggingIn:\n        return \"Logging in...\"\n      case .downloadingChunks:\n        return \"Downloading terrain...\"\n      case .joined:\n        return \"Joined successfully\"\n    }\n  }\n}\n\nenum ServerJoinError: LocalizedError {\n  case failedToRefreshAccount\n  case failedToSendJoinServerRequest\n\n  var errorDescription: String? {\n    switch self {\n      case .failedToRefreshAccount:\n        return \"Failed to refresh account.\"\n      case .failedToSendJoinServerRequest:\n        return \"Failed to send join server request.\"\n    }\n  }\n}\n\nstruct JoinServerAndThen<Content: View>: View {\n  @EnvironmentObject var resourcePack: Box<ResourcePack>\n  @EnvironmentObject var pluginEnvironment: PluginEnvironment\n  @EnvironmentObject var managedConfig: ManagedConfig\n  @EnvironmentObject var modal: Modal\n\n  @State var state: ServerJoinState = .connecting\n  @State var client: Client?\n\n  /// Both `timeReceived` and `terrainDownloaded` must be true for us\n  /// to be considered joined. This prevents annoying visual flashes caused\n  /// by the time changing shortly after rendering begins.\n  @State var timeReceived = false\n  @State var terrainDownloaded = false\n\n  var serverDescriptor: ServerDescriptor\n  /// Beware that this account may be outdated once the server has been\n  /// joined, as the account may have been refreshed. It should only be\n  /// used to join once (or only for its id and username).\n  var account: Account\n  var content: (Client) -> Content\n  var cancel: () -> Void\n\n  init(\n    _ serverDescriptor: ServerDescriptor,\n    with account: Account,\n    @ViewBuilder content: @escaping (Client) -> Content,\n    cancellationHandler cancel: @escaping () -> Void\n  ) {\n    self.serverDescriptor = serverDescriptor\n    self.account = account\n    self.content = content\n    self.cancel = cancel\n  }\n\n  var body: some View {\n    VStack {\n      switch state {\n        case .connecting, .loggingIn, .downloadingChunks:\n          Text(state.message)\n\n          if case let .downloadingChunks(received, total) = state {\n            HStack {\n              ProgressView(value: Double(received) / Double(total))\n              Text(\"\\(received) of \\(total)\")\n            }\n            #if !os(tvOS)\n            .frame(maxWidth: 200)\n            #endif\n          }\n\n          Button(\"Cancel\", action: cancel)\n            .buttonStyle(SecondaryButtonStyle())\n            #if !os(tvOS)\n            .frame(width: 150)\n            #endif\n        case .joined:\n          if let client = client {\n            content(client)\n          } else {\n            Text(\"Loading...\").onAppear {\n              modal.error(RichError(\"UI entered invalid state while joining server.\")) {\n                cancel()\n              }\n            }\n          }\n      }\n    }\n    .onAppear {\n      let client = Client(\n        resourcePack: resourcePack.value,\n        configuration: managedConfig\n      )\n\n      pluginEnvironment.addEventBus(client.eventBus)\n      pluginEnvironment.handleWillJoinServer(server: serverDescriptor, client: client)\n\n      Task {\n        do {\n          try await joinServer(serverDescriptor, with: account, client: client)\n        } catch {\n          modal.error(error)\n          cancel()\n        }\n      }\n\n      // An internal state variable used to reduce the number of state updates performed\n      // when downloading chunks (otherwise it lags SwiftUI).\n      var received = 0\n      client.eventBus.registerHandler { [weak client] event in\n        guard let client = client else {\n          return\n        }\n\n        handleEvent(&received, client, event)\n      }\n\n      self.client = client\n    }\n    .onDisappear {\n      client?.disconnect()\n    }\n  }\n\n  func handleEvent(_ received: inout Int, _ client: Client, _ event: Event) {\n    // TODO: Clean up Events API (there should probably just be one error event, see what the Discord\n    //   server reckons)\n    switch event {\n      case _ as LoginStartEvent:\n        ThreadUtil.runInMain {\n          state = .loggingIn\n        }\n      case let connectionFailedEvent as ConnectionFailedEvent:\n        modal.error(\n          RichError(\"Connection to \\(serverDescriptor) failed.\")\n            .becauseOf(connectionFailedEvent.networkError)\n        ) {\n          cancel()\n        }\n      case _ as JoinWorldEvent:\n        // Approximation of the number of chunks the server will send (used in progress indicator)\n        let totalChunksToReceieve = Int(Foundation.pow(Double(client.game.maxViewDistance * 2 + 3), 2))\n        state = .downloadingChunks(received: 0, total: totalChunksToReceieve)\n      case _ as World.Event.AddChunk:\n        ThreadUtil.runInMain {\n          if case let .downloadingChunks(_, total) = state {\n            // An intermediate variable is used to reduce the number of SwiftUI updates generated by downloading chunks\n            received += 1\n            if received % 50 == 0 {\n              state = .downloadingChunks(received: received, total: total)\n            }\n          }\n        }\n      case _ as TerrainDownloadCompletionEvent:\n        terrainDownloaded = true\n        if timeReceived {\n          state = .joined\n        }\n      case _ as World.Event.TimeUpdate:\n        timeReceived = true\n        if terrainDownloaded {\n          state = .joined\n        }\n      case let disconnectEvent as LoginDisconnectEvent:\n        modal.error(\n          RichError(\"Disconnected from server during login.\").with(\"Reason\", disconnectEvent.reason)\n        ) {\n          cancel()\n        }\n      case let packetError as PacketHandlingErrorEvent:\n        modal.error(\n          RichError(\"Failed to handle packet with id \\(packetError.packetId.hexWithPrefix).\")\n            .with(\"Reason\", packetError.error)\n        ) {\n          cancel()\n        }\n      case let packetError as PacketDecodingErrorEvent:\n        modal.error(\n          RichError(\"Failed to decode packet with id \\(packetError.packetId.hexWithPrefix).\")\n            .with(\"Reason\", packetError.error)\n        ) {\n          cancel()\n        }\n      case let generalError as ErrorEvent:\n        modal.error(RichError(generalError.message ?? \"Client error.\").becauseOf(generalError.error)) {\n          cancel()\n        }\n      default:\n        break\n    }\n  }\n\n  func joinServer(\n    _ descriptor: ServerDescriptor,\n    with account: Account,\n    client: Client\n  ) async throws {\n    // Refresh the account (if it's an online account) and then join the server\n    let refreshedAccount: Account\n    do {\n      refreshedAccount = try await managedConfig.refreshAccount(withId: account.id)\n    } catch {\n      throw ServerJoinError.failedToRefreshAccount\n        .with(\"Username\", account.username)\n        .becauseOf(error)\n    }\n\n    do {\n      try await client.joinServer(\n        describedBy: descriptor,\n        with: refreshedAccount\n      )\n    } catch {\n      throw ServerJoinError.failedToSendJoinServerRequest.becauseOf(error)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Play/MetalView.swift",
    "content": "import Foundation\nimport DeltaCore\nimport DeltaRenderer\nimport MetalKit\nimport SwiftUI\n\n@available(macOS 13, *)\n@available(iOS 16, *)\nstruct MetalView {\n  var renderCoordinator: RenderCoordinator\n\n  init(renderCoordinator: RenderCoordinator) {\n    self.renderCoordinator = renderCoordinator\n  }\n\n  func makeCoordinator() -> RenderCoordinator {\n    return renderCoordinator\n  }\n\n  func makeMTKView(renderCoordinator: RenderCoordinator) -> MTKView {\n    let mtkView = MTKView()\n\n    if let metalDevice = MTLCreateSystemDefaultDevice() {\n      mtkView.device = metalDevice\n    }\n\n    mtkView.delegate = renderCoordinator\n    mtkView.preferredFramesPerSecond = 10000\n    mtkView.framebufferOnly = true\n    mtkView.clearColor = MTLClearColorMake(0, 0, 0, 1)\n    mtkView.drawableSize = mtkView.frame.size\n    mtkView.depthStencilPixelFormat = .depth32Float\n\n    // Accept input\n    mtkView.becomeFirstResponder()\n    return mtkView\n  }\n}\n\n@available(macOS, deprecated: 13, renamed: \"MetalView\")\n@available(iOS, deprecated: 16, renamed: \"MetalView\")\nfinal class MetalViewClass {\n  var renderCoordinator: RenderCoordinator\n\n  init(renderCoordinator: RenderCoordinator) {\n    self.renderCoordinator = renderCoordinator\n  }\n\n  func makeCoordinator() -> RenderCoordinator {\n    return renderCoordinator\n  }\n\n  func makeMTKView(renderCoordinator: RenderCoordinator) -> MTKView {\n    let mtkView = MTKView()\n\n    if let metalDevice = MTLCreateSystemDefaultDevice() {\n      mtkView.device = metalDevice\n    }\n\n    mtkView.delegate = renderCoordinator\n    mtkView.preferredFramesPerSecond = 10000\n    mtkView.framebufferOnly = true\n    mtkView.clearColor = MTLClearColorMake(0, 0, 0, 1)\n    mtkView.drawableSize = mtkView.frame.size\n    mtkView.depthStencilPixelFormat = .depth32Float\n    mtkView.clearDepth = 1.0\n\n    // Accept input\n    mtkView.becomeFirstResponder()\n    return mtkView\n  }\n}\n\n\n#if canImport(AppKit)\n  @available(macOS 13, *)\n  extension MetalView: NSViewRepresentable {\n    func makeNSView(context: Context) -> some NSView {\n      return makeMTKView(renderCoordinator: context.coordinator)\n    }\n\n    func updateNSView(_ view: NSViewType, context: Context) {}\n  }\n\n  extension MetalViewClass: NSViewRepresentable {\n    func makeNSView(context: Context) -> some NSView {\n      return makeMTKView(renderCoordinator: context.coordinator)\n    }\n\n    func updateNSView(_ view: NSViewType, context: Context) {}\n  }\n#elseif canImport(UIKit)\n  @available(iOS 16, *)\n  extension MetalView: UIViewRepresentable {\n    func makeUIView(context: Context) -> some UIView {\n      return makeMTKView(renderCoordinator: context.coordinator)\n    }\n\n    func updateUIView(_ view: UIViewType, context: Context) {}\n  }\n\n  extension MetalViewClass: UIViewRepresentable {\n    func makeUIView(context: Context) -> some UIView {\n      return makeMTKView(renderCoordinator: context.coordinator)\n    }\n\n    func updateUIView(_ view: UIViewType, context: Context) {}\n  }\n#else\n  #error(\"Unsupported platform, no MetalView SwiftUI compatibility\")\n#endif\n"
  },
  {
    "path": "Sources/Client/Views/Play/PlayView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nstruct PlayView: View {\n  @EnvironmentObject var appState: StateWrapper<AppState>\n  @EnvironmentObject var controllerHub: ControllerHub\n\n  @State var inGameMenuPresented = false\n  @State var readyCount = 0\n  @State var takenAccounts: [Account] = []\n  @State var takenInputMethods: [InputMethod] = []\n\n  var server: ServerDescriptor\n  var paneCount: Int\n\n  init(_ server: ServerDescriptor, paneCount: Int = 1) {\n    self.server = server\n    self.paneCount = paneCount\n  }\n\n  var body: some View {\n    ZStack {\n      if paneCount == 1 {\n        WithSelectedAccount { account in\n          GameView(\n            connectingTo: server,\n            with: account,\n            controller: controllerHub.currentController,\n            controllerOnly: false,\n            inGameMenuPresented: $inGameMenuPresented\n          )\n        }\n      } else {\n        HStack(spacing: 0) {\n          ForEach(Array(0..<paneCount), id: \\.self) { i in\n            gamePane(i)\n            if i != paneCount - 1 {\n              Divider()\n            }\n          }\n        }\n      }\n\n      InGameMenu(presented: $inGameMenuPresented)\n    }\n      .padding(.top, 1)\n  }\n\n  var allPlayersChoseControllers: Bool {\n    takenInputMethods.allSatisfy(\\.isController)\n  }\n\n  func gamePane(_ playerIndex: Int) -> some View {\n    SelectAccountAndThen(excluding: takenAccounts) { account in\n      SelectInputMethodAndThen(excluding: takenInputMethods) { inputMethod in\n        // When all player choose controllers, player one gets keyboard and mouse as well to\n        // be able to do things such as opening the shared in-game menu.\n        let controllerOnly = allPlayersChoseControllers ? playerIndex != 0 : inputMethod.isController\n\n        VStack {\n          if readyCount == paneCount {\n            GameView(\n              connectingTo: server,\n              with: account,\n              controller: inputMethod.controller,\n              controllerOnly: controllerOnly,\n              inGameMenuPresented: $inGameMenuPresented\n            )\n          } else {\n            Text(\"Ready\")\n          }\n        }\n        .onAppear {\n          readyCount += 1\n          takenInputMethods.append(inputMethod)\n        }\n      } cancellationHandler: {\n        appState.update(to: .serverList)\n      }\n      .onAppear {\n        takenAccounts.append(account)\n      }\n    } cancellationHandler: {\n      appState.update(to: .serverList)\n    }\n    .frame(maxWidth: .infinity)\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Play/SelectAccountAndThen.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nstruct SelectAccountAndThen<Content: View>: View {\n  @EnvironmentObject var managedConfig: ManagedConfig\n\n  var excludedAccounts: [Account]\n  var content: (Account) -> Content\n  var cancellationHandler: () -> Void\n\n  init(\n    excluding excludedAccounts: [Account] = [],\n    @ViewBuilder content: @escaping (Account) -> Content,\n    cancellationHandler: @escaping () -> Void\n  ) {\n    self.excludedAccounts = excludedAccounts\n    self.content = content\n    self.cancellationHandler = cancellationHandler\n  }\n\n  var body: some View {\n    SelectOption(\n      from: managedConfig.config.orderedAccounts,\n      excluding: excludedAccounts,\n      title: \"Select an account\"\n    ) { account in\n      HStack {\n        Text(account.username)\n        Text(account.type)\n          .foregroundColor(.gray)\n      }\n    } andThen: { account in\n      content(account)\n    } cancellationHandler: {\n      cancellationHandler()\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Play/SelectInputMethodAndThen.swift",
    "content": "import SwiftUI\n\nstruct SelectInputMethodAndThen<Content: View>: View {\n  @EnvironmentObject var controllerHub: ControllerHub\n\n  var excludedMethods: [InputMethod]\n  var content: (InputMethod) -> Content\n  var cancellationHandler: () -> Void\n\n  var inputMethods: [InputMethod] {\n    [.keyboardAndMouse] + controllerHub.controllers.map(InputMethod.controller)\n  }\n\n  init(\n    excluding excludedMethods: [InputMethod] = [],\n    @ViewBuilder content: @escaping (InputMethod) -> Content,\n    cancellationHandler: @escaping () -> Void\n  ) {\n    self.excludedMethods = excludedMethods\n    self.content = content\n    self.cancellationHandler = cancellationHandler\n  }\n\n  var body: some View {\n    SelectOption(\n      from: inputMethods,\n      excluding: excludedMethods,\n      title: \"Select an input method\"\n    ) { method in\n      HStack {\n        Text(method.name)\n        if let detail = method.detail {\n          Text(detail)\n            .foregroundColor(.gray)\n        }\n      }\n    } andThen: { method in\n      content(method)\n    } cancellationHandler: {\n      cancellationHandler()\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Play/SelectOption.swift",
    "content": "import SwiftUI\n\n// TODO: The init is starting to get a bit bloated, perhaps a sign that this\n//   view is doing too much, or maybe that some things could be moved to\n//   view modifiers? (e.g. the title)\nstruct SelectOption<Option: Hashable, Row: View, Content: View>: View {\n  @State var selectedOption: Option?\n\n  var options: [Option]\n  var excludedOptions: [Option]\n  var title: String\n  var row: (Option) -> Row\n  var content: (Option) -> Content\n  var cancellationHandler: () -> Void\n\n  init(\n    from options: [Option],\n    excluding excludedOptions: [Option] = [],\n    title: String,\n    @ViewBuilder row: @escaping (Option) -> Row,\n    andThen content: @escaping (Option) -> Content,\n    cancellationHandler: @escaping () -> Void\n  ) {\n    self.options = options\n    self.excludedOptions = excludedOptions\n    self.title = title\n    self.row = row\n    self.content = content\n    self.cancellationHandler = cancellationHandler\n  }\n\n  var body: some View {\n    if let selectedOption = selectedOption {\n      content(selectedOption)\n    } else {\n      VStack {\n        Text(title)\n          .font(.title)\n\n        VStack {\n          Divider()\n          ForEach(options, id: \\.self) { option in\n            #if !os(tvOS)\n              HStack {\n                row(option)\n\n                Spacer()\n\n                Image(systemName: \"chevron.right\")\n              }\n              .contentShape(Rectangle())\n              #if !os(tvOS)\n              .onTapGesture {\n                guard !excludedOptions.contains(option) else {\n                  return\n                }\n                selectedOption = option\n              }\n              #endif\n              .padding(.top, 0.3)\n              .foregroundColor(excludedOptions.contains(option) ? .gray : .primary)\n\n              Divider()\n            #else\n              Button {\n                selectedOption = option\n              } label: {\n                row(option)\n              }\n              .disabled(excludedOptions.contains(option))\n            #endif\n          }\n\n          Button(\"Cancel\", action: cancellationHandler)\n            .buttonStyle(SecondaryButtonStyle())\n            .frame(width: 150)\n        }\n        .padding(.bottom, 10)\n      }\n      #if !os(tvOS)\n      .frame(width: 300)\n      #endif\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Play/WithController.swift",
    "content": "import SwiftUI\nimport Combine\n\nstruct WithController<Content: View>: View {\n  @State var cancellable: AnyCancellable?\n  /// If `false`, events aren't passed on to registered listeners.\n  @Binding var listening: Bool\n\n  var controller: Controller?\n  var content: () -> Content\n\n  private var onButtonPress: ((Controller.Button) -> Void)?\n  private var onButtonRelease: ((Controller.Button) -> Void)?\n  private var onThumbstickMove: ((Controller.Thumbstick, _ x: Float, _ y: Float) -> Void)?\n\n  init(\n    _ controller: Controller?,\n    listening: Binding<Bool>,\n    @ViewBuilder content: @escaping () -> Content\n  ) {\n    self.controller = controller\n    _listening = listening\n    self.content = content\n  }\n\n  func onButtonPress(_ action: @escaping (Controller.Button) -> Void) -> Self {\n    appendingAction(to: \\.onButtonPress, action)\n  }\n\n  func onButtonRelease(_ action: @escaping (Controller.Button) -> Void) -> Self {\n    appendingAction(to: \\.onButtonRelease, action)\n  }\n\n  func onThumbstickMove(\n    _ action: @escaping (Controller.Thumbstick, _ x: Float, _ y: Float) -> Void\n  ) -> Self {\n    appendingAction(to: \\.onThumbstickMove, action)\n  }\n\n  var body: some View {\n    content()\n      .onAppear {\n        observe(controller)\n      }\n      .onChange(of: controller) { controller in\n        observe(controller)\n      }\n  }\n\n  func observe(_ controller: Controller?) {\n    guard let controller = controller else {\n      cancellable = nil\n      return\n    }\n\n    cancellable = controller.eventPublisher.sink { event in\n      guard listening else {\n        return\n      }\n\n      switch event {\n        case let .buttonPressed(button):\n          onButtonPress?(button)\n        case let .buttonReleased(button):\n          onButtonRelease?(button)\n        case let .thumbstickMoved(thumbstick, x, y):\n          onThumbstickMove?(thumbstick, x, y)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Play/WithRenderCoordinator.swift",
    "content": "import SwiftUI\nimport DeltaCore\nimport DeltaRenderer\n\n/// A helper view which can be used to create a render coordinator for a client\n/// without having to introduce additional loading states into views.\nstruct WithRenderCoordinator<Content: View>: View {\n  var client: Client\n  var content: (RenderCoordinator) -> Content\n  var cancel: () -> Void\n\n  init(for client: Client, @ViewBuilder content: @escaping (RenderCoordinator) -> Content, cancellationHandler cancel: @escaping () -> Void) {\n    self.client = client\n    self.content = content\n    self.cancel = cancel\n  }\n\n  @State var renderCoordinator: RenderCoordinator?\n  @State var renderCoordinatorError: RendererError?\n\n  var body: some View {\n    VStack {\n      if let renderCoordinator = renderCoordinator {\n        content(renderCoordinator)\n      } else {\n        if let error = renderCoordinatorError {\n          Text(error.localizedDescription)\n          \n          Button(\"Done\", action: cancel)\n            .buttonStyle(SecondaryButtonStyle())\n            .frame(width: 150)\n        } else {\n          Text(\"Creating render coordinator\")\n        }\n      }\n    }\n    .onAppear {\n      do {\n        renderCoordinator = try RenderCoordinator(client)\n      } catch let error as RendererError {\n        renderCoordinatorError = error\n      } catch {\n        renderCoordinatorError = RendererError.unknown(error)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Play/WithSelectedAccount.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\n/// Gets the currently selected account, or redirects the user to settings to select an\n/// account.\nstruct WithSelectedAccount<Content: View>: View {\n  @EnvironmentObject var appState: StateWrapper<AppState>\n  @EnvironmentObject var modal: Modal\n  @EnvironmentObject var managedConfig: ManagedConfig\n\n  var content: (Account) -> Content\n\n  init(@ViewBuilder content: @escaping (Account) -> Content) {\n    self.content = content\n  }\n\n  var body: some View {\n    if let account = managedConfig.config.selectedAccount {\n      content(account)\n    } else {\n      Text(\"Loading account...\")\n        .onAppear {\n          modal.error(\"You must select an account.\") {\n            // TODO: Have an inline account selector instead of redirecting to settings.\n            appState.update(to: .settings(.accounts))\n          }\n        }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/RouterView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nstruct RouterView: View {\n  @EnvironmentObject var appState: StateWrapper<AppState>\n  @EnvironmentObject var managedConfig: ManagedConfig\n  @EnvironmentObject var controllerHub: ControllerHub\n\n  var body: some View {\n    VStack {\n      switch appState.current {\n        case .serverList:\n          ServerListView()\n        case .editServerList:\n          EditServerListView()\n        case .login:\n          AccountLoginView(completion: { account in\n            managedConfig.config.addAccount(account)\n            if managedConfig.config.accounts.count == 1 {\n              managedConfig.config.selectAccount(withId: account.id)\n            }\n            appState.update(to: .serverList)\n          }, cancelation: nil)\n        case .accounts:\n          AccountSettingsView(saveAction: {\n            appState.update(to: .serverList)\n          }).padding()\n        case .directConnect:\n          DirectConnectView()\n        case let .playServer(server, paneCount):\n          PlayView(server, paneCount: paneCount)\n        case let .settings(landingPage):\n          SettingsView(isInGame: false, landingPage: landingPage, onDone: {\n            appState.pop()\n          })\n      }\n    }\n    .onChange(of: appState.current) { newValue in\n      // Update Discord rich presence based on the current app state\n      switch newValue {\n        case .serverList:\n          DiscordManager.shared.updateRichPresence(to: .menu)\n        case .playServer(let descriptor, _):\n          DiscordManager.shared.updateRichPresence(to: .game(server: descriptor.name))\n        default:\n          break\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/ServerList/LANServerList.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nstruct LANServerList: View {\n  @ObservedObject var lanServerEnumerator: LANServerEnumerator\n  \n  var body: some View {\n    if !lanServerEnumerator.pingers.isEmpty {\n      ForEach(lanServerEnumerator.pingers, id: \\.self) { pinger in\n        NavigationLink(destination: ServerDetail(pinger: pinger)) {\n          ServerListItem(pinger: pinger)\n        }\n      }\n    } else {\n      Text(lanServerEnumerator.hasErrored ? \"LAN scan failed\" : \"scanning LAN...\").italic()\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/ServerList/ServerDetail.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nstruct ServerDetail: View {\n  @EnvironmentObject var appState: StateWrapper<AppState>\n  @ObservedObject var pinger: Pinger\n\n  var body: some View {\n    let descriptor = pinger.descriptor\n    VStack(alignment: .leading) {\n      Text(descriptor.name)\n        .font(.title)\n      Text(descriptor.description)\n        .padding(.bottom, 8)\n\n      if let result = pinger.response {\n        switch result {\n          case let .success(response):\n            LegacyFormattedTextView(\n              legacyString: \"\\(response.description.text)\",\n              fontSize: FontUtil.systemFontSize(for: .regular),\n              alignment: .right\n            ).padding(.bottom, 8)\n\n            Text(verbatim: \"\\(response.players.online)/\\(response.players.max) online\")\n\n            LegacyFormattedTextView(\n              legacyString: \"Version: \\(response.version.name) \\(response.version.protocolVersion == Constants.protocolVersion ? \"\" : \"(incompatible)\")\",\n              fontSize: FontUtil.systemFontSize(for: .regular),\n              alignment: .center\n            ).padding(.bottom, 8)\n\n            Button(\"Play\") {\n              appState.update(to: .playServer(descriptor, paneCount: 1))\n            }\n              .buttonStyle(PrimaryButtonStyle())\n              #if !os(tvOS)\n              .frame(width: 150)\n              #endif\n\n            Button(\"Play splitscreen\") {\n              appState.update(to: .playServer(descriptor, paneCount: 2))\n            }\n              .buttonStyle(SecondaryButtonStyle())\n              #if !os(tvOS)\n              .frame(width: 150)\n              #endif\n          case let .failure(error):\n            Text(error.localizedDescription)\n              .padding(.bottom, 8)\n            Button(\"Play\") {}\n              .buttonStyle(DisabledButtonStyle())\n              .frame(width: 150)\n              .disabled(true)\n            Button(\"Play splitscreen\") {}\n              .buttonStyle(SecondaryButtonStyle())\n              .frame(width: 150)\n              .disabled(true)\n        }\n      } else {\n        Text(\"Pinging..\")\n          .padding(.bottom, 8)\n        Button(\"Play\") { }\n          .buttonStyle(DisabledButtonStyle())\n          .frame(width: 150)\n          .disabled(true)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/ServerList/ServerListItem.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nstruct ServerListItem: View {\n  @StateObject var pinger: Pinger\n  \n  var indicatorColor: SwiftUI.Color {\n    let color: SwiftUI.Color\n    if let result = pinger.response {\n      switch result {\n        case let .success(response):\n          // Ping succeeded\n          let isCompatible = response.version.protocolVersion == Constants.protocolVersion\n          color = isCompatible ? .green : .yellow\n        case .failure:\n          // Connection failed\n          color = .red\n      }\n    } else {\n      // In the process of pinging\n      color = .red\n    }\n    return color\n  }\n  \n  var body: some View {\n    HStack {\n      Text(pinger.descriptor.name)\n      Spacer()\n      PingIndicator(color: indicatorColor)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/ServerList/ServerListView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nstruct ServerListView: View {\n  @EnvironmentObject var appState: StateWrapper<AppState>\n  @EnvironmentObject var managedConfig: ManagedConfig\n  @EnvironmentObject var modal: Modal\n\n  @State var pingers: [Pinger] = []\n  @State var lanServerEnumerator: LANServerEnumerator?\n  @State var updateAvailable = false\n\n  var body: some View {\n    NavigationView {\n      List {\n        // Server list\n        if !pingers.isEmpty {\n          ForEach(pingers, id: \\.self) { pinger in\n            NavigationLink(destination: ServerDetail(pinger: pinger)) {\n              ServerListItem(pinger: pinger)\n            }\n          }\n        } else {\n          Text(\"no servers\").italic()\n        }\n\n        #if !os(tvOS)\n          Divider()\n        #endif\n\n        if let lanServerEnumerator = lanServerEnumerator {\n          LANServerList(lanServerEnumerator: lanServerEnumerator)\n        } else {\n          Text(\"LAN scan failed\").italic()\n        }\n\n        #if os(tvOS)\n          Divider()\n\n          Button(\"Edit servers\") {\n            appState.update(to: .editServerList)\n          }\n\n          Button(\"Refresh servers\") {\n            refresh()\n          }\n\n          Button(\"Direct connect\") {\n            appState.update(to: .directConnect)\n          }\n\n          Button(\"Settings\") {\n            appState.update(to: .settings(nil))\n          }\n        #else\n          HStack {\n            // Edit server list\n            IconButton(\"square.and.pencil\") {\n              appState.update(to: .editServerList)\n            }\n\n            // Refresh server list (ping all servers) and discovered LAN servers\n            IconButton(\"arrow.clockwise\") {\n              refresh()\n            }\n\n            // Direct connect\n            IconButton(\"personalhotspot\") {\n              appState.update(to: .directConnect)\n            }\n\n            #if os(iOS) || os(tvOS)\n              // Settings\n              IconButton(\"gear\") {\n                appState.update(to: .settings(nil))\n              }\n            #endif\n          }\n        #endif\n\n        if (updateAvailable) {\n          Button(\"Update\") {\n            appState.update(to: .settings(.update))\n          }.padding(.top, 5)\n        }\n      }\n      #if !os(tvOS)\n      // TODO: Does this even do anything?\n      .listStyle(SidebarListStyle())\n      #endif\n    }\n    .onAppear {\n      // Check for updates\n      Task {\n        await checkForUpdates()\n      }\n\n      // Create server pingers\n      let servers = managedConfig.config.servers\n      pingers = servers.map { server in\n        Pinger(server)\n      }\n\n      refresh()\n\n      // TODO: The whole EventBus architecture is pretty unwieldy at the moment,\n      //   all this code just to create and start a lan server enumerator?\n      // Create LAN server enumerator\n      let eventBus = EventBus()\n      lanServerEnumerator = LANServerEnumerator(eventBus: eventBus)\n      eventBus.registerHandler { event in\n        switch event {\n          case let event as ErrorEvent:\n            log.warning(\"\\(event.message ?? \"Error\"): \\(event.error)\")\n          default:\n            break\n        }\n      }\n\n      do {\n        try lanServerEnumerator?.start()\n      } catch {\n        modal.error(RichError(\"Failed to start LAN server enumerator.\").becauseOf(error))\n      }\n    }\n    .onDisappear {\n      lanServerEnumerator?.stop()\n    }\n  }\n\n  // Check if any Delta Client updates are available. Does nothing if not on macOS.\n  func checkForUpdates() async {\n    #if os(macOS)\n      do {\n        let result = try Updater.isUpdateAvailable()\n        await MainActor.run {\n          updateAvailable = result\n        }\n      } catch {\n        modal.error(RichError(\"Failed to check for updates\").becauseOf(error))\n      }\n    #endif\n  }\n\n  /// Ping all servers and clear discovered LAN servers.\n  func refresh() {\n    for pinger in pingers {\n      Task {\n        try? await pinger.ping()\n      }\n    }\n\n    lanServerEnumerator?.clear()\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Settings/Account/AccountLoginView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nenum LoginViewState {\n  case chooseAccountType\n  case loginMicrosoft\n  case loginMojang\n  case loginOffline\n}\n\nstruct AccountLoginView: EditorView {\n  typealias Item = Account\n  \n  @State var state: LoginViewState = .chooseAccountType\n  \n  let completionHandler: (Item) -> Void\n  let cancelationHandler: (() -> Void)?\n  \n  /// Ignores `item` because this view is only ever used for logging into account not editing them.\n  init(_ item: Item? = nil, completion: @escaping (Item) -> Void, cancelation: (() -> Void)?) {\n    completionHandler = completion\n    cancelationHandler = cancelation\n  }\n  \n  var body: some View {\n    switch state {\n      case .chooseAccountType:\n        VStack {\n          Text(\"Choose account type\")\n          \n          Button(\"Microsoft\") {\n            state = .loginMicrosoft\n          }.buttonStyle(PrimaryButtonStyle())\n          \n          Button(\"Mojang\") {\n            state = .loginMojang\n          }.buttonStyle(PrimaryButtonStyle())\n          \n          Button(\"Offline\") {\n            state = .loginOffline\n          }.buttonStyle(PrimaryButtonStyle())\n          \n          Spacer().frame(height: 32)\n          \n          Button(\"Cancel\") {\n            cancelationHandler?()\n          }.buttonStyle(SecondaryButtonStyle())\n        }\n        .navigationTitle(\"Account Login\")\n        #if !os(iOS)\n        .onExitCommand {\n          cancelationHandler?()\n        }\n        #endif\n        #if !os(tvOS)\n        .frame(width: 200)\n        #endif\n      case .loginMicrosoft:\n        MicrosoftLoginView(loginViewState: $state, completionHandler: completionHandler)\n      case .loginMojang:\n        MojangLoginView(loginViewState: $state, completionHandler: completionHandler)\n      case .loginOffline:\n        OfflineLoginView(loginViewState: $state, completionHandler: completionHandler)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Settings/Account/AccountSettingsView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\n// TODO: Use Config.orderedAccounts to ensure that account ordering is at least consistent\nstruct AccountSettingsView: View {\n  @EnvironmentObject var managedConfig: ManagedConfig\n\n  @State var accounts: [Account] = []\n  @State var selectedIndex: Int? = nil\n\n  let saveAction: (() -> Void)?\n\n  init(saveAction: (() -> Void)? = nil) {\n    self.saveAction = saveAction\n  }\n\n  var body: some View {\n    VStack {\n      EditableList(\n        $accounts.onChange(save),\n        selected: $selectedIndex.onChange(saveSelected),\n        itemEditor: AccountLoginView.self,\n        row: row,\n        saveAction: saveAction,\n        cancelAction: nil,\n        emptyMessage: \"No accounts\",\n        forceShowCreation: managedConfig.accounts.isEmpty\n      )\n    }\n    .navigationTitle(\"Accounts\")\n    .onAppear {\n      accounts = Array(managedConfig.accounts.values)\n      selectedIndex = getSelectedIndex()\n    }\n  }\n\n  func row(\n    item: Account,\n    selected: Bool,\n    isFirst: Bool,\n    isLast: Bool,\n    handler: @escaping (EditableListAction) -> Void\n  ) -> some View {\n    HStack {\n      Image(systemName: \"chevron.right\")\n        .opacity(selected ? 1 : 0)\n\n      VStack(alignment: .leading) {\n        Text(item.username)\n          .font(.headline)\n        Text(item.type)\n          .font(.subheadline)\n      }\n\n      Spacer()\n\n      Button(\"Select\") { handler(.select) }\n        .disabled(selected)\n        #if !os(tvOS)\n        .buttonStyle(BorderlessButtonStyle())\n        #endif\n      IconButton(\"xmark\") { handler(.delete) }\n        #if os(tvOS)\n        .padding(.trailing, 20)\n        #endif\n    }\n  }\n\n  /// Saves the given accounts to the config file.\n  func save(_ accounts: [Account]) {\n    let accountId = getSelectedAccount()?.id\n\n    // Filter out duplicate accounts\n    var uniqueAccounts: [Account] = []\n    for account in accounts {\n      if !uniqueAccounts.contains(where: { $0.id == account.id }) {\n        uniqueAccounts.append(account)\n      }\n    }\n\n    selectedIndex = uniqueAccounts.firstIndex { $0.id == accountId }\n\n    if accounts.count != uniqueAccounts.count {\n      self.accounts = uniqueAccounts\n      return // Updating accounts will run this function again so we just stop here\n    }\n\n    managedConfig.accounts = [:]\n    managedConfig.config.addAccounts(uniqueAccounts)\n    managedConfig.config.selectAccount(withId: accountId)\n  }\n\n  /// Updates the selected account in the config file.\n  func saveSelected(_ index: Int?) {\n    guard let index = index else {\n      managedConfig.config.selectAccount(withId: nil)\n      return\n    }\n\n    managedConfig.config.selectAccount(withId: accounts[index].id)\n  }\n\n  /// Returns the currently selected account if any.\n  func getSelectedAccount() -> Account? {\n    guard let selectedIndex = selectedIndex else {\n      return nil\n    }\n\n    guard selectedIndex >= 0 && selectedIndex < accounts.count else {\n      self.selectedIndex = nil\n      return nil\n    }\n\n    return accounts[selectedIndex]\n  }\n\n  /// Returns the index of the currently selected account according to the current configuration.\n  func getSelectedIndex() -> Int? {\n    guard let selectedAccountId = managedConfig.selectedAccountId else {\n      return nil\n    }\n\n    return accounts.firstIndex { account in\n      account.id == selectedAccountId\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Settings/Account/MicrosoftLoginView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nenum MicrosoftLoginViewError: LocalizedError {\n  case failedToAuthorizeDevice\n  case failedToAuthenticate\n\n  var errorDescription: String? {\n    switch self {\n      case .failedToAuthorizeDevice:\n        return \"Failed to authorize device.\"\n      case .failedToAuthenticate:\n        return \"Failed to authenticate Microsoft account.\"\n    }\n  }\n}\n\nstruct MicrosoftLoginView: View {\n  enum MicrosoftState {\n    case authorizingDevice\n    case login(MicrosoftDeviceAuthorizationResponse)\n    case authenticatingUser\n  }\n\n  #if os(tvOS)\n  @Namespace var focusNamespace\n  #endif\n\n  @EnvironmentObject var modal: Modal\n  @EnvironmentObject var appState: StateWrapper<AppState>\n\n  @State var state: MicrosoftState = .authorizingDevice\n\n  @Binding var loginViewState: LoginViewState\n  #if os(tvOS)\n  @Environment(\\.resetFocus) var resetFocus\n  #endif\n\n  var completionHandler: (Account) -> Void\n\n  var body: some View {\n    VStack {\n      switch state {\n        case .authorizingDevice:\n          Text(\"Fetching device authorization code\")\n        case .login(let response):\n          Text(response.message)\n\n          #if !os(tvOS)\n            Link(\"Open in browser\", destination: response.verificationURI)\n              .padding(10)\n\n            Button(\"Copy code\") {\n              Clipboard.copy(response.userCode)\n            }\n            .buttonStyle(PrimaryButtonStyle())\n            .frame(width: 200)\n            .padding(.bottom, 26)\n          #endif\n\n          Button(\"Done\") {\n            state = .authenticatingUser\n            authenticate(with: response.deviceCode)\n          }\n          .buttonStyle(PrimaryButtonStyle())\n          #if !os(tvOS)\n          .frame(width: 200)\n          #else\n          .onAppear {\n            resetFocus(in: focusNamespace)\n          }\n          #endif\n        case .authenticatingUser:\n          Text(\"Authenticating...\")\n      }\n\n      Button(\"Cancel\") {\n        loginViewState = .chooseAccountType\n      }\n      .buttonStyle(SecondaryButtonStyle())\n      #if !os(tvOS)\n      .frame(width: 200)\n      #endif\n    }\n    .onAppear {\n      authorizeDevice()\n    }\n    #if os(tvOS)\n    .focusScope(focusNamespace)\n    #endif\n  }\n\n  func authorizeDevice() {\n    Task {\n      do {\n        let response = try await MicrosoftAPI.authorizeDevice()\n        state = .login(response)\n      } catch {\n        modal.error(MicrosoftLoginViewError.failedToAuthorizeDevice.becauseOf(error)) {\n          appState.update(to: .serverList)\n        }\n      }\n    }\n  }\n\n  func authenticate(with deviceCode: String) {\n    Task {\n      do {\n        let accessToken = try await MicrosoftAPI.getMicrosoftAccessToken(deviceCode)\n        let account = try await MicrosoftAPI.getMinecraftAccount(accessToken)\n        completionHandler(.microsoft(account))\n      } catch {\n        // We can trust MicrosoftAPIError's messages to be suitably human readable\n        let modalError: Error\n        if let error = error as? MicrosoftAPIError {\n          modalError = error\n        } else {\n          modalError = MicrosoftLoginViewError.failedToAuthenticate.becauseOf(error)\n        }\n\n        modal.error(modalError) {\n          appState.update(to: .serverList)\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Settings/Account/MojangLoginView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nstruct MojangLoginView: View {\n  @EnvironmentObject var managedConfig: ManagedConfig\n  \n  @State var isEmailValid = false\n  @State var email = \"\"\n  @State var password = \"\"\n  \n  @State var errorMessage: String?\n  @State var authenticating = false\n  \n  @Binding var loginViewState: LoginViewState\n\n  var completionHandler: (Account) -> Void\n\n  var body: some View {\n    VStack {\n      if authenticating {\n        Text(\"Authenticating...\")\n      } else {\n        if let errorMessage = errorMessage {\n          Text(errorMessage)\n            .foregroundColor(.red)\n        }\n        \n        EmailField(\"Email\", email: $email, isValid: $isEmailValid)\n        SecureField(\"Password\", text: $password)\n        \n        HStack {\n          Button(\"Back\") {\n            loginViewState = .chooseAccountType\n          }.buttonStyle(SecondaryButtonStyle())\n          Button(\"Login\") {\n            login()\n          }.buttonStyle(PrimaryButtonStyle())\n        }\n      }\n    }\n    #if !os(tvOS)\n    .frame(width: 200)\n    #endif\n  }\n  \n  func login() {\n    guard isEmailValid else {\n      displayError(\"Please enter a valid email address\")\n      return\n    }\n    \n    authenticating = true\n    \n    let email = email\n    let password = password\n    let clientToken = managedConfig.clientToken\n    \n    Task {\n      do {\n        let account = try await MojangAPI.login(email: email, password: password, clientToken: clientToken)\n        completionHandler(account)\n      } catch {\n        displayError(\"Failed to authenticate: \\(error)\")\n      }\n    }\n  }\n  \n  func displayError(_ message: String) {\n    ThreadUtil.runInMain {\n      errorMessage = message\n      authenticating = false\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Settings/Account/OfflineLoginView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nstruct OfflineLoginView: View {\n  @State private var username = \"\"\n  @State private var errorMessage: String?\n  \n  @Binding var loginViewState: LoginViewState\n\n  var completionHandler: (Account) -> Void\n  \n  var body: some View {\n    VStack {\n      if let errorMessage = errorMessage {\n        Text(errorMessage)\n          .foregroundColor(.red)\n      }\n      \n      TextField(\"Username\", text: $username)\n      \n      HStack {\n        Button(\"Back\") {\n          loginViewState = .chooseAccountType\n        }.buttonStyle(SecondaryButtonStyle())\n        Button(\"Login\") {\n          login()\n        }.buttonStyle(PrimaryButtonStyle())\n      }\n    }\n    #if !os(tvOS)\n    .frame(width: 200)\n    #endif\n  }\n  \n  func login() {\n    guard !username.isEmpty else {\n      displayError(\"Please provide a username\")\n      return\n    }\n    \n    let account = Account.offline(OfflineAccount(username: username))\n    completionHandler(account)\n  }\n  \n  func displayError(_ message: String) {\n    ThreadUtil.runInMain {\n      errorMessage = message\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Settings/ControlsSettingsView.swift",
    "content": "import SwiftUI\n\n#if !os(tvOS)\nstruct ControlsSettingsView: View {\n  @EnvironmentObject var managedConfig: ManagedConfig\n\n  @State var sensitivity: Float = 0\n  @State var toggleSprint = false\n  @State var toggleSneak = false\n\n  var body: some View {\n    ScrollView {\n      HStack {\n        Text(\"Sensitivity: \\(Self.formatSensitivity(sensitivity))\")\n          .frame(width: 150)\n\n        Slider(value: $sensitivity, in: 0...10, onEditingChanged: { isEditing in\n          if !isEditing {\n            sensitivity = Self.roundSensitivity(sensitivity)\n            managedConfig.mouseSensitivity = sensitivity\n          }\n        })\n      }\n      .frame(width: 450)\n\n      HStack {\n        Text(\"Toggle sprint\").frame(width: 150)\n        Spacer()\n        Toggle(\n          \"Toggle sprint\",\n          isOn: $toggleSprint.onChange { newValue in\n            managedConfig.toggleSprint = newValue\n          }\n        )\n          .labelsHidden()\n          .toggleStyle(.switch)\n        Spacer()\n      }\n      .frame(width: 400)\n\n      HStack {\n        Text(\"Toggle sneak\").frame(width: 150)\n        Spacer()\n        Toggle(\n          \"Toggle sneak\",\n          isOn: $toggleSneak.onChange { newValue in\n            managedConfig.toggleSneak = newValue\n          }\n        )\n          .labelsHidden()\n          .toggleStyle(.switch)\n        Spacer()\n      }\n      .frame(width: 400)\n\n      Text(\"Bindings\")\n        .font(.title)\n        .padding(.top, 16)\n\n      KeymapEditorView()\n    }\n    .onAppear {\n      sensitivity = managedConfig.mouseSensitivity\n      toggleSprint = managedConfig.toggleSprint\n      toggleSneak = managedConfig.toggleSneak\n    }\n  }\n\n  /// Rounds mouse sensitivity to the nearest even number percentage.\n  private static func roundSensitivity(_ sensitivity: Float) -> Float {\n    if abs(100 - sensitivity * 100) <= 3 {\n      return 1\n    }\n    return Float(Int(round(sensitivity * 100 / 2)) * 2) / 100\n  }\n\n  /// Formats mouse sensitivity as a rounded percentage.\n  private static func formatSensitivity(_ sensitivity: Float) -> String {\n    return \"\\(Int(roundSensitivity(sensitivity) * 100))%\"\n  }\n}\n#endif\n"
  },
  {
    "path": "Sources/Client/Views/Settings/KeymapEditorView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nstruct KeymapEditorView: View {\n  @EnvironmentObject var managedConfig: ManagedConfig\n\n  /// Whether key inputs are being captured by the view.\n  @State var inputCaptured = false\n  /// The input currently selected for rebinding.\n  @State var selectedInput: Input?\n\n  var body: some View {\n    InputView(listening: $inputCaptured, cursorCaptured: false) {\n      ForEach(Input.allCases.filter(\\.isBindable), id: \\.self) { (input: Input) in\n        let bindings = managedConfig.keymap.bindings\n        let key = bindings[input]\n        let isUnique = key == nil ? true : bindings.values.filter({ $0 == key }).count == 1\n        let isBound = bindings[input] != nil\n        let isSelected = selectedInput == input\n\n        let labelColor = Self.labelColor(isUnique: isUnique, isBound: isBound, isSelected: isSelected)\n\n        HStack {\n          // Input name (e.g. 'Sneak')\n          Text(input.humanReadableLabel)\n            .frame(width: 150)\n\n          // Button to set a new binding\n          let keyName = bindings[input]?.description ?? \"Unbound\"\n          Button(action: {\n            if isSelected {\n              managedConfig.keymap.bindings[input] = .leftMouseButton\n              selectedInput = nil\n              inputCaptured = false\n            } else {\n              selectedInput = input\n              inputCaptured = true\n            }\n          }, label: {\n            Text(isSelected ? \"> Press key <\" : keyName)\n              .foregroundColor(labelColor)\n          })\n          .buttonStyle(SecondaryButtonStyle())\n          .disabled(isSelected)\n\n          // Button to unbind an input\n          IconButton(\"xmark\", isDisabled: !isBound || isSelected) {\n            managedConfig.keymap.bindings.removeValue(forKey: input)\n          }\n        }\n        .frame(width: 400)\n      }\n    }\n    .passthroughClicks()\n    .avoidGeometryReader()\n    .onKeyPress { key, _ in\n      guard let selectedInput = selectedInput else {\n        return\n      }\n\n      if key == .escape {\n        managedConfig.keymap.bindings.removeValue(forKey: selectedInput)\n      } else {\n        managedConfig.keymap.bindings[selectedInput] = key\n      }\n\n      self.selectedInput = nil\n    }\n    .onKeyRelease { key in\n      inputCaptured = false\n    }\n\n    Button(\"Reset bindings to defaults\") {\n      managedConfig.keymap.bindings = Keymap.default.bindings\n    }\n    .buttonStyle(SecondaryButtonStyle())\n    .frame(width: 250)\n    .padding(.top, 10)\n  }\n\n  static func labelColor(isUnique: Bool, isBound: Bool, isSelected: Bool) -> Color {\n    if isSelected {\n      return .white\n    } else if !isBound {\n      return .yellow\n    } else if !isUnique {\n      return .red\n    } else {\n      return .white\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Settings/PluginSettingsView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\n#if os(macOS)\nenum PluginSettingsViewError: LocalizedError {\n  case failedToLoadPlugin(identifier: String)\n\n  var errorDescription: String? {\n    switch self {\n      case let .failedToLoadPlugin(identifier):\n        return \"Failed to load plugin '\\(identifier)'\"\n    }\n  }\n}\n\nstruct PluginSettingsView: View {\n  @EnvironmentObject var pluginEnvironment: PluginEnvironment\n  @EnvironmentObject var modal: Modal\n  @EnvironmentObject var managedConfig: ManagedConfig\n  @Environment(\\.storage) var storage: StorageDirectory\n  \n  func updateConfig() {\n    managedConfig.unloadedPlugins = Array(pluginEnvironment.unloadedPlugins.keys)\n  }\n  \n  var body: some View {\n    VStack {\n      HStack {\n        // Loaded plugins\n        VStack {\n          Text(\"Loaded\").font(.title2)\n          \n          ScrollView {\n            ForEach(Array(pluginEnvironment.plugins), id: \\.key) { (identifier, plugin) in\n              HStack {\n                Text(plugin.manifest.name)\n                \n                Button(\"Unload\") {\n                  pluginEnvironment.unloadPlugin(identifier)\n                  updateConfig()\n                }\n              }\n            }\n            \n            if pluginEnvironment.plugins.isEmpty {\n              Text(\"no loaded plugins\").italic()\n            }\n          }\n        }.frame(maxWidth: .infinity)\n        \n        // Unloaded plugins\n        VStack {\n          Text(\"Unloaded\").font(.title2)\n          \n          ScrollView {\n            ForEach(Array(pluginEnvironment.unloadedPlugins), id: \\.key) { (identifier, plugin) in\n              HStack {\n                Text(plugin.manifest.name)\n                \n                Button(\"Load\") {\n                  do {\n                    try pluginEnvironment.loadPlugin(from: plugin.bundle)\n                    updateConfig()\n                  } catch {\n                    modal.error(PluginSettingsViewError.failedToLoadPlugin(identifier: identifier).becauseOf(error))\n                  }\n                }\n              }\n            }\n            \n            if pluginEnvironment.unloadedPlugins.isEmpty {\n              Text(\"no unloaded plugins\").italic()\n            }\n          }\n        }.frame(maxWidth: .infinity)\n        \n        // Errors\n        if !pluginEnvironment.errors.isEmpty {\n          VStack {\n            HStack {\n              Text(\"Errors\").font(.title2)\n              Button(\"Clear\") {\n                pluginEnvironment.errors = []\n              }\n            }\n            \n            ScrollView {\n              VStack(alignment: .leading) {\n                ForEach(Array(pluginEnvironment.errors.enumerated()), id: \\.0) { (_, error) in\n                  Text(\"\\(error.bundle):\").font(.title)\n                  Text(String(\"\\(error.underlyingError)\"))\n                }\n              }\n            }\n          }.frame(maxWidth: .infinity)\n        }\n      }\n      \n      // Global actions\n      HStack {\n        Button(\"Unload all\") {\n          pluginEnvironment.unloadAll()\n          updateConfig()\n        }\n        Button(\"Reload all\") {\n          pluginEnvironment.reloadAll(storage.pluginDirectory)\n          updateConfig()\n        }\n        Button(\"Open plugins directory\") {\n          NSWorkspace.shared.selectFile(nil, inFileViewerRootedAtPath: storage.pluginDirectory.path)\n        }\n      }\n    }\n  }\n}\n#endif\n"
  },
  {
    "path": "Sources/Client/Views/Settings/Server/EditServerListView.swift",
    "content": "import SwiftUI\nimport DeltaCore\nimport Combine\n\nstruct EditServerListView: View {\n  @EnvironmentObject var appState: StateWrapper<AppState>\n  @EnvironmentObject var managedConfig: ManagedConfig\n  \n  @State var servers: [ServerDescriptor] = []\n  \n  var body: some View {\n    VStack {\n      EditableList(\n        $managedConfig.servers,\n        itemEditor: ServerEditorView.self,\n        row: { item, selected, isFirst, isLast, handler in\n          HStack {\n            #if !os(tvOS)\n            VStack {\n              IconButton(\"chevron.up\", isDisabled: isFirst) { handler(.moveUp) }\n              IconButton(\"chevron.down\", isDisabled: isLast) { handler(.moveDown) }\n            }\n            #endif\n            \n            VStack(alignment: .leading) {\n              Text(item.name)\n                .font(.headline)\n              Text(item.description)\n                .font(.subheadline)\n            }\n            \n            Spacer()\n            \n            HStack {\n              IconButton(\"square.and.pencil\") { handler(.edit) }\n              IconButton(\"xmark\") { handler(.delete) }\n                #if os(tvOS)\n                .padding(.trailing, 20)\n                #endif\n            }\n          }\n        },\n        saveAction: appState.pop,\n        cancelAction: appState.pop, // TODO: There is no cancel anymore\n        emptyMessage: \"No servers\")\n    }\n    .padding()\n    .navigationTitle(\"Edit Servers\")\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Settings/Server/ServerEditorView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nstruct ServerEditorView: EditorView {\n  @State var descriptor: ServerDescriptor\n  @State var errorMessage: String?\n  \n  var completionHandler: (ServerDescriptor) -> Void\n  var cancelationHandler: (() -> Void)?\n  \n  @State var isAddressValid = false\n  /// True if this is editing an existing server.\n  let isEditor: Bool\n  \n  init(_ item: ServerDescriptor?, completion: @escaping (ServerDescriptor) -> Void, cancelation: (() -> Void)?) {\n    completionHandler = completion\n    cancelationHandler = cancelation\n    \n    isEditor = item != nil\n    _descriptor = State(initialValue: item ?? ServerDescriptor(name: \"\", host: \"\", port: nil))\n  }\n  \n  private func verify() -> Bool {\n    if !isAddressValid {\n      errorMessage = \"Invalid IP\"\n    } else {\n      return true\n    }\n    return false\n  }\n  \n  var body: some View {\n    VStack {\n      TextField(\"Server name\", text: $descriptor.name)\n      AddressField(\"Server address\", host: $descriptor.host, port: $descriptor.port, isValid: $isAddressValid)\n      \n      if let message = errorMessage {\n        Text(message)\n          .bold()\n      }\n      \n      HStack {\n        if let cancel = cancelationHandler {\n          Button(\"Cancel\", action: cancel)\n            .buttonStyle(SecondaryButtonStyle())\n        }\n        Button(isEditor ? \"Save\" : \"Add\") {\n          if verify() { completionHandler(descriptor) }\n        }\n        .buttonStyle(PrimaryButtonStyle())\n      }\n      .padding(.top, 8)\n    }\n    #if !os(tvOS)\n    .frame(width: 200)\n    #endif\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Settings/SettingsView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nenum SettingsState: CaseIterable {\n  case video\n  case controls\n  case accounts\n  case update\n  case plugins\n  case troubleshooting\n}\n\nstruct SettingsView: View {\n  var isInGame: Bool\n  var done: () -> Void\n\n  @State private var currentPage: SettingsState?\n\n  init(\n    isInGame: Bool,\n    landingPage: SettingsState? = nil,\n    onDone done: @escaping () -> Void\n  ) {\n    self.isInGame = isInGame\n    self.done = done\n\n    #if os(iOS) || os(tvOS)\n      // On iOS, the navigation isn't a split view, so we should show the settings page selection\n      // view first instead of auto-selecting the first page.\n      self._currentPage = State(initialValue: landingPage)\n    #else\n      self._currentPage = State(initialValue: landingPage ?? SettingsState.allCases[0])\n    #endif\n  }\n\n  var body: some View {\n    NavigationView {\n      List {\n        NavigationLink(\n          \"Video\",\n          destination: VideoSettingsView().padding(),\n          tag: SettingsState.video,\n          selection: $currentPage\n        )\n\n        #if !os(tvOS)\n        NavigationLink(\n          \"Controls\",\n          destination: ControlsSettingsView().padding(),\n          tag: SettingsState.controls,\n          selection: $currentPage\n        )\n        #endif\n\n        if !isInGame {\n          NavigationLink(\n            \"Accounts\",\n            destination: AccountSettingsView().padding(),\n            tag: SettingsState.accounts,\n            selection: $currentPage\n          )\n\n          #if os(macOS)\n          NavigationLink(\n            \"Update\",\n            destination: UpdateView().padding(),\n            tag: SettingsState.update,\n            selection: $currentPage\n          )\n          NavigationLink(\n            \"Plugins\",\n            destination: PluginSettingsView().padding(),\n            tag: SettingsState.plugins,\n            selection: $currentPage\n          )\n          #endif\n\n          NavigationLink(\n            \"Troubleshooting\",\n            destination: TroubleshootingView().padding(),\n            tag: SettingsState.troubleshooting,\n            selection: $currentPage\n          )\n        }\n\n        Button(\"Done\", action: {\n          withAnimation(nil) {\n            done()\n          }\n        })\n          #if !os(tvOS)\n          .buttonStyle(BorderlessButtonStyle())\n          .keyboardShortcut(.escape, modifiers: [])\n          #endif\n      }\n      #if !os(tvOS)\n      .listStyle(SidebarListStyle())\n      #endif\n    }\n    .navigationTitle(\"Settings\")\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Settings/TroubleShootingView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\n/// An error which can occur during troubleshooting; how ironic.\nenum TroubleshootingError: LocalizedError {\n  case failedToDeleteCache\n  case failedToResetConfig\n  case failedToPerformFreshInstall\n\n  var errorDescription: String? {\n    switch self {\n      case .failedToDeleteCache:\n        return \"Failed to delete cache directory.\"\n      case .failedToResetConfig:\n        return \"Failed to reset configuration to defaults.\"\n      case .failedToPerformFreshInstall:\n        return \"Failed to perform fresh install.\"\n    }\n  }\n}\n\n/// A collection of useful troubleshooting actions. Optionally displays a cause for\n/// troubleshooting (in case the user was forcefully sent here).\nstruct TroubleshootingView: View {\n  @EnvironmentObject var appState: StateWrapper<AppState>\n  @EnvironmentObject var modal: Modal\n  @EnvironmentObject var managedConfig: ManagedConfig\n\n  @Environment(\\.storage) var storage: StorageDirectory\n\n  @State private var message: String? = nil\n\n  /// Content for a never-disappearing error message displayed above the option buttons.\n  private let error: (any Error)?\n\n  /// The error message to show if any.\n  var errorMessage: String? {\n    guard let error = error else {\n      return nil\n    }\n\n    if let error = error as? RichError {\n      return error.richDescription\n    } else {\n      return \"\\(error)\"\n    }\n  }\n\n  /// Creates a troubleshooting view with an optional cause for troubleshooting.\n  init(error: (any Error)? = nil) {\n    self.error = error\n  }\n\n  var body: some View {\n    VStack {\n      if let errorMessage = errorMessage {\n        Text(errorMessage)\n          .padding(.bottom, 10)\n      }\n\n      Button(\"Clear cache\") { // Clear cache\n        perform(\n          \"Clearing cache\",\n          \"Cleared cache successfully\",\n          action: storage.removeCache,\n          error: TroubleshootingError.failedToDeleteCache\n        ) {\n          message = nil\n        }\n      }.buttonStyle(SecondaryButtonStyle())\n\n      Button(\"Reset config\") { // Reset config\n        perform(\n          \"Resetting config\",\n          \"Reset config successfully\",\n          action: managedConfig.reset,\n          error: TroubleshootingError.failedToResetConfig\n        ) {\n          message = nil\n        }\n      }.buttonStyle(SecondaryButtonStyle())\n\n      Button(\"Perform fresh install\") {\n        perform(\n          \"Performing fresh install\",\n          action: {\n            try storage.backup()\n            try storage.reset()\n            #if os(macOS)\n              message = \"Relaunching...\"\n              try await delay(seconds: 1)\n              Utils.relaunch()\n            #else\n              message = \"Please relaunch app to complete fresh install.\"\n            #endif\n          },\n          error: TroubleshootingError.failedToPerformFreshInstall\n        ) {\n          message = nil\n        }\n      }.buttonStyle(SecondaryButtonStyle())\n\n      #if os(macOS)\n      Button(\"View logs\") {\n        NSWorkspace.shared.open(storage.currentLogFile)\n      }.buttonStyle(SecondaryButtonStyle())\n      #endif\n\n      if let message = message {\n        Text(message)\n          .padding(.top, 10)\n      }\n    }\n    #if !os(tvOS)\n    .frame(width: 200)\n    #endif\n  }\n\n  private func perform(\n    _ taskName: String,\n    _ successMessage: String? = nil,\n    action: @escaping () async throws -> Void,\n    error baseError: TroubleshootingError,\n    onErrorDismissed errorDismissedHandler: (() -> Void)? = nil\n  ) {\n    Task {\n      let initialMessage = \"\\(taskName)...\"\n      message = initialMessage\n      do {\n        try await delay(seconds: 0.5)\n        try await action()\n        if let successMessage = successMessage, message == initialMessage {\n          message = successMessage\n        }\n      } catch {\n        modal.error(baseError.becauseOf(error)) {\n          errorDismissedHandler?()\n        }\n      }\n    }\n  }\n\n  private func delay(seconds: Double) async throws {\n    try await Task.sleep(nanoseconds: UInt64(Duration.seconds(seconds).nanoseconds))\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Settings/UpdateView.swift",
    "content": "import SwiftUI\nimport ZIPFoundation\nimport DeltaCore\n\n#if os(macOS)\nstruct UpdateView: View {\n  enum UpdateViewState {\n    case loadingBranches\n    case selectBranch(branches: [String])\n    case updating\n  }\n\n  @EnvironmentObject var appState: StateWrapper<AppState>\n  @EnvironmentObject var modal: Modal\n  @Environment(\\.storage) var storage: StorageDirectory\n\n  @StateObject var progress = TaskProgress<Updater.UpdateStep>()\n\n  @State var state: UpdateViewState = .loadingBranches\n  @State var branch: String?\n  @State var updateVersion: String?\n\n  init() {}\n\n  var body: some View {\n    switch state {\n      case .loadingBranches:\n        Text(\"Loading branches...\")\n          .onAppear {\n            Task {\n              do {\n                let branches = try Updater.getBranches().map(\\.name)\n                ThreadUtil.runInMain {\n                  state = .selectBranch(branches: branches)\n                }\n              } catch {\n                modal.error(error) {\n                  appState.update(to: .serverList)\n                }\n              }\n            }\n          }\n      case let .selectBranch(branches):\n        VStack {\n          Menu {\n            ForEach(branches, id: \\.self) { branch in\n              Button(branch) {\n                self.branch = branch\n              }\n            }\n          } label: {\n            if let branch = branch {\n              Text(\"Branch: \\(branch)\")\n            } else {\n              Text(\"Select a branch\")\n            }\n          }\n\n          if let branch = branch {\n            Button(\"Update to latest commit\") {\n              state = .updating\n              Task {\n                do {\n                  let download = try Updater.getDownload(for: .nightly(branch: branch))\n                  updateVersion = download.version\n                  try await Updater.performUpdate(\n                    download: download,\n                    isNightly: true,\n                    storage: storage,\n                    progress: progress\n                  )\n                } catch {\n                  modal.error(error) {\n                    appState.update(to: .serverList)\n                  }\n                }\n              }\n            }\n            .buttonStyle(SecondaryButtonStyle())\n          } else {\n            Button(\"Update to latest commit\") {}\n              .disabled(true)\n              .buttonStyle(SecondaryButtonStyle())\n          }\n        }\n        .frame(width: 200)\n        .padding(.vertical)\n      case .updating:\n        // Shows the progress of an update\n        VStack(alignment: .leading, spacing: 16) {\n          Group {\n            if let version = updateVersion {\n              Text(\"Updating to \\(version)\")\n            } else {\n              Text(\"Fetching update information\")\n            }\n          }\n          .font(.title)\n\n          ProgressView(value: progress.progress) {\n            Text(progress.message)\n          }\n\n          Button(\"Cancel\") {\n            state = .loadingBranches\n          }\n          .buttonStyle(SecondaryButtonStyle())\n          .frame(width: 200)\n        }\n        .frame(maxWidth: 500)\n    }\n  }\n}\n#endif\n"
  },
  {
    "path": "Sources/Client/Views/Settings/VideoSettingsView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nstruct VideoSettingsView: View {\n  @EnvironmentObject var managedConfig: ManagedConfig\n\n  var body: some View {\n    ScrollView {\n      HStack {\n        Text(\"Render distance: \\(managedConfig.render.renderDistance)\")\n        #if os(tvOS)\n        ProgressView(value: Double(managedConfig.render.renderDistance) / 32)\n        #else\n        Slider(\n          value: Binding {\n            Float(managedConfig.render.renderDistance)\n          } set: { newValue in\n            managedConfig.render.renderDistance = Int(newValue.rounded())\n          },\n          in: 0...32,\n          step: 1\n        )\n          .frame(width: 220)\n        #endif\n      }\n      #if os(tvOS)\n      .focusable()\n      .onMoveCommand { direction in\n        switch direction {\n          case .left:\n            managedConfig.render.renderDistance -= 1\n          case .right:\n            managedConfig.render.renderDistance += 1\n          default:\n            break\n        }\n      }\n      #endif\n\n      #if !os(tvOS)\n      HStack {\n        Text(\"FOV: \\(Int(managedConfig.render.fovY.rounded()))\")\n        Spacer()\n        Slider(\n          value: Binding {\n            managedConfig.render.fovY\n          } set: { newValue in\n            managedConfig.render.fovY = newValue.rounded()\n          },\n          in: 30...110\n        )\n          .frame(width: 220)\n      }\n      #endif\n\n      HStack {\n        Text(\"Render mode\")\n        Spacer()\n        Picker(\"Render mode\", selection: $managedConfig.render.mode) {\n          ForEach(RenderMode.allCases) { mode in\n            Text(mode.rawValue.capitalized)\n          }\n        }\n        #if os(macOS)\n          .pickerStyle(RadioGroupPickerStyle())\n        #elseif os(iOS) || os(tvOS)\n          .pickerStyle(DefaultPickerStyle())\n        #endif\n        #if !os(tvOS)\n          .frame(width: 220)\n        #endif\n      }\n\n      // Order independent transparency doesn't work on tvOS yet (our implementation uses a Metal\n      // feature which isn't supported on tvOS).\n      #if !os(tvOS)\n        HStack {\n          Text(\"Order independent transparency\")\n          Spacer()\n          Toggle(\n            \"Order independent transparency\",\n            isOn: $managedConfig.render.enableOrderIndependentTransparency\n          )\n            .labelsHidden()\n            .toggleStyle(.switch)\n            .frame(width: 220)\n        }\n      #endif\n    }\n    #if !os(tvOS)\n    .frame(width: 450)\n    #endif\n    .navigationTitle(\"Video\")\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Startup/LoadAndThen.swift",
    "content": "import Combine\nimport DeltaCore\nimport SwiftUI\n\nstruct LoadResult {\n  var managedConfig: ManagedConfig\n  var resourcePack: Box<ResourcePack>\n  var pluginEnvironment: PluginEnvironment\n}\n\nstruct LoadAndThen<Content: View>: View {\n  @EnvironmentObject var modal: Modal\n\n  @StateObject var startup = TaskProgress<StartupStep>()\n  @State var loadResult: LoadResult?\n\n  @Binding var hasLoaded: Bool\n  @Binding var storage: StorageDirectory?\n  let arguments: CommandLineArguments\n  var content: (ManagedConfig, Box<ResourcePack>, PluginEnvironment) -> Content\n\n  init(\n    _ arguments: CommandLineArguments,\n    _ hasLoaded: Binding<Bool>,\n    _ storage: Binding<StorageDirectory?>,\n    content: @escaping (ManagedConfig, Box<ResourcePack>, PluginEnvironment) -> Content\n  ) {\n    self.arguments = arguments\n    _hasLoaded = hasLoaded\n    _storage = storage\n    self.content = content\n  }\n\n  func load() throws {\n    var previousStep: StartupStep?\n    startup.onChange { progress in\n      if progress.currentStep != previousStep {\n        let percentage = String(format: \"%.01f\", progress.progress * 100)\n        log.info(\"\\(progress.message) (\\(percentage)%)\")\n      }\n\n      previousStep = progress.currentStep\n    }\n\n    let stopwatch = Stopwatch()\n\n    guard var storage = StorageDirectory.platformDefault else {\n      throw RichError(\"Failed to get storage directory\")\n    }\n    try storage.ensureCreated()\n    storage.pluginDirectoryOverride = arguments.pluginsDirectory\n    self.storage = storage\n\n    // Load configuration, replacing it with defaults if it can't be read\n    let config: Config\n    do {\n      config = try Config.load(from: storage.configFile)\n    } catch {\n      modal.error(RichError(\"Failed to load config, using defaults\").becauseOf(error))\n      config = Config()\n      // Don't save the new config (wait until the user makes changes). This means that\n      // the user can quit the client and attempt to fix their config without having to\n      // start their config all over again.\n    }\n\n    let managedConfig = ManagedConfig(config, backedBy: storage.configFile) { error in\n      modal.error(RichError(\"Failed to save config\").becauseOf(error))\n    }\n\n    // Enable file logging as there isn't really a better place to put this. Despite\n    // not being part of loading per-say, it needs to be enabled as early as possible\n    // to be maximally useful, so we can't exactly wait till a better time.\n    do {\n      try enableFileLogger(loggingTo: storage.currentLogFile)\n    } catch {\n      modal.warning(\"Failed to enable file logger\")\n    }\n\n    Task {\n      let errors = await managedConfig.refreshAccounts()\n\n      if errors.isEmpty {\n        return\n      }\n\n      var richError = RichError(\"Failed to refresh accounts.\")\n      if errors.count > 1 {\n        for (i, error) in errors.enumerated() {\n          richError = richError.with(\"Reason \\(i + 1)\", error)\n        }\n      } else if let reason = errors.first {\n        if let reason = reason as? RichError {\n          richError = reason\n        } else {\n          richError = richError.becauseOf(reason)\n        }\n      }\n      modal.error(richError)\n    }\n\n    let pluginEnvironment = PluginEnvironment()\n    startup.perform(.loadPlugins) {\n      try pluginEnvironment.loadPlugins(\n        from: storage.pluginDirectory,\n        excluding: config.unloadedPlugins\n      )\n    } handleError: { error in\n      modal.error(\"Error occurred during plugin loading, no plugins will be available: \\(error)\")\n    }\n\n    // This check encompasses both missing entity models and missing assets.\n    let invalidateAssets = !FileSystem.directoryExists(\n      storage.assetDirectory.appendingPathComponent(\"minecraft/models/entity\")\n    )\n    try startup.perform(.downloadAssets, if: invalidateAssets) { progress in\n      try? FileManager.default.removeItem(at: storage.assetDirectory)\n      try ResourcePack.downloadVanillaAssets(\n        forVersion: Constants.versionString,\n        to: storage.assetDirectory,\n        progress: progress\n      )\n    }\n\n    try startup.perform(.loadRegistries) { progress in\n      try RegistryStore.populateShared(storage.registryDirectory, progress: progress)\n    }\n\n    // TODO: Track resource pack loading progress to improve loading screen granularity\n    // Load resource pack and cache it if necessary\n    let resourcePack = try startup.perform(.loadResourcePacks) {\n      let packCache = storage.cache(forResourcePackNamed: \"vanilla\")\n      let resourcePack = try ResourcePack.load(\n        from: storage.assetDirectory,\n        cacheDirectory: invalidateAssets ? nil : packCache\n      )\n      if !FileSystem.directoryExists(packCache) || invalidateAssets {\n        do {\n          try resourcePack.cache(to: packCache)\n        } catch {\n          modal.warning(\"Failed to cache vanilla resource pack\")\n        }\n      }\n      return resourcePack\n    }\n\n    log.info(\"Finished loading (\\(stopwatch.elapsed))\")\n\n    ThreadUtil.runInMain {\n      loadResult = LoadResult(\n        managedConfig: managedConfig,\n        resourcePack: Box(resourcePack),\n        pluginEnvironment: pluginEnvironment\n      )\n      hasLoaded = true\n    }\n  }\n\n  var body: some View {\n    VStack {\n      if let loadResult = loadResult {\n        content(loadResult.managedConfig, loadResult.resourcePack, loadResult.pluginEnvironment)\n      } else {\n        ProgressLoadingView(progress: startup.progress, message: startup.message)\n      }\n    }\n    .onAppear {\n      DispatchQueue(label: \"app-loading\").async {\n        do {\n          try load()\n        } catch {\n          let richError: RichError\n          if let error = error as? RichError {\n            richError = error\n          } else {\n            richError = RichError(\"Failed to load.\").becauseOf(error)\n          }\n\n          modal.error(richError) {\n            Foundation.exit(1)\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Startup/ProgressLoadingView.swift",
    "content": "import SwiftUI\nimport DeltaCore\n\nstruct ProgressLoadingView: View {\n  // MARK: Public properties\n\n  /// Loader progress\n  public var progress: Double\n  /// Loading message\n  public let message: String\n\n  // MARK: Private properties\n\n  /// Loader bar border height\n  private let loaderHeight: CGFloat = 20\n  /// Loader bar border width\n  private let loaderSize: CGFloat = 4\n  /// Bar inset\n  private let inset: CGFloat = 6\n  /// Progress bar width percentage\n  @State private var animatedProgress: Double = 0\n\n  // MARK: Init\n\n  /// Creates a new progress bar view.\n  /// - Parameters:\n  ///   - progress: The progress from 0 to 1.\n  ///   - message: A description for the current task.\n  init(progress: Double, message: String) {\n    if progress < 0 || progress > 1 {\n      log.error(\"Progress out of range: \\(progress)\")\n    }\n    self.progress = progress\n    self.message = message\n  }\n\n  // MARK: View\n\n  var body: some View {\n    GeometryReader { proxy in\n      let parentWidth = proxy.size.width\n      let loaderWidth = parentWidth * 0.6\n      let progressBarWidth = loaderWidth - 2 * inset\n\n      VStack(alignment: .center) {\n        Text(message)\n\n        HStack(alignment: .center) {\n          Color.white\n            .frame(width: progressBarWidth * animatedProgress)\n            .frame(maxHeight: .infinity, alignment: .leading)\n          Spacer()\n        }\n        .padding(.horizontal, inset)\n        .padding(.vertical, inset)\n        .frame(width: loaderWidth, height: loaderHeight)\n        .overlay(\n          PixellatedBorder()\n            .stroke(Color.white, lineWidth: loaderSize)\n        )\n      }\n      .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)\n      .background(Color.black)\n    }\n    .onChange(of: progress) { newProgress in\n      ThreadUtil.runInMain {\n        withAnimation(.easeInOut(duration: 1)) {\n          animatedProgress = newProgress\n        }\n      }\n    }\n    .onAppear {\n      withAnimation(.easeInOut(duration: 1)) {\n        animatedProgress = progress\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/Views/Startup/StartupStep.swift",
    "content": "import Foundation\nimport DeltaCore\n\n/// All major startup steps in order.\nenum StartupStep: CaseIterable, TaskStep {\n  case loadPlugins\n  case downloadAssets\n  case loadRegistries\n  case loadResourcePacks\n\n  /// The task description.\n  var message: String {\n    switch self {\n      case .loadPlugins: return \"Loading plugins\"\n      case .downloadAssets: return \"Downloading vanilla assets (might take a little while)\"\n      case .loadRegistries: return \"Loading registries\"\n      case .loadResourcePacks: return \"Loading resource pack\"\n    }\n  }\n\n  /// The task's expected duration relative to the total.\n  var relativeDuration: Double {\n    switch self {\n      case .loadPlugins: return 3\n      case .downloadAssets: return 15\n      case .loadRegistries: return 3\n      case .loadResourcePacks: return 15\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Client/main.swift",
    "content": "// Start the main SwiftUI loop\nDeltaClientApp.main()\n"
  },
  {
    "path": "Sources/ClientGtk/ChatView.swift",
    "content": "import DeltaCore\nimport SwiftCrossUI\n\nclass ChatViewState: Observable {\n  @Observed var error: String?\n  @Observed var messages: [String] = []\n  @Observed var message = \"\"\n}\n\nstruct ChatView: View {\n  var client: Client\n\n  var state = ChatViewState()\n\n  init(_ client: Client) {\n    self.client = client\n    client.eventBus.registerHandler(handleEvent)\n  }\n\n  func handleEvent(_ event: Event) {\n    switch event {\n      case let event as ChatMessageReceivedEvent:\n        state.messages.append(\n          event.message.content.toText(with: client.resourcePack.getDefaultLocale())\n        )\n      default:\n        break\n    }\n  }\n\n  var body: some View {\n    if let error = state.error {\n      Text(error)\n    }\n\n    TextField(\"Message\", state.$message)\n    Button(\"Send\") {\n      guard !state.message.isEmpty else {\n        return\n      }\n\n      // TODO: Create api for sending chat messages\n      do {\n        try client.sendPacket(ChatMessageServerboundPacket(state.message))\n      } catch {\n        state.error = \"Failed to send message: \\(error)\"\n      }\n      state.message = \"\"\n    }\n\n    ForEach(state.messages.reversed()) { message in\n      Text(message)\n    }\n    .frame(minHeight: 200)\n    .padding(.top, 10)\n  }\n}\n"
  },
  {
    "path": "Sources/ClientGtk/DeltaClientApp.swift",
    "content": "import DefaultBackend\nimport DeltaCore\nimport Dispatch\nimport Foundation\nimport SwiftCrossUI\n\n@main\nstruct DeltaClientApp: App {\n  enum DeltaClientState {\n    case loading(message: String)\n    case selectServer\n    case settings\n    case play(ServerDescriptor, ResourcePack)\n  }\n\n  class StateStorage: Observable {\n    @Observed var state = DeltaClientState.loading(message: \"Loading\")\n    var resourcePack: ResourcePack?\n  }\n\n  var identifier = \"dev.stackotter.DeltaClientApp\"\n\n  var state = StateStorage()\n\n  public init() {\n    load()\n  }\n\n  func load() {\n    DispatchQueue(label: \"loading\").asyncAfter(deadline: .now().advanced(by: .milliseconds(100))) {\n      let assetsDirectory = URL(fileURLWithPath: \"assets\")\n      let registryDirectory = URL(fileURLWithPath: \"registry\")\n      let cacheDirectory = URL(fileURLWithPath: \"cache\")\n\n      do {\n        // Download vanilla assets if they haven't already been downloaded\n        if !StorageManager.directoryExists(at: assetsDirectory) {\n          loading(\"Downloading assets\")\n          try ResourcePack.downloadVanillaAssets(\n            forVersion: Constants.versionString,\n            to: assetsDirectory,\n            progress: nil\n          )\n        }\n\n        // Load registries\n        loading(\"Loading registries\")\n        try RegistryStore.populateShared(registryDirectory, progress: nil)\n\n        // Load resource pack and cache it if necessary\n        loading(\"Loading resource pack\")\n        let packCache = cacheDirectory.appendingPathComponent(\"vanilla.rpcache/\")\n        var cacheExists = StorageManager.directoryExists(at: packCache)\n        state.resourcePack = try ResourcePack.load(\n          from: assetsDirectory,\n          cacheDirectory: cacheExists ? packCache : nil\n        )\n        cacheExists = StorageManager.directoryExists(at: packCache)\n        if !cacheExists {\n          do {\n            if let resourcePack = state.resourcePack {\n              try resourcePack.cache(to: packCache)\n            }\n          } catch {\n            log.warning(\"Failed to cache vanilla resource pack\")\n          }\n        }\n\n        self.state.state = .selectServer\n      } catch {\n        loading(\"Failed to load: \\(error.localizedDescription) (\\(error))\")\n      }\n    }\n  }\n\n  func loading(_ message: String) {\n    self.state.state = .loading(message: message)\n  }\n\n  var body: some Scene {\n    WindowGroup(\"Delta Client\") {\n      VStack {\n        switch state.state {\n          case .loading(let message):\n            Text(message)\n          case .selectServer:\n            ServerListView { server in\n              if let resourcePack = state.resourcePack {\n                state.state = .play(server, resourcePack)\n              }\n            } openSettings: {\n              state.state = .settings\n            }\n          case .settings:\n            SettingsView {\n              state.state = .selectServer\n            }\n          case .play(let server, let resourcePack):\n            GameView(server, resourcePack) {\n              state.state = .selectServer\n            }\n        }\n      }\n      .padding(10)\n    }\n    .defaultSize(width: 400, height: 200)\n  }\n}\n"
  },
  {
    "path": "Sources/ClientGtk/GameView.swift",
    "content": "import DeltaCore\nimport Dispatch\nimport SwiftCrossUI\n\nclass GameViewState: Observable {\n  enum State {\n    case error(String)\n    case connecting\n    case connected\n  }\n\n  var server: ServerDescriptor\n  var client: Client\n\n  @Observed var state = State.connecting\n\n  init(_ server: ServerDescriptor, _ resourcePack: ResourcePack) {\n    self.server = server\n    client = Client(\n      resourcePack: resourcePack, configuration: ConfigManager.default.coreConfiguration)\n    client.eventBus.registerHandler { [weak self] event in\n      guard let self = self else { return }\n      self.handleClientEvent(event)\n    }\n\n    // TODO: Use structured concurrency to get join server to wait until login is finished so that\n    // errors can be handled inline\n    if let account = ConfigManager.default.config.selectedAccount {\n      Task {\n        do {\n          try await client.joinServer(describedBy: server, with: account)\n        } catch {\n          state = .error(\"Failed to join server: \\(error.localizedDescription)\")\n        }\n      }\n    } else {\n      state = .error(\"Please select an account\")\n    }\n  }\n\n  func handleClientEvent(_ event: Event) {\n    switch event {\n      // TODO: This is absolutely ridiculous just for error handling. Unify all errors into a single\n      // error event\n      case let event as ConnectionFailedEvent:\n        state = .error(\"Connection failed: \\(event.networkError)\")\n      case is JoinWorldEvent:\n        state = .connected\n      case let event as LoginDisconnectEvent:\n        state = .error(\"Disconnected from server during login:\\n\\n\\(event.reason)\")\n      case let event as PlayDisconnectEvent:\n        state = .error(\"Disconnected from server during play:\\n\\n\\(event.reason)\")\n      case let packetError as PacketHandlingErrorEvent:\n        let id = String(packetError.packetId, radix: 16)\n        state = .error(\"Failed to handle packet with id 0x\\(id):\\n\\n\\(packetError.error)\")\n      case let packetError as PacketDecodingErrorEvent:\n        let id = String(packetError.packetId, radix: 16)\n        state = .error(\"Failed to decode packet with id 0x\\(id):\\n\\n\\(packetError.error)\")\n      case let event as ErrorEvent:\n        if let message = event.message {\n          state = .error(\"\\(message): \\(event.error)\")\n        } else {\n          state = .error(\"\\(event.error)\")\n        }\n      default:\n        break\n    }\n  }\n}\n\nstruct GameView: View {\n  var state: GameViewState\n\n  var completionHandler: () -> Void\n\n  init(\n    _ server: ServerDescriptor, _ resourcePack: ResourcePack,\n    _ completionHandler: @escaping () -> Void\n  ) {\n    state = GameViewState(server, resourcePack)\n    self.completionHandler = completionHandler\n  }\n\n  var body: some View {\n    switch state.state {\n      case .error(let message):\n        Text(message)\n        Button(\"Back\") {\n          completionHandler()\n        }\n      case .connecting:\n        Text(\"Connecting...\")\n      case .connected:\n        ChatView(state.client)\n        Button(\"Disconnect\") {\n          state.client.disconnect()\n          completionHandler()\n        }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/ClientGtk/ServerListView.swift",
    "content": "import DeltaCore\nimport SwiftCrossUI\n\nindirect enum DetailState {\n  case server(_ index: Int)\n  case adding\n  case editing(_ index: Int)\n\n  case error(_ message: String, returnState: DetailState)\n}\n\nclass ServerListViewState: Observable {\n  @Observed var servers: [ServerDescriptor] = ConfigManager.default.config.servers\n  @Observed var detailState = DetailState.adding\n\n  @Observed var name = \"\"\n  @Observed var address = \"\"\n}\n\nstruct ServerListView: View {\n  var joinServer: (ServerDescriptor) -> Void\n  var openSettings: () -> Void\n\n  var state = ServerListViewState()\n\n  var body: some View {\n    NavigationSplitView {\n      VStack {\n        Button(\"Add server\") {\n          state.detailState = .adding\n        }\n\n        Button(\"Settings\") {\n          openSettings()\n        }\n\n        ScrollView {\n          ForEach(state.servers.indices) { index in\n            Button(state.servers[index].name) {\n              state.detailState = .server(index)\n            }\n            .padding(.bottom, 5)\n          }\n        }\n      }\n      .frame(minWidth: 100)\n      .padding(.trailing, 10)\n    } detail: {\n      VStack {\n        switch state.detailState {\n          case .server(let index):\n            serverView(index)\n          case .adding:\n            addingView()\n          case .editing(let index):\n            editingView(index)\n          case .error(let message, let returnState):\n            Text(\"Error: \\(message)\")\n            Button(\"Back\") {\n              state.detailState = returnState\n            }\n        }\n      }\n      .padding(.leading, 10)\n    }\n  }\n\n  func serverView(_ index: Int) -> some View {\n    let server = state.servers[index]\n\n    return VStack {\n      Text(server.name)\n      Text(server.description)\n\n      Button(\"Connect\") {\n        joinServer(server)\n      }\n\n      Button(\"Edit\") {\n        state.name = server.name\n        state.address = server.description\n        state.detailState = .editing(index)\n      }\n    }\n  }\n\n  func addingView() -> some View {\n    return VStack {\n      Text(\"Add server\")\n      TextField(\"Name\", state.$name)\n      TextField(\"Address\", state.$address)\n\n      Button(\"Add\") {\n        do {\n          let (host, port) = try Self.parseAddress(state.address)\n          state.servers.append(ServerDescriptor(name: state.name, host: host, port: port))\n          save()\n\n          state.detailState = .server(state.servers.count - 1)\n          state.name = \"\"\n          state.address = \"\"\n        } catch AddressParsingError.invalidPort {\n          state.detailState = .error(\"Invalid port\", returnState: .adding)\n        } catch {\n          state.detailState = .error(\"Invalid address\", returnState: .adding)\n        }\n      }\n    }\n  }\n\n  func editingView(_ index: Int) -> some View {\n    return VStack {\n      Text(\"Edit server\")\n      TextField(\"Name\", state.$name)\n      TextField(\"Address\", state.$address)\n\n      Button(\"Apply\") {\n        do {\n          let (host, port) = try Self.parseAddress(state.address)\n          state.servers[index] = ServerDescriptor(name: state.name, host: host, port: port)\n          save()\n\n          state.detailState = .server(index)\n          state.name = \"\"\n          state.address = \"\"\n        } catch AddressParsingError.invalidPort {\n          state.detailState = .error(\"Invalid port\", returnState: .editing(index))\n        } catch {\n          state.detailState = .error(\"Invalid address\", returnState: .editing(index))\n        }\n      }\n\n      Button(\"Remove\") {\n        state.servers.remove(at: index)\n        save()\n\n        state.name = \"\"\n        state.address = \"\"\n        if state.servers.count > 0 {\n          state.detailState = .server(0)\n        } else {\n          state.detailState = .adding\n        }\n      }\n    }\n  }\n\n  private enum AddressParsingError: Error {\n    case invalidColonAmount\n    case invalidPort\n  }\n\n  private static func parseAddress(_ address: String) throws -> (host: String, port: UInt16?) {\n    let parts = address.split(separator: \":\")\n    guard parts.count > 0, parts.count <= 2 else {\n      throw AddressParsingError.invalidColonAmount\n    }\n\n    let host = String(parts[0])\n\n    var port: UInt16?\n    if parts.count == 2 {\n      guard let parsed = UInt16(parts[1]) else {\n        throw AddressParsingError.invalidPort\n      }\n      port = parsed\n    }\n\n    return (host, port)\n  }\n\n  private func save() {\n    var config = ConfigManager.default.config\n    config.servers = state.servers\n    ConfigManager.default.setConfig(to: config)\n  }\n}\n"
  },
  {
    "path": "Sources/ClientGtk/Settings/AccountInspectionView.swift",
    "content": "import DeltaCore\nimport SwiftCrossUI\n\nstruct AccountInspectorView: View {\n  var account: Account\n\n  var selectAccount: () -> Void\n  var removeAccount: () -> Void\n\n  public init(\n    account: Account, selectAccount: @escaping () -> Void, removeAccount: @escaping () -> Void\n  ) {\n    self.account = account\n    self.selectAccount = selectAccount\n    self.removeAccount = removeAccount\n  }\n\n  var body: some View {\n    VStack {\n      Text(\"Username: \\(account.username)\")\n      Text(\"Type: \\(account.type)\")\n\n      Button(\"Select account\") {\n        selectAccount()\n      }\n      Button(\"Remove account\") {\n        removeAccount()\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/ClientGtk/Settings/AccountListView.swift",
    "content": "import DeltaCore\nimport SwiftCrossUI\n\nstruct AccountListView: View {\n  var inspectAccount: (Account) -> Void\n  var offlineLogin: () -> Void\n  var microsoftLogin: () -> Void\n\n  var body: some View {\n    VStack {\n      Button(\"Add offline account\") {\n        offlineLogin()\n      }\n      Button(\"Add Microsoft account\") {\n        microsoftLogin()\n      }\n\n      Text(\"Selected account: \\(ConfigManager.default.config.selectedAccount?.username ?? \"none\")\")\n\n      ScrollView {\n        ForEach(Array(ConfigManager.default.config.accounts.values)) { account in\n          Button(account.username) {\n            inspectAccount(account)\n          }\n          .padding(.bottom, 5)\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/ClientGtk/Settings/MicrosoftLoginView.swift",
    "content": "import DeltaCore\nimport SwiftCrossUI\n\nenum MicrosoftState {\n  case authorizingDevice\n  case login(MicrosoftDeviceAuthorizationResponse)\n  case authenticatingUser\n\n  case error(String)\n}\n\nclass MicrosoftLoginViewState: Observable {\n  @Observed var state = MicrosoftState.authorizingDevice\n}\n\nstruct MicrosoftLoginView: View {\n  var completionHandler: (Account) -> Void\n\n  var state = MicrosoftLoginViewState()\n\n  public init(_ completionHandler: @escaping (Account) -> Void) {\n    self.completionHandler = completionHandler\n    authorizeDevice()\n  }\n\n  var body: some View {\n    VStack {\n      switch state.state {\n        case .authorizingDevice:\n          Text(\"Fetching device authorization code\")\n        case .login(let response):\n          Text(response.message)\n          Button(\"Done\") {\n            state.state = .authenticatingUser\n            authenticate(with: response)\n          }\n        case .authenticatingUser:\n          Text(\"Authenticating...\")\n        case .error(let message):\n          Text(message)\n      }\n    }\n  }\n\n  func authorizeDevice() {\n    Task {\n      do {\n        let response = try await MicrosoftAPI.authorizeDevice()\n        state.state = .login(response)\n      } catch {\n        state.state = .error(\"Failed to authorize device: \\(error)\")\n      }\n    }\n  }\n\n  func authenticate(with response: MicrosoftDeviceAuthorizationResponse) {\n    Task {\n      let account: MicrosoftAccount\n      do {\n        let accessToken = try await MicrosoftAPI.getMicrosoftAccessToken(response.deviceCode)\n        account = try await MicrosoftAPI.getMinecraftAccount(accessToken)\n      } catch {\n        state.state = .error(\n          \"Failed to authenticate Microsoft account: \\(error.localizedDescription)\"\n        )\n        return\n      }\n\n      completionHandler(Account.microsoft(account))\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/ClientGtk/Settings/OfflineLoginView.swift",
    "content": "import DeltaCore\nimport SwiftCrossUI\n\nclass OfflineLoginViewState: Observable {\n  @Observed var username = \"\"\n  @Observed var error: String?\n}\n\nstruct OfflineLoginView: View {\n  var completionHandler: (Account) -> Void\n\n  var state = OfflineLoginViewState()\n\n  var body: some View {\n    VStack {\n      TextField(\"Username\", state.$username)\n\n      if let error = state.error {\n        Text(error)\n      }\n\n      Button(\"Add account\") {\n        guard !state.username.isEmpty else {\n          state.error = \"Please provide a username\"\n          return\n        }\n\n        completionHandler(Account.offline(OfflineAccount(username: state.username)))\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/ClientGtk/Settings/SettingsView.swift",
    "content": "import DeltaCore\nimport SwiftCrossUI\n\nenum SettingsPage {\n  case accountList\n  case inspectAccount(Account)\n  case offlineLogin\n  case microsoftLogin\n}\n\nclass SettingsViewState: Observable {\n  @Observed var page = SettingsPage.accountList\n}\n\nstruct SettingsView: View {\n  var returnToServerList: () -> Void\n\n  var state = SettingsViewState()\n\n  var body: some View {\n    NavigationSplitView {\n      VStack {\n        Button(\"Accounts\") {\n          state.page = .accountList\n        }\n        Button(\"Close\") {\n          returnToServerList()\n        }\n      }\n      .padding(.trailing, 10)\n    } detail: {\n      VStack {\n        switch state.page {\n          case .accountList:\n            AccountListView { account in\n              state.page = .inspectAccount(account)\n            } offlineLogin: {\n              state.page = .offlineLogin\n            } microsoftLogin: {\n              state.page = .microsoftLogin\n            }\n          case .inspectAccount(let account):\n            AccountInspectorView(account: account) {\n              ConfigManager.default.selectAccount(account.id)\n              state.page = .accountList\n            } removeAccount: {\n              var accounts = ConfigManager.default.config.accounts\n              accounts.removeValue(forKey: account.id)\n              if ConfigManager.default.config.selectedAccountId == account.id {\n                ConfigManager.default.setAccounts(Array(accounts.values), selected: nil)\n              } else {\n                ConfigManager.default.setAccounts(\n                  Array(accounts.values), selected: ConfigManager.default.config.selectedAccountId)\n              }\n              state.page = .accountList\n            }\n          case .offlineLogin:\n            OfflineLoginView { account in\n              ConfigManager.default.addAccount(account)\n              state.page = .accountList\n            }\n          case .microsoftLogin:\n            MicrosoftLoginView { account in\n              ConfigManager.default.addAccount(account)\n              state.page = .accountList\n            }\n        }\n      }\n      .padding(.leading, 10)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/ClientGtk/Storage/Config.swift",
    "content": "import Foundation\nimport DeltaCore\n\npublic struct Config: Codable {\n  /// The random token used to identify ourselves to Mojang's API\n  public var clientToken: String = UUID().uuidString\n  /// The id of the currently selected account.\n  public var selectedAccountId: String?\n  /// The dictionary containing all of the user's accounts.\n  public var accounts: [String: Account] = [:]\n  /// The user's server list.\n  public var servers: [ServerDescriptor] = []\n  /// Rendering related configuration.\n  public var render: RenderConfiguration = RenderConfiguration()\n  /// Plugins that the user has explicitly unloaded.\n  public var unloadedPlugins: [String] = []\n  /// The user's keymap.\n  public var keymap: Keymap = Keymap.default\n  /// Whether to use the sprint key as a toggle.\n  public var toggleSprint: Bool = false\n  /// Whether to use the sneak key as a toggle.\n  public var toggleSneak: Bool = false\n  /// The in game mouse sensitivity\n  public var mouseSensitivity: Float = 1\n\n  /// The account the user has currently selected.\n  public var selectedAccount: Account? {\n    if let id = selectedAccountId {\n      return accounts[id]\n    } else {\n      return nil\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/ClientGtk/Storage/ConfigError.swift",
    "content": "import Foundation\n\npublic enum ConfigError: LocalizedError {\n  /// The account in question is of an invalid type.\n  case invalidAccountType\n  /// No account is selected.\n  case noAccountSelected\n  /// The currently selected account is of an invalid type.\n  case invalidSelectedAccountType\n  /// Failed to refresh the given account.\n  case accountRefreshFailed(Error)\n  \n  public var errorDescription: String? {\n    switch self {\n      case .invalidAccountType:\n        return \"The account in question is of an invalid type.\"\n      case .noAccountSelected:\n        return \"No account is selected.\"\n      case .invalidSelectedAccountType:\n        return \"The currently selected account is of an invalid type.\"\n      case .accountRefreshFailed(let error):\n        return \"\"\"\n        Failed to refresh the given account.\n        Reason: \\(error.localizedDescription).\n        \"\"\"\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/ClientGtk/Storage/ConfigManager.swift",
    "content": "import DeltaCore\nimport Foundation\n\n/// Manages the config stored in a config file.\npublic final class ConfigManager {\n  /// The manager for the default config file.\n  public static var `default` = ConfigManager(\n    for: StorageManager.default.absoluteFromRelative(\"config.json\")\n  )\n\n  /// The current config (thread-safe).\n  public private(set) var config: Config {\n    get {\n      lock.acquireReadLock()\n      defer { lock.unlock() }\n      return _config\n    }\n    set(newValue) {\n      lock.acquireWriteLock()\n      defer { lock.unlock() }\n      _config = newValue\n    }\n  }\n\n  /// The implementation of ClientConfiguration that allows DeltaCore to access required config values.\n  let coreConfiguration: CoreConfiguration\n\n  /// The non-threadsafe storage for ``config``.\n  private var _config: Config\n  /// The file to store config in.\n  private let configFile: URL\n\n  /// A queue to ensure that writing to the config file always happens serially.\n  private let queue = DispatchQueue(label: \"dev.stackotter.delta-client.ConfigManager\")\n  /// The lock used to synchronise access to ``ConfigManager/_config``.\n  private let lock = ReadWriteLock()\n\n  /// Creates a manager for the specified config file. Creates default config if required.\n  private init(for configFile: URL) {\n    self.configFile = configFile\n\n    // Create default config if no config file exists\n    guard StorageManager.fileExists(at: configFile) else {\n      _config = Config()\n      coreConfiguration = CoreConfiguration(_config)\n      let data: Data\n      do {\n        data = try JSONEncoder().encode(_config)\n        FileManager.default.createFile(atPath: configFile.path, contents: data, attributes: nil)\n      } catch {\n        // TODO: Proper error handling for ConfigManager\n        fatalError(\"Failed to encode config: \\(error)\")\n      }\n      return\n    }\n\n    // Read the current config from the config file\n    do {\n      let data = try Data(contentsOf: configFile)\n      _config = try CustomJSONDecoder().decode(Config.self, from: data)\n    } catch {\n      // Existing config is corrupted, overwrite it with defaults\n      log.error(\"Invalid config.json, overwriting with defaults\")\n\n      _config = Config()\n      let data: Data\n      do {\n        data = try JSONEncoder().encode(_config)\n        FileManager.default.createFile(atPath: configFile.path, contents: data, attributes: nil)\n      } catch {\n        fatalError(\"Failed to encode config: \\(error)\")\n      }\n    }\n    coreConfiguration = CoreConfiguration(_config)\n  }\n\n  /// Commits the given account to the config file.\n  /// - Parameters:\n  ///   - account: The account to add.\n  ///   - shouldSelect: Whether to select the account or not.\n  public func addAccount(_ account: Account, shouldSelect: Bool = false) {\n    config.accounts[account.id] = account\n\n    if shouldSelect {\n      config.selectedAccountId = account.id\n    }\n\n    try? commitConfig()\n  }\n\n  /// Selects the given account.\n  /// - Parameter id: The id of the account as received from the authentication servers (or generated from the username if offline).\n  public func selectAccount(_ id: String?) {\n    config.selectedAccountId = id\n    try? commitConfig()\n  }\n\n  /// Commits the given array of user accounts to the config file replacing any existing accounts.\n  /// - Parameters:\n  ///   - accounts: The user's accounts.\n  ///   - selected: Decides which account will be selected.\n  public func setAccounts(_ accounts: [Account], selected: String?) {\n    config.accounts = [:]\n    for account in accounts {\n      config.accounts[account.id] = account\n    }\n\n    config.selectedAccountId = selected\n\n    try? commitConfig()\n  }\n\n  /// Updates the config and writes it to the config file.\n  /// - Parameter config: The config to write.\n  /// - Parameter saveToFile: Whether to write to the file or just update internal references.\n  public func setConfig(to config: Config, saveToFile: Bool = true) {\n    self.config = config\n\n    do {\n      try commitConfig(saveToFile: saveToFile)\n    } catch {\n      log.error(\"Failed to write config to file: \\(error)\")\n    }\n  }\n\n  /// Resets the configuration to defaults.\n  public func resetConfig() throws {\n    log.info(\"Resetting config.json\")\n    config = Config()\n    try commitConfig()\n  }\n\n  /// Commits the current config to this manager's config file.\n  /// - Parameter saveToFile: Whether to write to the file or just update internal references.\n  private func commitConfig(saveToFile: Bool = true) throws {\n    coreConfiguration.config = config\n\n    if saveToFile {\n      try queue.sync {\n        let data = try JSONEncoder().encode(self.config)\n        try data.write(to: self.configFile)\n      }\n    }\n  }\n}\n\n/// Enables DeltaCore to access required configuration values.\n/// This is passed to the Client constructor.\nclass CoreConfiguration: ClientConfiguration {\n  var config: Config\n\n  init(_ config: Config) {\n    self.config = config\n  }\n\n  public var render: RenderConfiguration {\n    return config.render\n  }\n\n  public var keymap: Keymap {\n    return config.keymap\n  }\n\n  public var toggleSprint: Bool {\n    return config.toggleSprint\n  }\n\n  public var toggleSneak: Bool {\n    return config.toggleSneak\n  }\n}\n"
  },
  {
    "path": "Sources/ClientGtk/Storage/StorageManager.swift",
    "content": "import DeltaCore\nimport Foundation\n\nfinal class StorageManager {\n  static var `default` = StorageManager()\n\n  public var storageDirectory: URL\n\n  public var assetsDirectory: URL\n  public var registryDirectory: URL\n  public var cacheDirectory: URL\n\n  private init() {\n    if let applicationSupport = FileManager.default.urls(\n      for: .applicationSupportDirectory, in: .userDomainMask\n    ).first {\n      storageDirectory = applicationSupport.appendingPathComponent(\"delta-client\")\n    } else {\n      log.warning(\"Failed to get application support directory, using temporary directory instead\")\n      let fallback = FileManager.default.temporaryDirectory.appendingPathComponent(\n        \"delta-client.fallback\")\n      storageDirectory = fallback\n    }\n\n    assetsDirectory = storageDirectory.appendingPathComponent(\"assets\")\n    registryDirectory = storageDirectory.appendingPathComponent(\"registries\")\n    cacheDirectory = storageDirectory.appendingPathComponent(\"cache\")\n\n    log.trace(\"Using \\(storageDirectory.path) as storage directory\")\n\n    if !Self.directoryExists(at: storageDirectory) {\n      do {\n        log.info(\"Creating storage directory\")\n        try? FileManager.default.removeItem(at: storageDirectory)\n        try Self.createDirectory(at: storageDirectory)\n      } catch {\n        fatalError(\"Failed to create storage directory\")\n      }\n    }\n  }\n\n  // MARK: Static shortenings of FileManager methods\n\n  /// Checks if a file or directory exists at the given url.\n  static func itemExists(at url: URL) -> Bool {\n    return FileManager.default.fileExists(atPath: url.path)\n  }\n\n  /// Checks if a file or directory exists at the given url updating isDirectory.\n  static func itemExists(at url: URL, isDirectory: inout ObjCBool) -> Bool {\n    return FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory)\n  }\n\n  /// Checks if a file exists at the given url.\n  static func fileExists(at url: URL) -> Bool {\n    var isDirectory: ObjCBool = false\n    return itemExists(at: url, isDirectory: &isDirectory) && !isDirectory.boolValue\n  }\n\n  /// Checks if a directory exists at the given url.\n  static func directoryExists(at url: URL) -> Bool {\n    var isDirectory: ObjCBool = false\n    return itemExists(at: url, isDirectory: &isDirectory) && isDirectory.boolValue\n  }\n\n  /// Creates a directory at the given url with intermediate directories.\n  /// Replaces any existing item at the url with an empty directory.\n  static func createDirectory(at url: URL) throws {\n    try? FileManager.default.removeItem(at: url)\n    try FileManager.default.createDirectory(\n      at: url, withIntermediateDirectories: true, attributes: nil)\n  }\n\n  /// Returns the contents of a directory at the given URL.\n  static func contentsOfDirectory(at url: URL) throws -> [URL] {\n    return try FileManager.default.contentsOfDirectory(\n      at: url, includingPropertiesForKeys: nil, options: []\n    )\n  }\n\n  /// Copies the specified item to the destination directory.\n  static func copyItem(at item: URL, to destination: URL) throws {\n    try FileManager.default.copyItem(at: item, to: destination)\n  }\n\n  // MARK: Delta Client specific methods\n\n  /// Returns the absolute URL of a path relative to the storage directory.\n  public func absoluteFromRelative(_ relativePath: String) -> URL {\n    return storageDirectory.appendingPathComponent(relativePath)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Logger/Logger.swift",
    "content": "import Puppy\nimport Foundation\nimport Rainbow\n\n@_exported import enum Puppy.LogLevel\n\nstruct ConsoleLogFormatter: LogFormattable {\n  func formatMessage(\n    _ level: LogLevel,\n    message: String,\n    tag: String,\n    function: String,\n    file: String,\n    line: UInt,\n    swiftLogInfo: [String : String],\n    label: String,\n    date: Date,\n    threadID: UInt64\n  ) -> String {\n    let levelString: String\n    switch level {\n      case .trace:\n        levelString = \"TRACE\".lightWhite\n      case .verbose:\n        levelString = \"VERBO\".magenta\n      case.debug:\n        levelString = \"DEBUG\".green\n      case .info:\n        levelString = \"INFO \".lightBlue\n      case .notice:\n        levelString = \"NOTE \".lightYellow\n      case .warning:\n        levelString = \"WARN \".yellow.bold\n      case .error:\n        levelString = \"ERROR\".red.bold\n      case .critical:\n        levelString = \"CRIT \".red.bold\n    }\n    return \"[\\(levelString)] \\(message)\"\n  }\n}\n\nstruct FileLogFormatter: LogFormattable {\n  let dateFormatter = DateFormatter()\n\n  func formatMessage(\n    _ level: LogLevel,\n    message: String,\n    tag: String,\n    function: String,\n    file: String,\n    line: UInt,\n    swiftLogInfo: [String : String],\n    label: String,\n    date: Date,\n    threadID: UInt64\n  ) -> String {\n    let date = dateFormatter.string(from: date)\n    let moduleName = moduleName(file)\n    return \"[\\(date)] [\\(moduleName)] [\\(level)] \\(message)\"\n  }\n}\n\nvar consoleLogger = ConsoleLogger(\n  \"dev.stackotter.delta-client.ConsoleLogger\",\n  logLevel: .debug,\n  logFormat: ConsoleLogFormatter()\n)\n\nfunc createLogger() -> Puppy {\n  Rainbow.enabled = ProcessInfo.processInfo.environment.keys.contains(\"__XCODE_BUILT_PRODUCTS_DIR_PATHS\") ? false : Rainbow.enabled\n  var log = Puppy()\n  log.add(consoleLogger)\n  return log\n}\n\npublic var log = createLogger()\n\npublic func setConsoleLogLevel(_ logLevel: LogLevel) {\n  log.remove(consoleLogger)\n  consoleLogger = ConsoleLogger(\n    \"dev.stackotter.delta-client.ConsoleLogger\",\n    logLevel: logLevel,\n    logFormat: ConsoleLogFormatter()\n  )\n  log.add(consoleLogger)\n}\n\npublic func enableFileLogger(loggingTo file: URL) throws {\n  let rotationConfig = RotationConfig(\n    suffixExtension: .date_uuid,\n    maxFileSize: 5 * 1024 * 1024,\n    maxArchivedFilesCount: 3\n  )\n  let fileLogger = try FileRotationLogger(\n    \"dev.stackotter.delta-client.FileRotationLogger\",\n    logLevel: .debug,\n    logFormat: FileLogFormatter(),\n    fileURL: file.absoluteURL,\n    rotationConfig: rotationConfig\n  )\n  log.add(fileLogger)\n}\n"
  },
  {
    "path": "Sources/Core/Package.resolved",
    "content": "{\n  \"pins\" : [\n    {\n      \"identity\" : \"asn1parser\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/ASN1Parser\",\n      \"state\" : {\n        \"branch\" : \"main\",\n        \"revision\" : \"f92a5b26f5c92d38ae858a5a77048e9af82331a3\"\n      }\n    },\n    {\n      \"identity\" : \"async-http-client\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swift-server/async-http-client.git\",\n      \"state\" : {\n        \"revision\" : \"037b70291941fe43de668066eb6fb802c5e181d2\",\n        \"version\" : \"1.1.1\"\n      }\n    },\n    {\n      \"identity\" : \"bigint\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/attaswift/BigInt.git\",\n      \"state\" : {\n        \"revision\" : \"e07e00fa1fd435143a2dcf8b7eec9a7710b2fdfe\",\n        \"version\" : \"5.7.0\"\n      }\n    },\n    {\n      \"identity\" : \"circuitbreaker\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/Kitura/CircuitBreaker.git\",\n      \"state\" : {\n        \"revision\" : \"bd4255762e48cc3748a448d197f1297a4ba705f7\",\n        \"version\" : \"5.1.0\"\n      }\n    },\n    {\n      \"identity\" : \"cryptoswift\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/krzyzanowskim/CryptoSwift\",\n      \"state\" : {\n        \"revision\" : \"e45a26384239e028ec87fbcc788f513b67e10d8f\",\n        \"version\" : \"1.9.0\"\n      }\n    },\n    {\n      \"identity\" : \"ecs\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/ecs.git\",\n      \"state\" : {\n        \"branch\" : \"master\",\n        \"revision\" : \"c7660bcd24e31ef2fc3457f56a2bf4a58c3ad6ee\"\n      }\n    },\n    {\n      \"identity\" : \"fireblade-math\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/fireblade-math.git\",\n      \"state\" : {\n        \"branch\" : \"matrix2x2\",\n        \"revision\" : \"750239647673b07457bea80b39438a6db52198f1\"\n      }\n    },\n    {\n      \"identity\" : \"jjliso8601dateformatter\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/michaeleisel/JJLISO8601DateFormatter\",\n      \"state\" : {\n        \"revision\" : \"50d5ea26ffd3f82e3db8516d939d80b745e168cf\",\n        \"version\" : \"0.2.0\"\n      }\n    },\n    {\n      \"identity\" : \"loggerapi\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/Kitura/LoggerAPI.git\",\n      \"state\" : {\n        \"revision\" : \"e82d34eab3f0b05391082b11ea07d3b70d2f65bb\",\n        \"version\" : \"1.9.200\"\n      }\n    },\n    {\n      \"identity\" : \"opencombine\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/OpenCombine/OpenCombine.git\",\n      \"state\" : {\n        \"revision\" : \"8576f0d579b27020beccbccc3ea6844f3ddfc2c2\",\n        \"version\" : \"0.14.0\"\n      }\n    },\n    {\n      \"identity\" : \"puppy\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/sushichop/Puppy\",\n      \"state\" : {\n        \"revision\" : \"80ebe6ab25fb7050904647cb96f6ad7bee77bad6\",\n        \"version\" : \"0.9.0\"\n      }\n    },\n    {\n      \"identity\" : \"rainbow\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/onevcat/Rainbow\",\n      \"state\" : {\n        \"revision\" : \"cdf146ae671b2624917648b61c908d1244b98ca1\",\n        \"version\" : \"4.2.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-algorithms\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-algorithms.git\",\n      \"state\" : {\n        \"revision\" : \"87e50f483c54e6efd60e885f7f5aa946cee68023\",\n        \"version\" : \"1.2.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-asn1\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-asn1.git\",\n      \"state\" : {\n        \"revision\" : \"9f542610331815e29cc3821d3b6f488db8715517\",\n        \"version\" : \"1.6.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-async-algorithms\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-async-algorithms.git\",\n      \"state\" : {\n        \"revision\" : \"9d349bcc328ac3c31ce40e746b5882742a0d1272\",\n        \"version\" : \"1.1.3\"\n      }\n    },\n    {\n      \"identity\" : \"swift-async-dns-resolver\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-async-dns-resolver.git\",\n      \"state\" : {\n        \"revision\" : \"36eedf108f9eda4feeaf47f3dfce657a88ef19aa\",\n        \"version\" : \"0.6.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-atomics\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-atomics.git\",\n      \"state\" : {\n        \"revision\" : \"b601256eab081c0f92f059e12818ac1d4f178ff7\",\n        \"version\" : \"1.3.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-case-paths\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/pointfreeco/swift-case-paths\",\n      \"state\" : {\n        \"revision\" : \"206cbce3882b4de9aee19ce62ac5b7306cadd45b\",\n        \"version\" : \"1.7.3\"\n      }\n    },\n    {\n      \"identity\" : \"swift-certificates\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-certificates.git\",\n      \"state\" : {\n        \"revision\" : \"24ccdeeeed4dfaae7955fcac9dbf5489ed4f1a25\",\n        \"version\" : \"1.18.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-collections\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-collections.git\",\n      \"state\" : {\n        \"revision\" : \"6675bc0ff86e61436e615df6fc5174e043e57924\",\n        \"version\" : \"1.4.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-crypto\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-crypto.git\",\n      \"state\" : {\n        \"revision\" : \"bb4ba815dab96d4edc1e0b86d7b9acf9ff973a84\",\n        \"version\" : \"4.3.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-http-structured-headers\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-http-structured-headers.git\",\n      \"state\" : {\n        \"revision\" : \"76d7627bd88b47bf5a0f8497dd244885960dde0b\",\n        \"version\" : \"1.6.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-http-types\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-http-types.git\",\n      \"state\" : {\n        \"revision\" : \"45eb0224913ea070ec4fba17291b9e7ecf4749ca\",\n        \"version\" : \"1.5.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-image\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/swift-image.git\",\n      \"state\" : {\n        \"branch\" : \"master\",\n        \"revision\" : \"7160e2d8799f8b182498ab699aa695cb66cf4ea6\"\n      }\n    },\n    {\n      \"identity\" : \"swift-log\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-log.git\",\n      \"state\" : {\n        \"revision\" : \"ce592ae52f982c847a4efc0dd881cc9eb32d29f2\",\n        \"version\" : \"1.6.4\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio.git\",\n      \"state\" : {\n        \"revision\" : \"558f24a4647193b5a0e2104031b71c55d31ff83a\",\n        \"version\" : \"2.97.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-extras\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-extras.git\",\n      \"state\" : {\n        \"revision\" : \"abcf5312eb8ed2fb11916078aef7c46b06f20813\",\n        \"version\" : \"1.33.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-http2\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-http2.git\",\n      \"state\" : {\n        \"revision\" : \"6d8d596f0a9bfebb925733003731fe2d749b7e02\",\n        \"version\" : \"1.42.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-ssl\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-ssl.git\",\n      \"state\" : {\n        \"revision\" : \"df9c3406028e3297246e6e7081977a167318b692\",\n        \"version\" : \"2.36.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-numerics\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-numerics.git\",\n      \"state\" : {\n        \"revision\" : \"0c0290ff6b24942dadb83a929ffaaa1481df04a2\",\n        \"version\" : \"1.1.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-package-zlib\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/fourplusone/swift-package-zlib\",\n      \"state\" : {\n        \"revision\" : \"03ecd41814d8929362f7439529f9682536a8de13\",\n        \"version\" : \"1.2.11\"\n      }\n    },\n    {\n      \"identity\" : \"swift-parsing\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/pointfreeco/swift-parsing\",\n      \"state\" : {\n        \"revision\" : \"3432cb81164dd3d69a75d0d63205be5fbae2c34b\",\n        \"version\" : \"0.14.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-png\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/stackotter/swift-png\",\n      \"state\" : {\n        \"revision\" : \"b68a5662ef9887c8f375854720b3621f772bf8c5\"\n      }\n    },\n    {\n      \"identity\" : \"swift-service-lifecycle\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swift-server/swift-service-lifecycle.git\",\n      \"state\" : {\n        \"revision\" : \"9829955b385e5bb88128b73f1b8389e9b9c3191a\",\n        \"version\" : \"2.11.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-syntax\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swiftlang/swift-syntax\",\n      \"state\" : {\n        \"revision\" : \"2b59c0c741e9184ab057fd22950b491076d42e91\",\n        \"version\" : \"603.0.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-system\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-system.git\",\n      \"state\" : {\n        \"revision\" : \"7c6ad0fc39d0763e0b699210e4124afd5041c5df\",\n        \"version\" : \"1.6.4\"\n      }\n    },\n    {\n      \"identity\" : \"swiftcpudetect\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/JWhitmore1/SwiftCPUDetect\",\n      \"state\" : {\n        \"branch\" : \"main\",\n        \"revision\" : \"5ca694c6ad7eef1199d69463fa956c24c202465f\"\n      }\n    },\n    {\n      \"identity\" : \"swiftpackagesbase\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/ITzTravelInTime/SwiftPackagesBase\",\n      \"state\" : {\n        \"revision\" : \"79477622b5dbacc3722d485c5060c46a90740016\",\n        \"version\" : \"0.0.23\"\n      }\n    },\n    {\n      \"identity\" : \"swiftyrequest\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/Kitura/SwiftyRequest.git\",\n      \"state\" : {\n        \"revision\" : \"2c543777a5088bed811503a68551a4b4eceac198\",\n        \"version\" : \"3.2.200\"\n      }\n    },\n    {\n      \"identity\" : \"xctest-dynamic-overlay\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/pointfreeco/xctest-dynamic-overlay\",\n      \"state\" : {\n        \"revision\" : \"dfd70507def84cb5fb821278448a262c6ff2bbad\",\n        \"version\" : \"1.9.0\"\n      }\n    },\n    {\n      \"identity\" : \"zipfoundation\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/weichsel/ZIPFoundation.git\",\n      \"state\" : {\n        \"revision\" : \"22787ffb59de99e5dc1fbfe80b19c97a904ad48d\",\n        \"version\" : \"0.9.20\"\n      }\n    },\n    {\n      \"identity\" : \"zippyjson\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/michaeleisel/ZippyJSON\",\n      \"state\" : {\n        \"revision\" : \"ddbbc024ba5a826f9676035a0a090a0bc2d40755\",\n        \"version\" : \"1.2.15\"\n      }\n    },\n    {\n      \"identity\" : \"zippyjsoncfamily\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/michaeleisel/ZippyJSONCFamily\",\n      \"state\" : {\n        \"revision\" : \"c1c0f88977359ea85b81e128b2d988e8250dfdae\",\n        \"version\" : \"1.2.14\"\n      }\n    }\n  ],\n  \"version\" : 2\n}\n"
  },
  {
    "path": "Sources/Core/Package.swift",
    "content": "// swift-tools-version:5.9\n\nimport PackageDescription\n\nlet debugLocks = false\n\n// MARK: Products\n\nvar productTargets = [\"DeltaCore\", \"DeltaLogger\"]\n#if canImport(Metal)\n  productTargets.append(\"DeltaRenderer\")\n#endif\n\n// MARK: Targets\n\nvar targets: [Target] = [\n  .target(\n    name: \"DeltaCore\",\n    dependencies: [\n      \"DeltaLogger\",\n      \"ZIPFoundation\",\n      \"ASN1Parser\",\n      \"CryptoSwift\",\n      \"SwiftCPUDetect\",\n      .product(name: \"FirebladeECS\", package: \"ecs\"),\n      .product(\n        name: \"SwiftyRequest\", package: \"SwiftyRequest\", condition: .when(platforms: [.linux])),\n      .product(name: \"OpenCombine\", package: \"OpenCombine\", condition: .when(platforms: [.linux])),\n      .product(name: \"Atomics\", package: \"swift-atomics\"),\n      .product(\n        name: \"ZippyJSON\", package: \"ZippyJSON\", condition: .when(platforms: [.macOS, .iOS, .tvOS])),\n      .product(name: \"Parsing\", package: \"swift-parsing\"),\n      .product(name: \"Collections\", package: \"swift-collections\"),\n      .product(name: \"OrderedCollections\", package: \"swift-collections\"),\n      .product(name: \"FirebladeMath\", package: \"fireblade-math\"),\n      .product(name: \"AsyncDNSResolver\", package: \"swift-async-dns-resolver\"),\n      .product(name: \"Z\", package: \"swift-package-zlib\"),\n      .product(name: \"SwiftImage\", package: \"swift-image\"),\n      .product(name: \"PNG\", package: \"swift-png\"),\n    ],\n    path: \"Sources\",\n    swiftSettings: debugLocks ? [.define(\"DEBUG_LOCKS\")] : []\n  ),\n\n  .target(\n    name: \"DeltaLogger\",\n    dependencies: [\n      \"Puppy\",\n      \"Rainbow\",\n    ],\n    path: \"Logger\"\n  ),\n\n  .testTarget(\n    name: \"DeltaCoreUnitTests\",\n    dependencies: [\"DeltaCore\"]\n  ),\n]\n\n#if canImport(Metal)\n  targets.append(\n    .target(\n      name: \"DeltaRenderer\",\n      dependencies: [\n        \"DeltaCore\"\n      ],\n      path: \"Renderer\",\n      resources: [\n        .process(\"Shader/\")\n      ]\n    )\n  )\n#endif\n\nlet package = Package(\n  name: \"DeltaCore\",\n  platforms: [.macOS(.v11), .iOS(.v14)],\n  products: [\n    .library(name: \"DeltaCore\", type: .dynamic, targets: productTargets),\n    .library(name: \"StaticDeltaCore\", type: .static, targets: productTargets),\n  ],\n  dependencies: [\n    .package(url: \"https://github.com/weichsel/ZIPFoundation.git\", from: \"0.9.20\"),\n    .package(url: \"https://github.com/apple/swift-collections.git\", from: \"1.0.3\"),\n    .package(url: \"https://github.com/apple/swift-atomics.git\", from: \"1.0.2\"),\n    .package(url: \"https://github.com/stackotter/ecs.git\", branch: \"master\"),\n    .package(url: \"https://github.com/michaeleisel/ZippyJSON\", from: \"1.2.4\"),\n    .package(url: \"https://github.com/pointfreeco/swift-parsing\", from: \"0.12.1\"),\n    .package(url: \"https://github.com/stackotter/fireblade-math.git\", branch: \"matrix2x2\"),\n    .package(url: \"https://github.com/apple/swift-async-dns-resolver.git\", from: \"0.4.0\"),\n    .package(url: \"https://github.com/fourplusone/swift-package-zlib\", from: \"1.2.11\"),\n    .package(url: \"https://github.com/stackotter/swift-image.git\", branch: \"master\"),\n    .package(url: \"https://github.com/OpenCombine/OpenCombine.git\", from: \"0.13.0\"),\n    .package(\n      url: \"https://github.com/stackotter/swift-png\",\n      revision: \"b68a5662ef9887c8f375854720b3621f772bf8c5\"\n    ),\n    .package(url: \"https://github.com/stackotter/ASN1Parser\", branch: \"main\"),\n    .package(url: \"https://github.com/krzyzanowskim/CryptoSwift\", from: \"1.6.0\"),\n    .package(url: \"https://github.com/Kitura/SwiftyRequest.git\", from: \"3.1.0\"),\n    .package(url: \"https://github.com/JWhitmore1/SwiftCPUDetect\", branch: \"main\"),\n    .package(url: \"https://github.com/sushichop/Puppy\", .upToNextMinor(from: \"0.9.0\")),\n    .package(url: \"https://github.com/onevcat/Rainbow\", from: \"4.0.1\"),\n  ],\n  targets: targets\n)\n"
  },
  {
    "path": "Sources/Core/Renderer/Camera/Camera.swift",
    "content": "import Foundation\nimport Metal\nimport FirebladeMath\nimport DeltaCore\n\n/// Holds information about a camera to render from.\npublic struct Camera {\n  /// The vertical FOV in radians.\n  public private(set) var fovY: Float = 0.5 * .pi // 90deg\n  /// The near clipping plane.\n  public private(set) var nearDistance: Float = 0.04\n  /// The far clipping plant.\n  public private(set) var farDistance: Float = 800\n\n  /// The aspect ratio.\n  public private(set) var aspect: Float = 1\n  /// The camera's position.\n  public private(set) var position: Vec3f = [0, 0, 0]\n\n  // TODO: Rename xRot and yRot to pitch and yaw when refactoring Camera\n  /// The camera's rotation around the x axis (pitch). -pi/2 radians is straight up,\n  /// 0 is straight ahead, and pi/2 radians is straight down.\n  public private(set) var xRot: Float = 0\n  /// The camera's rotation around the y axis measured counter-clockwise from the\n  /// positive z axis when looking down from above (yaw).\n  public private(set) var yRot: Float = 0\n\n  /// The ray defining the camera's position and the direction it faces.\n  public var ray: Ray {\n    Ray(from: position, pitch: xRot, yaw: yRot)\n  }\n\n  // TODO: Rename these matrices to translation, rotation, and projection. Could\n  //   even replace translation and rotation with a single combined `framing`\n  //   matrix if no code ever uses them separately.\n  /// A translation matrix from world-space to player-centered coordinates.\n  public var worldToPlayer: Mat4x4f {\n    MatrixUtil.translationMatrix(-position)\n  }\n\n  /// A rotation matrix from player-centered coordinates to camera-space.\n  public var playerToCamera: Mat4x4f {\n    MatrixUtil.rotationMatrix(y: -(Float.pi + yRot)) * MatrixUtil.rotationMatrix(x: -xRot)\n  }\n\n  /// The projection matrix from camera-space to clip-space.\n  public var cameraToClip: Mat4x4f {\n    MatrixUtil.projectionMatrix(\n      near: nearDistance,\n      far: farDistance,\n      aspect: aspect,\n      fieldOfViewY: fovY\n    )\n  }\n\n  /// The camera's position as an entity position.\n  public var entityPosition: EntityPosition {\n    return EntityPosition(Vec3d(position))\n  }\n\n  /// The direction that the camera is pointing.\n  public var directionVector: Vec3f {\n    let rotationMatrix = MatrixUtil.rotationMatrix(y: Float.pi + yRot) * MatrixUtil.rotationMatrix(x: xRot)\n    let unitVector = Vec4f(0, 0, 1, 0)\n    return (unitVector * rotationMatrix).xyz\n  }\n\n  private var frustum: Frustum?\n\n  private var uniformsBuffers: [MTLBuffer] = []\n  private var uniformsIndex = 0\n  private var uniformsCount = 6\n\n  public init(_ device: MTLDevice) throws {\n    // TODO: Multiple-buffering should be implemented separately from the camera. I reckon the\n    //   camera shouldn't have to know about Metal at all, or throw any errors.\n    for i in 0..<uniformsCount {\n      guard let buffer = device.makeBuffer(\n        length: MemoryLayout<CameraUniforms>.stride,\n        options: .storageModeShared\n      ) else {\n        throw RenderError.failedtoCreateWorldUniformBuffers\n      }\n      buffer.label = \"Camera.uniforms-\\(i)\"\n      uniformsBuffers.append(buffer)\n    }\n  }\n\n  /// Update a buffer to contain the current world to clip uniforms.\n  public mutating func getUniformsBuffer() -> MTLBuffer {\n    let buffer = uniformsBuffers[uniformsIndex]\n    uniformsIndex = (uniformsIndex + 1) % uniformsCount\n    var uniforms = getUniforms()\n    buffer.contents().copyMemory(\n      from: &uniforms,\n      byteCount: MemoryLayout<CameraUniforms>.stride\n    )\n    return buffer\n  }\n\n  /// Get the world to clip uniforms.\n  public mutating func getUniforms() -> CameraUniforms {\n    return CameraUniforms(\n      framing: worldToPlayer * playerToCamera,\n      projection: cameraToClip\n    )\n  }\n\n  /// Sets this camera's vert\u0010ical FOV. Horizontal FOV is calculated from vertical FOV and aspect ratio.\n  public mutating func setFovY(_ fovY: Float) {\n    self.fovY = fovY\n    frustum = nil\n  }\n\n  /// Sets this camera's clipping planes.\n  public mutating func setClippingPlanes(near: Float, far: Float) {\n    nearDistance = near\n    farDistance = far\n    frustum = nil\n  }\n\n  /// Sets this camera's aspect ratio.\n  public mutating func setAspect(_ aspect: Float) {\n    self.aspect = aspect\n    frustum = nil\n  }\n\n  /// Sets this camera's position in world coordinates.\n  public mutating func setPosition(_ position: Vec3f) {\n    self.position = position\n    frustum = nil\n  }\n\n  /// Sets the rotation of this camera in radians.\n  public mutating func setRotation(xRot: Float, yRot: Float) {\n    self.xRot = xRot\n    self.yRot = yRot\n    frustum = nil\n  }\n\n  /// Calculates the camera's frustum from its parameters.\n  public func calculateFrustum() -> Frustum {\n    let worldToClip = getWorldToClipMatrix()\n    return Frustum(worldToClip: worldToClip)\n  }\n\n  /// Faces this camera in the direction of an entity's rotation.\n  public mutating func setRotation(playerLook: EntityRotation) {\n    xRot = playerLook.pitch\n    yRot = playerLook.yaw\n  }\n\n  // TODO: Make this a computed property\n  /// Returns this camera's world-space to clip-space transformation matrix.\n  public func getWorldToClipMatrix() -> Mat4x4f {\n    return worldToPlayer * playerToCamera * cameraToClip\n  }\n\n  // TODO: This whole frustum caching thing seems weird. It can probably just be moved to the usage\n  //   site. i.e. just call computeFrustum once to get the frustum.\n  /// Calculates the camera's frustum and saves it. Cached frustum can be fetched via ``getFrustum()``.\n  public mutating func cacheFrustum() {\n    self.frustum = calculateFrustum()\n  }\n\n  public func getFrustum() -> Frustum {\n    return frustum ?? calculateFrustum()\n  }\n\n  /// Determine if the specified chunk is visible from this camera.\n  public func isChunkVisible(at chunkPosition: ChunkPosition) -> Bool {\n    let frustum = getFrustum()\n    return frustum.approximatelyContains(chunkPosition.axisAlignedBoundingBox)\n  }\n\n  /// Determine if the specified chunk section is visible from this camera.\n  public func isChunkSectionVisible(at chunkSectionPosition: ChunkSectionPosition) -> Bool {\n    let frustum = getFrustum()\n    return frustum.approximatelyContains(chunkSectionPosition.axisAlignedBoundingBox)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Camera/Frustum.swift",
    "content": "import Foundation\nimport FirebladeMath\nimport DeltaCore\n\n// method from: http://web.archive.org/web/20120531231005/http://crazyjoke.free.fr/doc/3D/plane%20extraction.pdf\npublic struct Frustum {\n  public var worldToClip: Mat4x4f\n  public var left: Vec4d\n  public var right: Vec4d\n  public var top: Vec4d\n  public var bottom: Vec4d\n  public var near: Vec4d\n  public var far: Vec4d\n\n  public init(worldToClip: Mat4x4f) {\n    self.worldToClip = worldToClip\n\n    let columns = worldToClip.columns\n    left = Vec4d(columns.3 + columns.0)\n    right = Vec4d(columns.3 - columns.0)\n    bottom = Vec4d(columns.3 + columns.1)\n    top = Vec4d(columns.3 - columns.1)\n    near = Vec4d(columns.2)\n    far = Vec4d(columns.3 - columns.2)\n  }\n\n  public func approximatelyContains(_ boundingBox: AxisAlignedBoundingBox) -> Bool {\n    let homogenousVertices = boundingBox.getHomogenousVertices()\n    let planeVectors = [left, right, near, bottom, top]\n\n    for planeVector in planeVectors {\n      var boundingBoxLiesOutside = true\n      for vertex in homogenousVertices {\n        if dot(vertex, planeVector) > 0 {\n          boundingBoxLiesOutside = false\n          break\n        }\n      }\n\n      if boundingBoxLiesOutside {\n        return false\n      }\n    }\n\n    // the bounding box does not lie completely outside any of the frustum planes\n    // although it may still be outside the frustum (hence approximate)\n    return true\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/CameraUniforms.swift",
    "content": "import FirebladeMath\n\npublic struct CameraUniforms {\n  /// Translation and rotation (sets up the camera's framing).\n  public var framing: Mat4x4f\n  /// The projection converting camera-space coordinates to clip-space coordinates.\n  public var projection: Mat4x4f\n\n  public init(framing: Mat4x4f, projection: Mat4x4f) {\n    self.framing = framing\n    self.projection = projection\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/CaptureState.swift",
    "content": "import Foundation\n\nextension RenderCoordinator {\n  /// The state of a GPU frame capture.\n  struct CaptureState {\n    /// The number of frames remaining.\n    var framesRemaining: Int\n    /// The file that the capture will be outputted to.\n    var outputFile: URL\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/CelestialBodyUniforms.swift",
    "content": "import FirebladeMath\n\npublic struct CelestialBodyUniforms {\n  public var transformation: Mat4x4f\n  public var textureIndex: UInt16\n  public var uvPosition: Vec2f\n  public var uvSize: Vec2f\n  public var type: CelestialBodyType\n\n  public enum CelestialBodyType: UInt8 {\n    case sun = 0\n    case moon = 1\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/ChunkUniforms.swift",
    "content": "import DeltaCore\nimport FirebladeMath\n\npublic struct ChunkUniforms {\n  /// The translation to convert chunk-space coordinates (with origin at the\n  /// lowest, Northmost, Eastmost block of the chunk) to world-space coordinates.\n  public var transformation: Mat4x4f\n\n  public init(transformation: Mat4x4f) {\n    self.transformation = transformation\n  }\n\n  public init() {\n    transformation = MatrixUtil.identity\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/EndSkyUniforms.swift",
    "content": "import FirebladeMath\n\nstruct EndSkyUniforms {\n  var transformation: Mat4x4f\n  var textureIndex: UInt16\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/EndSkyVertex.swift",
    "content": "import FirebladeMath\n\nstruct EndSkyVertex {\n  var position: Vec3f\n  var uv: Vec2f\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Entity/EntityRenderer.swift",
    "content": "import DeltaCore\nimport FirebladeECS\nimport FirebladeMath\nimport Foundation\nimport MetalKit\n\n/// Renders all entities in the world the client is currently connected to.\npublic struct EntityRenderer: Renderer {\n  /// The color to render hit boxes as. Defaults to 0xe3c28d (light cream colour).\n  public static let hitBoxColor = DeltaCore.RGBColor(hexCode: 0xe3c28d)\n\n  /// The render pipeline state for rendering entities. Does not have blending enabled.\n  private var renderPipelineState: MTLRenderPipelineState\n  /// The render pipeline state for rendering block entities and block item entities.\n  private var blockRenderPipelineState: MTLRenderPipelineState\n  /// The buffer containing the uniforms for all rendered entities.\n  private var instanceUniformsBuffer: MTLBuffer?\n\n  private var entityTexturePalette: MetalTexturePalette\n  private var blockTexturePalette: MetalTexturePalette\n\n  private var entityModelPalette: EntityModelPalette\n  private var itemModelPalette: ItemModelPalette\n  private var blockModelPalette: BlockModelPalette\n\n  /// The client that entities will be renderer for.\n  private var client: Client\n  /// The device that will be used to render.\n  private var device: MTLDevice\n  /// The command queue used to perform operations outside of the main render loop.\n  private var commandQueue: MTLCommandQueue\n\n  private var profiler: Profiler<RenderingMeasurement>\n\n  /// Should get updated each frame via `setVisibleChunks`.\n  private var visibleChunks: Set<ChunkPosition> = []\n\n  /// Creates a new entity renderer.\n  public init(\n    client: Client,\n    device: MTLDevice,\n    commandQueue: MTLCommandQueue,\n    profiler: Profiler<RenderingMeasurement>,\n    blockTexturePalette: MetalTexturePalette\n  ) throws {\n    self.client = client\n    self.device = device\n    self.commandQueue = commandQueue\n    self.profiler = profiler\n    self.blockTexturePalette = blockTexturePalette\n\n    // Load library\n    // TODO: Avoid loading library again and again\n    let library = try MetalUtil.loadDefaultLibrary(device)\n    let vertexFunction = try MetalUtil.loadFunction(\"entityVertexShader\", from: library)\n    let fragmentFunction = try MetalUtil.loadFunction(\"entityFragmentShader\", from: library)\n    let blockVertexFunction = try MetalUtil.loadFunction(\"chunkVertexShader\", from: library)\n    let blockFragmentFunction = try MetalUtil.loadFunction(\"chunkFragmentShader\", from: library)\n\n    // Create render pipeline state\n    renderPipelineState = try MetalUtil.makeRenderPipelineState(\n      device: device,\n      label: \"EntityRenderer.renderPipelineState\",\n      vertexFunction: vertexFunction,\n      fragmentFunction: fragmentFunction,\n      blendingEnabled: false\n    )\n\n    // TODO: Consider supporting OIT here too? Probably not of much use cause most block item\n    //   entities aren't translucent, and there should never be many instances of them since\n    //   item entities merge.\n    blockRenderPipelineState = try MetalUtil.makeRenderPipelineState(\n      device: device,\n      label: \"EntityRenderer.blockRenderPipelineState\",\n      vertexFunction: blockVertexFunction,\n      fragmentFunction: blockFragmentFunction,\n      blendingEnabled: true\n    )\n\n    entityTexturePalette = try MetalTexturePalette(\n      palette: client.resourcePack.vanillaResources.entityTexturePalette,\n      device: device,\n      commandQueue: commandQueue\n    )\n\n    entityModelPalette = client.resourcePack.vanillaResources.entityModelPalette\n    itemModelPalette = client.resourcePack.vanillaResources.itemModelPalette\n    blockModelPalette = client.resourcePack.vanillaResources.blockModelPalette\n  }\n\n  /// Renders all entity hit boxes using instancing.\n  public mutating func render(\n    view: MTKView,\n    encoder: MTLRenderCommandEncoder,\n    commandBuffer: MTLCommandBuffer,\n    worldToClipUniformsBuffer: MTLBuffer,\n    camera: Camera\n  ) throws {\n    var isFirstPerson = false\n    client.game.accessPlayer { player in\n      isFirstPerson = player.camera.perspective == .firstPerson\n    }\n\n    // Get all renderable entities\n    var geometry = Geometry<EntityVertex>()\n    var blockGeometry = Geometry<BlockVertex>()\n    var translucentBlockGeometry = SortableMesh(uniforms: ChunkUniforms())\n    client.game.accessNexus { nexus in\n      // If the player is in first person view we don't render them\n      profiler.push(.getEntities)\n      let entities: Family<Requires4<EntityPosition, EntityRotation, EntityHitBox, EntityKindId>>\n      if isFirstPerson {\n        entities = nexus.family(\n          requiresAll: EntityPosition.self,\n          EntityRotation.self,\n          EntityHitBox.self,\n          EntityKindId.self,\n          excludesAll: ClientPlayerEntity.self\n        )\n      } else {\n        entities = nexus.family(\n          requiresAll: EntityPosition.self,\n          EntityRotation.self,\n          EntityHitBox.self,\n          EntityKindId.self\n        )\n      }\n      profiler.pop()\n\n      let renderDistance = client.configuration.render.renderDistance\n      let cameraChunk = camera.entityPosition.chunk\n\n      // Create uniforms for each entity\n      profiler.push(.createRegularEntityMeshes)\n      for (entity, position, rotation, hitbox, kindId) in entities.entityAndComponents {\n        // Don't render entities that are outside of the render distance\n        let chunkPosition = position.chunk\n        if !chunkPosition.isWithinRenderDistance(renderDistance, of: cameraChunk) {\n          continue\n        }\n\n        guard var kindIdentifier = kindId.entityKind?.identifier else {\n          log.warning(\"Unknown entity kind '\\(kindId.id)'\")\n          continue\n        }\n\n        if kindIdentifier == Identifier(name: \"ender_dragon\") {\n          kindIdentifier = Identifier(name: \"dragon\")\n        }\n\n        let lightLevel = client.game.world.getLightLevel(at: position.block)\n        buildEntityMesh(\n          entity: entity,\n          entityKindIdentifier: kindIdentifier,\n          position: Vec3f(position.smoothVector),\n          pitch: rotation.smoothPitch,\n          yaw: rotation.smoothYaw,\n          hitbox: hitbox.aabb(at: position.smoothVector),\n          lightLevel: lightLevel,\n          into: &geometry,\n          blockGeometry: &blockGeometry,\n          translucentBlockGeometry: &translucentBlockGeometry\n        )\n      }\n      profiler.pop()\n\n      profiler.push(.createBlockEntityMeshes)\n      for chunkPosition in visibleChunks {\n        guard let chunk = client.game.world.chunk(at: chunkPosition) else {\n          continue\n        }\n\n        for blockEntity in chunk.getBlockEntities() {\n          let position = blockEntity.position.floatVector + Vec3f(0.5, 0, 0.5)\n\n          let block = chunk.getBlock(at: blockEntity.position.relativeToChunk)\n          let direction = block.stateProperties.facing ?? .south\n\n          let lightLevel = client.game.world.getLightLevel(at: blockEntity.position)\n          buildEntityMesh(\n            entity: nil,\n            entityKindIdentifier: blockEntity.identifier,\n            position: position,\n            pitch: 0,\n            yaw: Self.blockEntityYaw(toFace: direction),\n            hitbox: AxisAlignedBoundingBox(position: .zero, size: Vec3d(1, 1, 1)),\n            lightLevel: lightLevel,\n            into: &geometry,\n            blockGeometry: &blockGeometry,\n            translucentBlockGeometry: &translucentBlockGeometry\n          )\n        }\n      }\n      profiler.pop()\n    }\n\n    profiler.push(.encodeEntities)\n    if !geometry.isEmpty {\n      encoder.setRenderPipelineState(renderPipelineState)\n      encoder.setFragmentTexture(entityTexturePalette.arrayTexture, index: 0)\n\n      var mesh = Mesh<EntityVertex, Void>(geometry, uniforms: ())\n      try mesh.render(into: encoder, with: device, commandQueue: commandQueue)\n    }\n\n    if !blockGeometry.isEmpty || !translucentBlockGeometry.isEmpty {\n      encoder.setRenderPipelineState(blockRenderPipelineState)\n      encoder.setVertexBuffer(blockTexturePalette.textureStatesBuffer, offset: 0, index: 3)\n      encoder.setFragmentTexture(blockTexturePalette.arrayTexture, index: 0)\n\n      if !blockGeometry.isEmpty {\n        var blockMesh = Mesh<BlockVertex, ChunkUniforms>(blockGeometry, uniforms: ChunkUniforms())\n        try blockMesh.render(into: encoder, with: device, commandQueue: commandQueue)\n      }\n\n      if !translucentBlockGeometry.isEmpty {\n        try translucentBlockGeometry.render(\n          viewedFrom: camera.position,\n          sort: true,\n          encoder: encoder,\n          device: device,\n          commandQueue: commandQueue\n        )\n      }\n    }\n    profiler.pop()\n  }\n\n  private func buildEntityMesh(\n    entity: Entity? = nil,\n    entityKindIdentifier: Identifier,\n    position: Vec3f,\n    pitch: Float,\n    yaw: Float,\n    hitbox: AxisAlignedBoundingBox,\n    lightLevel: LightLevel,\n    into geometry: inout Geometry<EntityVertex>,\n    blockGeometry: inout Geometry<BlockVertex>,\n    translucentBlockGeometry: inout SortableMesh\n  ) {\n    var translucentBlockElement = SortableMeshElement()\n    EntityMeshBuilder(\n      entity: entity,\n      entityKind: entityKindIdentifier,\n      position: position,\n      pitch: pitch,\n      yaw: yaw,\n      entityModelPalette: entityModelPalette,\n      itemModelPalette: itemModelPalette,\n      blockModelPalette: blockModelPalette,\n      entityTexturePalette: entityTexturePalette.palette,\n      blockTexturePalette: blockTexturePalette.palette,\n      hitbox: hitbox,\n      lightLevel: lightLevel\n    ).build(\n      into: &geometry,\n      blockGeometry: &blockGeometry,\n      translucentBlockGeometry: &translucentBlockElement\n    )\n    translucentBlockGeometry.add(translucentBlockElement)\n  }\n\n  /// Computes the yaw required for a block entity to face a given direction.\n  private static func blockEntityYaw(toFace direction: Direction) -> Float {\n    switch direction {\n      case .south, .up, .down:\n        return 0\n      case .west:\n        return .pi / 2\n      case .north:\n        return .pi\n      case .east:\n        return -.pi / 2\n    }\n  }\n\n  /// Sets the chunks that block entities should be rendered from.\n  public mutating func setVisibleChunks(_ visibleChunks: Set<ChunkPosition>) {\n    self.visibleChunks = visibleChunks\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Entity/EntityVertex.swift",
    "content": "/// The vertex format used by the entity shader.\npublic struct EntityVertex {\n  public var x: Float\n  public var y: Float\n  public var z: Float\n  public var r: Float\n  public var g: Float\n  public var b: Float\n  public var u: Float\n  public var v: Float\n  public var skyLightLevel: UInt8\n  public var blockLightLevel: UInt8\n  /// ``UInt16/max`` indicates that no texture is to be used. I would usually use\n  /// an optional to model that, but this type needs to be compatible with C as we\n  /// pass it off to the shaders for rendering.\n  public var textureIndex: UInt16\n\n  public init(\n    x: Float,\n    y: Float,\n    z: Float,\n    r: Float,\n    g: Float,\n    b: Float,\n    u: Float,\n    v: Float,\n    skyLightLevel: UInt8,\n    blockLightLevel: UInt8,\n    textureIndex: UInt16?\n  ) {\n    self.x = x\n    self.y = y\n    self.z = z\n    self.r = r\n    self.g = g\n    self.b = b\n    self.u = u\n    self.v = v\n    self.skyLightLevel = skyLightLevel\n    self.blockLightLevel = blockLightLevel\n    self.textureIndex = textureIndex ?? .max\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/EntityUniforms.swift",
    "content": "import FirebladeMath\n\npublic struct EntityUniforms {\n  /// The transformation for an instance of the generic entity hitbox. Scales and\n  /// translates the hitbox to the correct size and world-space position.\n  public var transformation: Mat4x4f\n\n  public init(transformation: Mat4x4f) {\n    self.transformation = transformation\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/FogUniforms.swift",
    "content": "import FirebladeMath\n\n/// Uniforms used to render distance fog.\nstruct FogUniforms {\n  var fogColor: Vec3f\n  var fogStart: Float\n  var fogEnd: Float\n  var fogDensity: Float\n  var isLinear: Bool\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/GUI/GUIContext.swift",
    "content": "import Metal\nimport DeltaCore\n\nstruct GUIContext {\n  var font: Font\n  var fontArrayTexture: MTLTexture\n  var guiTexturePalette: GUITexturePalette\n  var guiArrayTexture: MTLTexture\n  var itemTexturePalette: TexturePalette\n  var itemArrayTexture: MTLTexture\n  var itemModelPalette: ItemModelPalette\n  var blockArrayTexture: MTLTexture\n  var blockModelPalette: BlockModelPalette\n  var blockTexturePalette: TexturePalette\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/GUI/GUIElementMesh.swift",
    "content": "import MetalKit\nimport FirebladeMath\nimport DeltaCore\n\n/// A generic texture-backed GUI element.\nstruct GUIElementMesh {\n  /// The amount of extra room to allocate when creating a vertex buffer to avoid needing to create\n  /// a new one too soon. Currently set to leave enough room for 20 extra quads. This measurably\n  /// cuts down the number of new vertex buffers created.\n  private static let vertexBufferHeadroom = 80 * MemoryLayout<GUIVertex>.stride\n\n  /// The element's position.\n  var position: Vec2i = .zero\n  /// The unscaled size.\n  var size: Vec2i\n  /// The vertices making up the element grouped by quad as an optimisation for converting arrays of\n  /// quads to meshes.\n  var vertices: GUIVertexStorage\n  /// The array texture used to render this element.\n  var arrayTexture: MTLTexture?\n\n  /// The buffer containing ``vertices``.\n  var vertexBuffer: MTLBuffer?\n  /// The mesh's uniforms.\n  var uniformsBuffer: MTLBuffer?\n\n  /// The minimum size that the vertex buffer must be.\n  var requiredVertexBufferSize: Int {\n    return vertices.count * MemoryLayout<GUIVertex>.stride\n  }\n\n  /// Creates a mesh from a collection of quads.\n  init(size: Vec2i, arrayTexture: MTLTexture?, quads: [GUIQuad]) {\n    self.size = size\n    self.arrayTexture = arrayTexture\n\n    // Basically just a fancy Array.map (it's measurably faster than using flatmap in this case and\n    // this is performance critical, otherwise I would never use this code)\n    let quadCount = quads.count\n    vertices = .tuples(Array(unsafeUninitializedCapacity: quadCount) { buffer, count in\n      quads.withUnsafeBufferPointer { quadsPointer in\n        for i in 0..<quads.count {\n          buffer[i] = quadsPointer[i].toVertexTuple()\n        }\n      }\n\n      count = quadCount\n    })\n  }\n\n  /// Creates a mesh from a collection of vertices.\n  init(size: Vec2i, arrayTexture: MTLTexture, vertices: GUIVertexStorage) {\n    self.size = size\n    self.arrayTexture = arrayTexture\n    self.vertices = vertices\n  }\n\n  /// Creates a mesh that displays the specified gui sprite.\n  init(\n    sprite: GUISpriteDescriptor,\n    guiTexturePalette: GUITexturePalette,\n    guiArrayTexture: MTLTexture\n  ) throws {\n     self.init(\n      size: sprite.size,\n      arrayTexture: guiArrayTexture,\n      quads: [GUIQuad(\n        for: sprite,\n        guiTexturePalette: guiTexturePalette,\n        guiArrayTexture: guiArrayTexture\n      )]\n    )\n  }\n\n  /// Creates a mesh that displays a single slice of a texture.\n  init(\n    slice: Int,\n    texture: MTLTexture\n  ) {\n    self.init(\n      size: [16, 16],\n      arrayTexture: texture,\n      quads: [GUIQuad(\n        position: [0, 0],\n        size: [16, 16],\n        uvMin: [0, 0],\n        uvSize: [1, 1],\n        textureIndex: UInt16(slice)\n      )]\n    )\n  }\n\n  init(\n    size: Vec2i,\n    color: Vec4f\n  ) {\n    self.init(\n      size: size,\n      arrayTexture: nil,\n      quads: [GUIQuad(\n        position: .zero,\n        size: Vec2f(size),\n        color: color\n      )]\n    )\n  }\n\n  /// Renders the mesh. Expects ``GUIUniforms`` to be bound at vertex buffer index 1. Also expects\n  /// pipeline state to be set to ``GUIRenderer/pipelineState``.\n  mutating func render(\n    into encoder: MTLRenderCommandEncoder,\n    with device: MTLDevice\n  ) throws {\n    let vertexCount = vertices.count\n\n    // Avoid rendering empty mesh\n    if vertexCount == 0 {\n      return\n    }\n\n    let vertexBuffer: MTLBuffer\n    if let vertexBufferTemp = self.vertexBuffer {\n      vertexBuffer = vertexBufferTemp\n    } else {\n      vertexBuffer = try MetalUtil.makeBuffer(\n        device,\n        length: requiredVertexBufferSize + Self.vertexBufferHeadroom,\n        options: []\n      )\n      self.vertexBuffer = vertexBuffer\n    }\n\n    let uniformsBuffer: MTLBuffer\n    var uniforms = GUIElementUniforms(position: Vec2f(position))\n    if let uniformsBufferTemp = self.uniformsBuffer {\n      uniformsBuffer = uniformsBufferTemp\n    } else {\n      uniformsBuffer = try MetalUtil.makeBuffer(\n        device,\n        length: MemoryLayout<GUIElementUniforms>.stride,\n        options: []\n      )\n      self.uniformsBuffer = uniformsBuffer\n    }\n\n    // Assume that the buffers are outdated\n    vertices.withUnsafeMutableVertexBufferPointer { buffer in\n      vertexBuffer.contents().copyMemory(\n        from: buffer.baseAddress!,\n        byteCount: vertexCount * MemoryLayout<GUIVertex>.stride\n      )\n    }\n\n    uniformsBuffer.contents().copyMemory(\n      from: &uniforms,\n      byteCount: MemoryLayout<GUIElementUniforms>.stride\n    )\n\n    encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 1)\n    encoder.setVertexBuffer(uniformsBuffer, offset: 0, index: 2)\n    encoder.setFragmentTexture(arrayTexture, index: 0)\n    encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: vertexCount / 4 * 6)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/GUI/GUIElementMeshArray.swift",
    "content": "import FirebladeMath\n\nextension Array where Element == GUIElementMesh {\n  mutating func translate(amount: Vec2i) {\n    for i in 0..<count {\n      self[i].position &+= amount\n    }\n  }\n\n  func size() -> Vec2i {\n    var iterator = makeIterator()\n    guard let first = iterator.next() else {\n      return [0, 0]\n    }\n\n    var minX = first.position.x\n    var maxX = minX + first.size.x\n    var minY = first.position.y\n    var maxY = minY + first.size.y\n\n    while let mesh = iterator.next() {\n      let position = mesh.position\n      let size = mesh.size\n      let meshMaxX = position.x + size.x\n      let meshMaxY = position.y + size.y\n      if meshMaxX > maxX {\n        maxX = meshMaxX\n      }\n      if position.x < minX {\n        minX = position.x\n      }\n      if meshMaxY > maxY {\n        maxY = meshMaxY\n      }\n      if position.y < minY {\n        minY = position.y\n      }\n    }\n\n    return [maxX - minX, maxY - minY]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/GUI/GUIElementUniforms.swift",
    "content": "import FirebladeMath\n\n/// The uniforms for a GUI element.\nstruct GUIElementUniforms {\n  /// The element's position.\n  var position: Vec2f\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/GUI/GUIQuad.swift",
    "content": "import Metal\nimport FirebladeMath\nimport DeltaCore\n\n/// A convenient way to construct the vertices for a GUI quad.\nstruct GUIQuad {\n  static let vertices: [(position: Vec2f, uv: Vec2f)] = [\n    (position: [0, 0], uv: [0, 0]),\n    (position: [1, 0], uv: [1, 0]),\n    (position: [1, 1], uv: [1, 1]),\n    (position: [0, 1], uv: [0, 1])\n  ]\n  private static let verticesBuffer = vertices.withUnsafeBufferPointer { $0 }\n\n  var position: Vec2f\n  var size: Vec2f\n  var uvMin: Vec2f\n  var uvSize: Vec2f\n  var textureIndex: UInt16\n  var tint: Vec4f\n\n  init(\n    position: Vec2f,\n    size: Vec2f,\n    uvMin: Vec2f,\n    uvSize: Vec2f,\n    textureIndex: UInt16,\n    tint: Vec4f = [1, 1, 1, 1]\n  ) {\n    self.position = position\n    self.size = size\n    self.uvMin = uvMin\n    self.uvSize = uvSize\n    self.textureIndex = textureIndex\n    self.tint = tint\n  }\n\n  /// Creates a quad instance for a solid color rectangle.\n  init(\n    position: Vec2f,\n    size: Vec2f,\n    color: Vec4f\n  ) {\n    self.position = position\n    self.size = size\n    self.tint = color\n    self.uvMin = .zero\n    self.uvSize = .zero\n    self.textureIndex = UInt16.max\n  }\n\n  /// Creates a quad instance for the given sprite.\n  init(\n    for sprite: GUISpriteDescriptor,\n    guiTexturePalette: GUITexturePalette,\n    guiArrayTexture: MTLTexture,\n    position: Vec2i = .zero\n  ) {\n    let textureSize: Vec2f = [\n      Float(guiArrayTexture.width),\n      Float(guiArrayTexture.height)\n    ]\n\n    self.position = Vec2f(position)\n    size = Vec2f(sprite.size)\n    uvMin = Vec2f(sprite.position) / textureSize\n    uvSize = self.size / textureSize\n    textureIndex = UInt16(guiTexturePalette.textureIndex(for: sprite.slice))\n    tint = [1, 1, 1, 1]\n  }\n\n  /// Gets the vertices of the quad as an array.\n  func toVertices() -> [GUIVertex] {\n    // Basically just creating an array containing four vertices but fancilly to make it faster (I'm\n    // only doing it this way because it measurably speeds up some other parts of the code).\n    return Array(unsafeUninitializedCapacity: 4) { buffer, count in\n      let tuple = toVertexTuple()\n      buffer[0] = tuple.0\n      buffer[1] = tuple.1\n      buffer[2] = tuple.2\n      buffer[3] = tuple.3\n\n      count = 4\n    }\n  }\n\n  /// An alternative to ``toVertices()`` that can be used in performance critical situations.\n  func toVertexTuple() -> (GUIVertex, GUIVertex, GUIVertex, GUIVertex) { // swiftlint:disable:this large_tuple\n    (\n      GUIVertex(\n        position: Self.verticesBuffer[0].position * size + position,\n        uv: Self.verticesBuffer[0].uv * uvSize + uvMin,\n        tint: tint,\n        textureIndex: textureIndex\n      ),\n      GUIVertex(\n        position: Self.verticesBuffer[1].position * size + position,\n        uv: Self.verticesBuffer[1].uv * uvSize + uvMin,\n        tint: tint,\n        textureIndex: textureIndex\n      ),\n      GUIVertex(\n        position: Self.verticesBuffer[2].position * size + position,\n        uv: Self.verticesBuffer[2].uv * uvSize + uvMin,\n        tint: tint,\n        textureIndex: textureIndex\n      ),\n      GUIVertex(\n        position: Self.verticesBuffer[3].position * size + position,\n        uv: Self.verticesBuffer[3].uv * uvSize + uvMin,\n        tint: tint,\n        textureIndex: textureIndex\n      )\n    )\n  }\n\n  /// Translates the quad by the given amount.\n  /// - Parameter amount: The amount of pixels to translate by along each axis.\n  mutating func translate(amount: Vec2f) {\n    self.position += amount\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/GUI/GUIRenderer.swift",
    "content": "import DeltaCore\nimport FirebladeMath\nimport MetalKit\n\n#if canImport(UIKit)\n  import UIKit\n#endif\n\n/// The renderer for the GUI (chat, f3, scoreboard etc.).\npublic final class GUIRenderer: Renderer {\n  static let scale: Float = 2\n\n  var device: MTLDevice\n  var font: Font\n  var locale: MinecraftLocale\n  var uniformsBuffer: MTLBuffer\n  var pipelineState: MTLRenderPipelineState\n  var profiler: Profiler<RenderingMeasurement>\n  var previousUniforms: GUIUniforms?\n\n  var client: Client\n\n  var fontArrayTexture: MTLTexture\n  var guiTexturePalette: GUITexturePalette\n  var guiArrayTexture: MTLTexture\n  var itemTexturePalette: TexturePalette\n  var itemArrayTexture: MTLTexture\n  var itemModelPalette: ItemModelPalette\n  var blockArrayTexture: MTLTexture\n  var blockModelPalette: BlockModelPalette\n  var blockTexturePalette: TexturePalette\n  var entityArrayTexture: MTLTexture\n  var entityTexturePalette: TexturePalette\n  var entityModelPalette: EntityModelPalette\n\n  var cache: [GUIElementMesh] = []\n\n  public init(\n    client: Client,\n    device: MTLDevice,\n    commandQueue: MTLCommandQueue,\n    profiler: Profiler<RenderingMeasurement>\n  ) throws {\n    self.client = client\n    self.device = device\n    self.profiler = profiler\n\n    // Create array texture\n    font = client.resourcePack.vanillaResources.fontPalette.defaultFont\n    locale = client.resourcePack.getDefaultLocale()\n\n    let resources = client.resourcePack.vanillaResources\n    let font = resources.fontPalette.defaultFont\n    fontArrayTexture = try font.createArrayTexture(\n      device: device,\n      commandQueue: commandQueue\n    )\n    fontArrayTexture.label = \"fontArrayTexture\"\n\n    guiTexturePalette = try GUITexturePalette(resources.guiTexturePalette)\n    guiArrayTexture = try MetalTexturePalette.createArrayTexture(\n      for: resources.guiTexturePalette,\n      device: device,\n      commandQueue: commandQueue,\n      includeAnimations: false\n    )\n    guiArrayTexture.label = \"guiArrayTexture\"\n\n    itemTexturePalette = resources.itemTexturePalette\n    itemArrayTexture = try MetalTexturePalette.createArrayTexture(\n      for: resources.itemTexturePalette,\n      device: device,\n      commandQueue: commandQueue,\n      includeAnimations: false\n    )\n    itemArrayTexture.label = \"itemArrayTexture\"\n    itemModelPalette = resources.itemModelPalette\n\n    blockTexturePalette = resources.blockTexturePalette\n    blockArrayTexture = try MetalTexturePalette.createArrayTexture(\n      for: resources.blockTexturePalette,\n      device: device,\n      commandQueue: commandQueue,\n      includeAnimations: false\n    )\n    blockArrayTexture.label = \"blockArrayTexture\"\n    blockModelPalette = resources.blockModelPalette\n\n    entityTexturePalette = resources.entityTexturePalette\n    entityArrayTexture = try MetalTexturePalette.createArrayTexture(\n      for: resources.entityTexturePalette,\n      device: device,\n      commandQueue: commandQueue,\n      includeAnimations: false\n    )\n    entityArrayTexture.label = \"entityArrayTexture\"\n    entityModelPalette = resources.entityModelPalette\n\n    // Create uniforms buffer\n    uniformsBuffer = try MetalUtil.makeBuffer(\n      device,\n      length: MemoryLayout<GUIUniforms>.stride,\n      options: []\n    )\n\n    // Create pipeline state\n    let library = try MetalUtil.loadDefaultLibrary(device)\n    pipelineState = try MetalUtil.makeRenderPipelineState(\n      device: device,\n      label: \"GUIRenderer\",\n      vertexFunction: try MetalUtil.loadFunction(\"guiVertex\", from: library),\n      fragmentFunction: try MetalUtil.loadFunction(\"guiFragment\", from: library),\n      blendingEnabled: true\n    )\n  }\n\n  public func render(\n    view: MTKView,\n    encoder: MTLRenderCommandEncoder,\n    commandBuffer: MTLCommandBuffer,\n    worldToClipUniformsBuffer: MTLBuffer,\n    camera: Camera\n  ) throws {\n    // Construct uniforms\n    profiler.push(.updateUniforms)\n    let drawableSize = view.drawableSize\n    let width = Float(drawableSize.width)\n    let height = Float(drawableSize.height)\n    let scalingFactor = Self.scale * Self.screenScalingFactor()\n\n    // Adjust scale per screen scale factor\n    var uniforms = createUniforms(width, height, scalingFactor)\n    if uniforms != previousUniforms || true {\n      uniformsBuffer.contents().copyMemory(\n        from: &uniforms,\n        byteCount: MemoryLayout<GUIUniforms>.size\n      )\n      previousUniforms = uniforms\n    }\n    profiler.pop()\n\n    // Create meshes\n    let effectiveDrawableSize = Vec2i(\n      Int(width / scalingFactor),\n      Int(height / scalingFactor)\n    )\n\n    client.game.mutateGUIState { guiState in\n      guiState.drawableSize = effectiveDrawableSize\n      guiState.drawableScalingFactor = scalingFactor\n    }\n\n    let renderable = client.game.compileGUI(withFont: font, locale: locale, connection: nil)\n\n    let meshes = try meshes(for: renderable)\n\n    profiler.push(.encode)\n    // Set vertex buffers\n    encoder.setVertexBuffer(uniformsBuffer, offset: 0, index: 0)\n\n    // Set pipeline\n    encoder.setRenderPipelineState(pipelineState)\n\n    let optimizedMeshes = try Self.optimizeMeshes(meshes)\n\n    for (i, var mesh) in optimizedMeshes.enumerated() {\n      if i < cache.count {\n        let previousMesh = cache[i]\n        if (previousMesh.vertexBuffer?.length ?? 0) >= mesh.requiredVertexBufferSize {\n          mesh.vertexBuffer = previousMesh.vertexBuffer\n          mesh.uniformsBuffer = previousMesh.uniformsBuffer\n        } else {\n          mesh.uniformsBuffer = previousMesh.uniformsBuffer\n        }\n\n        try mesh.render(into: encoder, with: device)\n        cache[i] = mesh\n      } else {\n        try mesh.render(into: encoder, with: device)\n        cache.append(mesh)\n      }\n    }\n    profiler.pop()\n  }\n\n  func meshes(for renderable: GUIElement.GUIRenderable) throws -> [GUIElementMesh] {\n    var meshes: [GUIElementMesh]\n    switch renderable.content {\n      case let .text(wrappedLines, hangingIndent, color):\n        let builder = TextMeshBuilder(font: font)\n        meshes = try wrappedLines.compactMap { (line: String) in\n          do {\n            return try builder.build(\n              line,\n              fontArrayTexture: fontArrayTexture,\n              color: color\n            )\n          } catch let error as LocalizedError {\n            throw error.with(\"Text\", line)\n          }\n        }\n        for i in meshes.indices where i != 0 {\n          meshes[i].position.x += hangingIndent\n          meshes[i].position.y += Font.defaultCharacterHeight + 1\n        }\n      case let .sprite(descriptor):\n        meshes = try [\n          GUIElementMesh(\n            sprite: descriptor,\n            guiTexturePalette: guiTexturePalette,\n            guiArrayTexture: guiArrayTexture\n          )\n        ]\n      case let .item(itemId):\n        meshes = try self.meshes(forItemWithId: itemId)\n      case nil, .interactable, .background:\n        if case let .background(color) = renderable.content {\n          meshes = [\n            GUIElementMesh(size: renderable.size, color: color)\n          ]\n        } else {\n          meshes = []\n        }\n        meshes += try renderable.children.flatMap(meshes(for:))\n    }\n    meshes.translate(amount: renderable.relativePosition)\n    return meshes\n  }\n\n  func meshes(forItemWithId itemId: Int) throws -> [GUIElementMesh] {\n    guard let model = itemModelPalette.model(for: itemId) else {\n      throw GUIRendererError.invalidItemId(itemId)\n    }\n\n    switch model {\n      case let .layered(textures, _):\n        return textures.map { texture in\n          switch texture {\n            case let .block(index):\n              return GUIElementMesh(slice: index, texture: blockArrayTexture)\n            case let .item(index):\n              return GUIElementMesh(slice: index, texture: itemArrayTexture)\n          }\n        }\n      case let .blockModel(modelId):\n        guard let model = blockModelPalette.model(for: modelId, at: nil) else {\n          log.warning(\"Missing block model of id \\(modelId) (for item)\")\n          return []\n        }\n\n        // TODO: Use this assumption to just lift all the display transforms when loading the\n        //   block model palette.\n        // Get the block's transformation assuming that each block model part has the same\n        // associated gui transformation (I don't see why this wouldn't always be true).\n        var transformation: Mat4x4f\n        if let transformsIndex = model.parts.first?.displayTransformsIndex {\n          transformation = blockModelPalette.displayTransforms[transformsIndex].gui\n        } else {\n          transformation = MatrixUtil.identity\n        }\n\n        transformation *=\n          MatrixUtil.translationMatrix([-0.5, -0.5, -0.5])\n          * MatrixUtil.rotationMatrix(x: .pi)\n          * MatrixUtil.rotationMatrix(y: -.pi / 4)\n          * MatrixUtil.rotationMatrix(x: -.pi / 6)\n          * MatrixUtil.scalingMatrix(9.76)\n          * MatrixUtil.translationMatrix([8, 8, 8])\n\n        var geometry = Geometry<BlockVertex>()\n        var translucentGeometry = SortableMeshElement()\n        BlockMeshBuilder(\n          model: model,\n          position: BlockPosition(x: 0, y: 0, z: 0),\n          modelToWorld: transformation,\n          culledFaces: [],\n          lightLevel: LightLevel(sky: 15, block: 15),\n          neighbourLightLevels: [:],\n          tintColor: [1, 1, 1],\n          blockTexturePalette: blockTexturePalette\n        ).build(into: &geometry, translucentGeometry: &translucentGeometry)\n\n        var vertices: [GUIVertex] = []\n        vertices.reserveCapacity(geometry.vertices.count)\n        for vertex in geometry.vertices + translucentGeometry.vertices {\n          vertices.append(\n            GUIVertex(\n              position: [vertex.x, vertex.y],\n              uv: [vertex.u, vertex.v],\n              tint: [vertex.r, vertex.g, vertex.b, 1],\n              textureIndex: vertex.textureIndex\n            )\n          )\n        }\n\n        var mesh = GUIElementMesh(\n          size: [16, 16],\n          arrayTexture: blockArrayTexture,\n          vertices: .flatArray(vertices)\n        )\n        mesh.position = [0, 0]\n        return [mesh]\n      case let .entity(identifier, transforms):\n        var entityIdentifier = identifier\n        entityIdentifier.name = entityIdentifier.name.replacingOccurrences(of: \"item/\", with: \"\")\n\n        // Dummy meshes, we don't handle rendering inventory entities which themselves\n        // contain block item entities cause that doesn't happen (famous last words...)\n        var blockGeometry = Geometry<BlockVertex>()\n        var translucentBlockGeometry = SortableMeshElement()\n\n        var geometry = Geometry<EntityVertex>()\n        EntityMeshBuilder(\n          entity: nil,\n          entityKind: entityIdentifier,\n          position: .zero,\n          pitch: 0,\n          yaw: 0,\n          entityModelPalette: entityModelPalette,\n          itemModelPalette: itemModelPalette,\n          blockModelPalette: blockModelPalette,\n          entityTexturePalette: entityTexturePalette,\n          blockTexturePalette: blockTexturePalette,\n          hitbox: AxisAlignedBoundingBox(position: .zero, size: Vec3d(1, 1, 1)),\n          lightLevel: .default  // Doesn't matter cause the GUI doesn't use light levels\n        ).build(\n          into: &geometry,\n          blockGeometry: &blockGeometry,\n          translucentBlockGeometry: &translucentBlockGeometry\n        )\n\n        let transformation: Mat4x4f =\n          MatrixUtil.translationMatrix(Vec3f(0, -0.5, 0))\n          * MatrixUtil.rotationMatrix(y: .pi / 2)\n          * transforms.gui\n          * MatrixUtil.scalingMatrix(Vec3f(-1, -1, 1))\n          * MatrixUtil.scalingMatrix(16)\n          * MatrixUtil.translationMatrix([8, 8, 0])\n\n        var vertices: [GUIVertex] = []\n        vertices.reserveCapacity(geometry.vertices.count)\n        for vertex in geometry.vertices {\n          let position = (Vec4f(vertex.x, vertex.y, vertex.z, 1) * transformation).xyz\n          vertices.append(\n            GUIVertex(\n              position: [position.x, position.y],\n              uv: [vertex.u, vertex.v],\n              tint: [vertex.r, vertex.g, vertex.b, 1],\n              textureIndex: vertex.textureIndex\n            )\n          )\n        }\n\n        var mesh = GUIElementMesh(\n          size: [16, 16],\n          arrayTexture: entityArrayTexture,\n          vertices: .flatArray(vertices)\n        )\n        mesh.position = [0, 0]\n        return [mesh]\n      case .empty:\n        return []\n    }\n  }\n\n  static func optimizeMeshes(_ meshes: [GUIElementMesh]) throws -> [GUIElementMesh] {\n    var textureToIndex: [String: Int] = [:]\n    var boxes: [[(position: Vec2i, size: Vec2i)]] = []\n    var combinedMeshes: [GUIElementMesh] = []\n\n    for mesh in meshes {\n      var texture = \"textureless\"\n      if let arrayTexture = mesh.arrayTexture {\n        guard let label = arrayTexture.label else {\n          throw GUIRendererError.textureMissingLabel\n        }\n        texture = label\n      }\n\n      // If the mesh's texture's current layer is below a layer that this mesh overlaps with, then\n      // force a new layer to be created\n      let box = (position: mesh.position, size: mesh.size)\n      if let index = textureToIndex[texture] {\n        let higherLayers = textureToIndex.values.filter { $0 > index }\n        var done = false\n        for layer in higherLayers {\n          if doIntersect(mesh, combinedMeshes[layer]) {\n            for otherBox in boxes[layer] {\n              if doIntersect(box, otherBox) {\n                textureToIndex[texture] = nil\n                done = true\n                break\n              }\n            }\n            if done {\n              break\n            }\n          }\n        }\n      }\n\n      if let index = textureToIndex[texture] {\n        combine(&combinedMeshes[index], mesh)\n        boxes[index].append(box)\n      } else {\n        textureToIndex[texture] = combinedMeshes.count\n        combinedMeshes.append(mesh)\n        boxes.append([box])\n      }\n    }\n\n    return combinedMeshes\n  }\n\n  static func doIntersect(_ mesh: GUIElementMesh, _ other: GUIElementMesh) -> Bool {\n    doIntersect(\n      (position: mesh.position, size: mesh.size),\n      (position: other.position, size: other.size)\n    )\n  }\n\n  static func doIntersect(\n    _ box: (position: Vec2i, size: Vec2i),\n    _ other: (position: Vec2i, size: Vec2i)\n  ) -> Bool {\n    let pos1 = box.position\n    let size1 = box.size\n    let pos2 = other.position\n    let size2 = other.size\n\n    let overlapsX = abs((pos1.x + size1.x / 2) - (pos2.x + size2.x / 2)) * 2 < (size1.x + size2.x)\n    let overlapsY = abs((pos1.y + size1.y / 2) - (pos2.y + size2.y / 2)) * 2 < (size1.y + size2.y)\n    return overlapsX && overlapsY\n  }\n\n  static func combine(_ mesh: inout GUIElementMesh, _ other: GUIElementMesh) {\n    var other = other\n    normalizeMeshPosition(&mesh)\n    normalizeMeshPosition(&other)\n    mesh.vertices.append(contentsOf: other.vertices)\n    mesh.size = Vec2i(\n      max(mesh.size.x, other.size.x),\n      max(mesh.size.y, other.size.y)\n    )\n  }\n\n  /// Moves the mesh's vertices so that its position can be the origin.\n  static func normalizeMeshPosition(_ mesh: inout GUIElementMesh) {\n    if mesh.position == .zero {\n      return\n    }\n\n    let position = Vec2f(mesh.position)\n    mesh.vertices.mutateEach { vertex in\n      vertex.position += position\n    }\n\n    mesh.position = .zero\n    mesh.size &+= Vec2i(position)\n  }\n\n  /// Gets the scaling factor of the screen that Delta Client's currently getting rendered for.\n  public static func screenScalingFactor() -> Float {\n    // Higher density displays have higher scaling factors to keep content a similar real world\n    // size across screens.\n    #if canImport(AppKit)\n      let screenScalingFactor = Float(NSApp.windows.first?.screen?.backingScaleFactor ?? 1)\n    #elseif canImport(UIKit)\n      let screenScalingFactor = Float(UIScreen.main.scale)\n    #else\n      #error(\"Unsupported platform, unknown screen scale factor\")\n    #endif\n    return screenScalingFactor\n  }\n\n  func createUniforms(_ width: Float, _ height: Float, _ scale: Float) -> GUIUniforms {\n    let transformation = Mat3x3f([\n      [2 / width, 0, -1],\n      [0, -2 / height, 1],\n      [0, 0, 1],\n    ])\n    return GUIUniforms(screenSpaceToNormalized: transformation, scale: scale)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/GUI/GUIRendererError.swift",
    "content": "import Foundation\n\n/// An error thrown by ``GUIRenderer``.\nenum GUIRendererError: LocalizedError {\n  case failedToCreateVertexBuffer\n  case failedToCreateIndexBuffer\n  case failedToCreateUniformsBuffer\n  case failedToCreateCharacterUniformsBuffer\n  case emptyText\n  case invalidCharacter(Character)\n  case invalidItemId(Int)\n  case missingItemTexture(Int)\n  case failedToCreateTextMeshBuffer\n  case textureMissingLabel\n\n  var errorDescription: String? {\n    switch self {\n      case .failedToCreateVertexBuffer:\n        return \"Failed to create vertex buffer\"\n      case .failedToCreateIndexBuffer:\n        return \"Failed to create index buffer\"\n      case .failedToCreateUniformsBuffer:\n        return \"Failed to create uniforms buffer\"\n      case .failedToCreateCharacterUniformsBuffer:\n        return \"Failed to create character uniforms buffer\"\n      case .emptyText:\n        return \"Text was empty\"\n      case .invalidCharacter(let character):\n        return \"The selected font does not include '\\(character)'\"\n      case .invalidItemId(let id):\n        return \"No item exists with id \\(id)\"\n      case .missingItemTexture(let id):\n        return \"Missing texture for item with id \\(id)\"\n      case .failedToCreateTextMeshBuffer:\n        return \"Failed to create text mesh buffer\"\n      case .textureMissingLabel:\n        return \"Failed to combine GUI meshes because texture was missing label\"\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/GUI/GUIUniforms.swift",
    "content": "import FirebladeMath\n\n/// The GUI's uniforms.\npublic struct GUIUniforms: Equatable {\n  /// The transformation to convert screen space coordinates to normalized device coordinates.\n  var screenSpaceToNormalized: Mat3x3f\n  /// The GUI scale.\n  var scale: Float\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/GUI/GUIVertex.swift",
    "content": "import FirebladeMath\n\n/// A vertex in the GUI.\nstruct GUIVertex: Equatable {\n  /// The position.\n  var position: Vec2f\n  /// The uv coordinate.\n  var uv: Vec2f\n  /// The color to tint the vertex.\n  var tint: Vec4f\n  /// The index of the texture in the array texture. If equal to ``UInt16/max``, no texture is\n  /// sampled and the fragment color will be equal to the tint.\n  var textureIndex: UInt16\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/GUI/GUIVertexStorage.swift",
    "content": "/// Vertices are stored in tuples as an optimisation.\ntypealias GUIQuadVertices = (GUIVertex, GUIVertex, GUIVertex, GUIVertex) // swiftlint:disable:this large_tuple\n\n/// Specialized storage for GUI vertices that can work with two different storage formats (with\n/// identical memory layouts, but different types). This is useful because using tuples that group\n/// vertices into groups of four is faster when generating the vertices for lots of quads, but some\n/// code still generates vertices in the `flatArray` format.\nenum GUIVertexStorage {\n  case tuples([GUIQuadVertices])\n  // TODO: Refactor block mesh generation to generate vertices in tuples so that this type can be\n  // removed. Doing so should hopefully also improve block mesh generation performance.\n  case flatArray([GUIVertex])\n}\n\nextension GUIVertexStorage {\n  static let empty = GUIVertexStorage.tuples([])\n\n  var count: Int {\n    switch self {\n      case .tuples(let tuples):\n        return tuples.count * 4\n      case .flatArray(let array):\n        return array.count\n    }\n  }\n\n  @discardableResult\n  mutating func withUnsafeMutableRawPointer<Return>(_ action: (UnsafeMutableRawPointer) -> Return) -> Return {\n    switch self {\n      case .tuples(var tuples):\n        return action(&tuples)\n      case .flatArray(var array):\n        return action(&array)\n    }\n  }\n\n  @discardableResult\n  mutating func withUnsafeMutableVertexBufferPointer<Return>(_ action: (UnsafeMutableBufferPointer<GUIVertex>) -> Return) -> Return {\n    switch self {\n      case .tuples(var tuples):\n        return tuples.withUnsafeMutableBufferPointer { pointer in\n          return pointer.withMemoryRebound(to: GUIVertex.self) { buffer in\n            return action(buffer)\n          }\n        }\n      case .flatArray(var array):\n        return array.withUnsafeMutableBufferPointer { pointer in\n          return action(pointer)\n        }\n    }\n  }\n\n  mutating func mutateEach(_ action: (inout GUIVertex) -> Void) {\n    switch self {\n      case .tuples(var tuples):\n        self = .tuples([]) // Avoid copy caused by CoW by making `tuples` uniquely referenced\n        for i in 0..<tuples.count {\n          action(&tuples[i].0)\n          action(&tuples[i].1)\n          action(&tuples[i].2)\n          action(&tuples[i].3)\n        }\n        self = .tuples(tuples)\n      case .flatArray(var array):\n        self = .flatArray([]) // Avoid copy caused by CoW by making `array` uniquely referenced\n        for i in 0..<array.count {\n          action(&array[i])\n        }\n        self = .flatArray(array)\n    }\n  }\n\n  mutating func append(contentsOf other: GUIVertexStorage) {\n    switch self {\n      case .tuples(var tuples):\n        self = .tuples([])\n        tuples.append(contentsOf: other.toTuples())\n        self = .tuples(tuples)\n      case .flatArray(var array):\n        self = .flatArray([])\n        array.append(contentsOf: other.toFlatArray())\n        self = .flatArray(array)\n    }\n  }\n\n  private func toTuples() -> [GUIQuadVertices] {\n    switch self {\n      case .tuples(let tuples):\n        return tuples\n      case .flatArray(let array):\n        // This should never trigger becaues if the length of this array is not a multiple of 4 it\n        // would mess up the rendering code anyway (which assumes quads made up of 4 vertices each).\n        precondition(\n          array.count % 4 == 0,\n          \"Flat array of GUI vertices must have a length which is a multiple of 4\"\n        )\n\n        return array.withUnsafeBufferPointer { pointer in\n          return pointer.withMemoryRebound(to: GUIQuadVertices.self) { buffer in\n            return Array(buffer)\n          }\n        }\n    }\n  }\n\n  private func toFlatArray() -> [GUIVertex] {\n    switch self {\n      case .tuples(let tuples):\n        return tuples.withUnsafeBufferPointer { pointer in\n          return pointer.withMemoryRebound(to: GUIVertex.self) { buffer in\n            return Array(buffer)\n          }\n        }\n      case .flatArray(let array):\n        return array\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/GUI/TextMeshBuilder.swift",
    "content": "import Metal\nimport FirebladeMath\nimport DeltaCore\n\nstruct TextMeshBuilder {\n  var font: Font\n\n  func descriptor(for character: Character) throws -> CharacterDescriptor {\n    guard let descriptor = font.descriptor(for: character) else {\n      guard let descriptor = font.descriptor(for: \"�\") else {\n        log.warning(\"Failed to replace invalid character '\\(character)' with placeholder '�'.\")\n        throw TextMeshBuilderError.invalidCharacter(character)\n      }\n      return descriptor\n    }\n    return descriptor\n  }\n\n  /// `indent` must be less than `maximumWidth` and `maximumWidth` must greater than the width of\n  /// each individual character in the string.\n  func wrap(_ text: String, maximumWidth: Int, indent: Int) -> [String] {\n    assert(indent < maximumWidth, \"indent must be smaller than maximumWidth\")\n\n    if text == \"\" {\n      return [\"\"]\n    }\n\n    var wrapIndex: String.Index? = nil\n    var latestSpace: String.Index? = nil\n    var width = 0\n    for i in text.indices {\n      let character = text[i]\n      // TODO: Figure out how to load the rest of the characters (such as stars) from the font to\n      // fix chat rendering on Hypixel\n      let descriptor: CharacterDescriptor\n      do {\n        descriptor = try self.descriptor(for: character)\n      } catch {\n        continue\n      }\n\n      assert(\n        descriptor.renderedWidth < maximumWidth,\n        \"maximumWidth must be greater than every individual character in the string\"\n      )\n\n      width += descriptor.renderedWidth\n      if i != text.startIndex {\n        width += 1 // character spacing\n      }\n\n      // TODO: wrap on other characters such as '-' as well\n      if character == \" \" {\n        latestSpace = i\n      }\n\n      if width > maximumWidth {\n        if let spaceIndex = latestSpace {\n          wrapIndex = spaceIndex\n        } else {\n          wrapIndex = i\n        }\n        break\n      }\n    }\n\n    var lines: [String] = []\n    if let wrapIndex = wrapIndex {\n      lines = [String(text[text.startIndex..<wrapIndex])]\n\n      var startIndex = wrapIndex\n      while text[startIndex] == \" \" {\n        startIndex = text.index(after: startIndex)\n        if startIndex == text.endIndex {\n          return lines // NOTE: early return\n        }\n      }\n      let nonWrappedText = text[startIndex...]\n      lines.append(contentsOf: wrap(\n        String(nonWrappedText),\n        maximumWidth: maximumWidth - indent,\n        indent: 0\n      ))\n    } else {\n      lines = [text]\n    }\n\n    return lines\n  }\n\n  /// - Returns: `nil` if the input string is empty.\n  func build(\n    _ text: String,\n    fontArrayTexture: MTLTexture,\n    color: Vec4f = [1, 1, 1, 1],\n    outlineColor: Vec4f? = nil\n  ) throws -> GUIElementMesh? {\n    if text.isEmpty {\n      return nil\n    }\n\n    var currentX = 0\n    let currentY = 0\n    let spacing = 1\n    var quads: [GUIQuad] = []\n    quads.reserveCapacity(text.count)\n    for character in text {\n      let descriptor: CharacterDescriptor\n      do {\n        descriptor = try self.descriptor(for: character)\n      } catch {\n        continue\n      }\n\n      var quad = try Self.build(\n        descriptor,\n        fontArrayTexture: fontArrayTexture,\n        color: color\n      )\n      quad.translate(amount: Vec2f(\n        Float(currentX),\n        Float(currentY)\n      ))\n      quads.append(quad)\n\n      currentX += Int(quad.size.x) + spacing\n    }\n\n    guard !quads.isEmpty else {\n      throw GUIRendererError.emptyText\n    }\n\n    // Create outline\n    if let outlineColor = outlineColor {\n      var outlineQuads: [GUIQuad] = []\n      let outlineTranslations: [Vec2f] = [\n        [-1, 0],\n        [1, 0],\n        [0, -1],\n        [0, 1]\n      ]\n\n      for translation in outlineTranslations {\n        for var quad in quads {\n          quad.translate(amount: translation)\n          quad.tint = outlineColor\n          outlineQuads.append(quad)\n        }\n      }\n\n      // Outline is rendered before the actual text\n      quads = outlineQuads + quads\n    }\n\n    let width = currentX - spacing\n    let height = Font.defaultCharacterHeight\n    return GUIElementMesh(size: [width, height], arrayTexture: fontArrayTexture, quads: quads)\n  }\n\n  /// Creates a quad instance for the given character.\n  private static func build(\n    _ character: CharacterDescriptor,\n    fontArrayTexture: MTLTexture,\n    color: Vec4f\n  ) throws -> GUIQuad {\n    let arrayTextureWidth = Float(fontArrayTexture.width)\n    let arrayTextureHeight = Float(fontArrayTexture.height)\n\n    let position = Vec2f(\n      0,\n      Float(Font.defaultCharacterHeight - character.renderedHeight - character.verticalOffset)\n    )\n    let size = Vec2f(\n      Float(character.renderedWidth),\n      Float(character.renderedHeight)\n    )\n    let uvMin = Vec2f(\n      Float(character.x) / arrayTextureWidth,\n      Float(character.y) / arrayTextureHeight\n    )\n    let uvSize = Vec2f(\n      Float(character.width) / arrayTextureWidth,\n      Float(character.height) / arrayTextureHeight\n    )\n\n    return GUIQuad(\n      position: position,\n      size: size,\n      uvMin: uvMin,\n      uvSize: uvSize,\n      textureIndex: UInt16(character.texture),\n      tint: color\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/GUI/TextMeshBuilderError.swift",
    "content": "import Foundation\n\nenum TextMeshBuilderError: Error {\n  case invalidCharacter(Character)\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Logger.swift",
    "content": "@_exported import DeltaLogger\n"
  },
  {
    "path": "Sources/Core/Renderer/Mesh/BlockMeshBuilder.swift",
    "content": "import DeltaCore\nimport FirebladeMath\n\n/// Builds the mesh for a single block.\nstruct BlockMeshBuilder {\n  let model: BlockModel\n  let position: BlockPosition\n  let modelToWorld: Mat4x4f\n  let culledFaces: DirectionSet\n  let lightLevel: LightLevel\n  let neighbourLightLevels: [Direction: LightLevel]  // TODO: Convert to array for faster access\n  let tintColor: Vec3f\n  let blockTexturePalette: TexturePalette  // TODO: Remove when texture type is baked into block models\n\n  func build(\n    into geometry: inout Geometry<BlockVertex>,\n    translucentGeometry: inout SortableMeshElement\n  ) {\n    var translucentGeometryParts: [(size: Float, geometry: Geometry<BlockVertex>)] = []\n    for part in model.parts {\n      buildPart(\n        part,\n        into: &geometry,\n        translucentGeometry: &translucentGeometryParts\n      )\n    }\n\n    translucentGeometry = Self.mergeTranslucentGeometry(\n      translucentGeometryParts,\n      position: position\n    )\n  }\n\n  func buildPart(\n    _ part: BlockModelPart,\n    into geometry: inout Geometry<BlockVertex>,\n    translucentGeometry: inout [(size: Float, geometry: Geometry<BlockVertex>)]\n  ) {\n    for element in part.elements {\n      var elementTranslucentGeometry = Geometry<BlockVertex>()\n      buildElement(\n        element,\n        into: &geometry,\n        translucentGeometry: &elementTranslucentGeometry\n      )\n\n      if !elementTranslucentGeometry.isEmpty {\n        // Calculate a size used for sorting nested translucent elements (required for blocks such\n        // as honey blocks and slime blocks).\n        let minimum = (Vec4f(0, 0, 0, 1) * element.transformation * modelToWorld).xyz\n        let maximum = (Vec4f(1, 1, 1, 1) * element.transformation * modelToWorld).xyz\n        let size = (maximum - minimum).magnitude\n        translucentGeometry.append((size: size, geometry: elementTranslucentGeometry))\n      }\n    }\n  }\n\n  func buildElement(\n    _ element: BlockModelElement,\n    into geometry: inout Geometry<BlockVertex>,\n    translucentGeometry: inout Geometry<BlockVertex>\n  ) {\n    let vertexToWorld = element.transformation * modelToWorld\n    for face in element.faces {\n      if let cullface = face.cullface, culledFaces.contains(cullface) {\n        continue\n      }\n\n      let faceLightLevel = LightLevel.max(\n        neighbourLightLevels[face.actualDirection] ?? .default,\n        lightLevel\n      )\n\n      buildFace(\n        face,\n        transformedBy: vertexToWorld,\n        into: &geometry,\n        translucentGeometry: &translucentGeometry,\n        faceLightLevel: faceLightLevel,\n        shouldShade: element.shade\n      )\n    }\n  }\n\n  func buildFace(\n    _ face: BlockModelFace,\n    transformedBy vertexToWorld: Mat4x4f,\n    into geometry: inout Geometry<BlockVertex>,\n    translucentGeometry: inout Geometry<BlockVertex>,\n    faceLightLevel: LightLevel,\n    shouldShade: Bool\n  ) {\n    // TODO: Bake texture type into block model\n    let textureType = blockTexturePalette.textures[face.texture].type\n    if textureType == .translucent {\n      buildFace(\n        face,\n        transformedBy: vertexToWorld,\n        into: &translucentGeometry,\n        faceLightLevel: faceLightLevel,\n        shouldShade: shouldShade,\n        textureType: textureType\n      )\n    } else {\n      buildFace(\n        face,\n        transformedBy: vertexToWorld,\n        into: &geometry,\n        faceLightLevel: faceLightLevel,\n        shouldShade: shouldShade,\n        textureType: textureType\n      )\n    }\n  }\n\n  func buildFace(\n    _ face: BlockModelFace,\n    transformedBy vertexToWorld: Mat4x4f,\n    into geometry: inout Geometry<BlockVertex>,\n    faceLightLevel: LightLevel,\n    shouldShade: Bool,\n    textureType: TextureType\n  ) {\n    // Add face winding\n    let offset = UInt32(geometry.vertices.count)  // The index of the first vertex of this face\n    for index in CubeGeometry.faceWinding {\n      geometry.indices.append(index &+ offset)\n    }\n\n    let faceVertexPositions = CubeGeometry.faceVertices[face.direction.rawValue]\n\n    // Calculate shade of face\n    let faceDirection = face.actualDirection.rawValue\n    let shade = shouldShade ? CubeGeometry.shades[faceDirection] : 1\n\n    // Calculate the tint color to apply to the face\n    let tint: Vec3f\n    if face.isTinted {\n      tint = tintColor * shade\n    } else {\n      tint = Vec3f(repeating: shade)\n    }\n\n    let textureIndex = UInt16(face.texture)\n    let isTransparent = textureType == .transparent\n\n    // Add vertices to mesh\n    for (uvIndex, vertexPosition) in faceVertexPositions.enumerated() {\n      let position = (Vec4f(vertexPosition, 1) * vertexToWorld).xyz\n      let uv = face.uvs[uvIndex]\n      let vertex = BlockVertex(\n        x: position.x,\n        y: position.y,\n        z: position.z,\n        u: uv.x,\n        v: uv.y,\n        r: tint.x,\n        g: tint.y,\n        b: tint.z,\n        a: 1,\n        skyLightLevel: UInt8(faceLightLevel.sky),\n        blockLightLevel: UInt8(faceLightLevel.block),\n        textureIndex: textureIndex,\n        isTransparent: isTransparent\n      )\n      geometry.vertices.append(vertex)\n    }\n  }\n\n  /// Sort the geometry assuming that smaller translucent elements are always inside of bigger\n  /// elements in the same block (e.g. honey block, slime block). The geometry is then combined\n  /// into a single element to add to the final mesh to reduce sorting calculations while\n  /// rendering.\n  private static func mergeTranslucentGeometry(\n    _ geometries: [(size: Float, geometry: Geometry<BlockVertex>)],\n    position: BlockPosition\n  ) -> SortableMeshElement {\n    var geometries = geometries  // TODO: This may cause an unnecessary copy\n    geometries.sort { first, second in\n      return second.size > first.size\n    }\n\n    // Counts used to reserve a suitable amount of capacity\n    var vertexCount = 0\n    var indexCount = 0\n    for (_, geometry) in geometries {\n      vertexCount += geometry.vertices.count\n      indexCount += geometry.indices.count\n    }\n\n    var vertices: [BlockVertex] = []\n    var indices: [UInt32] = []\n    vertices.reserveCapacity(vertexCount)\n    indices.reserveCapacity(indexCount)\n\n    for (_, geometry) in geometries {\n      let startingIndex = UInt32(vertices.count)\n      vertices.append(contentsOf: geometry.vertices)\n      indices.append(\n        contentsOf: geometry.indices.map { index in\n          return index + startingIndex\n        }\n      )\n    }\n\n    let geometry = Geometry(vertices: vertices, indices: indices)\n    return SortableMeshElement(\n      geometry: geometry,\n      centerPosition: position.floatVector + Vec3f(0.5, 0.5, 0.5)\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Mesh/BlockNeighbour.swift",
    "content": "import DeltaCore\n\n/// A representation of a neighbouring block that can be used efficiently when generating meshes.\nstruct BlockNeighbour {\n  /// The direction that the neighbouring block is located compared to the block.\n  let direction: Direction\n  /// If the neighbour is in another chunk,\n  /// the direction of the chunk containing the neighbour.\n  let chunkDirection: CardinalDirection?\n  /// The index of the neighbour block in its chunk.\n  let index: Int\n\n  /// Gets the locations of all blocks neighbouring a given block.\n  /// - Parameters:\n  ///   - index: Block index relative the block's chunk section.\n  ///   - sectionIndex: The index of the section that the block is in.\n  /// - Returns: The block's neighbours.\n  static func neighbours(\n    ofBlockAt index: Int,\n    inSection sectionIndex: Int\n  ) -> [BlockNeighbour] {\n    let indexInChunk = index &+ sectionIndex &* Chunk.Section.numBlocks\n    var neighbours: [BlockNeighbour] = []\n    neighbours.reserveCapacity(6)\n\n    let indexInLayer = indexInChunk % Chunk.blocksPerLayer\n    if indexInLayer >= Chunk.width {\n      neighbours.append(BlockNeighbour(\n        direction: .north,\n        chunkDirection: nil,\n        index: indexInChunk &- Chunk.width\n      ))\n    } else {\n      neighbours.append(BlockNeighbour(\n        direction: .north,\n        chunkDirection: .north,\n        index: indexInChunk + Chunk.blocksPerLayer - Chunk.width\n      ))\n    }\n\n    if indexInLayer < Chunk.blocksPerLayer &- Chunk.width {\n      neighbours.append(BlockNeighbour(\n        direction: .south,\n        chunkDirection: nil,\n        index: indexInChunk &+ Chunk.width\n      ))\n    } else {\n      neighbours.append(BlockNeighbour(\n        direction: .south,\n        chunkDirection: .south,\n        index: indexInChunk - Chunk.blocksPerLayer + Chunk.width\n      ))\n    }\n\n    let indexInRow = indexInChunk % Chunk.width\n    if indexInRow != Chunk.width &- 1 {\n      neighbours.append(BlockNeighbour(\n        direction: .east,\n        chunkDirection: nil,\n        index: indexInChunk &+ 1\n      ))\n    } else {\n      neighbours.append(BlockNeighbour(\n        direction: .east,\n        chunkDirection: .east,\n        index: indexInChunk &- 15\n      ))\n    }\n\n    if indexInRow != 0 {\n      neighbours.append(BlockNeighbour(\n        direction: .west,\n        chunkDirection: nil,\n        index: indexInChunk &- 1\n      ))\n    } else {\n      neighbours.append(BlockNeighbour(\n        direction: .west,\n        chunkDirection: .west,\n        index: indexInChunk &+ 15\n      ))\n    }\n\n    if indexInChunk < Chunk.numBlocks &- Chunk.blocksPerLayer {\n      neighbours.append(BlockNeighbour(\n        direction: .up,\n        chunkDirection: nil,\n        index: indexInChunk &+ Chunk.blocksPerLayer\n      ))\n\n      if indexInChunk >= Chunk.blocksPerLayer {\n        neighbours.append(BlockNeighbour(\n          direction: .down,\n          chunkDirection: nil,\n          index: indexInChunk &- Chunk.blocksPerLayer\n        ))\n      }\n    }\n\n    return neighbours\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Mesh/ChunkSectionMesh.swift",
    "content": "import FirebladeMath\nimport Foundation\nimport MetalKit\n\n/// A renderable mesh of a chunk section.\npublic struct ChunkSectionMesh {\n  /// The mesh containing transparent and opaque blocks only. Doesn't need sorting.\n  public var transparentAndOpaqueMesh: Mesh<BlockVertex, ChunkUniforms>\n  /// The mesh containing translucent blocks. Requires sorting when the player moves (clever stuff is done to minimise sorts in ``WorldRenderer``).\n  public var translucentMesh: SortableMesh\n  /// Whether the mesh contains fluids or not.\n  public var containsFluids = false\n\n  public var isEmpty: Bool {\n    return transparentAndOpaqueMesh.isEmpty && translucentMesh.isEmpty\n  }\n\n  /// Create a new chunk section mesh.\n  public init(_ uniforms: ChunkUniforms) {\n    transparentAndOpaqueMesh = Mesh<BlockVertex, ChunkUniforms>(uniforms: uniforms)\n    translucentMesh = SortableMesh(uniforms: uniforms)\n  }\n\n  /// Clear the mesh's geometry and invalidate its buffers. Leaves GPU buffers intact for reuse.\n  public mutating func clearGeometry() {\n    transparentAndOpaqueMesh.clearGeometry()\n    translucentMesh.clear()\n  }\n\n  /// Encode the render commands for transparent and opaque mesh of this chunk section.\n  /// - Parameters:\n  ///   - renderEncoder: Encoder for rendering geometry.\n  ///   - device: The device to use.\n  ///   - commandQueue: The command queue to use for creating buffers.\n  public mutating func renderTransparentAndOpaque(\n    renderEncoder: MTLRenderCommandEncoder,\n    device: MTLDevice,\n    commandQueue: MTLCommandQueue\n  ) throws {\n    try transparentAndOpaqueMesh.render(\n      into: renderEncoder, with: device, commandQueue: commandQueue)\n  }\n\n  /// Encode the render commands for translucent mesh of this chunk section.\n  /// - Parameters:\n  ///   - position: Position the mesh is viewed from. Used for sorting.\n  ///   - sortTranslucent: Indicates whether sorting should be enabled for translucent mesh rendering.\n  ///   - renderEncoder: Encoder for rendering geometry.\n  ///   - device: The device to use.\n  ///   - commandQueue: The command queue to use for creating buffers.\n  public mutating func renderTranslucent(\n    viewedFrom position: Vec3f,\n    sortTranslucent: Bool,\n    renderEncoder: MTLRenderCommandEncoder,\n    device: MTLDevice,\n    commandQueue: MTLCommandQueue\n  ) throws {\n    try translucentMesh.render(\n      viewedFrom: position,\n      sort: sortTranslucent,\n      encoder: renderEncoder,\n      device: device,\n      commandQueue: commandQueue\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Mesh/ChunkSectionMeshBuilder.swift",
    "content": "import DeltaCore\nimport FirebladeMath\nimport Foundation\nimport MetalKit\n\n/// Builds renderable meshes from chunk sections.\n///\n/// Assumes that all relevant chunks have already been locked.\npublic struct ChunkSectionMeshBuilder {  // TODO: Bring docs up to date\n  /// A lookup to quickly convert block index to block position.\n  private static let indexToPosition = generateIndexLookup()\n  /// A lookup tp quickly get the block indices for blocks' neighbours.\n  private static let blockNeighboursLookup = (0..<Chunk.numSections).map {\n    generateNeighbours(sectionIndex: $0)\n  }\n\n  /// The world containing the chunk section to prepare.\n  public var world: World\n  /// The chunk containing the section to prepare.\n  public var chunk: Chunk\n  /// The position of the section to prepare.\n  public var sectionPosition: ChunkSectionPosition\n  /// The chunks surrounding ``chunk``.\n  public var neighbourChunks: ChunkNeighbours\n\n  /// The resources containing the textures and block models for the builds to use.\n  private let resources: ResourcePack.Resources\n\n  /// Creates a new mesh builder.\n  ///\n  /// Assumes that all chunks required to prepare this section have been locked. See ``WorldMesh/chunksRequiredToPrepare(chunkAt:)``.\n  /// - Parameters:\n  ///   - sectionPosition: The position of the section in the world.\n  ///   - chunk: The chunk the section is in.\n  ///   - world: The world the chunk is in.\n  ///   - neighbourChunks: The chunks surrounding the chunk the section is in. Used for face culling on the edge of the chunk.\n  ///   - resourcePack: The resource pack to use for block models.\n  public init(\n    forSectionAt sectionPosition: ChunkSectionPosition,\n    in chunk: Chunk,\n    withNeighbours neighbourChunks: ChunkNeighbours,\n    world: World,\n    resources: ResourcePack.Resources\n  ) {\n    self.world = world\n    self.sectionPosition = sectionPosition\n    self.chunk = chunk\n    self.neighbourChunks = neighbourChunks\n    self.resources = resources\n  }\n\n  /// Builds a mesh for the section at ``sectionPosition`` in ``chunk``.\n  ///\n  /// Assumes that the chunk has been locked already.\n  /// - Parameter existingMesh: If present, the builder will attempt to reuse existing buffers if possible.\n  /// - Returns: A mesh. `nil` if the mesh would be empty.\n  public func build(reusing existingMesh: ChunkSectionMesh? = nil) -> ChunkSectionMesh? {\n    // Create uniforms\n    let position = Vec3f(\n      Float(sectionPosition.sectionX) * 16,\n      Float(sectionPosition.sectionY) * 16,\n      Float(sectionPosition.sectionZ) * 16\n    )\n    let modelToWorldMatrix = MatrixUtil.translationMatrix(position)\n    let uniforms = ChunkUniforms(transformation: modelToWorldMatrix)\n\n    var mesh = existingMesh ?? ChunkSectionMesh(uniforms)\n    mesh.clearGeometry()\n\n    // Populate mesh with geometry\n    let section = chunk.getSections(acquireLock: false)[sectionPosition.sectionY]\n    let indexToNeighbours = Self.blockNeighboursLookup[sectionPosition.sectionY]\n\n    let xOffset = sectionPosition.sectionX * Chunk.Section.width\n    let yOffset = sectionPosition.sectionY * Chunk.Section.height\n    let zOffset = sectionPosition.sectionZ * Chunk.Section.depth\n\n    if section.blockCount != 0 {\n      var transparentAndOpaqueGeometry = Geometry<BlockVertex>()\n      for blockIndex in 0..<Chunk.Section.numBlocks {\n        let blockId = section.getBlockId(at: blockIndex)\n        if blockId != 0 {\n          var position = Self.indexToPosition[blockIndex]\n          position.x += xOffset\n          position.y += yOffset\n          position.z += zOffset\n          addBlock(\n            at: position,\n            atBlockIndex: blockIndex,\n            with: blockId,\n            transparentAndOpaqueGeometry: &transparentAndOpaqueGeometry,\n            translucentMesh: &mesh.translucentMesh,\n            indexToNeighbours: indexToNeighbours,\n            containsFluids: &mesh.containsFluids\n          )\n        }\n      }\n      mesh.transparentAndOpaqueMesh.vertices = transparentAndOpaqueGeometry.vertices\n      mesh.transparentAndOpaqueMesh.indices = transparentAndOpaqueGeometry.indices\n    }\n\n    if mesh.isEmpty {\n      return nil\n    }\n\n    return mesh\n  }\n\n  // MARK: Block mesh building\n\n  /// Adds a block to the mesh.\n  private func addBlock(\n    at position: BlockPosition,\n    atBlockIndex blockIndex: Int,\n    with blockId: Int,\n    transparentAndOpaqueGeometry: inout Geometry<BlockVertex>,\n    translucentMesh: inout SortableMesh,\n    indexToNeighbours: [[BlockNeighbour]],\n    containsFluids: inout Bool\n  ) {\n    // Get block model\n    guard let blockModel = resources.blockModelPalette.model(for: blockId, at: position) else {\n      log.warning(\"Skipping block with no block models\")\n      return\n    }\n\n    // Get block\n    guard let block = RegistryStore.shared.blockRegistry.block(withId: blockId) else {\n      log.warning(\n        \"Skipping block with non-existent state id \\(blockId), failed to get block information\"\n      )\n      return\n    }\n\n    // Render fluid if present\n    if let fluidState = block.fluidState {\n      containsFluids = true\n      addFluid(\n        at: position,\n        atBlockIndex: blockIndex,\n        with: blockId,\n        translucentMesh: &translucentMesh,\n        indexToNeighbours: indexToNeighbours\n      )\n\n      if !fluidState.isWaterlogged {\n        return\n      }\n    }\n\n    // Return early if block model is empty (such as air)\n    if blockModel.cullableFaces.isEmpty && blockModel.nonCullableFaces.isEmpty {\n      return\n    }\n\n    // Get block indices of neighbouring blocks\n    let neighbours = indexToNeighbours[blockIndex]\n\n    // Calculate face visibility\n    let culledFaces = getCullingNeighbours(at: position, blockId: blockId, neighbours: neighbours)\n\n    // Return early if there can't possibly be any visible faces\n    if blockModel.cullableFaces == DirectionSet.all\n      && culledFaces == DirectionSet.all\n      && blockModel.nonCullableFaces.isEmpty\n    {\n      return\n    }\n\n    // Find the cullable faces which are visible\n    var visibleFaces = blockModel.cullableFaces.subtracting(culledFaces)\n\n    // Return early if there are no always visible faces and no non-culled faces\n    if blockModel.nonCullableFaces.isEmpty && visibleFaces.isEmpty {\n      return\n    }\n\n    // Add non cullable faces to the visible faces set (they are always rendered)\n    if !blockModel.nonCullableFaces.isEmpty {\n      visibleFaces = visibleFaces.union(blockModel.nonCullableFaces)\n\n      // Return early if no faces are visible\n      if visibleFaces.isEmpty {\n        return\n      }\n    }\n\n    // Get lighting\n    let positionRelativeToChunkSection = position.relativeToChunkSection\n    let lightLevel = chunk.getLighting(acquireLock: false).getLightLevel(\n      at: positionRelativeToChunkSection,\n      inSectionAt: sectionPosition.sectionY\n    )\n    let neighbourLightLevels = getNeighbouringLightLevels(\n      neighbours: neighbours,\n      visibleFaces: visibleFaces\n    )\n\n    // Get tint color\n    guard let biome = chunk.biome(at: position.relativeToChunk, acquireLock: false) else {\n      let biomeId = chunk.biomeId(at: position, acquireLock: false).map(String.init) ?? \"unknown\"\n      log.warning(\"Block at \\(position) has invalid biome with id \\(biomeId)\")\n      return\n    }\n\n    let tintColor = resources.biomeColors.color(for: block, at: position, in: biome)\n\n    // Create model to world transformation matrix\n    let offset = block.getModelOffset(at: position)\n    let modelToWorld = MatrixUtil.translationMatrix(\n      positionRelativeToChunkSection.floatVector + offset\n    )\n\n    // Add block model to mesh\n    addBlockModel(\n      blockModel,\n      to: &transparentAndOpaqueGeometry,\n      translucentMesh: &translucentMesh,\n      position: position,\n      modelToWorld: modelToWorld,\n      culledFaces: culledFaces,\n      lightLevel: lightLevel,\n      neighbourLightLevels: neighbourLightLevels,\n      tintColor: tintColor?.floatVector ?? [1, 1, 1]\n    )\n  }\n\n  private func addBlockModel(\n    _ model: BlockModel,\n    to transparentAndOpaqueGeometry: inout Geometry<BlockVertex>,\n    translucentMesh: inout SortableMesh,\n    position: BlockPosition,\n    modelToWorld: Mat4x4f,\n    culledFaces: DirectionSet,\n    lightLevel: LightLevel,\n    neighbourLightLevels: [Direction: LightLevel],\n    tintColor: Vec3f\n  ) {\n    var translucentGeometry = SortableMeshElement(centerPosition: [0, 0, 0])\n    BlockMeshBuilder(\n      model: model,\n      position: position,\n      modelToWorld: modelToWorld,\n      culledFaces: culledFaces,\n      lightLevel: lightLevel,\n      neighbourLightLevels: neighbourLightLevels,\n      tintColor: tintColor,\n      blockTexturePalette: resources.blockTexturePalette\n    ).build(\n      into: &transparentAndOpaqueGeometry,\n      translucentGeometry: &translucentGeometry\n    )\n\n    if !translucentGeometry.isEmpty {\n      translucentMesh.add(translucentGeometry)\n    }\n  }\n\n  /// Adds a fluid block to the mesh.\n  /// - Parameters:\n  ///   - position: The position of the block in world coordinates.\n  ///   - blockIndex: The index of the block in the chunk section.\n  ///   - blockId: The block's id.\n  ///   - translucentMesh: The mesh to add the fluid to.\n  ///   - indexToNeighbours: The lookup table used to find the neighbours of a block quickly.\n  private func addFluid(\n    at position: BlockPosition,\n    atBlockIndex blockIndex: Int,\n    with blockId: Int,\n    translucentMesh: inout SortableMesh,\n    indexToNeighbours: [[BlockNeighbour]]\n  ) {\n    guard\n      let block = RegistryStore.shared.blockRegistry.block(withId: blockId),\n      let fluid = block.fluidState?.fluid\n    else {\n      log.warning(\"Failed to get fluid block with block id \\(blockId)\")\n      return\n    }\n\n    let neighbouringBlockIds = getNeighbouringBlockIds(neighbours: indexToNeighbours[blockIndex])\n    let cullingNeighbours = getCullingNeighbours(\n      at: position.relativeToChunkSection,\n      forFluid: fluid,\n      blockId: blockId,\n      neighbouringBlocks: neighbouringBlockIds\n    )\n    var neighbouringBlocks = [Direction: Block](minimumCapacity: 6)\n    for (direction, neighbourBlockId) in neighbouringBlockIds {\n      let neighbourBlock = RegistryStore.shared.blockRegistry.block(withId: neighbourBlockId)\n      neighbouringBlocks[direction] = neighbourBlock\n    }\n\n    let lightLevel =\n      chunk\n      .getLighting(acquireLock: false)\n      .getLightLevel(atIndex: blockIndex, inSectionAt: sectionPosition.sectionY)\n    let neighbouringLightLevels = getNeighbouringLightLevels(\n      neighbours: indexToNeighbours[blockIndex],\n      visibleFaces: [.up, .down, .north, .east, .south, .west]\n    )\n\n    let builder = FluidMeshBuilder(\n      position: position,\n      blockIndex: blockIndex,\n      block: block,\n      fluid: fluid,\n      chunk: chunk,\n      cullingNeighbours: cullingNeighbours,\n      neighbouringBlocks: neighbouringBlocks,\n      lightLevel: lightLevel,\n      neighbouringLightLevels: neighbouringLightLevels,\n      blockTexturePalette: resources.blockTexturePalette,\n      world: world\n    )\n    builder.build(into: &translucentMesh)\n  }\n\n  // MARK: Helper\n\n  /// Gets the chunk that contains the given neighbour block.\n  /// - Parameter neighbourBlock: The neighbour block.\n  /// - Returns: The chunk containing the block.\n  func getChunk(for neighbourBlock: BlockNeighbour) -> Chunk {\n    if let direction = neighbourBlock.chunkDirection {\n      return neighbourChunks.neighbour(in: direction)\n    } else {\n      return chunk\n    }\n  }\n\n  /// Gets the block id of each block neighbouring a block using the given neighbour indices.\n  ///\n  /// Blocks in neighbouring chunks are also included. Neighbours in cardinal directions will\n  /// always be returned. If the block at `sectionIndex` is at y-level 0 or 255 the down or up neighbours\n  /// will be omitted respectively (as there will be none). Otherwise, all neighbours are included.\n  /// - Returns: A mapping from each possible direction to a corresponding block id.\n  func getNeighbouringBlockIds(neighbours: [BlockNeighbour]) -> [(Direction, Int)] {\n    // Convert a section relative index to a chunk relative index\n    var neighbouringBlocks: [(Direction, Int)] = []\n    neighbouringBlocks.reserveCapacity(6)\n\n    for neighbour in neighbours {\n      let blockId = getChunk(for: neighbour).getBlockId(at: neighbour.index, acquireLock: false)\n      neighbouringBlocks.append((neighbour.direction, blockId))\n    }\n\n    return neighbouringBlocks\n  }\n\n  /// Gets the light levels of the blocks surrounding a block.\n  /// - Parameters:\n  ///   - neighbours: The neighbours to get the light levels of.\n  ///   - visibleFaces: The set of faces to get the light levels of.\n  /// - Returns: A dictionary of face direction to light level for all faces in `visibleFaces`.\n  func getNeighbouringLightLevels(\n    neighbours: [BlockNeighbour],\n    visibleFaces: DirectionSet\n  ) -> [Direction: LightLevel] {\n    var lightLevels = [Direction: LightLevel](minimumCapacity: 6)\n    for neighbour in neighbours {\n      if visibleFaces.contains(neighbour.direction) {\n        let lightLevel = getChunk(for: neighbour)\n          .getLighting(acquireLock: false)\n          .getLightLevel(at: neighbour.index)\n        lightLevels[neighbour.direction] = lightLevel\n      }\n    }\n    return lightLevels\n  }\n\n  /// Gets an array of the direction of all blocks neighbouring the block at `position` that have\n  /// full faces facing the block at `position`.\n  /// - Parameters:\n  ///   - position: The position of the block relative to the section.\n  ///   - blockId: The id of the block at the given position.\n  ///   - neighbourIndices: The neighbour indices lookup table to use.\n  /// - Returns: The set of directions of neighbours that can possibly cull a face.\n  func getCullingNeighbours(\n    at position: BlockPosition,\n    forFluid fluid: Fluid? = nil,\n    blockId: Int,\n    neighbours: [BlockNeighbour]\n  ) -> DirectionSet {\n    let neighbouringBlocks = getNeighbouringBlockIds(neighbours: neighbours)\n    return getCullingNeighbours(\n      at: position,\n      forFluid: fluid,\n      blockId: blockId,\n      neighbouringBlocks: neighbouringBlocks\n    )\n  }\n\n  /// Gets an array of the direction of all blocks neighbouring the block at `position` that have\n  /// full faces facing the block at `position`.\n  /// - Parameters:\n  ///   - position: The position of the block relative to `sectionPosition`.\n  ///   - blockId: The id of the block at the given position.\n  ///   - neighbouringBlocks: The block ids of neighbouring blocks.\n  /// - Returns: The set of directions of neighbours that can possibly cull a face.\n  func getCullingNeighbours(\n    at position: BlockPosition,\n    forFluid fluid: Fluid? = nil,\n    blockId: Int,\n    neighbouringBlocks: [(Direction, Int)]\n  ) -> DirectionSet {\n    // TODO: Skip directions that the block can't be culled from if possible\n    var cullingNeighbours = DirectionSet()\n    let blockCullsSameKind = RegistryStore.shared.blockRegistry.selfCullingBlocks.contains(blockId)\n\n    for (direction, neighbourBlockId) in neighbouringBlocks where neighbourBlockId != 0 {\n      // We assume that block model variants always have the same culling faces as eachother, so\n      // no position is passed to getModel.\n      guard\n        let blockModel = resources.blockModelPalette.model(for: neighbourBlockId, at: nil)\n      else {\n        log.debug(\"Skipping neighbour with no block models.\")\n        continue\n      }\n\n      let culledByOwnKind = blockCullsSameKind && blockId == neighbourBlockId\n      if blockModel.cullingFaces.contains(direction.opposite) || culledByOwnKind {\n        cullingNeighbours.insert(direction)\n      } else if let fluid = fluid {\n        guard\n          let neighbourBlock = RegistryStore.shared.blockRegistry.block(withId: neighbourBlockId)\n        else {\n          continue\n        }\n\n        if neighbourBlock.fluidId == fluid.id {\n          cullingNeighbours.insert(direction)\n        }\n      }\n    }\n\n    return cullingNeighbours\n  }\n\n  // MARK: Lookup table generation\n\n  /// Generates a lookup table to quickly convert from section block index to block position.\n  private static func generateIndexLookup() -> [BlockPosition] {\n    var lookup: [BlockPosition] = []\n    lookup.reserveCapacity(Chunk.Section.numBlocks)\n    for y in 0..<Chunk.Section.height {\n      for z in 0..<Chunk.Section.depth {\n        for x in 0..<Chunk.Section.width {\n          let position = BlockPosition(x: x, y: y, z: z)\n          lookup.append(position)\n        }\n      }\n    }\n    return lookup\n  }\n\n  private static func generateNeighbours(sectionIndex: Int) -> [[BlockNeighbour]] {\n    var neighbours: [[BlockNeighbour]] = []\n    for i in 0..<Chunk.Section.numBlocks {\n      neighbours.append(BlockNeighbour.neighbours(ofBlockAt: i, inSection: sectionIndex))\n    }\n    return neighbours\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Mesh/CubeGeometry.swift",
    "content": "import Foundation\nimport FirebladeMath\nimport DeltaCore\n\nstruct CubeGeometry {\n  static let faceWinding: [UInt32] = [0, 1, 2, 2, 3, 0]\n\n  static let cubeVertices: [Vec3f] = [\n    Vec3f([0, 1, 0]),\n    Vec3f([0, 0, 0]),\n    Vec3f([1, 0, 0]),\n    Vec3f([1, 1, 0]),\n    Vec3f([0, 1, 1]),\n    Vec3f([0, 0, 1]),\n    Vec3f([1, 0, 1]),\n    Vec3f([1, 1, 1])\n  ]\n\n  /// Indexed by ``Direction/rawValue``.\n  static let faceVertices: [[Vec3f]] = [\n    CubeGeometry.generateFaceVertices(facing: .down),\n    CubeGeometry.generateFaceVertices(facing: .up),\n    CubeGeometry.generateFaceVertices(facing: .north),\n    CubeGeometry.generateFaceVertices(facing: .south),\n    CubeGeometry.generateFaceVertices(facing: .west),\n    CubeGeometry.generateFaceVertices(facing: .east)\n  ]\n\n  /// Indexed by ``Direction/rawValue`` and used to generate ``faceVertices``.\n  static let faceVertexIndices: [[Int]] = [\n    [6, 2, 1, 5],\n    [3, 7, 4, 0],\n    [0, 1, 2, 3],\n    [7, 6, 5, 4],\n    [4, 5, 1, 0],\n    [3, 2, 6, 7]\n  ]\n\n  public static let shades: [Float] = [\n    0.6, // down\n    1.0, // up\n    0.9, 0.9, // north, south\n    0.7, 0.7  // east, west\n  ]\n\n  static func generateFaceVertices(facing face: Direction) -> [Vec3f] {\n    let vertexIndices = faceVertexIndices[face.rawValue]\n\n    let vertices = vertexIndices.map { index in\n      return cubeVertices[index]\n    }\n\n    return vertices\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Mesh/EntityMeshBuilder.swift",
    "content": "import CoreFoundation\nimport DeltaCore\nimport FirebladeECS\nimport Foundation\n\npublic struct EntityMeshBuilder {\n  /// Associates entity kinds with hardcoded entity texture identifiers. Used to manually\n  /// instruct Delta Client where to find certain textures that aren't in the standard\n  /// locations.\n  public static let hardcodedTextureIdentifiers: [Identifier: Identifier] = [\n    Identifier(name: \"player\"): Identifier(name: \"entity/steve\"),\n    Identifier(name: \"dragon\"): Identifier(name: \"entity/enderdragon/dragon\"),\n    Identifier(name: \"chest\"): Identifier(name: \"entity/chest/normal\"),\n  ]\n\n  /// Used to get extra metadata for rendering item entities and the Ender Dragon. Not required if just\n  /// rendering an entity for things such as block entity items.\n  public let entity: Entity?\n  public let entityKind: Identifier\n  public let position: Vec3f\n  public let pitch: Float\n  public let yaw: Float\n  public let entityModelPalette: EntityModelPalette\n  public let itemModelPalette: ItemModelPalette\n  public let blockModelPalette: BlockModelPalette\n  public let entityTexturePalette: TexturePalette\n  public let blockTexturePalette: TexturePalette\n  public let hitbox: AxisAlignedBoundingBox\n  public let lightLevel: LightLevel\n\n  static let colors: [Vec3f] = [\n    [1, 0, 0],\n    [0, 1, 0],\n    [0, 0, 1],\n    [1, 1, 0],\n    [1, 0, 1],\n    [0, 1, 1],\n    [0, 0, 0],\n    [1, 1, 1],\n  ]\n\n  // TODO: Propagate all warnings as errors and then handle them and emit them as warnings in EntityRenderer instead\n  /// `blockGeometry` and `translucentBlockGeometry` are used to render block entities and block item entities.\n  func build(\n    into geometry: inout Geometry<EntityVertex>,\n    blockGeometry: inout Geometry<BlockVertex>,\n    translucentBlockGeometry: inout SortableMeshElement\n  ) {\n    if let model = entityModelPalette.models[entityKind] {\n      buildModel(model, into: &geometry)\n    } else if let itemMetadata = entity?.get(component: EntityMetadata.self)?.itemMetadata {\n      guard let itemStack = itemMetadata.slot.stack else {\n        // If there's no stack, then we're still waiting for the server to send\n        // the item's metadata so don't render anything yet (to avoid seeing the\n        // 'missing entity' hitbox for a split second whenever a new item entity\n        // spawns in).\n        return\n      }\n\n      guard let itemModel = itemModelPalette.model(for: itemStack.itemId) else {\n        buildAABB(hitbox, into: &geometry)\n        return\n      }\n\n      // TODO: Figure out why these bobbing constants and hardcoded translations are so weird\n      //   (they're even still slightly off vanilla, there must be a different order of transformations\n      //   that makes these numbers nice or something).\n      let time = CFAbsoluteTimeGetCurrent() * TickScheduler.defaultTicksPerSecond\n      let phaseOffset = Double(itemMetadata.bobbingPhaseOffset)\n      let verticalOffset = Float(Foundation.sin(time / 10 + phaseOffset)) / 8 * 3\n      let spinAngle = -Float((time / 20 + phaseOffset).remainder(dividingBy: 2 * .pi))\n      let bob =\n        MatrixUtil.translationMatrix(Vec3f(0, verticalOffset, 0))\n        * MatrixUtil.rotationMatrix(y: spinAngle)\n\n      switch itemModel {\n        case let .entity(identifier, transforms):\n          // Remove identifier prefix (entity model palette doesn't have any `item/` or `entity/` prefixes).\n          var entityIdentifier = identifier\n          entityIdentifier.name = entityIdentifier.name.replacingOccurrences(of: \"item/\", with: \"\")\n\n          guard let entityModel = entityModelPalette.models[entityIdentifier] else {\n            log.warning(\"Missing entity model for entity with '\\(entityIdentifier)' (as item)\")\n            return\n          }\n\n          let transformation =\n            bob * transforms.ground * MatrixUtil.translationMatrix(Vec3f(0, 11.0 / 64, 0))\n          buildModel(\n            entityModel,\n            textureIdentifier: entityIdentifier,\n            transformation: transformation,\n            into: &geometry\n          )\n        case let .blockModel(id):\n          guard let blockModel = blockModelPalette.model(for: id, at: nil) else {\n            log.warning(\n              \"Missing block model for item entity (block id: \\(id), item id: \\(itemStack.itemId))\"\n            )\n            return\n          }\n\n          var neighbourLightLevels: [Direction: LightLevel] = [:]\n          for direction in Direction.allDirections {\n            neighbourLightLevels[direction] = lightLevel\n          }\n\n          // TODO: Try using the transformation code from the GUIRenderer and see if that cleans things up a bit.\n          let transformation =\n            MatrixUtil.translationMatrix(Vec3f(-0.5, 0, -0.5))\n            * bob\n            * MatrixUtil.scalingMatrix(0.25)\n            * MatrixUtil.translationMatrix(Vec3f(0, 7.0 / 32.0, 0))\n            * MatrixUtil.rotationMatrix(y: yaw + .pi)\n            * MatrixUtil.translationMatrix(position)\n\n          let builder = BlockMeshBuilder(\n            model: blockModel,\n            position: .zero,\n            modelToWorld: transformation,\n            culledFaces: [],\n            lightLevel: lightLevel,\n            neighbourLightLevels: neighbourLightLevels,\n            tintColor: Vec3f(1, 1, 1),\n            blockTexturePalette: blockTexturePalette\n          )\n          builder.build(into: &blockGeometry, translucentGeometry: &translucentBlockGeometry)\n        case .layered:\n          buildAABB(hitbox, into: &geometry)\n        case .empty:\n          break\n      }\n    } else {\n      buildAABB(hitbox, into: &geometry)\n    }\n\n    if let dragonParts = entity?.get(component: EnderDragonParts.self) {\n      for part in dragonParts.parts {\n        let aabb = part.aabb(withParentPosition: Vec3d(position))\n        buildAABB(aabb, into: &geometry)\n      }\n    }\n  }\n\n  func buildAABB(_ aabb: AxisAlignedBoundingBox, into geometry: inout Geometry<EntityVertex>) {\n    let transformation =\n      MatrixUtil.scalingMatrix(Vec3f(aabb.size))\n      * MatrixUtil.translationMatrix(Vec3f(aabb.position))\n    for direction in Direction.allDirections {\n      let offset = UInt32(geometry.vertices.count)\n      for index in CubeGeometry.faceWinding {\n        geometry.indices.append(index &+ offset)\n      }\n\n      let faceVertexPositions = CubeGeometry.faceVertices[direction.rawValue]\n      for vertexPosition in faceVertexPositions {\n        let position = (Vec4f(vertexPosition, 1) * transformation).xyz\n        let color = EntityRenderer.hitBoxColor.floatVector\n        let vertex = EntityVertex(\n          x: position.x,\n          y: position.y,\n          z: position.z,\n          r: color.x,\n          g: color.y,\n          b: color.z,\n          u: 0,\n          v: 0,\n          skyLightLevel: UInt8(lightLevel.sky),\n          blockLightLevel: UInt8(lightLevel.block),\n          textureIndex: nil\n        )\n        geometry.vertices.append(vertex)\n      }\n    }\n  }\n\n  /// The unit of `transformation` is blocks.\n  func buildModel(\n    _ model: JSONEntityModel,\n    textureIdentifier: Identifier? = nil,\n    transformation: Mat4x4f = MatrixUtil.identity,\n    into geometry: inout Geometry<EntityVertex>\n  ) {\n    let baseTextureIdentifier = textureIdentifier ?? entityKind\n    let texture: Int?\n    if let identifier = Self.hardcodedTextureIdentifiers[baseTextureIdentifier] {\n      texture = entityTexturePalette.textureIndex(for: identifier)\n    } else {\n      // Entity textures can be in all sorts of structures so we just have a few\n      // educated guesses for now.\n      let textureIdentifier = Identifier(\n        namespace: baseTextureIdentifier.namespace,\n        name: \"entity/\\(baseTextureIdentifier.name)\"\n      )\n      let nestedTextureIdentifier = Identifier(\n        namespace: baseTextureIdentifier.namespace,\n        name: \"entity/\\(baseTextureIdentifier.name)/\\(baseTextureIdentifier.name)\"\n      )\n      texture =\n        entityTexturePalette.textureIndex(for: textureIdentifier)\n        ?? entityTexturePalette.textureIndex(for: nestedTextureIdentifier)\n    }\n\n    for (index, submodel) in model.models.enumerated() {\n      buildSubmodel(\n        submodel,\n        index: index,\n        textureIndex: texture,\n        transformation: transformation,\n        into: &geometry\n      )\n    }\n  }\n\n  /// The unit of `transformation` is blocks.\n  func buildSubmodel(\n    _ submodel: JSONEntityModel.Submodel,\n    index: Int,\n    textureIndex: Int?,\n    transformation: Mat4x4f = MatrixUtil.identity,\n    into geometry: inout Geometry<EntityVertex>\n  ) {\n    var transformation = transformation\n    if let rotation = submodel.rotate {\n      let translation = submodel.translate ?? .zero\n      transformation =\n        MatrixUtil.rotationMatrix(-MathUtil.radians(from: rotation))\n        * MatrixUtil.translationMatrix(translation / 16)\n        * transformation\n    }\n\n    for box in submodel.boxes ?? [] {\n      buildBox(\n        box,\n        color: index < Self.colors.count ? Self.colors[index] : [0.5, 0.5, 0.5],\n        transformation: transformation,\n        textureIndex: textureIndex,\n        // We already invert 'y' and 'z'\n        invertedAxes: [\n          submodel.invertAxis?.contains(\"x\") == true,\n          submodel.invertAxis?.contains(\"y\") != true,\n          submodel.invertAxis?.contains(\"z\") != true,\n        ],\n        into: &geometry\n      )\n    }\n\n    for (nestedIndex, nestedSubmodel) in (submodel.submodels ?? []).enumerated() {\n      buildSubmodel(\n        nestedSubmodel,\n        index: nestedIndex,\n        textureIndex: textureIndex,\n        transformation: transformation,\n        into: &geometry\n      )\n    }\n  }\n\n  /// The unit of `transformation` is 16 units per block.\n  func buildBox(\n    _ box: JSONEntityModel.Box,\n    color: Vec3f,\n    transformation: Mat4x4f,\n    textureIndex: Int?,\n    invertedAxes: [Bool],\n    into geometry: inout Geometry<EntityVertex>\n  ) {\n    var boxPosition = Vec3f(\n      box.coordinates[0],\n      box.coordinates[1],\n      box.coordinates[2]\n    )\n    var boxSize = Vec3f(\n      box.coordinates[3],\n      box.coordinates[4],\n      box.coordinates[5]\n    )\n\n    let textureOffset = Vec2f(box.textureOffset ?? .zero)\n\n    let baseBoxSize = boxSize\n    if let additionalSize = box.sizeAdd {\n      let growth = Vec3f(repeating: additionalSize)\n      boxPosition -= growth\n      boxSize += 2 * growth\n    }\n\n    for direction in Direction.allDirections {\n      // The index of the first vertex of this face\n      let offset = UInt32(geometry.vertices.count)\n      for index in CubeGeometry.faceWinding {\n        geometry.indices.append(index &+ offset)\n      }\n\n      var uvOrigin: Vec2f\n      var uvSize: Vec2f\n      let verticalAxis: Axis\n      let horizontalAxis: Axis\n      switch direction {\n        case .east:\n          uvOrigin = textureOffset + Vec2f(0, baseBoxSize.z)\n          uvSize = Vec2f(baseBoxSize.z, baseBoxSize.y)\n          verticalAxis = .y\n          horizontalAxis = .z\n        case .north:\n          uvOrigin = textureOffset + Vec2f(baseBoxSize.z, baseBoxSize.z)\n          uvSize = Vec2f(baseBoxSize.x, baseBoxSize.y)\n          verticalAxis = .y\n          horizontalAxis = .x\n        case .west:\n          uvOrigin = textureOffset + Vec2f(baseBoxSize.z + baseBoxSize.x, baseBoxSize.z)\n          uvSize = Vec2f(baseBoxSize.z, baseBoxSize.y)\n          verticalAxis = .y\n          horizontalAxis = .z\n        case .south:\n          uvOrigin = textureOffset + Vec2f(baseBoxSize.z * 2 + baseBoxSize.x, baseBoxSize.z)\n          uvSize = Vec2f(baseBoxSize.x, baseBoxSize.y)\n          verticalAxis = .y\n          horizontalAxis = .x\n        case .up:\n          uvOrigin = textureOffset + Vec2f(baseBoxSize.z, 0)\n          uvSize = Vec2f(baseBoxSize.x, baseBoxSize.z)\n          verticalAxis = .z\n          horizontalAxis = .x\n        case .down:\n          uvOrigin = textureOffset + Vec2f(baseBoxSize.z + baseBoxSize.x, 0)\n          uvSize = Vec2f(baseBoxSize.x, baseBoxSize.z)\n          verticalAxis = .z\n          horizontalAxis = .x\n      }\n\n      if invertedAxes[horizontalAxis.index] {\n        uvOrigin.x += uvSize.x\n        uvSize.x *= -1\n      }\n      if invertedAxes[verticalAxis.index] {\n        uvOrigin.y += uvSize.y\n        uvSize.y *= -1\n      }\n\n      let textureSize = Vec2f(\n        Float(entityTexturePalette.width),\n        Float(entityTexturePalette.height)\n      )\n      let uvs = [\n        uvOrigin,\n        uvOrigin + Vec2f(0, uvSize.y),\n        uvOrigin + Vec2f(uvSize.x, uvSize.y),\n        uvOrigin + Vec2f(uvSize.x, 0),\n      ].map { pixelUV in\n        pixelUV / textureSize\n      }\n\n      let faceVertexPositions = CubeGeometry.faceVertices[direction.rawValue]\n      for (uv, vertexPosition) in zip(uvs, faceVertexPositions) {\n        var position = vertexPosition * boxSize + boxPosition\n        position /= 16\n        position =\n          (Vec4f(position, 1) * transformation * MatrixUtil.rotationMatrix(y: yaw + .pi))\n          .xyz\n        position += self.position\n        let vertex = EntityVertex(\n          x: position.x,\n          y: position.y,\n          z: position.z,\n          r: textureIndex == nil ? color.x : 1,\n          g: textureIndex == nil ? color.y : 1,\n          b: textureIndex == nil ? color.z : 1,\n          u: uv.x,\n          v: uv.y,\n          skyLightLevel: UInt8(lightLevel.sky),\n          blockLightLevel: UInt8(lightLevel.block),\n          textureIndex: textureIndex.map(UInt16.init)\n        )\n        geometry.vertices.append(vertex)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Mesh/FluidMeshBuilder.swift",
    "content": "import DeltaCore\nimport FirebladeMath\n\n/// Builds the fluid mesh for a block.\nstruct FluidMeshBuilder {  // TODO: Make fluid meshes look more like they do in vanilla\n  /// The UVs for the top of a fluid when flowing.\n  static let flowingUVs: [Vec2f] = [\n    [0.75, 0.25],\n    [0.25, 0.25],\n    [0.25, 0.75],\n    [0.75, 0.75],\n  ]\n\n  /// The UVs for the top of a fluid when still.\n  static let stillUVs: [Vec2f] = [\n    [1, 0],\n    [0, 0],\n    [0, 1],\n    [1, 1],\n  ]\n\n  /// The component directions of the direction to each corner. The index of each is used as the\n  /// corner's index in arrays.\n  private static let cornerToDirections: [[Direction]] = [\n    [.north, .east],\n    [.north, .west],\n    [.south, .west],\n    [.south, .east],\n  ]\n\n  /// Maps directions to the indices of the corners connected to that edge.\n  private static let directionToCorners: [Direction: [Int]] = [\n    .north: [0, 1],\n    .west: [1, 2],\n    .south: [2, 3],\n    .east: [3, 0],\n  ]\n\n  let position: BlockPosition\n  // Take index as well as position to avoid duplicate calculation\n  let blockIndex: Int\n  let block: Block\n  let fluid: Fluid\n  let chunk: Chunk\n  let cullingNeighbours: DirectionSet\n  let neighbouringBlocks: [Direction: Block]\n  let lightLevel: LightLevel\n  let neighbouringLightLevels: [Direction: LightLevel]\n  let blockTexturePalette: TexturePalette\n  let world: World\n\n  func build(into translucentMesh: inout SortableMesh) {\n    // If block is surrounded by the same fluid on all sides, don't render anything.\n    if neighbouringBlocks.count == 6 {\n      let neighbourFluids = Set<Int?>(neighbouringBlocks.values.map { $0.fluidId })\n      if neighbourFluids.count == 1 && neighbourFluids.contains(fluid.id) {\n        return\n      }\n    }\n\n    var tint = Vec3f(1, 1, 1)\n    if block.fluidState?.fluid.identifier.name == \"water\" {\n      guard\n        let tintColor = chunk.biome(\n          at: position.relativeToChunk,\n          acquireLock: false\n        )?.waterColor.floatVector\n      else {\n        // TODO: use a fallback color instead\n        log.warning(\"Failed to get water tint\")\n        return\n      }\n      tint = tintColor\n    }\n\n    let heights = calculateHeights()\n    let isFlowing = Set(heights).count > 1  // If the corners aren't all the same height, it's flowing\n    let topCornerPositions = calculatePositions(heights)\n\n    // Get textures\n    guard\n      let flowingTextureIndex = blockTexturePalette.textureIndex(for: fluid.flowingTexture),\n      let stillTextureIndex = blockTexturePalette.textureIndex(for: fluid.stillTexture)\n    else {\n      log.warning(\"Failed to get textures for fluid\")\n      return\n    }\n\n    let flowingTexture = UInt16(flowingTextureIndex)\n    let stillTexture = UInt16(stillTextureIndex)\n\n    build(\n      into: &translucentMesh,\n      topCornerPositions: topCornerPositions,\n      heights: heights,\n      flowingTexture: flowingTexture,\n      stillTexture: stillTexture,\n      isFlowing: isFlowing,\n      tint: tint\n    )\n  }\n\n  func build(\n    into translucentMesh: inout SortableMesh,\n    topCornerPositions: [Vec3f],\n    heights: [Float],\n    flowingTexture: UInt16,\n    stillTexture: UInt16,\n    isFlowing: Bool,\n    tint: Vec3f\n  ) {\n    let basePosition = position.relativeToChunkSection.floatVector + Vec3f(0.5, 0, 0.5)\n\n    // Make cullingNeighbours mutable and prevent top face culling when covered by non-liquids\n    var cullingNeighbours = cullingNeighbours\n    if neighbouringBlocks[.up]?.fluidId != fluid.id {\n      cullingNeighbours.remove(.up)\n    }\n\n    // Iterate through all visible faces\n    for direction in Direction.allDirections where !cullingNeighbours.contains(direction) {\n      let shade = CubeGeometry.shades[direction.rawValue]\n      let tint = tint * shade\n\n      var geometry = Geometry<BlockVertex>()\n      switch direction {\n        case .up:\n          buildTopFace(\n            into: &geometry,\n            isFlowing: isFlowing,\n            heights: heights,\n            topCornerPositions: topCornerPositions,\n            tint: tint,\n            stillTexture: stillTexture,\n            flowingTexture: flowingTexture\n          )\n        case .north, .east, .south, .west:\n          buildSideFace(\n            direction,\n            into: &geometry,\n            heights: heights,\n            topCornerPositions: topCornerPositions,\n            basePosition: basePosition,\n            flowingTexture: flowingTexture,\n            tint: tint\n          )\n        case .down:\n          buildBottomFace(\n            into: &geometry,\n            basePosition: basePosition,\n            topCornerPositions: topCornerPositions,\n            stillTexture: stillTexture,\n            tint: tint\n          )\n      }\n\n      geometry.indices.append(contentsOf: CubeGeometry.faceWinding)\n\n      translucentMesh.add(\n        SortableMeshElement(\n          geometry: geometry,\n          centerPosition: position.floatVector + Vec3f(0.5, 0.5, 0.5)\n        )\n      )\n    }\n  }\n\n  private func buildTopFace(\n    into geometry: inout Geometry<BlockVertex>,\n    isFlowing: Bool,\n    heights: [Float],\n    topCornerPositions: [Vec3f],\n    tint: Vec3f,\n    stillTexture: UInt16,\n    flowingTexture: UInt16\n  ) {\n    var positions: [Vec3f]\n    let uvs: [Vec2f]\n    let texture: UInt16\n    if isFlowing {\n      texture = flowingTexture\n\n      let (lowestCornersCount, lowestCornerIndex) = countLowestCorners(heights)\n      uvs = generateFlowingTopFaceUVs(lowestCornersCount: lowestCornersCount)\n\n      // Rotate corner positions so that the lowest and the opposite from the lowest are on both triangles\n      positions = []\n      for i in 0..<4 {\n        positions.append(topCornerPositions[(i + lowestCornerIndex) & 0x3])  // & 0x3 performs mod 4\n      }\n    } else {\n      positions = topCornerPositions\n      uvs = Self.stillUVs\n      texture = stillTexture\n    }\n\n    addVertices(to: &geometry, at: positions.reversed(), uvs: uvs, texture: texture, tint: tint)\n  }\n\n  private func buildSideFace(\n    _ direction: Direction,\n    into geometry: inout Geometry<BlockVertex>,\n    heights: [Float],\n    topCornerPositions: [Vec3f],\n    basePosition: Vec3f,\n    flowingTexture: UInt16,\n    tint: Vec3f\n  ) {\n    // The lookup will never be nil because directionToCorners contains values for north, east, south and west\n    // swiftlint:disable force_unwrapping\n    let cornerIndices = Self.directionToCorners[direction]!\n    // swiftlint:enable force_unwrapping\n\n    var uvs = Self.flowingUVs\n    uvs[0][1] += (1 - heights[cornerIndices[0]]) / 2\n    uvs[1][1] += (1 - heights[cornerIndices[1]]) / 2\n    var positions = cornerIndices.map { topCornerPositions[$0] }\n    let offsets = cornerIndices.map { Self.cornerToDirections[$0] }.reversed()\n    for offset in offsets {\n      positions.append(basePosition + offset[0].vector / 2 + offset[1].vector / 2)\n    }\n\n    addVertices(to: &geometry, at: positions, uvs: uvs, texture: flowingTexture, tint: tint)\n  }\n\n  private func buildBottomFace(\n    into geometry: inout Geometry<BlockVertex>,\n    basePosition: Vec3f,\n    topCornerPositions: [Vec3f],\n    stillTexture: UInt16,\n    tint: Vec3f\n  ) {\n    let uvs = [Vec2f](Self.stillUVs.reversed())\n    var positions: [Vec3f] = []\n    positions.reserveCapacity(4)\n    for i in 0..<4 {\n      var position = topCornerPositions[(i - 1) & 0x3]  // & 0x3 is mod 4\n      position.y = basePosition.y\n      positions.append(position)\n    }\n\n    addVertices(to: &geometry, at: positions, uvs: uvs, texture: stillTexture, tint: tint)\n  }\n\n  /// Convert corner heights to corner positions relative to the current chunk section.\n  private func calculatePositions(_ heights: [Float]) -> [Vec3f] {\n    let basePosition = position.relativeToChunkSection.floatVector + Vec3f(0.5, 0, 0.5)\n    var positions: [Vec3f] = []\n    for (index, height) in heights.enumerated() {\n      let directions = Self.cornerToDirections[index]\n      var position = basePosition\n      for direction in directions {\n        position += direction.vector / 2\n      }\n      position.y += height\n      positions.append(position)\n    }\n    return positions\n  }\n\n  /// Calculate the height of each corner of a fluid.\n  private func calculateHeights() -> [Float] {\n    // If under a fluid block of the same type, all corners are 1\n    if neighbouringBlocks[.up]?.fluidId == block.fluidId {\n      return [1, 1, 1, 1]\n    }\n\n    // Begin with all corners as the height of the current fluid\n    let height = getFluidLevel(block)\n    var heights = [height, height, height, height]\n\n    // Loop through corners\n    for (index, directions) in Self.cornerToDirections.enumerated() {\n      if heights[index] == 1 {\n        continue\n      }\n\n      // Get positions of blocks surrounding the current corner\n      let zOffset = directions[0].intVector\n      let xOffset = directions[1].intVector\n      let positions: [BlockPosition] = [\n        position + xOffset,\n        position + zOffset,\n        position + xOffset + zOffset,\n      ]\n\n      // Get the highest fluid level around the corner\n      var maxHeight = height\n      for neighbourPosition in positions {\n        // If any of the surrounding blocks have the fluid above them, this corner should have a height of 1\n        let upperNeighbourBlock = world.getBlock(\n          at: neighbourPosition + Direction.up.intVector,\n          acquireLock: false\n        )\n\n        if block.fluidId == upperNeighbourBlock.fluidId {\n          maxHeight = 1\n          break\n        }\n\n        let neighbourBlock = world.getBlock(at: neighbourPosition, acquireLock: false)\n        if block.fluidId == neighbourBlock.fluidId {\n          let neighbourHeight = getFluidLevel(neighbourBlock)\n          if neighbourHeight > maxHeight {\n            maxHeight = neighbourHeight\n          }\n        }\n      }\n      heights[index] = maxHeight\n    }\n\n    return heights\n  }\n\n  /// Returns the height of a fluid from 0 to 1.\n  /// - Parameter block: Block containing the fluid.\n  /// - Returns: A height.\n  private func getFluidLevel(_ block: Block) -> Float {\n    if let height = block.fluidState?.height {\n      return 0.9 - Float(7 - height) / 8\n    } else {\n      return 0.8125\n    }\n  }\n\n  private func countLowestCorners(_ heights: [Float]) -> (count: Int, index: Int) {\n    var lowestCornerHeight: Float = 1\n    var lowestCornerIndex = 0\n    var lowestCornersCount = 0  // The number of corners at the lowest height\n    for (index, height) in heights.enumerated() {\n      if height < lowestCornerHeight {\n        lowestCornersCount = 1\n        lowestCornerHeight = height\n        lowestCornerIndex = index\n      } else if height == lowestCornerHeight {\n        lowestCornersCount += 1\n      }\n    }\n\n    let previousCornerIndex = (lowestCornerIndex - 1) & 0x3\n    if lowestCornersCount == 2 {\n      // If there are two lowest corners next to eachother, take the first (when going anticlockwise)\n      if heights[previousCornerIndex] == lowestCornerHeight {\n        lowestCornerIndex = previousCornerIndex\n      }\n    } else if lowestCornersCount == 3 {\n      // If there are three lowest corners, take the last (when going anticlockwise)\n      let nextCornerIndex = (lowestCornerIndex + 1) & 0x3\n      if heights[previousCornerIndex] == lowestCornerHeight\n        && heights[nextCornerIndex] == lowestCornerHeight\n      {\n        lowestCornerIndex = nextCornerIndex\n      } else if heights[nextCornerIndex] == lowestCornerHeight {\n        lowestCornerIndex = (lowestCornerIndex + 2) & 0x3\n      }\n    }\n\n    return (count: lowestCornersCount, index: lowestCornerIndex)\n  }\n\n  private func generateFlowingTopFaceUVs(lowestCornersCount: Int) -> [Vec2f] {\n    var uvs = Self.flowingUVs\n\n    // Rotate UVs 45 degrees if flowing diagonally\n    if lowestCornersCount == 1 || lowestCornersCount == 3 {\n      let uvRotation = MatrixUtil.rotationMatrix2d(\n        lowestCornersCount == 1 ? Float.pi / 4 : 3 * Float.pi / 4)\n      let center = Vec2f(repeating: 0.5)\n      for (index, uv) in uvs.enumerated() {\n        uvs[index] = (uv - center) * uvRotation + center\n      }\n    }\n\n    return uvs\n  }\n\n  private func addVertices<Positions: Collection>(\n    to geometry: inout Geometry<BlockVertex>,\n    at positions: Positions,\n    uvs: [Vec2f],\n    texture: UInt16,\n    tint: Vec3f\n  ) where Positions.Element == Vec3f {\n    var index = 0\n    for position in positions {\n      let vertex = BlockVertex(\n        x: position.x,\n        y: position.y,\n        z: position.z,\n        u: uvs[index].x,\n        v: uvs[index].y,\n        r: tint.x,\n        g: tint.y,\n        b: tint.z,\n        a: 1,\n        skyLightLevel: UInt8(lightLevel.sky),\n        blockLightLevel: UInt8(lightLevel.block),\n        textureIndex: texture,\n        isTransparent: false\n      )\n      geometry.vertices.append(vertex)\n      index += 1\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Mesh/Geometry.swift",
    "content": "import Foundation\n\n/// The simplest representation of renderable geometry data. Just vertices and vertex winding.\npublic struct Geometry<Vertex> {\n  /// Vertex data.\n  var vertices: [Vertex] = []\n  /// Vertex windings.\n  var indices: [UInt32] = []\n\n  public var isEmpty: Bool {\n    return vertices.isEmpty || indices.isEmpty\n  }\n\n  public init(vertices: [Vertex] = [], indices: [UInt32] = []) {\n    self.vertices = vertices\n    self.indices = indices\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Mesh/Mesh.swift",
    "content": "import Foundation\nimport Metal\n\npublic enum MeshError: LocalizedError {\n  case failedToCreateBuffer\n\n  public var errorDescription: String? {\n    switch self {\n      case .failedToCreateBuffer:\n        return \"Failed to create buffer.\"\n    }\n  }\n}\n\n/// Holds and renders geometry data.\npublic struct Mesh<Vertex, Uniforms> {\n  /// The vertices in the mesh.\n  public var vertices: [Vertex]\n  /// The vertex windings.\n  public var indices: [UInt32]\n  /// The mesh's uniforms.\n  public var uniforms: Uniforms\n\n  /// A GPU buffer containing the vertices.\n  public var vertexBuffer: MTLBuffer?\n  /// A GPU buffer containing the vertex windings.\n  public var indexBuffer: MTLBuffer?\n  /// A GPU buffer containing the model to world transformation matrix.\n  public var uniformsBuffer: MTLBuffer?\n\n  /// If `false`, ``vertexBuffer`` will be recreated next time ``render(into:with:commandQueue:)`` is called.\n  public var vertexBufferIsValid = false\n  /// If `false`, ``indexBuffer`` will be recreated next time ``render(into:with:commandQueue:)`` is called.\n  public var indexBufferIsValid = false\n  /// If `false`, ``uniformsBuffer`` will be recreated next time ``render(into:with:commandQueue:)`` is called.\n  public var uniformsBufferIsValid = false\n\n  /// `true` if the mesh contains no geometry.\n  public var isEmpty: Bool {\n    return vertices.isEmpty || indices.isEmpty\n  }\n\n  /// Create a new populated mesh.\n  public init(vertices: [Vertex], indices: [UInt32], uniforms: Uniforms) {\n    self.vertices = vertices\n    self.indices = indices\n    self.uniforms = uniforms\n  }\n\n  /// Create a new mesh with geometry.\n  public init(_ geometry: Geometry<Vertex>? = nil, uniforms: Uniforms) {\n    self.init(\n      vertices: geometry?.vertices ?? [],\n      indices: geometry?.indices ?? [],\n      uniforms: uniforms\n    )\n  }\n\n  /// Encodes the draw commands to render this mesh into a render encoder. Creates buffers if necessary.\n  /// - Parameters:\n  ///   - encoder: Render encode to encode commands into.\n  ///   - device: Device to use.\n  ///   - commandQueue: Command queue used to create buffers if not created already.\n  public mutating func render(\n    into encoder: MTLRenderCommandEncoder,\n    with device: MTLDevice,\n    commandQueue: MTLCommandQueue\n  ) throws {\n    if isEmpty {\n      return\n    }\n    // Get buffers. If the buffer is valid and not nil, it is used. If the buffer is invalid and not nil,\n    // it is repopulated with the new data (if big enough, otherwise a new buffer is created). If the\n    // buffer is nil, a new one is created.\n    let vertexBuffer =\n      try\n      ((vertexBufferIsValid ? vertexBuffer : nil)\n      ?? MetalUtil.createPrivateBuffer(\n        labelled: \"vertexBuffer\",\n        containing: vertices,\n        reusing: vertexBuffer,\n        device: device,\n        commandQueue: commandQueue\n      ))\n\n    let indexBuffer =\n      try\n      ((indexBufferIsValid ? indexBuffer : nil)\n      ?? MetalUtil.createPrivateBuffer(\n        labelled: \"indexBuffer\",\n        containing: indices,\n        reusing: indexBuffer,\n        device: device,\n        commandQueue: commandQueue\n      ))\n\n    let uniformsBuffer =\n      try\n      ((uniformsBufferIsValid ? uniformsBuffer : nil)\n      ?? MetalUtil.createPrivateBuffer(\n        labelled: \"uniformsBuffer\",\n        containing: [uniforms],\n        reusing: uniformsBuffer,\n        device: device,\n        commandQueue: commandQueue\n      ))\n\n    // Update cached buffers. Unnecessary assignments won't affect performance because `MTLBuffer`s\n    // are just descriptors, not the actual data\n    self.vertexBuffer = vertexBuffer\n    self.indexBuffer = indexBuffer\n    self.uniformsBuffer = uniformsBuffer\n\n    // Buffers are now all valid\n    vertexBufferIsValid = true\n    indexBufferIsValid = true\n    uniformsBufferIsValid = true\n\n    // Encode draw call\n    encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)\n    encoder.setVertexBuffer(uniformsBuffer, offset: 0, index: 2)\n\n    encoder.drawIndexedPrimitives(\n      type: .triangle,\n      indexCount: indices.count,\n      indexType: .uint32,\n      indexBuffer: indexBuffer,\n      indexBufferOffset: 0\n    )\n  }\n\n  /// Force buffers to be recreated on next call to ``render(into:for:commandQueue:)``.\n  ///\n  /// The underlying private buffer may be reused, but it will be repopulated with the new data.\n  ///\n  /// - Parameters:\n  ///   - keepVertexBuffer: If `true`, the vertex buffer is not invalidated.\n  ///   - keepIndexBuffer: If `true`, the index buffer is not invalidated.\n  ///   - keepUniformsBuffer: If `true`, the uniforms buffer is not invalidated.\n  public mutating func invalidateBuffers(\n    keepVertexBuffer: Bool = false, keepIndexBuffer: Bool = false, keepUniformsBuffer: Bool = false\n  ) {\n    vertexBufferIsValid = keepVertexBuffer\n    indexBufferIsValid = keepIndexBuffer\n    uniformsBufferIsValid = keepUniformsBuffer\n  }\n\n  /// Clears the mesh's geometry and invalidates its buffers.\n  public mutating func clearGeometry() {\n    vertices = []\n    indices = []\n    invalidateBuffers(keepUniformsBuffer: true)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Mesh/SortableMesh.swift",
    "content": "import FirebladeMath\nimport Foundation\nimport MetalKit\n\n/// A mesh that can be sorted after the initial preparation.\n///\n/// Use for translucent meshes. Only really designed for grid-aligned objects.\npublic struct SortableMesh {\n  /// Distinct mesh elements that should be rendered in order of distance.\n  public var elements: [SortableMeshElement] = []\n\n  /// The mesh may be empty even if this is false if all of the elements contain no geometry.\n  public var isEmpty: Bool {\n    return elements.isEmpty\n  }\n\n  /// The mesh that is updated each time this mesh is sorted.\n  public var underlyingMesh: Mesh<BlockVertex, ChunkUniforms>\n\n  /// Creates a new sortable mesh.\n  /// - Parameters:\n  ///   - elements: Distinct mesh elements that should be rendered in order of distance.\n  ///   - uniforms: The mesh's uniforms.\n  public init(_ elements: [SortableMeshElement] = [], uniforms: ChunkUniforms) {\n    self.elements = elements\n    underlyingMesh = Mesh<BlockVertex, ChunkUniforms>(uniforms: uniforms)\n  }\n\n  /// Removes all elements from the mesh.\n  public mutating func clear() {\n    elements = []\n    underlyingMesh.clearGeometry()\n    underlyingMesh.invalidateBuffers(keepUniformsBuffer: true)\n  }\n\n  /// Add an element to the mesh. Updates the element's id.\n  public mutating func add(_ element: SortableMeshElement) {\n    var element = element\n    element.id = elements.count\n    elements.append(element)\n  }\n\n  /// Encode the render commands for this mesh.\n  /// - Parameters:\n  ///   - position: The position to sort from.\n  ///   - sort: If `false`, the mesh will not be sorted and the previous one will be rendered (unless no previous mesh has been rendered).\n  ///   - encoder: The encoder to encode the render commands into.\n  ///   - device: The device to use.\n  ///   - commandQueue: The command queue to use when creating buffers.\n  public mutating func render(\n    viewedFrom position: Vec3f,\n    sort: Bool,\n    encoder: MTLRenderCommandEncoder,\n    device: MTLDevice,\n    commandQueue: MTLCommandQueue\n  ) throws {\n    if underlyingMesh.isEmpty && elements.isEmpty {\n      return\n    }\n\n    if sort || underlyingMesh.isEmpty {\n      // TODO: reuse vertices from mesh and just recreate winding\n      // Sort elements by distance in descending order.\n      let newElements = elements.sorted(by: {\n        let squaredDistance1 = distance_squared(position, $0.centerPosition)\n        let squaredDistance2 = distance_squared(position, $1.centerPosition)\n        return squaredDistance1 > squaredDistance2\n      })\n\n      if underlyingMesh.isEmpty || newElements != elements {\n        elements = newElements\n\n        underlyingMesh.clearGeometry()\n        for element in elements {\n          let windingOffset = UInt32(underlyingMesh.vertices.count)\n          underlyingMesh.vertices.append(contentsOf: element.vertices)\n          for index in element.indices {\n            underlyingMesh.indices.append(index + windingOffset)\n          }\n        }\n      }\n    }\n\n    // Could be reached if all elements contain no geometry\n    if underlyingMesh.isEmpty {\n      return\n    }\n\n    try underlyingMesh.render(into: encoder, with: device, commandQueue: commandQueue)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Mesh/SortableMeshElement.swift",
    "content": "import FirebladeMath\nimport Foundation\n\n/// An element of a ``SortableMesh``.\npublic struct SortableMeshElement {\n  /// The element's unique id within its mesh.\n  public var id: Int\n  /// The vertex data.\n  public var vertices: [BlockVertex] = []\n  /// The vertex windings.\n  public var indices: [UInt32] = []\n  /// The position of the center of the mesh.\n  public var centerPosition: Vec3f\n\n  /// Whether the element contains any geometry or not.\n  public var isEmpty: Bool {\n    return indices.isEmpty\n  }\n\n  /// Create a new element.\n  public init(\n    id: Int = 0,\n    vertices: [BlockVertex] = [],\n    indices: [UInt32] = [],\n    centerPosition: Vec3f = [0, 0, 0]\n  ) {\n    self.id = id\n    self.vertices = vertices\n    self.indices = indices\n    self.centerPosition = centerPosition\n  }\n\n  /// Create a new element with geometry.\n  /// - Parameters:\n  ///   - geometry: The element's geometry\n  ///   - centerPosition: The position of the center of the element.\n  public init(id: Int = 0, geometry: Geometry<BlockVertex>, centerPosition: Vec3f) {\n    self.init(\n      id: id,\n      vertices: geometry.vertices,\n      indices: geometry.indices,\n      centerPosition: centerPosition\n    )\n  }\n}\n\nextension SortableMeshElement: Equatable {\n  public static func == (lhs: SortableMeshElement, rhs: SortableMeshElement) -> Bool {\n    return lhs.id == rhs.id\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/RenderCoordinator.swift",
    "content": "import Foundation\nimport FirebladeMath\nimport MetalKit\nimport DeltaCore\n\npublic enum RendererError: LocalizedError {\n  case getMetalDevice\n  case makeRenderCommandQueue\n  case camera(Error)\n  case skyBoxRenderer(Error)\n  case worldRenderer(Error)\n  case guiRenderer(Error)\n  case screenRenderer(Error)\n  case depthState(Error)\n  case unknown(Error)\n  \n  public var errorDescription: String? {\n    switch self {\n      case .getMetalDevice:\n        return \"Failed to get metal device\"\n      case .makeRenderCommandQueue:\n        return \"Failed to make render command queue\"\n      case .camera(let cameraError):\n        return \"Failed to create camera: \\(cameraError.labeledLocalizedDescription)\"\n      case .skyBoxRenderer(let skyBoxError):\n        return \"Failed to create sky box renderer: \\(skyBoxError.labeledLocalizedDescription)\"\n      case .worldRenderer(let worldError):\n        return \"Failed to create world renderer: \\(worldError.labeledLocalizedDescription)\"\n      case .guiRenderer(let guiError):\n        return \"Failed to create GUI renderer: \\(guiError.labeledLocalizedDescription)\"\n      case .screenRenderer(let screenError):\n        return \"Failed to create Screen renderer: \\(screenError.labeledLocalizedDescription)\"\n      case .depthState(let depthError):\n        return \"Failed to create depth state: \\(depthError.labeledLocalizedDescription)\"\n      case .unknown(let error):\n        return \"Failed with an unknown error \\(error.labeledLocalizedDescription)\"\n    }\n  }\n}\n\nextension Error {\n  public var labeledLocalizedDescription: String? {\n    \"\\(String(describing: self)) - \\(self.localizedDescription)\"\n  }\n}\n\n/// Coordinates the rendering of the game (e.g. blocks and entities).\npublic final class RenderCoordinator: NSObject, MTKViewDelegate {\n  // MARK: Public properties\n\n  /// Statistics that measure the renderer's current performance.\n  public var statistics: RenderStatistics\n\n  // MARK: Private properties\n\n  /// The client to render.\n  private var client: Client\n\n  /// The renderer for the world's sky box.\n  private var skyBoxRenderer: SkyBoxRenderer\n\n  /// The renderer for the current world. Only renders blocks.\n  private var worldRenderer: WorldRenderer\n\n  /// The renderer for rendering the GUI.\n  private var guiRenderer: GUIRenderer\n\n  /// The renderer for rendering on screen. Can perform upscaling.\n  private var screenRenderer: ScreenRenderer\n\n  /// The camera that is rendered from.\n  private var camera: Camera\n\n  /// The device used to render.\n  private var device: MTLDevice\n\n  /// The depth stencil state. It's the same for every renderer so it's just made once here.\n  private var depthState: MTLDepthStencilState\n\n  /// The command queue.\n  private var commandQueue: MTLCommandQueue\n\n  /// The time that the cpu started encoding the previous frame.\n  private var previousFrameStartTime: Double = 0\n\n  /// The current frame capture state (`nil` if no capture is in progress).\n  private var captureState: CaptureState?\n\n  /// The renderer profiler.\n  private var profiler = Profiler<RenderingMeasurement>(\"Rendering\")\n\n  /// The number of frames rendered so far.\n  private var frameCount = 0\n\n  /// The longest a frame has taken to encode so far.\n  private var longestFrame: Double = 0\n\n  // MARK: Init\n\n  /// Creates a render coordinator.\n  /// - Parameter client: The client to render for.\n  public required init(_ client: Client) throws {\n    guard let device = MTLCreateSystemDefaultDevice() else {\n      throw RendererError.getMetalDevice\n    }\n\n    guard let commandQueue = device.makeCommandQueue() else {\n      throw RendererError.makeRenderCommandQueue\n    }\n\n    self.client = client\n    self.device = device\n    self.commandQueue = commandQueue\n\n    // Setup camera\n    do {\n      camera = try Camera(device)\n    } catch {\n      throw RendererError.camera(error)\n    }\n\n    do {\n      skyBoxRenderer = try SkyBoxRenderer(\n        client: client,\n        device: device,\n        commandQueue: commandQueue\n      )\n    } catch {\n      throw RendererError.skyBoxRenderer(error)\n    }\n\n    do {\n      worldRenderer = try WorldRenderer(\n        client: client,\n        device: device,\n        commandQueue: commandQueue,\n        profiler: profiler\n      )\n    } catch {\n      throw RendererError.worldRenderer(error)\n    }\n\n    do {\n      guiRenderer = try GUIRenderer(\n        client: client,\n        device: device,\n        commandQueue: commandQueue,\n        profiler: profiler\n      )\n    } catch {\n      throw RendererError.guiRenderer(error)\n    }\n\n    do {\n      screenRenderer = try ScreenRenderer(\n        client: client,\n        device: device,\n        profiler: profiler\n      )\n    } catch {\n      throw RendererError.screenRenderer(error)\n    }\n\n    // Create depth stencil state\n    do {\n      depthState = try MetalUtil.createDepthState(device: device)\n    } catch {\n      throw RendererError.depthState(error)\n    }\n\n    statistics = RenderStatistics(gpuCountersEnabled: false)\n\n    super.init()\n  }\n\n  // MARK: Render\n\n  public func draw(in view: MTKView) {\n    let time = CFAbsoluteTimeGetCurrent()\n    let frameTime = time - previousFrameStartTime\n    previousFrameStartTime = time\n\n    profiler.push(.updateRenderTarget)\n    do {\n      try screenRenderer.updateRenderTarget(for: view)\n    } catch {\n      log.error(\"Failed to update render target: \\(error)\")\n      client.eventBus.dispatch(ErrorEvent(error: error, message: \"Failed to update render target\"))\n      return\n    }\n\n    profiler.pop()\n\n    // Fetch offscreen render pass descriptor from ScreenRenderer\n    let renderPassDescriptor = screenRenderer.renderDescriptor\n\n\n    // The CPU start time if vsync was disabled\n    let cpuStartTime = CFAbsoluteTimeGetCurrent()\n\n    profiler.push(.updateCamera)\n    // Create world to clip uniforms buffer\n    let uniformsBuffer = getCameraUniforms(view)\n    profiler.pop()\n\n    // When the render distance is above 2, move the fog 1 chunk closer to conceal\n    // more of the world edge.\n    let renderDistance = max(client.configuration.render.renderDistance - 1, 2)\n\n    let fogColor = client.game.world.getFogColor(\n      forViewerWithRay: camera.ray,\n      withRenderDistance: renderDistance\n    )\n\n    renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(\n      red: Double(fogColor.x),\n      green: Double(fogColor.y),\n      blue: Double(fogColor.z),\n      alpha: 1\n    )\n\n    profiler.push(.createRenderCommandEncoder)\n    // Create command buffer\n    guard let commandBuffer = commandQueue.makeCommandBuffer() else {\n      log.error(\"Failed to create command buffer\")\n      client.eventBus.dispatch(ErrorEvent(\n        error: RenderError.failedToCreateCommandBuffer,\n        message: \"RenderCoordinator failed to create command buffer\"\n      ))\n      return\n    }\n\n    // Create render encoder\n    guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else {\n      log.error(\"Failed to create render encoder\")\n      client.eventBus.dispatch(ErrorEvent(\n        error: RenderError.failedToCreateRenderEncoder,\n        message: \"RenderCoordinator failed to create render encoder\"\n      ))\n      return\n    }\n    profiler.pop()\n\n    profiler.push(.skyBox)\n    do {\n      try skyBoxRenderer.render(\n        view: view,\n        encoder: renderEncoder,\n        commandBuffer: commandBuffer,\n        worldToClipUniformsBuffer: uniformsBuffer,\n        camera: camera\n      )\n    } catch {\n      log.error(\"Failed to render sky box: \\(error)\")\n      client.eventBus.dispatch(ErrorEvent(error: error, message: \"Failed to render sky box\"))\n      return\n    }\n    profiler.pop()\n\n    // Configure the render encoder\n    renderEncoder.setDepthStencilState(depthState)\n    renderEncoder.setFrontFacing(.counterClockwise)\n    renderEncoder.setVertexBuffer(uniformsBuffer, offset: 0, index: 1)\n\n    switch client.configuration.render.mode {\n      case .normal:\n        renderEncoder.setCullMode(.front)\n      case .wireframe:\n        renderEncoder.setCullMode(.none)\n        renderEncoder.setTriangleFillMode(.lines)\n    }\n\n    profiler.push(.world)\n    do {\n      try worldRenderer.render(\n        view: view,\n        encoder: renderEncoder,\n        commandBuffer: commandBuffer,\n        worldToClipUniformsBuffer: uniformsBuffer,\n        camera: camera\n      )\n    } catch {\n      log.error(\"Failed to render world: \\(error)\")\n      client.eventBus.dispatch(ErrorEvent(error: error, message: \"Failed to render world\"))\n      return\n    }\n    profiler.pop()\n\n    profiler.push(.gui)\n    do {\n      try guiRenderer.render(\n        view: view,\n        encoder: renderEncoder,\n        commandBuffer: commandBuffer,\n        worldToClipUniformsBuffer: uniformsBuffer,\n        camera: camera\n      )\n    } catch {\n      log.error(\"Failed to render GUI: \\(error)\")\n      client.eventBus.dispatch(ErrorEvent(error: error, message: \"Failed to render GUI\"))\n      return\n    }\n    profiler.pop()\n\n    profiler.push(.commitToGPU)\n    // Finish measurements for render statistics\n    let cpuFinishTime = CFAbsoluteTimeGetCurrent()\n\n    // Finish encoding the frame\n    guard let drawable = view.currentDrawable else {\n      log.warning(\"Failed to get current drawable\")\n      return\n    }\n\n    renderEncoder.endEncoding()\n\n    profiler.push(.waitForRenderPassDescriptor)\n    // Get current render pass descriptor\n    guard let renderPassDescriptor = view.currentRenderPassDescriptor else {\n      log.error(\"Failed to get the current render pass descriptor\")\n      client.eventBus.dispatch(ErrorEvent(\n        error: RenderError.failedToGetCurrentRenderPassDescriptor,\n        message: \"RenderCoordinator failed to get the current render pass descriptor\"\n      ))\n      return\n    }\n    profiler.pop()\n\n    guard let quadRenderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else {\n      log.error(\"Failed to create quad render encoder\")\n      client.eventBus.dispatch(ErrorEvent(\n        error: RenderError.failedToCreateRenderEncoder,\n        message: \"RenderCoordinator failed to create render encoder\"\n      ))\n      return\n    }\n\n    profiler.push(.renderOnScreen)\n    do {\n      try screenRenderer.render(\n        view: view,\n        encoder: quadRenderEncoder,\n        commandBuffer: commandBuffer,\n        worldToClipUniformsBuffer: uniformsBuffer,\n        camera: camera\n      )\n    } catch {\n      log.error(\"Failed to perform on-screen rendering: \\(error)\")\n      client.eventBus.dispatch(ErrorEvent(error: error, message: \"Failed to perform on-screen rendering pass.\"))\n      return\n    }\n    profiler.pop()\n\n    quadRenderEncoder.endEncoding()\n    commandBuffer.present(drawable)\n\n    let cpuElapsed = cpuFinishTime - cpuStartTime\n    statistics.addMeasurement(\n      frameTime: frameTime,\n      cpuTime: cpuElapsed,\n      gpuTime: nil\n    )\n\n    // Update statistics in gui\n    client.game.updateRenderStatistics(to: statistics)\n\n    commandBuffer.commit()\n    profiler.pop()\n\n    // Update frame capture state and stop current capture if necessary\n    captureState?.framesRemaining -= 1\n    if let captureState = captureState, captureState.framesRemaining == 0 {\n      let captureManager = MTLCaptureManager.shared()\n      captureManager.stopCapture()\n      client.eventBus.dispatch(FinishFrameCaptureEvent(file: captureState.outputFile))\n\n      self.captureState = nil\n    }\n\n    frameCount += 1\n    profiler.endTrial()\n\n    if frameCount % 60 == 0 {\n      longestFrame = cpuElapsed\n      // profiler.printSummary()\n      profiler.clear()\n    }\n  }\n\n  /// Captures the specified number of frames into a GPU trace file.\n  public func captureFrames(count: Int, to file: URL) throws {\n    let captureManager = MTLCaptureManager.shared()\n\n    guard captureManager.supportsDestination(.gpuTraceDocument) else {\n      throw RenderError.gpuTraceNotSupported\n    }\n\n    let captureDescriptor = MTLCaptureDescriptor()\n    captureDescriptor.captureObject = device\n    captureDescriptor.destination = .gpuTraceDocument\n    captureDescriptor.outputURL = file\n\n    do {\n      try captureManager.startCapture(with: captureDescriptor)\n    } catch {\n      throw RenderError.failedToStartCapture(error)\n    }\n\n    captureState = CaptureState(framesRemaining: count, outputFile: file)\n  }\n\n  // MARK: Helper\n\n  public func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { }\n\n  /// Gets the camera uniforms for the current frame.\n  /// - Parameter view: The view that is being rendered to. Used to get aspect ratio.\n  /// - Returns: A buffer containing the uniforms.\n  private func getCameraUniforms(_ view: MTKView) -> MTLBuffer {\n    let aspect = Float(view.drawableSize.width / view.drawableSize.height)\n    camera.setAspect(aspect)\n\n    let effectiveFovY = client.configuration.render.fovY * client.game.fovMultiplier()\n    camera.setFovY(MathUtil.radians(from: effectiveFovY))\n\n    client.game.accessPlayer { player in\n      var eyePosition = Vec3f(player.position.smoothVector)\n      eyePosition.y += 1.625 // TODO: don't hardcode this, use the player's eye height\n\n      var cameraPosition = Vec3f(repeating: 0)\n\n      var pitch = player.rotation.smoothPitch\n      var yaw = player.rotation.smoothYaw\n\n      switch player.camera.perspective {\n        case .thirdPersonRear:\n          cameraPosition.z += 3\n          cameraPosition = (Vec4f(cameraPosition, 1) * MatrixUtil.rotationMatrix(x: pitch) * MatrixUtil.rotationMatrix(y: Float.pi + yaw)).xyz\n          cameraPosition += eyePosition\n        case .thirdPersonFront:\n          pitch = -pitch\n          yaw += Float.pi\n\n          cameraPosition.z += 3\n          cameraPosition = (Vec4f(cameraPosition, 1) * MatrixUtil.rotationMatrix(x: pitch) * MatrixUtil.rotationMatrix(y: Float.pi + yaw)).xyz\n          cameraPosition += eyePosition\n        case .firstPerson:\n          cameraPosition = eyePosition\n      }\n\n      camera.setPosition(cameraPosition)\n      camera.setRotation(xRot: pitch, yRot: yaw)\n    }\n\n    camera.cacheFrustum()\n    return camera.getUniformsBuffer()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/RenderError.swift",
    "content": "import DeltaCore\nimport Foundation\n\npublic enum RenderError: LocalizedError {\n  /// Failed to create a metal array texture.\n  case failedToCreateArrayTexture\n  /// Failed to create a private metal array texture.\n  case failedToCreatePrivateArrayTexture\n  /// Failed to get the Delta Core bundle.\n  case failedToGetBundle\n  /// Failed to find default.metallib in the bundle.\n  case failedToLocateMetallib\n  /// Failed to create a metal library from `default.metallib`.\n  case failedToCreateMetallib(Error)\n  /// Failed to load the shaders from the metallib.\n  case failedToLoadShaders\n  /// Failed to create the buffers that hold the world uniforms.\n  case failedtoCreateWorldUniformBuffers\n  /// Failed to create the render pipeline state for the world renderer.\n  case failedToCreateWorldRenderPipelineState(Error)\n  /// Failed to create the depth stencil state for the world renderer.\n  case failedToCreateWorldDepthStencilState\n  /// Failed to create the render pipeline state for a renderer.\n  case failedToCreateRenderPipelineState(Error, label: String)\n  /// Failed to create the depth stencil state for the entity renderer.\n  case failedToCreateEntityDepthStencilState\n  /// Failed to create the block texture array.\n  case failedToCreateBlockTextureArray(Error)\n  /// Failed to create the render encoder.\n  case failedToCreateRenderEncoder\n  /// Failed to create the command buffer.\n  case failedToCreateCommandBuffer\n  /// Failed to create geometry buffers for the entity renderer.\n  case failedToCreateEntityGeometryBuffers\n  /// Failed to create a metal buffer.\n  case failedToCreateBuffer(label: String?)\n  /// Failed to get the current render pass descriptor for a frame.\n  case failedToGetCurrentRenderPassDescriptor\n  /// Failed to create an event for the gpu timer.\n  case failedToCreateTimerEvent\n  /// Failed to get the specified counter set (it is likely not supported by the selected device).\n  case failedToGetCounterSet(_ rawValue: String)\n  /// Failed to create the buffer used for sampling GPU counters.\n  case failedToMakeCounterSampleBuffer(Error)\n  /// Failed to sample the GPU counters used to calculate FPS.\n  case failedToSampleCounters\n  /// The current device does not support capturing a gpu trace and outputting to a file.\n  case gpuTraceNotSupported\n  /// Failed to start GPU frame capture.\n  case failedToStartCapture(Error)\n  /// Failed to update render target textures\n  case failedToUpdateRenderTargetSize\n  /// Failed to create blit command encoder.\n  case failedToCreateBlitCommandEncoder\n  /// A required texture is missing.\n  case missingTexture(Identifier)\n\n  public var errorDescription: String? {\n    switch self {\n      case .failedToUpdateRenderTargetSize:\n        return \"Failed to update render target texture size.\"\n      case .failedToCreateArrayTexture:\n        return \"Failed to create an array texture.\"\n      case .failedToCreatePrivateArrayTexture:\n        return \"Failed to create a private array texture.\"\n      case .failedToGetBundle:\n        return \"Failed to get the Delta Core bundle..\"\n      case .failedToLocateMetallib:\n        return \"Failed to find default.metallib in the bundle.\"\n      case .failedToCreateMetallib(let error):\n        return \"\"\"\n          Failed to create a metal library from `default.metallib`.\n          Reason: \\(error.localizedDescription)\n          \"\"\"\n      case .failedToLoadShaders:\n        return \"Failed to load the shaders from the metallib.\"\n      case .failedtoCreateWorldUniformBuffers:\n        return \"Failed to create the buffers that hold the world uniforms.\"\n      case .failedToCreateWorldRenderPipelineState(let error):\n        return \"\"\"\n          Failed to create the render pipeline state for the world renderer.\n          Reason: \\(error.localizedDescription)\n          \"\"\"\n      case .failedToCreateWorldDepthStencilState:\n        return \"Failed to create the depth stencil state for the entity renderer.\"\n      case .failedToCreateRenderPipelineState(let error, let label):\n        return \"\"\"\n          Failed to create render pipeline state.\n          Reason: \\(error.localizedDescription)\n          Label: \\(label)\n          \"\"\"\n      case .failedToCreateEntityDepthStencilState:\n        return \" Failed to create the depth stencil state for the entity renderer.\"\n      case .failedToCreateBlockTextureArray(let error):\n        return \"\"\"\n          Failed to create the block texture array.\n          Reason: \\(error.localizedDescription)\n          \"\"\"\n      case .failedToCreateRenderEncoder:\n        return \"Failed to create the render encoder.\"\n      case .failedToCreateCommandBuffer:\n        return \"Failed to create the command buffer.\"\n      case .failedToCreateEntityGeometryBuffers:\n        return \"Failed to create geometry buffers for the entity renderer.\"\n      case .failedToCreateBuffer(let label):\n        return \"Failed to create a buffer with label: \\(label ?? \"no label provided\").\"\n      case .failedToGetCurrentRenderPassDescriptor:\n        return \"Failed to get the current render pass descriptor for a frame.\"\n      case .failedToCreateTimerEvent:\n        return\n          \"Failed to get the specified counter set (it is likely not supported by the selected device).\"\n      case .failedToGetCounterSet(let rawValue):\n        return \"\"\"\n          Failed to get the specified counter set (it is likely not supported by the selected device).\n          Raw value: \\(rawValue)\n          \"\"\"\n      case .failedToMakeCounterSampleBuffer(let error):\n        return \"\"\"\n          Failed to create the buffer used for sampling GPU counters.\n          Reason: \\(error.localizedDescription)\n          \"\"\"\n      case .failedToSampleCounters:\n        return \"Failed to sample the GPU counters used to calculate FPS.\"\n      case .gpuTraceNotSupported:\n        return \"The current device does not support capturing a gpu trace and outputting to a file.\"\n      case .failedToStartCapture(let error):\n        return \"\"\"\n          Failed to start GPU frame capture.\n          Reason: \\(error.localizedDescription)\n          \"\"\"\n      case .failedToCreateBlitCommandEncoder:\n        return \"Failed to create blit command encoder.\"\n      case .missingTexture(let identifier):\n        return \"The required '\\(identifier)' texture is missing.\"\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Renderer.swift",
    "content": "import Metal\nimport MetalKit\n\n/// A protocol that renderers should conform to.\npublic protocol Renderer {\n  /// Renders a frame.\n  ///\n  /// Should not call `renderEncoder.endEncoding()` or `commandBuffer.commit()`.\n  /// A render coordinator should will manage the encoder and command buffer.\n  mutating func render(\n    view: MTKView,\n    encoder: MTLRenderCommandEncoder,\n    commandBuffer: MTLCommandBuffer,\n    worldToClipUniformsBuffer: MTLBuffer,\n    camera: Camera\n  ) throws\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/RenderingMeasurement.swift",
    "content": "public enum RenderingMeasurement: String, Hashable {\n  case waitForRenderPassDescriptor\n  case updateCamera\n  case createRenderCommandEncoder\n\n  case skyBox\n\n  case world\n  case updateWorldMesh\n  case updateAnimatedTextures\n  case updateLightMap\n  case updateFogUniforms\n  case encodeOpaque\n  case encodeBlockOutline\n  case encodeTranslucent\n\n  case entities\n  case getEntities\n  case createRegularEntityMeshes\n  case createBlockEntityMeshes\n  case encodeEntities\n\n  case gui\n  case updateUniforms\n  case updateContent\n  case createMeshes\n\n  case renderOnScreen\n  case updateRenderTarget\n  case encodeUpscale\n\n  case encode\n\n  case commitToGPU\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Resources/Font+Metal.swift",
    "content": "import MetalKit\nimport DeltaCore\n\nextension Font {\n  /// Creates an array texture containing the font's atlases.\n  /// - Parameters:\n  ///   - device: The device to create the texture with.\n  ///   - commandQueue: The command queue to use for blit operations.\n  /// - Returns: An array texture containing the textures in ``textures``.\n  public func createArrayTexture(\n    device: MTLDevice,\n    commandQueue: MTLCommandQueue\n  ) throws -> MTLTexture {\n    guard !textures.isEmpty else {\n      throw FontError.emptyFont\n    }\n\n    // Calculate minimum dimensions to fit all textures.\n    guard let width = textures.map({ texture in\n      return texture.width\n    }).max() else {\n      throw FontError.failedToGetArrayTextureWidth\n    }\n\n    guard let height = textures.map({ texture in\n      return texture.height\n    }).max() else {\n      throw FontError.failedToGetArrayTextureHeight\n    }\n\n    // Create texture descriptor\n    let textureDescriptor = MTLTextureDescriptor()\n    textureDescriptor.width = width\n    textureDescriptor.height = height\n    textureDescriptor.arrayLength = textures.count\n    textureDescriptor.pixelFormat = .bgra8Unorm\n    textureDescriptor.textureType = .type2DArray\n    #if os(macOS)\n    textureDescriptor.storageMode = .managed\n    #elseif os(iOS) || os(tvOS)\n    textureDescriptor.storageMode = .shared\n    #else\n    #error(\"Unsupported platform, can't determine storageMode for texture\")\n    #endif\n\n    guard let arrayTexture = device.makeTexture(descriptor: textureDescriptor) else {\n      throw FontError.failedToCreateArrayTexture\n    }\n\n    // Populate texture\n    let bytesPerPixel = 4\n    for (index, texture) in textures.enumerated() {\n      let bytesPerRow = bytesPerPixel * texture.width\n      let byteCount = bytesPerRow * texture.height\n\n      texture.image.withUnsafeBytes { pointer in\n        arrayTexture.replace(\n          region: MTLRegion(\n            origin: MTLOrigin(x: 0, y: 0, z: 0),\n            size: MTLSize(\n              width: texture.width,\n              height: texture.height,\n              depth: 1\n            )\n          ),\n          mipmapLevel: 0,\n          slice: index,\n          withBytes: pointer.baseAddress!,\n          bytesPerRow: bytesPerRow,\n          bytesPerImage: byteCount\n        )\n      }\n    }\n\n    guard\n      let commandBuffer = commandQueue.makeCommandBuffer(),\n      let blitCommandEncoder = commandBuffer.makeBlitCommandEncoder()\n    else {\n      throw RenderError.failedToCreateBlitCommandEncoder\n    }\n\n    textureDescriptor.storageMode = .private\n    guard let privateArrayTexture = device.makeTexture(descriptor: textureDescriptor) else {\n      throw RenderError.failedToCreatePrivateArrayTexture\n    }\n\n    blitCommandEncoder.copy(from: arrayTexture, to: privateArrayTexture)\n    blitCommandEncoder.endEncoding()\n    commandBuffer.commit()\n\n    return privateArrayTexture\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Resources/MetalTexturePalette.swift",
    "content": "import Metal\nimport DeltaCore\n\n/// An error thrown by ``MetalTexturePalette``.\npublic enum MetalTexturePaletteError: LocalizedError {\n  case failedToCreateCommandBuffer\n  case failedToCreateBlitCommandEncoder\n\n  public var errorDescription: String? {\n    switch self {\n      case .failedToCreateCommandBuffer:\n        return \"Failed to create command buffer for mipmap generation.\"\n      case .failedToCreateBlitCommandEncoder:\n        return \"Failed to create blit command encoder for mipmap generation.\"\n    }\n  }\n}\n\n/// A Metal-specific wrapper for ``TexturePalette``. Handles animation-related buffers, and the\n/// palette's static array texture.\npublic struct MetalTexturePalette {\n  /// The underlying texture palette.\n  public var palette: TexturePalette\n\n  /// The texture palette's animation state.\n  public var animationState: TexturePalette.AnimationState\n\n  /// The underlying array texture.\n  public var arrayTexture: MTLTexture\n\n  /// The buffer storing texture states.\n  public var textureStatesBuffer: MTLBuffer?\n\n  /// The small buffer containing only the current time (we cannot just use the GPU's concept of\n  /// time instead because the GPU does not use the same timebase as the CPU).\n  public var timeBuffer: MTLBuffer?\n\n  /// The global device to use for creating textures etc.\n  public let device: MTLDevice\n\n  /// The global command queue to use for creating textures etc.\n  public let commandQueue: MTLCommandQueue\n\n  /// For each texture there is a node that points to the previous animation frame and the next\n  /// animation frame (in the array texture).\n  public var textureStates: [TextureState]\n\n  /// The time at which the palette was created measured in `CFAbsoluteTime`.\n  public var creationTime: Double\n\n  /// Maps a texture index to the index of its first frame (in the palette's array texture).\n  public var textureIndexToFirstFrameIndex: [Int]\n\n  /// The animation state of a texture in a format that is useful on the GPU.\n  public struct TextureState {\n    /// The array texture index of the texture's current animation frame.\n    public var currentFrameIndex: UInt16\n    /// The array texture index of the texture's next animation frame. `65535` indicates that the value\n    /// is not present. Usually this would be represented using `Optional`, however that does not\n    /// lend itself well to being copied to and interpreted by the GPU.\n    public var nextFrameIndex: UInt16\n    /// The time at which the previous update occured. Used for interpolation. Measured in ticks.\n    public var previousUpdate: UInt32\n    /// The time at which the next frame index will become the current frame index. Used for\n    /// interpolation. Measured in ticks.\n    public var nextUpdate: UInt32\n  }\n\n  /// Creates a new Metal wrapper for the given texture palette.\n  public init(\n    palette: TexturePalette,\n    device: MTLDevice,\n    commandQueue: MTLCommandQueue\n  ) throws {\n    self.palette = palette\n    self.device = device\n    self.commandQueue = commandQueue\n\n    arrayTexture = try Self.createArrayTexture(\n      for: palette,\n      device: device,\n      commandQueue: commandQueue\n    )\n    animationState = palette.defaultAnimationState\n\n    var frameIndex = 0\n    textureIndexToFirstFrameIndex = []\n    textureStates = []\n    creationTime = CFAbsoluteTimeGetCurrent()\n    for texture in palette.textures {\n      textureIndexToFirstFrameIndex.append(frameIndex)\n      let frameTicks = texture.animation?.frames.first?.time ?? 0\n      textureStates.append(TextureState(\n        currentFrameIndex: UInt16(frameIndex),\n        nextFrameIndex: 65535,\n        previousUpdate: UInt32(0),\n        nextUpdate: UInt32(frameTicks)\n      ))\n      frameIndex += texture.frameCount\n    }\n\n    textureStatesBuffer = device.makeBuffer(\n      bytes: &textureStates,\n      length: MemoryLayout<TextureState>.stride * textureStates.count\n    )\n    textureStatesBuffer?.label = \"MetalTexturePalette.textureStatesBuffer\"\n\n    var tick: Float = 0\n    timeBuffer = device.makeBuffer(bytes: &tick, length: MemoryLayout<Float>.stride)\n    timeBuffer?.label = \"MetalTexturePalette.timeBuffer\"\n  }\n\n  /// Gets the index of the texture referred to by the given identifier if any.\n  public func textureIndex(for identifier: Identifier) -> Int? {\n    palette.textureIndex(for: identifier)\n  }\n\n  /// Returns a metal array texture containing all textures (including each individual animation\n  /// frame of each texture if `includeAnimations` is set to `true`).\n  public static func createArrayTexture(\n    for palette: TexturePalette,\n    device: MTLDevice,\n    commandQueue: MTLCommandQueue,\n    includeAnimations: Bool = true\n  ) throws -> MTLTexture {\n    let count: Int\n    if includeAnimations {\n      count = palette.textures.map{ texture in\n        return texture.frameCount\n      }.reduce(0, +)\n    } else {\n      count = palette.textures.count\n    }\n\n    let width = palette.width\n    let height = palette.height\n\n    let textureDescriptor = MTLTextureDescriptor()\n    textureDescriptor.width = width\n    textureDescriptor.height = height\n    textureDescriptor.pixelFormat = .bgra8Unorm\n    textureDescriptor.textureType = .type2DArray\n    textureDescriptor.arrayLength = count\n\n    #if os(macOS)\n    textureDescriptor.storageMode = .managed\n    #elseif os(iOS) || os(tvOS)\n    textureDescriptor.storageMode = .shared\n    #else\n    #error(\"Unsupported platform, can't determine storageMode for texture\")\n    #endif\n\n    textureDescriptor.mipmapLevelCount = 1 + Int(Foundation.log2(Double(width)).rounded(.down))\n\n    guard let arrayTexture = device.makeTexture(descriptor: textureDescriptor) else {\n      throw RenderError.failedToCreateArrayTexture\n    }\n\n    arrayTexture.label = \"arrayTexture\"\n\n    let bytesPerPixel = 4\n    var frameIndex = 0\n    for texture in palette.textures {\n      let frameWidth = texture.width\n      let frameCount = texture.frameCount\n      let frameHeight = texture.height / frameCount\n      let bytesPerRow = bytesPerPixel * frameWidth\n      let bytesPerFrame = bytesPerRow * frameHeight\n\n      guard frameHeight <= height else {\n        frameIndex += frameCount\n        continue\n      }\n\n      for frame in 0..<frameCount {\n        let offset = frame * bytesPerFrame\n        texture.image.withUnsafeBytes { pointer in\n          arrayTexture.replace(\n            region: MTLRegion(\n              origin: MTLOrigin(x: 0, y: 0, z: 0),\n              size: MTLSize(width: frameWidth, height: frameHeight, depth: 1)\n            ),\n            mipmapLevel: 0,\n            slice: frameIndex,\n            withBytes: pointer.baseAddress!.advanced(by: offset),\n            bytesPerRow: bytesPerRow,\n            bytesPerImage: bytesPerFrame\n          )\n        }\n        frameIndex += 1\n\n        if !includeAnimations {\n          // Only include the first frame of each texture if animations aren't needed\n          break\n        }\n      }\n    }\n\n    guard let commandBuffer = commandQueue.makeCommandBuffer() else {\n      throw MetalTexturePaletteError.failedToCreateCommandBuffer\n    }\n\n    guard let blitCommandEncoder = commandBuffer.makeBlitCommandEncoder() else {\n      throw MetalTexturePaletteError.failedToCreateBlitCommandEncoder\n    }\n\n    textureDescriptor.storageMode = .private\n    guard let privateArrayTexture = device.makeTexture(descriptor: textureDescriptor) else {\n      throw RenderError.failedToCreatePrivateArrayTexture\n    }\n\n    blitCommandEncoder.copy(from: arrayTexture, to: privateArrayTexture)\n    blitCommandEncoder.generateMipmaps(for: privateArrayTexture)\n    blitCommandEncoder.endEncoding()\n    commandBuffer.commit()\n\n    return privateArrayTexture\n  }\n\n  public mutating func update() {\n    var time = Float(CFAbsoluteTimeGetCurrent() - creationTime) / 0.05\n    timeBuffer?.contents().copyMemory(from: &time, byteCount: MemoryLayout<Float>.stride)\n\n    let tick = Int(time.rounded(.down))\n\n    let updatedTextures = animationState.update(tick: tick)\n\n    guard !updatedTextures.isEmpty else {\n      return\n    }\n\n    for (textureIndex, latestUpdateTick) in updatedTextures {\n      let texture = palette.textures[textureIndex]\n      guard let animation = texture.animation else {\n        continue\n      }\n\n      let frameIndex = animationState.frame(forTextureAt: textureIndex)\n      let frame = animation.frames[frameIndex]\n\n      let firstFrameIndex = textureIndexToFirstFrameIndex[textureIndex]\n      let nextFrameIndex = (frameIndex + 1) % animation.frames.count\n      textureStates[textureIndex] = TextureState(\n        currentFrameIndex: UInt16(firstFrameIndex + frameIndex),\n        nextFrameIndex: animation.interpolate ? UInt16(firstFrameIndex + nextFrameIndex) : 65535,\n        previousUpdate: UInt32(latestUpdateTick),\n        nextUpdate: UInt32(latestUpdateTick + frame.time)\n      )\n    }\n\n    textureStatesBuffer?.contents().copyMemory(\n      from: &textureStates,\n      byteCount: MemoryLayout<TextureState>.stride * textureStates.count\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/ScreenRenderer.swift",
    "content": "import Foundation\nimport MetalKit\nimport DeltaCore\nimport FirebladeMath\n\n/// The renderer managing offscreen rendering and displaying final result in view.\npublic final class ScreenRenderer: Renderer {\n  /// The device used to render.\n  private var device: MTLDevice\n\n  /// Renderer's own pipeline state (for drawing on-screen)\n  private var pipelineState: MTLRenderPipelineState\n\n  /// Offscreen render pass descriptor used to perform rendering into renderer's internal textures\n  private var offScreenRenderPassDescriptor: MTLRenderPassDescriptor!\n\n  /// Renderer's profiler\n  private var profiler: Profiler<RenderingMeasurement>\n\n  /// Render target texture into which offscreen rendering is performed\n  private var renderTargetTexture: MTLTexture?\n\n  /// Render target depth texture\n  private var renderTargetDepthTexture: MTLTexture?\n\n  /// The accumulation texture used for rendering of order independent transparency.\n  private var transparencyAccumulationTexture: MTLTexture?\n\n  /// The revealage texture used for rendering of order independent transparency.\n  private var transparencyRevealageTexture: MTLTexture?\n\n  /// Client for which rendering is performed\n  private var client: Client\n\n  public init(\n    client: Client,\n    device: MTLDevice,\n    profiler: Profiler<RenderingMeasurement>\n  ) throws {\n    self.device = device\n    self.client = client\n    self.profiler = profiler\n\n    // Create pipeline state\n    let library = try MetalUtil.loadDefaultLibrary(device)\n    pipelineState = try MetalUtil.makeRenderPipelineState(\n      device: device,\n      label: \"ScreenRenderer\",\n      vertexFunction: try MetalUtil.loadFunction(\"screenVertexFunction\", from: library),\n      fragmentFunction: try MetalUtil.loadFunction(\"screenFragmentFunction\", from: library),\n      blendingEnabled: false,\n      isOffScreenPass: false\n    )\n  }\n\n  public var renderDescriptor: MTLRenderPassDescriptor {\n    return offScreenRenderPassDescriptor\n  }\n\n  public func updateRenderTarget(for view: MTKView) throws {\n    let drawableSize = view.drawableSize\n    let width = Int(drawableSize.width)\n    let height = Int(drawableSize.height)\n\n    if let texture = renderTargetTexture {\n      if texture.width == width && texture.height == height {\n        // No updates necessary, early exit\n        return\n      }\n    }\n\n    renderTargetTexture = try MetalUtil.createTexture(\n      device: device,\n      width: width,\n      height: height,\n      pixelFormat: view.colorPixelFormat\n    ) { descriptor in\n      descriptor.storageMode = .private\n    }\n\n    renderTargetDepthTexture = try MetalUtil.createTexture(\n      device: device,\n      width: width,\n      height: height,\n      pixelFormat: .depth32Float\n    ) { descriptor in\n      descriptor.storageMode = .private\n    }\n\n    // Create accumulation texture for order independent transparency\n    transparencyAccumulationTexture = try MetalUtil.createTexture(\n      device: device,\n      width: width,\n      height: height,\n      pixelFormat: .bgra8Unorm\n    )\n\n    // Create revealage texture for order independent transparency\n    transparencyRevealageTexture = try MetalUtil.createTexture(\n      device: device,\n      width: width,\n      height: height,\n      pixelFormat: .r8Unorm\n    )\n\n    // Update render pass descriptor. Set clear colour to sky colour\n    let passDescriptor = MetalUtil.createRenderPassDescriptor(\n      device,\n      targetRenderTexture: renderTargetTexture!,\n      targetDepthTexture: renderTargetDepthTexture!\n    )\n\n    let accumulationClearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 0)\n    passDescriptor.colorAttachments[1].texture = transparencyAccumulationTexture\n    passDescriptor.colorAttachments[1].clearColor = accumulationClearColor\n    passDescriptor.colorAttachments[1].loadAction = MTLLoadAction.clear\n    passDescriptor.colorAttachments[1].storeAction = MTLStoreAction.store\n\n    let revealageClearColor = MTLClearColor(red: 1, green: 0, blue: 0, alpha: 0)\n    passDescriptor.colorAttachments[2].texture = transparencyRevealageTexture\n    passDescriptor.colorAttachments[2].clearColor = revealageClearColor\n    passDescriptor.colorAttachments[2].loadAction = MTLLoadAction.clear\n    passDescriptor.colorAttachments[2].storeAction = MTLStoreAction.store\n\n    offScreenRenderPassDescriptor = passDescriptor\n  }\n\n  public func render(\n    view: MTKView,\n    encoder: MTLRenderCommandEncoder,\n    commandBuffer: MTLCommandBuffer,\n    worldToClipUniformsBuffer: MTLBuffer,\n    camera: Camera\n  ) throws {\n    // TODO: Investigate using a blit operation instead.\n\n    profiler.push(.encode)\n    // Set pipeline for rendering on-screen\n    encoder.setRenderPipelineState(pipelineState)\n\n    // Use texture from offscreen rendering as fragment shader source to draw contents on-screen\n    encoder.setFragmentTexture(self.renderTargetTexture, index: 0)\n    encoder.setFragmentTexture(self.renderTargetDepthTexture, index: 1)\n\n    // A quad with total of 6 vertices (2 overlapping triangles) is drawn to present\n    // rendering results on-screen. The geometry is defined within the shader (hard-coded).\n    encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6)\n    profiler.pop()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Shader/ChunkOITShaders.metal",
    "content": "#include <metal_stdlib>\n#include \"ChunkTypes.metal\"\n\nusing namespace metal;\n\n// These shaders are used in the order independent transparency pipeline based off this blog\n// article: https://casual-effects.blogspot.com/2015/03/implemented-weighted-blended-order.html?m=1\n// The vertex shader used in this pipeline is just the regular one in ChunkShaders.metal\n// Compositing is performed using chunkOITCompositingVertexShader and chunkOITFragmentShader\n\nstruct FragmentOut {\n  float4 accumulation [[ color(1) ]];\n  float revealage [[ color(2) ]];\n};\n\nconstexpr sampler textureSampler (mag_filter::nearest, min_filter::nearest, mip_filter::linear);\n\nconstant float precomputedWeight = 0.286819249;\n\nfragment FragmentOut chunkOITFragmentShader(RasterizerData in [[stage_in]],\n                                            texture2d_array<float, access::sample> textureArray [[texture(0)]],\n                                            constant uint8_t *lightMap [[buffer(0)]],\n                                            constant float &time [[buffer(1)]],\n                                            constant FogUniforms &fogUniforms [[buffer(2)]]) {\n  // Sample the relevant texture slice\n  FragmentOut out;\n  float4 color = textureArray.sample(textureSampler, in.uv, in.textureState.currentFrameIndex);\n  if (in.textureState.nextFrameIndex != 65535) {\n    float start = (float)in.textureState.previousUpdate;\n    float end = (float)in.textureState.nextUpdate;\n    float progress = (time - start) / (end - start);\n    float4 nextColor = textureArray.sample(textureSampler, in.uv, in.textureState.nextFrameIndex);\n    color = mix(color, nextColor, progress);\n  }\n\n  // Apply light level\n  int index = in.skyLightLevel * 16 + in.blockLightLevel;\n  float4 brightness;\n  brightness.r = (float)lightMap[index * 4];\n  brightness.g = (float)lightMap[index * 4 + 1];\n  brightness.b = (float)lightMap[index * 4 + 2];\n  brightness.a = 255;\n\n  color *= brightness / 255.0;\n  color *= in.tint;\n\n  // Apply distance fog\n  float distance = length(in.cameraSpacePosition);\n  float linearFogIntensity = smoothstep(fogUniforms.fogStart, fogUniforms.fogEnd, distance);\n  float exponentialFogIntensity = clamp(1.0 - exp(-fogUniforms.fogDensity * distance), 0.0, 1.0);\n  float fogIntensity = linearFogIntensity * fogUniforms.isLinear\n                     + exponentialFogIntensity * !fogUniforms.isLinear;\n  color.rgb = color.rgb * (1.0 - fogIntensity) + fogUniforms.fogColor * fogIntensity;\n\n  // As the fog approaches an intensity of 1.0, the alpha of the fragment has to increase to 1.0\n  // as well so that the fragment disappears into the fog. Otherwise the fragment is still visible\n  // even once everything around it has disappeared into the fog (some weirdness with additive blending\n  // during the compositing step). Cubing the intensity to curve it a bit more makes it look a bit\n  // more correct (still looks a bit odd though).\n  float fogAlphaIntensity = fogIntensity * fogIntensity * fogIntensity * fogIntensity;\n  color.a = color.a * (1.0 - fogAlphaIntensity) + fogAlphaIntensity;\n\n  // Premultiply alpha\n  color.rgb *= color.a;\n\n  // Order independent transparency code adapted from https://casual-effects.blogspot.com/2015/03/implemented-weighted-blended-order.html?m=1\n  // I have changed the maths a lot to get it working well at all. I've hardcoded the depth for now\n  // and it seems to be working quite well.\n\n  // This code was used to calculate z which was used in weight calculations. This had super weird\n  // artifacts and it turns out that just hardcoding the depth to 16 works way better than any depth\n  // weighting algorithms I have tried. The precomputedWeight is calculated using Equation 7 from\n  // the 2013 paper by Morgan mcGuire and Louis Bavoil of NVIDIA: https://jcgt.org/published/0002/02/09/\n  //\n  //   float d = in.position.z;\n  //   float far = 400;\n  //   float near = 0.04;\n  //   float z = (far * near) / (d * (far - near) - far) + 32;\n  //\n  //   constant float z = 16;\n  //   constant float zCubed = z * z * z;\n  //   constant float precomputedWeight = max(1e-2, min(3e3, 10 / (1e-5 + zCubed / 125 + zCubed * zCubed / 8e6)));\n\n  float w = color.a * precomputedWeight;\n  out.accumulation = color * w;\n  out.revealage = color.a;\n\n  return out;\n}\n\n// You're welcome for the alignment\nconstant float4 screenCorners[6] = {\n  float4( 1,  1, 0, 1),\n  float4( 1, -1, 0, 1),\n  float4(-1,  1, 0, 1),\n  float4( 1, -1, 0, 1),\n  float4(-1, -1, 0, 1),\n  float4(-1,  1, 0, 1)\n};\n\nvertex OITCompositingRasterizerData chunkOITCompositingVertexShader(uint vertexId [[vertex_id]]) {\n  OITCompositingRasterizerData out;\n  out.position = screenCorners[vertexId];\n\n  return out;\n}\n\nfragment float4 chunkOITCompositingFragmentShader(OITCompositingRasterizerData in [[stage_in]],\n                                                  float4 accumulation [[color(1)]],\n                                                  float revealage [[color(2)]]) {\n  return float4(accumulation.rgb / max(min(accumulation.a, 5e4), 1e-4), 1 - revealage);\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Shader/ChunkShaders.metal",
    "content": "#include <metal_stdlib>\n#include \"ChunkTypes.metal\"\n\nusing namespace metal;\n\nconstexpr sampler textureSampler (mag_filter::nearest, min_filter::nearest, mip_filter::linear);\n\nvertex RasterizerData chunkVertexShader(uint vertexId [[vertex_id]],\n                                        uint instanceId [[instance_id]],\n                                        constant Vertex *vertices [[buffer(0)]],\n                                        constant CameraUniforms &cameraUniforms [[buffer(1)]],\n                                        constant ChunkUniforms &chunkUniforms [[buffer(2)]],\n                                        constant TextureState *textureStates [[buffer(3)]]) {\n  Vertex in = vertices[vertexId];\n  RasterizerData out;\n\n  float4 cameraSpacePosition = float4(in.x, in.y, in.z, 1.0) * chunkUniforms.transformation * cameraUniforms.framing;\n  out.position = cameraSpacePosition * cameraUniforms.projection;\n  out.cameraSpacePosition = float3(cameraSpacePosition / cameraSpacePosition.w);\n  out.uv = float2(in.u, in.v);\n  out.hasTexture = in.textureIndex != 65535;\n  if (out.hasTexture) {\n    out.textureState = textureStates[in.textureIndex];\n  }\n  out.isTransparent = in.isTransparent;\n  out.tint = float4(in.r, in.g, in.b, in.a);\n  out.skyLightLevel = in.skyLightLevel;\n  out.blockLightLevel = in.blockLightLevel;\n\n  return out;\n}\n\nfragment float4 chunkFragmentShader(RasterizerData in [[stage_in]],\n                                    texture2d_array<float, access::sample> textureArray [[texture(0)]],\n                                    constant uint8_t *lightMap [[buffer(0)]],\n                                    constant float &time [[buffer(1)]],\n                                    constant FogUniforms &fogUniforms [[buffer(2)]]) {\n  // Sample the relevant texture slice\n  float4 color;\n  if (in.hasTexture) {\n    color = textureArray.sample(textureSampler, in.uv, in.textureState.currentFrameIndex);\n    // If the texture is animated and requires interpolation, interpolate between the current\n    // frame and the next.\n    if (in.textureState.nextFrameIndex != 65535) {\n      float start = (float)in.textureState.previousUpdate;\n      float end = (float)in.textureState.nextUpdate;\n      float progress = (time - start) / (end - start);\n      float4 nextColor = textureArray.sample(textureSampler, in.uv, in.textureState.nextFrameIndex);\n      color = mix(color, nextColor, progress);\n    }\n  } else {\n    color = float4(1, 1, 1, 1);\n  }\n\n  // Discard transparent fragments\n  if (in.isTransparent && color.a < 0.33) {\n    discard_fragment();\n  }\n\n  // Apply light level\n  int index = in.skyLightLevel * 16 + in.blockLightLevel;\n  float4 brightness;\n  brightness.r = (float)lightMap[index * 4];\n  brightness.g = (float)lightMap[index * 4 + 1];\n  brightness.b = (float)lightMap[index * 4 + 2];\n  brightness.a = 255;\n  color *= brightness / 255.0;\n\n  // A bit of branchless programming for you\n  color = color * in.tint;\n\n  // TODO: Rename isTransparent to isTranslucent (which would require inverting its value)\n  // Here transparent means opaque or fully transparent (basically just 'not-translucent')\n  color.w = color.w * !in.isTransparent // If not transparent, take the original alpha\n          + in.isTransparent; // If transparent, make alpha 1\n\n  float distance = length(in.cameraSpacePosition);\n\n  float linearFogIntensity = smoothstep(fogUniforms.fogStart, fogUniforms.fogEnd, distance);\n  float exponentialFogIntensity = clamp(1.0 - exp(-fogUniforms.fogDensity * distance), 0.0, 1.0);\n\n  float fogIntensity = linearFogIntensity * fogUniforms.isLinear\n                     + exponentialFogIntensity * !fogUniforms.isLinear;\n\n  color.rgb = color.rgb * (1.0 - fogIntensity) + fogUniforms.fogColor.rgb * fogIntensity;\n  return color;\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Shader/ChunkTypes.metal",
    "content": "#include <metal_stdlib>\n\nusing namespace metal;\n\nstruct TextureState {\n  uint16_t currentFrameIndex;\n  uint16_t nextFrameIndex;\n  uint32_t previousUpdate;\n  uint32_t nextUpdate;\n};\n\nstruct Vertex {\n  float x;\n  float y;\n  float z;\n  float u;\n  float v;\n  float r;\n  float g;\n  float b;\n  float a;\n  uint8_t skyLightLevel; // TODO: pack sky and block light into a single uint8 to reduce size of vertex\n  uint8_t blockLightLevel;\n  uint16_t textureIndex;\n  bool isTransparent;\n};\n\nstruct RasterizerData {\n  float4 position [[position]];\n  float3 cameraSpacePosition;\n  float2 uv;\n  float4 tint;\n  TextureState textureState;\n  bool hasTexture;\n  bool isTransparent;\n  uint8_t skyLightLevel;\n  uint8_t blockLightLevel;\n};\n\nstruct CameraUniforms {\n  float4x4 framing;\n  float4x4 projection;\n};\n\nstruct ChunkUniforms {\n  float4x4 transformation;\n};\n\nstruct OITCompositingRasterizerData {\n  float4 position [[position]];\n};\n\nstruct FogUniforms {\n  float3 fogColor;\n  float fogStart;\n  float fogEnd;\n  float fogDensity;\n  bool isLinear;\n};\n"
  },
  {
    "path": "Sources/Core/Renderer/Shader/EntityShaders.metal",
    "content": "#include <metal_stdlib>\n#include \"ChunkTypes.metal\"\n\nusing namespace metal;\n\nstruct EntityVertex {\n  float x;\n  float y;\n  float z;\n  float r;\n  float g;\n  float b;\n  float u;\n  float v;\n  uint8_t skyLightLevel;\n  uint8_t blockLightLevel;\n  uint16_t textureIndex;\n};\n\nstruct EntityRasterizerData {\n  float4 position [[position]];\n  float4 color;\n  float2 uv;\n  uint8_t skyLightLevel;\n  uint8_t blockLightLevel;\n  uint16_t textureIndex;\n};\n\nvertex EntityRasterizerData entityVertexShader(constant EntityVertex *vertices [[buffer(0)]],\n                                        constant CameraUniforms &cameraUniforms [[buffer(1)]],\n                                        uint vertexId [[vertex_id]]) {\n  EntityVertex in = vertices[vertexId];\n  EntityRasterizerData out;\n\n  out.position = float4(in.x, in.y, in.z, 1.0) * cameraUniforms.framing * cameraUniforms.projection;\n  out.color = float4(in.r, in.g, in.b, 1.0);\n  out.uv = float2(in.u, in.v);\n  out.textureIndex = in.textureIndex;\n  out.skyLightLevel = in.skyLightLevel;\n  out.blockLightLevel = in.blockLightLevel;\n\n  return out;\n}\n\nconstexpr sampler textureSampler (mag_filter::nearest, min_filter::nearest, mip_filter::linear);\n\nfragment float4 entityFragmentShader(EntityRasterizerData in [[stage_in]],\n                                    texture2d_array<float, access::sample> textureArray [[texture(0)]],\n                                    constant uint8_t *lightMap [[buffer(0)]]) {\n  float4 color;\n  if (in.textureIndex == 65535) {\n    color = in.color;\n  } else {\n    color = textureArray.sample(textureSampler, in.uv, in.textureIndex);\n  }\n  if (color.a < 0.3) {\n    discard_fragment();\n  }\n\n  int index = in.skyLightLevel * 16 + in.blockLightLevel;\n  float4 brightness;\n  brightness.r = (float)lightMap[index * 4];\n  brightness.g = (float)lightMap[index * 4 + 1];\n  brightness.b = (float)lightMap[index * 4 + 2];\n  brightness.a = 255;\n  color *= brightness / 255.0;\n\n  return color;\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Shader/GUIShaders.metal",
    "content": "#include <metal_stdlib>\n#include \"GUITypes.metal\"\n\nusing namespace metal;\n\nconstant const uint vertexIndexLookup[] = {0, 1, 2, 2, 3, 0};\n\nvertex FragmentInput guiVertex(constant GUIUniforms &uniforms [[buffer(0)]],\n                               constant GUIVertex *vertices [[buffer(1)]],\n                               constant GUIElementUniforms &elementUniforms [[buffer(2)]],\n                               uint vertexId [[vertex_id]],\n                               uint instanceId [[instance_id]]) {\n  uint index = vertexIndexLookup[vertexId % 6] + vertexId / 6 * 4;\n  GUIVertex in = vertices[index];\n\n  FragmentInput out;\n\n  float2 position = in.position;\n  position += elementUniforms.position;\n  position *= uniforms.scale;\n\n  float3 transformed = float3(position, 1) * uniforms.screenSpaceToNormalized;\n  out.position = float4(transformed.xy, 0, transformed.z);\n\n  out.uv = in.uv;\n  out.tint = in.tint;\n  out.textureIndex = in.textureIndex;\n\n  return out;\n}\n\nconstexpr sampler textureSampler (mag_filter::nearest, min_filter::nearest, mip_filter::linear);\n\nfragment float4 guiFragment(FragmentInput in [[stage_in]],\n                            texture2d_array<float, access::sample> textureArray [[texture(0)]]) {\n  float4 color;\n  if (in.textureIndex == 65535) {\n    color = in.tint;\n  } else {\n    color = textureArray.sample(textureSampler, in.uv, in.textureIndex);\n    if (color.a < 0.33) {\n      discard_fragment();\n    }\n    color *= in.tint;\n  }\n  return color;\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Shader/GUITypes.metal",
    "content": "#include <metal_stdlib>\n\nusing namespace metal;\n\nstruct GUIVertex {\n  float2 position;\n  float2 uv;\n  float4 tint;\n  uint16_t textureIndex;\n};\n\nstruct GUIUniforms {\n  float3x3 screenSpaceToNormalized;\n  float scale;\n};\n\nstruct GUIElementUniforms {\n  float2 position;\n};\n\nstruct FragmentInput {\n  float4 position [[position]];\n  float2 uv;\n  uint16_t textureIndex;\n  float4 tint;\n};\n"
  },
  {
    "path": "Sources/Core/Renderer/Shader/ScreenShaders.metal",
    "content": "#include <metal_stdlib>\nusing namespace metal;\n\nconstant float2 quadVertices[] = {\n  float2(-1.0,  1.0),\n  float2(-1.0, -1.0),\n  float2( 1.0, -1.0),\n  float2(-1.0,  1.0),\n  float2( 1.0,  1.0),\n  float2( 1.0, -1.0)\n};\n\nstruct QuadVertex {\n  float4 position [[position]];\n  float2 uv;\n};\n\nvertex QuadVertex screenVertexFunction(uint id [[vertex_id]]) {\n  auto quadVertex = quadVertices[id];\n  return {\n    .position = float4(quadVertex, 1.0, 1.0),\n    .uv =  quadVertex * float2(0.5, -0.5) + 0.5,\n  };\n}\n\nfragment float4 screenFragmentFunction(QuadVertex vert [[stage_in]],\n                                       texture2d<float> offscreenResult [[texture(0)]],\n                                       depth2d<float> offscreenResultDepth [[texture(1)]],\n                                       constant struct FogUniforms &fogUniforms [[buffer(0)]]) {\n  constexpr sampler smplr(coord::normalized);\n  float4 color = offscreenResult.sample(smplr, vert.uv);\n  return color;\n};\n"
  },
  {
    "path": "Sources/Core/Renderer/Shader/SkyShaders.metal",
    "content": "#include <metal_stdlib>\nusing namespace metal;\n\nstruct SkyPlaneVertex {\n  float4 transformedPosition [[position]];\n  // The position in the world's coordinate system but centered on the player.\n  float3 playerSpacePosition;\n};\n\nstruct SkyPlaneUniforms {\n  float4 skyColor;\n  float4 fogColor;\n  float fogStart;\n  float fogEnd;\n  float size;\n  float verticalOffset;\n  float4x4 playerToClip;\n};\n\nvertex SkyPlaneVertex skyPlaneVertex(uint id [[vertex_id]],\n                                     constant float3 *vertices [[buffer(0)]],\n                                     constant struct SkyPlaneUniforms &uniforms [[buffer(1)]]) {\n  float3 position = vertices[id];\n  position *= uniforms.size / 2;\n  position += float3(0, uniforms.verticalOffset, 0);\n  return {\n    .transformedPosition = float4(position, 1.0) * uniforms.playerToClip,\n    .playerSpacePosition = position\n  };\n}\n\nfragment float4 skyPlaneFragment(SkyPlaneVertex vert [[stage_in]],\n                                 constant struct SkyPlaneUniforms &uniforms [[buffer(0)]]) {\n  float distance = length(vert.playerSpacePosition);\n  float fogIntensity = smoothstep(uniforms.fogStart, uniforms.fogEnd, distance);\n  return uniforms.skyColor * (1.0 - fogIntensity) + uniforms.fogColor * fogIntensity;\n}\n\nstruct SunriseDiscVertex {\n  float4 position [[position]];\n  float4 color;\n};\n\nstruct SunriseDiscUniforms {\n  float4 color;\n  float4x4 transformation;\n};\n\nvertex SunriseDiscVertex sunriseDiscVertex(uint id [[vertex_id]],\n                                           constant float3 *vertices [[buffer(0)]],\n                                           constant struct SunriseDiscUniforms &uniforms [[buffer(1)]]) {\n  float3 inPosition = vertices[id];\n  float4 color = uniforms.color;\n\n  // Modify disc tilt based on color alpha\n  inPosition.y *= uniforms.color.a;\n\n  // Set the ring vertices' alphas to 0\n  color.a *= id == 0;\n\n  float4 outPosition = float4(inPosition, 1.0) * uniforms.transformation;\n\n  return {\n    .position = outPosition,\n    .color = color\n  };\n}\n\nfragment float4 sunriseDiscFragment(SunriseDiscVertex vert [[stage_in]]) {\n  return vert.color;\n}\n\nstruct CelestialBodyVertex {\n  float4 position [[position]];\n  float2 uv;\n  uint16_t index;\n};\n\nstruct CelestialBodyUniforms {\n  float4x4 transformation;\n  uint16_t textureIndex;\n  float2 uvPosition;\n  float2 uvSize;\n  uint8_t type;\n};\n\n#define SUN_TYPE 0\n#define MOON_TYPE 1\n\nconstexpr sampler textureSampler (mag_filter::nearest, min_filter::nearest, mip_filter::linear);\n\nvertex CelestialBodyVertex celestialBodyVertex(uint id [[vertex_id]],\n                                               constant float3 *vertices [[buffer(0)]],\n                                               constant struct CelestialBodyUniforms &uniforms [[buffer(1)]]) {\n  float3 position = vertices[id];\n\n  // The quad goes from -1 to 1 along the x and z axes, simply shift the xz coordinates to get the uvs.\n  float2 uv = position.xz / 2.0 + float2(0.5, 0.5);\n  uv *= uniforms.uvSize;\n  uv += uniforms.uvPosition;\n\n  return {\n    .position = float4(position, 1.0) * uniforms.transformation,\n    .uv = uv,\n    .index = uniforms.textureIndex\n  };\n}\n\nfragment float4 celestialBodyFragment(CelestialBodyVertex in [[stage_in]],\n                                      texture2d_array<float, access::sample> textureArray [[texture(0)]]) {\n  return textureArray.sample(textureSampler, in.uv, in.index);\n}\n\nstruct StarVertex {\n  float4 position [[position]];\n};\n\nstruct StarUniforms {\n  float4x4 transformation;\n  float brightness;\n};\n\nvertex StarVertex starVertex(uint id [[vertex_id]],\n                             constant float3 *vertices [[buffer(0)]],\n                             constant struct StarUniforms &uniforms [[buffer(1)]]) {\n  float3 position = vertices[id];\n\n  return {\n    .position = float4(position, 1.0) * uniforms.transformation,\n  };\n}\n\nfragment float4 starFragment(StarVertex in [[stage_in]],\n                             constant struct StarUniforms &uniforms [[buffer(0)]]) {\n  return float4(uniforms.brightness);\n}\n\nstruct EndSkyInVertex {\n  float3 position;\n  float2 uv;\n};\n\nstruct EndSkyVertex {\n  float4 position [[position]];\n  float2 uv;\n};\n\nstruct EndSkyUniforms {\n  float4x4 transformation;\n  uint16_t textureIndex;\n};\n\nvertex EndSkyVertex endSkyVertex(uint id [[vertex_id]],\n                             constant struct EndSkyInVertex *vertices [[buffer(0)]],\n                             constant struct EndSkyUniforms &uniforms [[buffer(1)]]) {\n  struct EndSkyInVertex in = vertices[id];\n\n  return {\n    .position = float4(in.position, 1.0) * uniforms.transformation,\n    .uv = in.uv\n  };\n}\n\nconstexpr sampler endSkyTextureSampler (mag_filter::nearest,\n                                        min_filter::nearest,\n                                        mip_filter::linear,\n                                        address::repeat);\n\nfragment float4 endSkyFragment(struct EndSkyVertex in [[stage_in]],\n                             texture2d_array<float, access::sample> textureArray [[texture(0)]],\n                             constant struct EndSkyUniforms &uniforms [[buffer(0)]]) {\n  // The end sky is half the size of the texture palette and tiles 16 times across each face.\n  // This is achieved by some UV maths and using fmod (I love fmod).\n  float2 uv = fmod(in.uv * 16 / 2, 0.5);\n\n  float4 color = textureArray.sample(endSkyTextureSampler, uv, uniforms.textureIndex);\n  return color * (float4(40.0, 40.0, 40.0, 255.0) / 255.0);\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/SkyBoxRenderer.swift",
    "content": "import MetalKit\nimport DeltaCore\n\n/// The sky box consists of a sky plane (above the player), and a void plane\n/// (below the player if the player is below the void plane visibility threshold).\n/// It also includes distance fog cast on the planes which is what creates the\n/// smooth transition from the fog color at the horizon to the sky color overhead.\npublic final class SkyBoxRenderer: Renderer {\n  private let client: Client\n\n  private let skyPlaneRenderPipelineState: MTLRenderPipelineState\n  private let sunriseDiscRenderPipelineState: MTLRenderPipelineState\n  private let celestialBodyRenderPipelineState: MTLRenderPipelineState\n  private let starRenderPipelineState: MTLRenderPipelineState\n  private let endSkyRenderPipelineState: MTLRenderPipelineState\n\n  private let quadVertexBuffer: MTLBuffer\n  private let quadIndexBuffer: MTLBuffer\n\n  private var skyPlaneUniformsBuffer: MTLBuffer\n  private var voidPlaneUniformsBuffer: MTLBuffer\n\n  private let sunriseDiscVertexBuffer: MTLBuffer\n  private let sunriseDiscIndexBuffer: MTLBuffer\n  private var sunriseDiscUniformsBuffer: MTLBuffer\n\n  private let sunUniformsBuffer: MTLBuffer\n  private let moonUniformsBuffer: MTLBuffer\n\n  private let starVertexBuffer: MTLBuffer\n  private let starIndexBuffer: MTLBuffer\n  private let starUniformsBuffer: MTLBuffer\n\n  private let endSkyVertexBuffer: MTLBuffer\n  private let endSkyIndexBuffer: MTLBuffer\n  private let endSkyUniformsBuffer: MTLBuffer\n\n  private let environmentTexturePalette: MetalTexturePalette\n\n  /// The vertices for the sky plane quad (also used for the void plane).\n  private static var quadVertices: [Vec3f] = [\n    Vec3f(-1, 0, 1),\n    Vec3f(1, 0, 1),\n    Vec3f(1, 0, -1),\n    Vec3f(-1, 0, -1)\n  ]\n  /// The indices for the sky plane, void plane, sun, or moon quad.\n  private static var quadIndices: [UInt32] = [0, 1, 2, 2, 3, 0]\n\n  /// The vertical offset of the sky plane relative to the camera.\n  private static let skyPlaneOffset: Float = 16\n  /// The sky plane's size in blocks.\n  private static let skyPlaneSize: Float = 768\n  /// The void plane's size in blocks.\n  private static let voidPlaneSize: Float = 768\n  /// The vertical offset of the void plane relative to the camera.\n  private static let voidPlaneOffset: Float = -4\n\n  /// The sun's size in blocks.\n  private static let sunSize: Float = 60\n  /// The sun's distance from the camera.\n  private static let sunDistance: Float = 100\n  /// The moon's size in blocks.\n  private static let moonSize: Float = 40\n  /// The moon's distance from the camera.\n  private static let moonDistance: Float = 100\n\n  /// The y-level at which the void plane becomes visible in superflat worlds.\n  private static let superFlatVoidPlaneVisibilityThreshold: Float = 1\n  /// The y-level at which the void plane becomes visible in all worlds other than\n  /// superflat worlds.\n  private static let defaultVoidPlaneVisibilityThreshold: Float = 63\n\n  public init(client: Client, device: MTLDevice, commandQueue: MTLCommandQueue) throws {\n    self.client = client\n\n    let library = try MetalUtil.loadDefaultLibrary(device)\n    skyPlaneRenderPipelineState = try MetalUtil.makeRenderPipelineState(\n      device: device,\n      label: \"SkyBoxRenderer.skyPlane\",\n      vertexFunction: try MetalUtil.loadFunction(\"skyPlaneVertex\", from: library),\n      fragmentFunction: try MetalUtil.loadFunction(\"skyPlaneFragment\", from: library),\n      blendingEnabled: false\n    )\n\n    sunriseDiscRenderPipelineState = try MetalUtil.makeRenderPipelineState(\n      device: device,\n      label: \"SkyBoxRenderer.sunriseDisc\",\n      vertexFunction: try MetalUtil.loadFunction(\"sunriseDiscVertex\", from: library),\n      fragmentFunction: try MetalUtil.loadFunction(\"sunriseDiscFragment\", from: library),\n      blendingEnabled: true\n    )\n\n    celestialBodyRenderPipelineState = try MetalUtil.makeRenderPipelineState(\n      device: device,\n      label: \"SkyBoxRenderer.celestialBody\",\n      vertexFunction: try MetalUtil.loadFunction(\"celestialBodyVertex\", from: library),\n      fragmentFunction: try MetalUtil.loadFunction(\"celestialBodyFragment\", from: library),\n      blendingEnabled: true\n    ) { descriptor in\n      // The sun and moon textures just have their alpha set to one so they require\n      // different blending to usual.\n      descriptor.colorAttachments[0].destinationRGBBlendFactor = .one\n    }\n\n    starRenderPipelineState = try MetalUtil.makeRenderPipelineState(\n      device: device,\n      label: \"SkyBoxRenderer.stars\",\n      vertexFunction: try MetalUtil.loadFunction(\"starVertex\", from: library),\n      fragmentFunction: try MetalUtil.loadFunction(\"starFragment\", from: library),\n      blendingEnabled: true\n    ) { descriptor in\n      // The stars are rendered similarly to the sun and moon. They add\n      // to the color instead of linearly blending with the colour because\n      // they're shining through from behind instead of overlayed on top.\n      descriptor.colorAttachments[0].destinationRGBBlendFactor = .one\n    }\n\n    endSkyRenderPipelineState = try MetalUtil.makeRenderPipelineState(\n      device: device,\n      label: \"SkyBoxRenderer.endSky\",\n      vertexFunction: try MetalUtil.loadFunction(\"endSkyVertex\", from: library),\n      fragmentFunction: try MetalUtil.loadFunction(\"endSkyFragment\", from: library),\n      blendingEnabled: false\n    )\n\n    // TODO: Make these both private (storage mode) once that's simpler to do (after MetalUtil\n    //   rewrite/replacement)\n    quadVertexBuffer = try MetalUtil.makeBuffer(\n      device,\n      bytes: &Self.quadVertices,\n      length: Self.quadVertices.count * MemoryLayout<Vec3f>.stride,\n      options: .storageModeShared,\n      label: \"quadVertexBuffer\"\n    )\n\n    quadIndexBuffer = try MetalUtil.makeBuffer(\n      device,\n      bytes: &Self.quadIndices,\n      length: Self.quadIndices.count * MemoryLayout<UInt32>.stride,\n      options: .storageModeShared,\n      label: \"quadIndexBuffer\"\n    )\n\n    skyPlaneUniformsBuffer = try MetalUtil.makeBuffer(\n      device,\n      length: MemoryLayout<SkyPlaneUniforms>.stride,\n      options: .storageModeShared,\n      label: \"skyPlaneUniformsBuffer\"\n    )\n\n    voidPlaneUniformsBuffer = try MetalUtil.makeBuffer(\n      device,\n      length: MemoryLayout<SkyPlaneUniforms>.stride,\n      options: .storageModeShared,\n      label: \"skyPlaneUniformsBuffer\"\n    )\n\n    var sunriseDiscVertices = Self.generateSunriseDiscVertices()\n    sunriseDiscVertexBuffer = try MetalUtil.makeBuffer(\n      device,\n      bytes: &sunriseDiscVertices,\n      length: sunriseDiscVertices.count * MemoryLayout<Vec3f>.stride,\n      options: .storageModeShared,\n      label: \"sunriseDiscVertexBuffer\"\n    )\n\n    var sunriseDiscIndices = Self.generateSunriseDiscIndices()\n    sunriseDiscIndexBuffer = try MetalUtil.makeBuffer(\n      device,\n      bytes: &sunriseDiscIndices,\n      length: sunriseDiscIndices.count * MemoryLayout<UInt32>.stride,\n      options: .storageModeShared,\n      label: \"sunriseDiscIndexBuffer\"\n    )\n\n    sunriseDiscUniformsBuffer = try MetalUtil.makeBuffer(\n      device,\n      length: MemoryLayout<SunriseDiscUniforms>.stride,\n      options: .storageModeShared,\n      label: \"sunriseDiscUniformsBuffer\"\n    )\n\n    sunUniformsBuffer = try MetalUtil.makeBuffer(\n      device,\n      length: MemoryLayout<CelestialBodyUniforms>.stride,\n      options: .storageModeShared,\n      label: \"sunUniformsBuffer\"\n    )\n\n    moonUniformsBuffer = try MetalUtil.makeBuffer(\n      device,\n      length: MemoryLayout<CelestialBodyUniforms>.stride,\n      options: .storageModeShared,\n      label: \"moonUniformsBuffer\"\n    )\n\n    environmentTexturePalette = try MetalTexturePalette(\n      palette: client.resourcePack.vanillaResources.environmentTexturePalette,\n      device: device,\n      commandQueue: commandQueue\n    )\n\n    var starVertices = Self.generateStarVertices()\n    starVertexBuffer = try MetalUtil.makeBuffer(\n      device,\n      bytes: &starVertices,\n      length: starVertices.count * MemoryLayout<Vec3f>.stride,\n      options: .storageModeShared,\n      label: \"starVertexBuffer\"\n    )\n\n    var starIndices = Self.generateStarIndices(starCount: starVertices.count / 4)\n    starIndexBuffer = try MetalUtil.makeBuffer(\n      device,\n      bytes: &starIndices,\n      length: starIndices.count * MemoryLayout<UInt32>.stride,\n      options: .storageModeShared,\n      label: \"starIndexBuffer\"\n    )\n\n    starUniformsBuffer = try MetalUtil.makeBuffer(\n      device,\n      length: MemoryLayout<StarUniforms>.stride,\n      options: .storageModeShared,\n      label: \"starUniformsBuffer\"\n    )\n\n    var endSkyVertices = Self.generateEndSkyVertices()\n    endSkyVertexBuffer = try MetalUtil.makeBuffer(\n      device,\n      bytes: &endSkyVertices,\n      length: endSkyVertices.count * MemoryLayout<EndSkyVertex>.stride,\n      options: .storageModeShared,\n      label: \"endSkyVertexBuffer\"\n    )\n\n    var endSkyIndices = Self.generateEndSkyIndices()\n    endSkyIndexBuffer = try MetalUtil.makeBuffer(\n      device,\n      bytes: &endSkyIndices,\n      length: endSkyIndices.count * MemoryLayout<UInt32>.stride,\n      options: .storageModeShared,\n      label: \"endSkyIndexBuffer\"\n    )\n\n    endSkyUniformsBuffer = try MetalUtil.makeBuffer(\n      device,\n      length: MemoryLayout<EndSkyUniforms>.stride,\n      options: .storageModeShared,\n      label: \"endSkyUniformsBuffer\"\n    )\n  }\n\n  public func render(\n    view: MTKView,\n    encoder: MTLRenderCommandEncoder,\n    commandBuffer: MTLCommandBuffer,\n    worldToClipUniformsBuffer: MTLBuffer,\n    camera: Camera\n  ) throws {\n    if client.game.world.dimension.isEnd {\n      let texturePalette = client.resourcePack.vanillaResources.environmentTexturePalette\n      let identifier = Identifier(name: \"environment/end_sky\")\n      guard\n        let textureIndex = texturePalette.textureIndex(for: identifier) \n      else {\n        throw RenderError.missingTexture(identifier)\n      }\n      var endSkyUniforms = EndSkyUniforms(\n        transformation: camera.playerToCamera * camera.cameraToClip,\n        textureIndex: UInt16(textureIndex)\n      )\n      endSkyUniformsBuffer.contents().copyMemory(\n        from: &endSkyUniforms,\n        byteCount: MemoryLayout<EndSkyUniforms>.stride\n      )\n\n      encoder.setRenderPipelineState(endSkyRenderPipelineState)\n      encoder.setCullMode(.none)\n      encoder.setVertexBuffer(endSkyVertexBuffer, offset: 0, index: 0)\n      encoder.setVertexBuffer(endSkyUniformsBuffer, offset: 0, index: 1)\n      encoder.setFragmentBuffer(endSkyUniformsBuffer, offset: 0, index: 0)\n      encoder.setFragmentTexture(environmentTexturePalette.arrayTexture, index: 0)\n      encoder.drawIndexedPrimitives(\n        type: .triangle,\n        indexCount: endSkyIndexBuffer.length / MemoryLayout<UInt32>.stride,\n        indexType: .uint32,\n        indexBuffer: endSkyIndexBuffer,\n        indexBufferOffset: 0\n      )\n\n      return\n    }\n\n    // Only the overworld has a sky plane.\n    guard client.game.world.dimension.isOverworld else {\n      return\n    }\n\n    // Below 4 render distance the sky plane, void plane, and sunrises/sunsets\n    // don't get rendered anymore.\n    guard client.configuration.render.renderDistance >= 4 else {\n      return\n    }\n\n    let playerToClip = camera.playerToCamera * camera.cameraToClip\n\n    // When the render distance is above 2, move the fog 1 chunk closer to conceal\n    // more of the world edge.\n    let renderDistance = max(client.configuration.render.renderDistance - 1, 2)\n\n    let position = camera.ray.origin\n    let blockPosition = BlockPosition(x: Int(position.x), y: Int(position.y), z: Int(position.z))\n\n    let skyColor = client.game.world.getSkyColor(at: blockPosition)\n    let fogColor = client.game.world.getFogColor(\n      forViewerWithRay: camera.ray,\n      withRenderDistance: renderDistance\n    )\n\n    // Render the sky plane.\n    var skyPlaneUniforms = SkyPlaneUniforms(\n      skyColor: Vec4f(skyColor, 1),\n      fogColor: Vec4f(fogColor, 1),\n      fogStart: 0,\n      fogEnd: Float(renderDistance * Chunk.width),\n      size: Self.skyPlaneSize,\n      verticalOffset: Self.skyPlaneOffset,\n      playerToClip: playerToClip\n    )\n\n    skyPlaneUniformsBuffer.contents().copyMemory(\n      from: &skyPlaneUniforms,\n      byteCount: MemoryLayout<SkyPlaneUniforms>.stride\n    )\n\n    encoder.setRenderPipelineState(skyPlaneRenderPipelineState)\n    encoder.setVertexBuffer(quadVertexBuffer, offset: 0, index: 0)\n    encoder.setVertexBuffer(skyPlaneUniformsBuffer, offset: 0, index: 1)\n    encoder.setFragmentBuffer(skyPlaneUniformsBuffer, offset: 0, index: 0)\n\n    encoder.drawIndexedPrimitives(\n      type: .triangle,\n      indexCount: Self.quadIndices.count,\n      indexType: .uint32,\n      indexBuffer: quadIndexBuffer,\n      indexBufferOffset: 0\n    )\n\n    // Render the sunrise/sunset disc if applicable.\n    let daylightCyclePhase = client.game.world.getDaylightCyclePhase()\n    switch daylightCyclePhase {\n      case let .sunrise(color), let .sunset(color):\n        let rotation: Mat4x4f\n        if daylightCyclePhase.isSunrise {\n          rotation = MatrixUtil.identity\n        } else {\n          rotation = MatrixUtil.rotationMatrix(.pi, around: .y)\n        }\n\n        let transformation = rotation\n          * camera.playerToCamera\n          * camera.cameraToClip\n\n        var sunriseDiscUniforms = SunriseDiscUniforms(\n          color: color,\n          transformation: transformation\n        )\n\n        sunriseDiscUniformsBuffer.contents().copyMemory(\n          from: &sunriseDiscUniforms,\n          byteCount: MemoryLayout<SunriseDiscUniforms>.stride\n        )\n\n        encoder.setRenderPipelineState(sunriseDiscRenderPipelineState)\n        encoder.setVertexBuffer(sunriseDiscVertexBuffer, offset: 0, index: 0)\n        encoder.setVertexBuffer(sunriseDiscUniformsBuffer, offset: 0, index: 1)\n\n        encoder.drawIndexedPrimitives(\n          type: .triangle,\n          indexCount: sunriseDiscIndexBuffer.length / MemoryLayout<UInt32>.stride,\n          indexType: .uint32,\n          indexBuffer: sunriseDiscIndexBuffer,\n          indexBufferOffset: 0\n        )\n      case .day, .night:\n        break\n    }\n\n    // TODO: Make a similar system to the GUITexturePalette system where certain\n    //   textures are guaranteed to be present. Also have a way to get UV bounds\n    //   for specific 'sprites'.\n    // Render the sun\n    let sunTextureIndex = UInt16(\n      environmentTexturePalette.textureIndex(\n        for: Identifier(name: \"environment/sun\")\n      )!\n    )\n    var sunUniforms = CelestialBodyUniforms(\n      transformation:\n        MatrixUtil.scalingMatrix(Self.sunSize / 2)\n          * MatrixUtil.translationMatrix(Vec3f(0, Self.sunDistance, 0))\n          * MatrixUtil.rotationMatrix(-.pi / 2, around: .y)\n          * MatrixUtil.rotationMatrix(client.game.world.getSunAngleRadians(), around: .z)\n          * playerToClip,\n      textureIndex: sunTextureIndex,\n      uvPosition: Vec2f(0, 0),\n      uvSize: Vec2f(1/8, 1/8),\n      type: .sun\n    )\n\n    sunUniformsBuffer.contents().copyMemory(\n      from: &sunUniforms,\n      byteCount: MemoryLayout<CelestialBodyUniforms>.stride\n    )\n\n    encoder.setRenderPipelineState(celestialBodyRenderPipelineState)\n    encoder.setVertexBuffer(quadVertexBuffer, offset: 0, index: 0)\n    encoder.setVertexBuffer(sunUniformsBuffer, offset: 0, index: 1)\n    encoder.setFragmentTexture(environmentTexturePalette.arrayTexture, index: 0)\n\n    encoder.drawIndexedPrimitives(\n      type: .triangle,\n      indexCount: Self.quadIndices.count,\n      indexType: .uint32,\n      indexBuffer: quadIndexBuffer,\n      indexBufferOffset: 0\n    )\n\n    // Render the moon\n    let moonPhase = client.game.world.getMoonPhase()\n    let moonUVPosition = Vec2f(\n      Float(moonPhase % 4) * 1/8,\n      Float(moonPhase / 4) * 1/8\n    )\n    let moonTextureIndex = UInt16(\n      environmentTexturePalette.textureIndex(\n        for: Identifier(name: \"environment/moon_phases\")\n      )!\n    )\n    var moonUniforms = CelestialBodyUniforms(\n      transformation:\n        MatrixUtil.scalingMatrix(Self.moonSize / 2)\n          * MatrixUtil.translationMatrix(Vec3f(0, Self.moonDistance, 0))\n          * MatrixUtil.rotationMatrix(-.pi / 2, around: .y)\n          * MatrixUtil.rotationMatrix(client.game.world.getSunAngleRadians() + .pi, around: .z)\n          * playerToClip,\n      textureIndex: moonTextureIndex,\n      uvPosition: moonUVPosition,\n      uvSize: Vec2f(1/8, 1/8),\n      type: .sun\n    )\n\n    moonUniformsBuffer.contents().copyMemory(\n      from: &moonUniforms,\n      byteCount: MemoryLayout<CelestialBodyUniforms>.stride\n    )\n\n    encoder.setVertexBuffer(moonUniformsBuffer, offset: 0, index: 1)\n\n    encoder.drawIndexedPrimitives(\n      type: .triangle,\n      indexCount: Self.quadIndices.count,\n      indexType: .uint32,\n      indexBuffer: quadIndexBuffer,\n      indexBufferOffset: 0\n    )\n\n    let starBrightness = client.game.world.getStarBrightness()\n    if starBrightness > 0 {\n      var starUniforms = StarUniforms(\n        transformation:\n          MatrixUtil.rotationMatrix(-.pi / 2, around: .y)\n            * MatrixUtil.rotationMatrix(client.game.world.getSunAngleRadians(), around: .z)\n            * playerToClip,\n        brightness: starBrightness\n      )\n      starUniformsBuffer.contents().copyMemory(\n        from: &starUniforms,\n        byteCount: MemoryLayout<StarUniforms>.stride\n      )\n\n      encoder.setRenderPipelineState(starRenderPipelineState)\n      encoder.setVertexBuffer(starVertexBuffer, offset: 0, index: 0)\n      encoder.setVertexBuffer(starUniformsBuffer, offset: 0, index: 1)\n      encoder.setFragmentBuffer(starUniformsBuffer, offset: 0, index: 0)\n\n      encoder.drawIndexedPrimitives(\n        type: .triangle,\n        indexCount: starIndexBuffer.length / MemoryLayout<UInt32>.stride,\n        indexType: .uint32,\n        indexBuffer: starIndexBuffer,\n        indexBufferOffset: 0\n      )\n    }\n\n    // Render the void plane if visible.\n    let voidPlaneVisibilityThreshold = client.game.world.isFlat\n      ? Self.superFlatVoidPlaneVisibilityThreshold\n      : Self.defaultVoidPlaneVisibilityThreshold\n\n    if position.y <= voidPlaneVisibilityThreshold {\n      var voidPlaneUniforms = SkyPlaneUniforms(\n        skyColor: Vec4f(0, 0, 0, 1),\n        fogColor: Vec4f(fogColor, 1),\n        fogStart: 0,\n        fogEnd: Float(renderDistance * Chunk.width),\n        size: Self.voidPlaneSize,\n        verticalOffset: Self.voidPlaneOffset,\n        playerToClip: playerToClip\n      )\n\n      voidPlaneUniformsBuffer.contents().copyMemory(\n        from: &voidPlaneUniforms,\n        byteCount: MemoryLayout<SkyPlaneUniforms>.stride\n      )\n\n      encoder.setRenderPipelineState(skyPlaneRenderPipelineState)\n      encoder.setVertexBuffer(quadVertexBuffer, offset: 0, index: 0)\n      encoder.setVertexBuffer(voidPlaneUniformsBuffer, offset: 0, index: 1)\n      encoder.setFragmentBuffer(voidPlaneUniformsBuffer, offset: 0, index: 0)\n\n      encoder.drawIndexedPrimitives(\n        type: .triangle,\n        indexCount: Self.quadIndices.count,\n        indexType: .uint32,\n        indexBuffer: quadIndexBuffer,\n        indexBufferOffset: 0\n      )\n    }\n  }\n\n  /// Generates the vertices for a triangle fan. Think of it like a pizza, which\n  /// has a single central vertex which is touched by one corner of every slice.\n  /// The triangle fan generated has 16 slices, with the central vertex being\n  /// vertex 0, and the next 16 vertices forming a clockwise ring.\n  static func generateSunriseDiscVertices() -> [Vec3f] {\n    var vertices: [Vec3f] = [Vec3f(100, 0, 0)]\n    for i in 0..<16 {\n      let t = Float(i) / 16 * 2 * .pi\n      vertices.append(Vec3f(\n        120 * Foundation.cos(t),\n        40 * Foundation.cos(t),\n        120 * Foundation.sin(t)\n      ))\n    }\n    return vertices\n  }\n\n  /// Generates the indices for a triangle fan. Think of it like a pizza, which\n  /// has a single central vertex which is touched by one corner of every slice.\n  /// The triangle fan is assumed to have 16 slices, with the central vertex\n  /// being vertex 0, and the next 16 vertices forming a clockwise ring.\n  static func generateSunriseDiscIndices() -> [UInt32] {\n    var indices: [UInt32] = []\n    for i in 0..<16 {\n      indices.append(contentsOf: [\n        0,\n        UInt32(i) + 1,\n        ((UInt32(i) + 1) % 16) + 1\n      ])\n    }\n    return indices\n  }\n\n  /// Generates the vertices for the star mesh.\n  static func generateStarVertices() -> [Vec3f] {\n    let baseVertices: [Vec4f] = [\n      Vec4f(-1, 0, -1, 1),\n      Vec4f(1, 0, -1, 1),\n      Vec4f(1, 0, 1, 1),\n      Vec4f(-1, 0, 1, 1)\n    ]\n\n    var vertices: [Vec3f] = []\n    var random = Random(10842)\n    for _ in 0..<1500 {\n      let direction = Vec3f(\n        random.nextFloat(),\n        random.nextFloat(),\n        random.nextFloat()\n      ) * 2 - 1\n\n      // Generate the size before skipping the iteration because otherwise\n      // the random number generator gets out of sync with what it would be\n      // for Vanilla\n      let starSize = 0.15 + random.nextFloat() * 0.1\n\n      let magnitude = direction.magnitude\n      guard magnitude > 0.0001, magnitude < 1 else {\n        continue\n      }\n\n      let yaw = -Foundation.atan2(direction.x, direction.z)\n      let horizontalMagnitude = Foundation.sqrt(\n        direction.x * direction.x + direction.z * direction.z\n      )\n      let pitch = Foundation.atan2(horizontalMagnitude, direction.y)\n\n      // Using `nextDouble` to have the same behaviour as Vanilla (it matters because of the\n      // random number generator).\n      let starRotation = Float(random.nextDouble() * 2 * .pi)\n      let transformation = MatrixUtil.scalingMatrix(starSize)\n        * MatrixUtil.rotationMatrix(-starRotation, around: .y)\n        * MatrixUtil.translationMatrix(Vec3f(0, 100, 0))\n        * MatrixUtil.rotationMatrix(pitch, around: .x)\n        * MatrixUtil.rotationMatrix(yaw, around: .y)\n\n      vertices.append(contentsOf: baseVertices.map { vertex in\n        let position = vertex * transformation\n        return Vec3f(\n          position.x,\n          position.y,\n          position.z\n        ) / position.w\n      })\n    }\n    return vertices\n  }\n\n  /// Generates the indices for the star mesh.\n  static func generateStarIndices(starCount: Int) -> [UInt32] {\n    var indices: [UInt32] = []\n    for i in 0..<starCount {\n      let offset = i * 4\n      let quadIndices = [0, 1, 2, 2, 3, 0]\n        .map { index in\n          UInt32(index + offset)\n        }\n      indices.append(contentsOf: quadIndices)\n    }\n    return indices\n  }\n\n  static func generateEndSkyVertices() -> [EndSkyVertex] {\n    return [\n      // North\n      EndSkyVertex(position: Vec3f(-100, 100, -100), uv: Vec2f(0, 0)),\n      EndSkyVertex(position: Vec3f(100, 100, -100), uv: Vec2f(1, 0)),\n      EndSkyVertex(position: Vec3f(100, -100, -100), uv: Vec2f(1, 1)),\n      EndSkyVertex(position: Vec3f(-100, -100, -100), uv: Vec2f(0, 1)),\n\n      // South\n      EndSkyVertex(position: Vec3f(-100, -100, 100), uv: Vec2f(0, 0)),\n      EndSkyVertex(position: Vec3f(100, -100, 100), uv: Vec2f(1, 0)),\n      EndSkyVertex(position: Vec3f(100, 100, 100), uv: Vec2f(1, 1)),\n      EndSkyVertex(position: Vec3f(-100, 100, 100), uv: Vec2f(0, 1)),\n\n      // East\n      EndSkyVertex(position: Vec3f(100, -100, -100), uv: Vec2f(0, 0)),\n      EndSkyVertex(position: Vec3f(100, 100, -100), uv: Vec2f(1, 0)),\n      EndSkyVertex(position: Vec3f(100, 100, 100), uv: Vec2f(1, 1)),\n      EndSkyVertex(position: Vec3f(100, -100, 100), uv: Vec2f(0, 1)),\n\n      // West\n      EndSkyVertex(position: Vec3f(-100, 100, -100), uv: Vec2f(0, 0)),\n      EndSkyVertex(position: Vec3f(-100, -100, -100), uv: Vec2f(1, 0)),\n      EndSkyVertex(position: Vec3f(-100, -100, 100), uv: Vec2f(1, 1)),\n      EndSkyVertex(position: Vec3f(-100, 100, 100), uv: Vec2f(0, 1)),\n\n      // Above\n      EndSkyVertex(position: Vec3f(100, 100, 100), uv: Vec2f(0, 0)),\n      EndSkyVertex(position: Vec3f(-100, 100, 100), uv: Vec2f(1, 0)),\n      EndSkyVertex(position: Vec3f(-100, 100, -100), uv: Vec2f(1, 1)),\n      EndSkyVertex(position: Vec3f(100, 100, -100), uv: Vec2f(0, 1)),\n\n      // Before\n      EndSkyVertex(position: Vec3f(-100, -100, -100), uv: Vec2f(0, 0)),\n      EndSkyVertex(position: Vec3f(100, -100, -100), uv: Vec2f(1, 0)),\n      EndSkyVertex(position: Vec3f(100, -100, 100), uv: Vec2f(1, 1)),\n      EndSkyVertex(position: Vec3f(-100, -100, 100), uv: Vec2f(0, 1)),\n    ]\n  }\n\n  static func generateEndSkyIndices() -> [UInt32] {\n    var indices: [UInt32] = []\n    for i in 0..<6 {\n      indices.append(contentsOf: [\n        0, 1, 2, 2, 3, 0\n      ].map { UInt32($0 + i * 4) })\n    }\n    return indices\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/SkyPlaneUniforms.swift",
    "content": "import FirebladeMath\n\npublic struct SkyPlaneUniforms {\n  public var skyColor: Vec4f\n  public var fogColor: Vec4f\n  public var fogStart: Float\n  public var fogEnd: Float\n  public var size: Float\n  public var verticalOffset: Float\n  public var playerToClip: Mat4x4f\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/StarUniforms.swift",
    "content": "import FirebladeMath\n\n/// Uniforms for the star mesh rendered at night.\npublic struct StarUniforms {\n  public var transformation: Mat4x4f\n  public var brightness: Float\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/SunriseDiscUniforms.swift",
    "content": "import FirebladeMath\n\n// TODO: Maybe to be more clear that it's not just sunrise, but sunset too, the\n//   sunrise disc could be named more technically, like the RayleighScatteringDisc\n//   or something (I don't like that one though, it loses clarity in other ways).\n/// The uniforms for the sunrise/sunset disc.\npublic struct SunriseDiscUniforms {\n  public var color: Vec4f\n  public var transformation: Mat4x4f\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/Util/MetalUtil.swift",
    "content": "import Metal\n\npublic enum MetalUtil {\n  /// Makes a render pipeline state with the given properties.\n  public static func makeRenderPipelineState(\n    device: MTLDevice,\n    label: String,\n    vertexFunction: MTLFunction,\n    fragmentFunction: MTLFunction,\n    blendingEnabled: Bool,\n    editDescriptor: ((MTLRenderPipelineDescriptor) -> Void)? = nil,\n    isOffScreenPass: Bool = true\n  ) throws -> MTLRenderPipelineState {\n    let descriptor = MTLRenderPipelineDescriptor()\n    descriptor.label = label\n    descriptor.vertexFunction = vertexFunction\n    descriptor.fragmentFunction = fragmentFunction\n    descriptor.colorAttachments[0].pixelFormat = .bgra8Unorm\n    descriptor.depthAttachmentPixelFormat = .depth32Float\n\n    // Optionally include accumulation and revealage buffers for order independent transparency\n    if isOffScreenPass {\n      descriptor.colorAttachments[1].pixelFormat = .bgra8Unorm\n      descriptor.colorAttachments[2].pixelFormat = .r8Unorm\n    }\n\n    if blendingEnabled {\n      descriptor.colorAttachments[0].isBlendingEnabled = true\n      descriptor.colorAttachments[0].rgbBlendOperation = .add\n      descriptor.colorAttachments[0].alphaBlendOperation = .add\n      descriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha\n      descriptor.colorAttachments[0].sourceAlphaBlendFactor = .zero\n      descriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha\n      descriptor.colorAttachments[0].destinationAlphaBlendFactor = .zero\n    }\n\n    editDescriptor?(descriptor)\n\n    do {\n      return try device.makeRenderPipelineState(descriptor: descriptor)\n    } catch {\n      throw RenderError.failedToCreateRenderPipelineState(error, label: label)\n    }\n  }\n\n  /// Loads the default metal library from the app bundle.\n  ///\n  /// The default library is at `DeltaClient.app/Contents/Resources/DeltaCore_DeltaCore.bundle/Resources/default.metallib`.\n  public static func loadDefaultLibrary(_ device: MTLDevice) throws -> MTLLibrary {\n    #if os(macOS)\n      let bundlePath = \"Contents/Resources/DeltaCore_DeltaRenderer.bundle\"\n    #elseif os(iOS) || os(tvOS)\n      let bundlePath = \"DeltaCore_DeltaRenderer.bundle\"\n    #else\n      #error(\"Unsupported platform, unknown DeltaCore bundle location\")\n    #endif\n\n    guard let bundle = Bundle(url: Bundle.main.bundleURL.appendingPathComponent(bundlePath)) else {\n      throw RenderError.failedToGetBundle\n    }\n\n    guard let libraryURL = bundle.url(forResource: \"default\", withExtension: \"metallib\") else {\n      throw RenderError.failedToLocateMetallib\n    }\n\n    do {\n      return try device.makeLibrary(URL: libraryURL)\n    } catch {\n      throw RenderError.failedToCreateMetallib(error)\n    }\n  }\n\n  /// Loads a metal function from the given library.\n  /// - Parameters:\n  ///   - name: Name of the function.\n  ///   - library: Library containing the function.\n  /// - Returns: The function.\n  public static func loadFunction(_ name: String, from library: MTLLibrary) throws -> MTLFunction {\n    guard let function = library.makeFunction(name: name) else {\n      log.warning(\"Failed to load shader: '\\(name)'\")\n      throw RenderError.failedToLoadShaders\n    }\n\n    return function\n  }\n\n  /// Creates a populated buffer.\n  /// - Parameters:\n  ///   - device: Device to create the buffer with.\n  ///   - bytes: Bytes to populate the buffer with.\n  ///   - length: Length of the buffer.\n  ///   - options: Resource options for the buffer\n  ///   - label: Label to give the buffer.\n  /// - Returns: A new buffer.\n  public static func makeBuffer(\n    _ device: MTLDevice,\n    bytes: UnsafeRawPointer,\n    length: Int,\n    options: MTLResourceOptions,\n    label: String? = nil\n  ) throws -> MTLBuffer {\n    guard let buffer = device.makeBuffer(bytes: bytes, length: length, options: options) else {\n      throw RenderError.failedToCreateBuffer(label: label)\n    }\n\n    buffer.label = label\n    return buffer\n  }\n\n  /// Creates a buffer for sampling the requested set of counters.\n  /// - Parameters:\n  ///   - device: The device that sampling will be performed on.\n  ///   - commonCounterSet: The counter set that the buffer will be used to sample.\n  ///   - sampleCount: The size of sampling buffer to create.\n  /// - Returns: A buffer for storing counter samples.\n  public static func makeCounterSampleBuffer(\n    _ device: MTLDevice,\n    counterSet commonCounterSet: MTLCommonCounterSet,\n    sampleCount: Int\n  ) throws -> MTLCounterSampleBuffer {\n    var counterSet: MTLCounterSet?\n    for deviceCounterSet in device.counterSets ?? [] {\n      if deviceCounterSet.name.caseInsensitiveCompare(commonCounterSet.rawValue) == .orderedSame {\n        counterSet = deviceCounterSet\n        break\n      }\n    }\n\n    guard let counterSet = counterSet else {\n      throw RenderError.failedToGetCounterSet(commonCounterSet.rawValue)\n    }\n\n    let descriptor = MTLCounterSampleBufferDescriptor()\n    descriptor.counterSet = counterSet\n    descriptor.storageMode = .shared\n    descriptor.sampleCount = sampleCount\n\n    do {\n      return try device.makeCounterSampleBuffer(descriptor: descriptor)\n    } catch {\n      throw RenderError.failedToMakeCounterSampleBuffer(error)\n    }\n  }\n\n  /// Creates an empty buffer.\n  /// - Parameters:\n  ///   - device: Device to create the buffer with.\n  ///   - length: Length of the buffer.\n  ///   - options: Resource options for the buffer.\n  ///   - label: Label to give the buffer.\n  /// - Returns: An empty buffer.\n  public static func makeBuffer(\n    _ device: MTLDevice,\n    length: Int,\n    options: MTLResourceOptions,\n    label: String? = nil\n  ) throws -> MTLBuffer {\n    guard let buffer = device.makeBuffer(length: length, options: options) else {\n      throw RenderError.failedToCreateBuffer(label: label)\n    }\n\n    buffer.label = label\n    return buffer\n  }\n\n  /// Creates a simple depth stencil state.\n  /// - Parameters:\n  ///   - device: Device to create the state with.\n  ///   - readOnly: If `true`, the depth texture will not be written to.\n  /// - Returns: A depth stencil state.\n  public static func createDepthState(\n    device: MTLDevice,\n    readOnly: Bool = false\n  ) throws -> MTLDepthStencilState {\n    let depthDescriptor = MTLDepthStencilDescriptor()\n    depthDescriptor.depthCompareFunction = .lessEqual\n    depthDescriptor.isDepthWriteEnabled = !readOnly\n\n    guard let depthState = device.makeDepthStencilState(descriptor: depthDescriptor) else {\n      throw RenderError.failedToCreateWorldDepthStencilState\n    }\n\n    return depthState\n  }\n\n  /// Creates a custom render pass descriptor with default clear / store actions.\n  /// - Parameter device: Device to create the descriptor with.\n  /// - Returns: A render pass descriptor with custom render targets.\n  public static func createRenderPassDescriptor(\n    _ device: MTLDevice,\n    targetRenderTexture: MTLTexture,\n    targetDepthTexture: MTLTexture,\n    clearColour: MTLClearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 1)\n  ) -> MTLRenderPassDescriptor {\n    let renderPassDescriptor = MTLRenderPassDescriptor()\n    renderPassDescriptor.colorAttachments[0].texture = targetRenderTexture\n    renderPassDescriptor.colorAttachments[0].clearColor = clearColour\n    renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadAction.clear\n    renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreAction.store\n\n    renderPassDescriptor.depthAttachment.texture = targetDepthTexture\n    renderPassDescriptor.depthAttachment.loadAction = MTLLoadAction.clear\n    renderPassDescriptor.depthAttachment.storeAction = MTLStoreAction.store\n\n    return renderPassDescriptor\n  }\n\n  public static func createTexture(\n    device: MTLDevice,\n    width: Int,\n    height: Int,\n    pixelFormat: MTLPixelFormat,\n    editDescriptor: ((MTLTextureDescriptor) -> Void)? = nil\n  ) throws -> MTLTexture {\n    let descriptor = MTLTextureDescriptor()\n    descriptor.usage = [.shaderRead, .renderTarget]\n    descriptor.width = width\n    descriptor.height = height\n    descriptor.pixelFormat = pixelFormat\n\n    editDescriptor?(descriptor)\n\n    guard let texture = device.makeTexture(descriptor: descriptor) else {\n      throw RenderError.failedToUpdateRenderTargetSize\n    }\n\n    return texture\n  }\n\n  /// Creates a buffer on the GPU containing a given array. Reuses the supplied private buffer if it's big enough.\n  /// - Returns: A new private buffer.\n  public static func createPrivateBuffer<T>(\n    labelled label: String = \"buffer\",\n    containing items: [T],\n    reusing existingBuffer: MTLBuffer? = nil,\n    device: MTLDevice,\n    commandQueue: MTLCommandQueue\n  ) throws -> MTLBuffer {\n    precondition(\n      existingBuffer?.storageMode == .private || existingBuffer == nil,\n      \"existingBuffer must have a storageMode of private\"\n    )\n\n    // First copy the array to a scratch buffer (accessible from both CPU and GPU)\n    let bufferSize = MemoryLayout<T>.stride * items.count\n    guard\n      let sharedBuffer = device.makeBuffer(\n        bytes: items,\n        length: bufferSize,\n        options: [.storageModeShared]\n      )\n    else {\n      throw RenderError.failedToCreateBuffer(label: label)\n    }\n\n    // Create a private buffer (only accessible from GPU) or reuse the existing buffer if possible\n    let privateBuffer: MTLBuffer\n    if let existingBuffer = existingBuffer, existingBuffer.length >= bufferSize {\n      privateBuffer = existingBuffer\n    } else {\n      guard\n        let buffer = device.makeBuffer(length: bufferSize, options: [.storageModePrivate])\n      else {\n        throw RenderError.failedToCreateBuffer(label: label)\n      }\n      privateBuffer = buffer\n    }\n    privateBuffer.label = label\n\n    guard\n      let commandBuffer = commandQueue.makeCommandBuffer(),\n      let encoder = commandBuffer.makeBlitCommandEncoder()\n    else {\n      throw RenderError.failedToCreateBlitCommandEncoder\n    }\n\n    // Encode and commit a blit operation to copy the contents of the scratch buffer into the private buffer\n    encoder.copy(\n      from: sharedBuffer,\n      sourceOffset: 0,\n      to: privateBuffer,\n      destinationOffset: 0,\n      size: bufferSize\n    )\n    encoder.endEncoding()\n    commandBuffer.commit()\n\n    return privateBuffer\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/World/BlockVertex.swift",
    "content": "import Foundation\n\n/// The vertex format used by the chunk block shader.\npublic struct BlockVertex {\n  var x: Float\n  var y: Float\n  var z: Float\n  var u: Float\n  var v: Float\n  var r: Float\n  var g: Float\n  var b: Float\n  var a: Float\n  var skyLightLevel: UInt8\n  var blockLightLevel: UInt8\n  var textureIndex: UInt16\n  var isTransparent: Bool\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/World/LightMap.swift",
    "content": "import Foundation\nimport Metal\nimport FirebladeMath\n// TODO: Move Vec3 and extensions of FirebladeMath into FirebladeMath to avoid this unnecessary import\nimport DeltaCore\n\n// TODO: Make LightMap an ECS singleton\nstruct LightMap {\n  static let gamma: Double = 0\n\n  var pixels: [Vec3<UInt8>]\n  var blockFlicker: Double = 1\n  var ambientLight: Double\n  var baseLevels: [Double]\n  var lastFlickerUpdateTick: Int?\n  var hasChanged = true\n\n  init(ambientLight: Double) {\n    baseLevels = Self.generateBaseLevels(ambientLight)\n    self.ambientLight = ambientLight\n\n    pixels = []\n    for _ in 0..<LightLevel.levelCount {\n      for _ in 0...LightLevel.levelCount {\n        pixels.append(.zero)\n      }\n    }\n  }\n\n  // TODO: Refactor this function to be more delta clienty and look less like spaghetti Java code\n  mutating func update(\n    tick: Int,\n    sunAngleRadians: Float,\n    ambientLight: Double,\n    dimensionHasSkyLight: Bool\n  ) {\n    guard tick != lastFlickerUpdateTick || ambientLight != self.ambientLight else {\n      return\n    }\n\n    hasChanged = true\n\n    // Update base levels if ambient light has changed\n    if ambientLight != self.ambientLight {\n      baseLevels = Self.generateBaseLevels(ambientLight)\n      self.ambientLight = ambientLight\n    }\n\n    // Update block brightness\n    updateBlockFlicker(tick)\n    let blockBrightness = blockFlicker + 1.5\n\n    // Update sky brightness\n    // TODO: When lightning is occurring, hardcode brightness to 1\n    // TODO: implement night vision and water effects\n    let sunBrightness = Self.getSunBrightness(sunAngleRadians: sunAngleRadians)\n    let skyBrightness = sunBrightness * 0.95 + 0.05\n\n    let r = MathUtil.lerp(from: skyBrightness, to: 1, progress: 0.35)\n    let g = MathUtil.lerp(from: skyBrightness, to: 1, progress: 0.35)\n    let b = 1.0\n    let skyColor = Vec3d(r, g, b)\n\n    // Update light map\n    for skyLightLevel in 0..<LightLevel.levelCount {\n      for blockLightLevel in 0..<LightLevel.levelCount {\n        let sky = baseLevels[skyLightLevel] * skyBrightness\n        let block = baseLevels[blockLightLevel] * blockBrightness\n        let blockColor = Vec3d(\n          block,\n          block * ((block * 0.6 + 0.4) * 0.6 + 0.4),\n          block * (block * block * 0.6 + 0.4)\n        )\n\n        var pixel = blockColor\n\n        if dimensionHasSkyLight {\n          pixel += skyColor * sky\n          pixel = MathUtil.lerp(from: pixel, to: Vec3d(repeating: 0.75), progress: 0.04)\n        } else {\n          pixel = MathUtil.lerp(from: pixel, to: [0.99, 1.12, 1.0], progress: 0.25)\n        }\n\n        pixel = MathUtil.clamp(pixel, min: 0.0, max: 1.0)\n\n        var copy = pixel\n        copy.x = Self.inverseGamma(pixel.x)\n        copy.y = Self.inverseGamma(pixel.y)\n        copy.z = Self.inverseGamma(pixel.z)\n\n        pixel = MathUtil.lerp(from: pixel, to: copy, progress: Self.gamma)\n        pixel = MathUtil.lerp(from: pixel, to: Vec3d(repeating: 0.75), progress: 0.04)\n        pixel = MathUtil.clamp(pixel, min: 0.0, max: 1.0)\n        pixel *= 255\n\n        let index = Self.index(skyLightLevel, blockLightLevel)\n        pixels[index] = Vec3<UInt8>(pixel)\n      }\n    }\n  }\n\n  mutating func updateBlockFlicker(_ tick: Int) {\n    let updateCount: Int\n    if let lastFlickerUpdateTick = lastFlickerUpdateTick {\n      updateCount = tick - lastFlickerUpdateTick\n    } else {\n      updateCount = 1\n    }\n\n    guard updateCount != 0 else {\n      return\n    }\n\n    var rng = Random()\n    for _ in 0..<updateCount {\n      blockFlicker += (rng.nextDouble() - rng.nextDouble()) * rng.nextDouble() * rng.nextDouble() * 0.1\n      blockFlicker *= 0.9\n    }\n\n    lastFlickerUpdateTick = tick\n  }\n\n  mutating func getBuffer(_ device: MTLDevice, reusing previousBuffer: MTLBuffer? = nil) throws -> MTLBuffer {\n    defer {\n      hasChanged = false\n    }\n\n    let byteCount = LightLevel.levelCount * LightLevel.levelCount * MemoryLayout<Vec3<UInt8>>.stride\n    if let previousBuffer = previousBuffer {\n      if hasChanged {\n        previousBuffer.contents().copyMemory(from: &pixels, byteCount: byteCount)\n      }\n      return previousBuffer\n    } else {\n      return try MetalUtil.makeBuffer(\n        device,\n        bytes: &pixels,\n        length: byteCount,\n        options: .storageModeShared,\n        label: \"lightMap\"\n      )\n    }\n  }\n\n  static func index(_ skyLightLevel: Int, _ blockLightLevel: Int) -> Int {\n    return skyLightLevel * LightLevel.levelCount + blockLightLevel\n  }\n\n  static func inverseGamma(_ value: Double) -> Double {\n    let value = 1 - value\n    return 1 - value * value * value * value\n  }\n\n  static func generateBaseLevels(_ ambientLight: Double) -> [Double] {\n    var levels: [Double] = []\n\n    for i in 0..<LightLevel.levelCount {\n      var level = Double(i) / Double(LightLevel.maximumLightLevel)\n      level /= (4 - 3 * level)\n      levels.append(MathUtil.lerp(from: level, to: 1, progress: ambientLight))\n    }\n\n    return levels\n  }\n\n  static func getSunBrightness(sunAngleRadians: Float) -> Double {\n    // TODO: Implement the effect of rain and thunder on sun brightness\n    var brightness = Double(Foundation.cos(sunAngleRadians) * 2 + 0.2)\n    brightness = MathUtil.clamp(brightness, 0, 1)\n    return brightness * 0.8 + 0.2\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/World/Visibility/ChunkSectionFace.swift",
    "content": "import DeltaCore\n\n/// Used by ``ChunkSectionFaceConnectivity`` to efficiently identify pairs of faces.\n///\n/// If the values of any two faces are added together, the result is a unique integer\n/// that represents that pair of faces.\n///\n/// ``ChunkSectionFaceConnectivity`` uses this property to efficiently store/access\n/// whether a given pair of faces is connected. A bit field is used where the value\n/// for each pair is the offset of a bit representing whether those two faces are\n/// connected.\n///\n/// Its cases are in the same order as ``DirectionSet``.\npublic enum ChunkSectionFace: Int, CaseIterable {\n  case north = 0\n  case south = 1\n  case east = 2\n  case west = 4\n  case up = 7\n  case down = 12\n\n  public static var faces: [ChunkSectionFace] = [\n    .north,\n    .south,\n    .east,\n    .west,\n    .up,\n    .down]\n\n  public static func forDirection(_ direction: Direction) -> ChunkSectionFace {\n    switch direction {\n      case .down:\n        return .down\n      case .up:\n        return .up\n      case .north:\n        return .north\n      case .south:\n        return .south\n      case .west:\n        return .west\n      case .east:\n        return .east\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/World/Visibility/ChunkSectionFaceConnectivity.swift",
    "content": "import DeltaCore\n\n/// Stores which pairs faces in a chunk section are connected to each other. Not threadsafe.\n///\n/// ``setConnected(_:_:)`` and ``setDisconnected(_:_:)`` are separate functions because this allows\n/// branching to be eliminated and in most cases the parameter specifying whether they are connected\n/// or not would be hardcoded anyway.\n///\n/// The efficiency of this storage method comes from using a bit field as the underlying storage and\n/// using ``ChunkSectionFace``'s raw values to efficiently identify each pair of faces. See\n/// ``ChunkSectionFace`` for a more detailed explanation.\npublic struct ChunkSectionFaceConnectivity: Equatable {\n  // MARK: Public properties\n\n  /// The connectivity for a fully connected chunk section.\n  public static let fullyConnected = ChunkSectionFaceConnectivity(bitField: 0xffffffff)\n\n  // MARK: Private properties\n\n  /// The underlying storage for the connectivity information.\n  private var bitField: UInt32 = 0\n\n  // MARK: Init\n\n  /// Creates an empty connectivity graph where none of the faces are connected.\n  public init() {}\n\n  /// Only use if you know what you're doing.\n  /// - Parameter bitField: Initial value of the underlying bitfield.\n  private init(bitField: UInt32) {\n    self.bitField = bitField\n  }\n\n  // MARK: Public methods\n\n  /// Marks a pair of faces as connected.\n  /// - Parameters:\n  ///   - firstFace: The first face.\n  ///   - secondFace: The seconds face.\n  public mutating func setConnected(_ firstFace: ChunkSectionFace, _ secondFace: ChunkSectionFace) {\n    let hashValue = firstFace.rawValue + secondFace.rawValue\n    bitField |= 1 << hashValue\n  }\n\n  /// Marks a pair of faces as disconnected.\n  /// - Parameters:\n  ///   - firstFace: The first face.\n  ///   - secondFace: The second face.\n  public mutating func setDisconnected(_ firstFace: ChunkSectionFace, _ secondFace: ChunkSectionFace) {\n    let hashValue = firstFace.rawValue + secondFace.rawValue\n    bitField ^= ~(1 << hashValue)\n  }\n\n  /// Gets whether a pair of faces are connected or not.\n  /// - Parameters:\n  ///   - firstFace: The first face.\n  ///   - secondFace: The second face.\n  /// - Returns: Whether the faces are connected or not.\n  public func areConnected(_ firstFace: ChunkSectionFace, _ secondFace: ChunkSectionFace) -> Bool {\n    let hashValue = firstFace.rawValue + secondFace.rawValue\n    return bitField & (1 << hashValue) != 0\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/World/Visibility/ChunkSectionVoxelGraph.swift",
    "content": "import DeltaCore\n\n/// A 3d data structure used for flood filling chunk sections. Each 'voxel' is represented by an integer.\n///\n/// The graph is initialised with `true` representing voxels that cannot be seen through whatsoever and `false` representing all other voxels.\n/// ``calculateConnectivity()`` figures out which faces are connected. When faces aren't connected it means that a ray cannot be created\n/// that enters through one of the faces and out the other. When faces are connected it doesn't necessarily mean such a ray exists, only that it might exist.\n///\n/// Flood fills fill connected volumes of `false` with `true` and record which faces the fill reached. If two faces are reached by the\n/// same fill, they are counted as connected.\npublic struct ChunkSectionVoxelGraph {\n  /// The width, height and depth of the image (they're all equal to this number).\n  public let dimension: Int\n  /// The number of voxels per layer of the graph (``dimension`` squared).\n  public let voxelsPerLayer: Int\n\n  /// The initial number of voxels that weren't set to 0.\n  private var initialVoxelCount: Int\n  /// Stores the image's voxels. Indexed by `y * voxelsPerLayer + z * dimension + x`.\n  private var voxels: [Bool]\n  /// Used to efficiently set voxels values in ``voxels``. Will be invalid in copies of the graph (because it's a struct).\n  private var mutableVoxelsPointer: UnsafeMutableBufferPointer<Bool>\n\n  // MARK: Private methods\n\n  /// Creates a new graph of the section for figuring out face connectivity.\n  /// - Parameter section: Section to create graph for.\n  /// - Parameter blockModelPalette: Used to determine which blocks are solid.\n  public init(for section: Chunk.Section, blockModelPalette: BlockModelPalette) {\n    assert(\n      Chunk.Section.width == Chunk.Section.height && Chunk.Section.height == Chunk.Section.depth,\n      \"Attempted to create a ChunkSectionVoxelGraph from non-cubic chunk section\")\n\n    dimension = Chunk.Section.width\n    voxelsPerLayer = dimension * dimension\n\n    assert(dimension.isPowerOfTwo, \"Attempted to initialise a ChunkSectionVoxelGraph with a dimension that isn't a power of two.\")\n\n    initialVoxelCount = 0\n    voxels = []\n    voxels.reserveCapacity(section.blocks.count)\n    for block in section.blocks {\n      let isFullyOpaque = blockModelPalette.isBlockFullyOpaque(Int(block))\n      voxels.append(isFullyOpaque)\n      if isFullyOpaque {\n        initialVoxelCount += 1\n      }\n    }\n\n    mutableVoxelsPointer = self.voxels.withUnsafeMutableBufferPointer { $0 }\n\n    assert(\n      dimension * dimension * dimension == voxels.count,\n      \"Attempted to initialise a ChunkSectionVoxelGraph with an invalid number of voxels for its dimension (dimension=\\(dimension), voxel_count=\\(voxels.count)\"\n    )\n  }\n\n  // MARK: Public methods\n\n  /// Gets information about the connectivity of the section's faces. See ``ChunkSectionVoxelGraph``.\n  /// - Returns: Information about which pairs of faces are connected.\n  public mutating func calculateConnectivity() -> ChunkSectionFaceConnectivity {\n    if initialVoxelCount == 0 {\n      return ChunkSectionFaceConnectivity.fullyConnected\n    }\n\n    var connectivity = ChunkSectionFaceConnectivity()\n\n    // Make sure that the pointer is still valid\n    mutableVoxelsPointer = voxels.withUnsafeMutableBufferPointer { $0 }\n\n    let emptyVoxelCoordinates = emptyVoxelCoordinates()\n    var nextEmptyVoxel = 0\n\n  outerLoop:\n    while true {\n      var seedX: Int?\n      var seedY: Int?\n      var seedZ: Int?\n\n      // Find the next voxel that hasn't been filled yet\n      while true {\n        if nextEmptyVoxel == emptyVoxelCoordinates.count {\n          break outerLoop\n        }\n\n        let (x, y, z) = emptyVoxelCoordinates[nextEmptyVoxel]\n        nextEmptyVoxel += 1\n\n        if getVoxel(x: x, y: y, z: z) == false {\n          seedX = x\n          seedY = y\n          seedZ = z\n          break\n        }\n      }\n\n      // Flood fill the section (starting at the seed voxel) and update the connectivity\n      // of the section depending on which faces the fill reached.\n      if let seedX = seedX, let seedY = seedY, let seedZ = seedZ {\n        var group = DirectionSet()\n        iterativeFloodFill(x: seedX, y: seedY, z: seedZ, group: &group)\n        for i in 0..<5 {\n          for j in (i+1)..<6 {\n            let first = Direction.allDirections[i]\n            let second = Direction.allDirections[j]\n\n            if group.contains(first) && group.contains(second) {\n              let first = ChunkSectionFace.allCases[i]\n              let second = ChunkSectionFace.allCases[j]\n              connectivity.setConnected(first, second)\n            }\n          }\n        }\n      } else {\n        break\n      }\n    }\n\n    return connectivity\n  }\n\n  /// Gets the value of the given voxel.\n  ///\n  /// Does not validate the position. Behaviour is undefined if the position is not inside the chunk.\n  /// - Parameters:\n  ///   - x: x coordinate of the voxel.\n  ///   - y: y coordinate of the voxel.\n  ///   - z: z coordinate of the voxel.\n  /// - Returns: Current value of the voxel.\n  private func getVoxel(x: Int, y: Int, z: Int) -> Bool {\n    return voxels.withUnsafeBufferPointer { $0[(y &* dimension &+ z) &* dimension &+ x] }\n  }\n\n  /// Sets the value of the given voxel.\n  /// - Parameters:\n  ///   - x: x coordinate of the voxel.\n  ///   - y: y coordinate of the voxel.\n  ///   - z: z coordiante of the voxel.\n  ///   - value: New value for the voxel.\n  private mutating func setVoxel(x: Int, y: Int, z: Int, to value: Bool) {\n    mutableVoxelsPointer[(y &* dimension &+ z) &* dimension &+ x] = value\n  }\n\n  /// Iteratively flood fills all voxels connected to the seed voxel that are set to `false`.\n  /// - Parameters:\n  ///   - x: x coordinate of the seed voxel.\n  ///   - y: y coordinate of the seed voxel.\n  ///   - z: z coordinate of the seed voxel.\n  ///   - group: Stores which faces the flood fill has reached. Should be empty to start off.\n  private mutating func iterativeFloodFill(x: Int, y: Int, z: Int, group: inout DirectionSet) {\n    var stack: [(Int, Int, Int)] = [(x, y, z)]\n    // 256 is just a rough estimate of how deep the stack might get. In reality the stack actually\n    // varies from under 80 to over 20000 depending on the chunk section.\n    stack.reserveCapacity(256)\n\n    while let position = stack.popLast() {\n      let x = position.0\n      let y = position.1\n      let z = position.2\n\n      if isInBounds(x: x, y: y, z: z) && getVoxel(x: x, y: y, z: z) == false {\n        if x == 0 {\n          group.insert(.west)\n        } else if x == dimension &- 1 {\n          group.insert(.east)\n        }\n\n        if y == 0 {\n          group.insert(.down)\n        } else if y == dimension &- 1 {\n          group.insert(.up)\n        }\n\n        if z == 0 {\n          group.insert(.north)\n        } else if z == dimension &- 1 {\n          group.insert(.south)\n        }\n\n        setVoxel(x: x, y: y, z: z, to: true)\n\n        stack.append((x &+ 1, y, z))\n        stack.append((x &- 1, y, z))\n        stack.append((x, y &+ 1, z))\n        stack.append((x, y &- 1, z))\n        stack.append((x, y, z &+ 1))\n        stack.append((x, y, z &- 1))\n      }\n    }\n  }\n\n  /// Only works if ``dimension`` is a power of two.\n  /// - Parameters:\n  ///   - x: x coordinate of point to check.\n  ///   - y: y coordinate of point to check.\n  ///   - z: z coordinate of point to check.\n  /// - Returns: Whether the point is inside the bounds of the image or not.\n  private func isInBounds(x: Int, y: Int, z: Int) -> Bool {\n    return (x | y | z) >= 0 && (x | y | z) < dimension\n  }\n\n  /// - Returns: The coordinates of all voxels set to 0.\n  private func emptyVoxelCoordinates() -> [(Int, Int, Int)] {\n    var coordinates: [(Int, Int, Int)] = Array()\n    coordinates.reserveCapacity(voxels.count / 2) // TODO: determine a better initial capacity\n\n    for y in 0..<dimension {\n      for z in 0..<dimension {\n        for x in 0..<dimension {\n          if getVoxel(x: x, y: y, z: z) == false {\n            coordinates.append((x, y, z))\n          }\n        }\n      }\n    }\n\n    return coordinates\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/World/Visibility/VisibilityGraph+SearchQueueEntry.swift",
    "content": "import DeltaCore\n\nextension VisibilityGraph {\n  /// An entry in the breadth-first-search queue. Used when traversing a ``VisibilityGraph`` in ``chunkSectionsVisible(from:camera:)``.\n  struct SearchQueueEntry {\n    let position: ChunkSectionPosition\n    let entryFace: Direction?\n    let directions: DirectionSet\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/World/Visibility/VisibilityGraph.swift",
    "content": "import DeltaCore\nimport Collections\n\n/// A graph structure used to determine which chunk sections are visible from a given camera.\n///\n/// It uses a conservative approach, which means that some chunks will be incorrectly identified\n/// as visible, but no chunks will be incorrectly identified as not visible.\n///\n/// See https://tomcc.github.io/2014/08/31/visibility-1.html for an in-depth explanation of the\n/// technique.\npublic struct VisibilityGraph {\n  // MARK: Public properties\n\n  /// Number of sections in the graph.\n  public var sectionCount: Int {\n    lock.acquireReadLock()\n    defer { lock.unlock() }\n    return sections.count\n  }\n\n  // MARK: Private properties\n\n  /// Used to make the graph threadsafe.\n  private var lock = ReadWriteLock()\n  /// Stores the connectivity for each chunk in the graph (see ``ChunkSectionFaceConnectivity``).\n  private var sections: [ChunkSectionPosition: (connectivity: ChunkSectionFaceConnectivity, isEmpty: Bool)] = [:]\n  /// All of the chunks currently in the visibility graph.\n  private var chunks: Set<ChunkPosition> = []\n  /// Block model palette used to determine whether blocks are see through or not.\n  private let blockModelPalette: BlockModelPalette\n\n  /// The x coordinate of the west-most chunk in the graph.\n  private var minimumX = 0\n  /// The x coordinate of the east-most chunk in the graph.\n  private var maximumX = 0\n  /// The z coordinate of the north-most chunk in the graph.\n  private var minimumZ = 0\n  /// The z coordinate of the south-most chunk in the graph.\n  private var maximumZ = 0\n\n  // MARK: Init\n\n  /// Creates an empty visibility graph.\n  /// - Parameter blockModelPalette: Used to determine whether blocks are see through or not.\n  public init(blockModelPalette: BlockModelPalette) {\n    self.blockModelPalette = blockModelPalette\n  }\n\n  // MARK: Public methods\n\n  /// Adds a chunk to the visibility graph.\n  /// - Parameters:\n  ///   - chunk: Chunk to add.\n  ///   - position: Position of chunk.\n  public mutating func addChunk(_ chunk: Chunk, at position: ChunkPosition) {\n    lock.acquireWriteLock()\n    defer { lock.unlock() }\n\n    let isFirstChunk = chunks.isEmpty\n    chunks.insert(position)\n\n    // Update the bounding box of the visibility graph\n    if isFirstChunk {\n      minimumX = position.chunkX\n      minimumZ = position.chunkZ\n      maximumX = position.chunkX\n      maximumZ = position.chunkZ\n    } else {\n      if position.chunkX < minimumX {\n        minimumX = position.chunkX\n      } else if position.chunkX > maximumX {\n        maximumX = position.chunkX\n      }\n\n      if position.chunkZ < minimumZ {\n        minimumZ = position.chunkZ\n      } else if position.chunkZ > maximumZ {\n        maximumZ = position.chunkZ\n      }\n    }\n\n    // Calculate connectivity\n    for (sectionY, section) in chunk.getSections().enumerated() {\n      let sectionPosition = ChunkSectionPosition(position, sectionY: sectionY)\n      var connectivityGraph = ChunkSectionVoxelGraph(for: section, blockModelPalette: blockModelPalette)\n      sections[sectionPosition] = (connectivity: connectivityGraph.calculateConnectivity(), isEmpty: section.isEmpty)\n    }\n  }\n\n  /// Removes the given chunk from the visibility graph.\n  /// - Parameter position: The position of the chunk to remove.\n  public mutating func removeChunk(at position: ChunkPosition) {\n    lock.acquireWriteLock()\n    defer { lock.unlock() }\n\n    chunks.remove(position)\n\n    // Update the bounds of the graph if necessary\n    if chunks.isEmpty {\n      minimumX = 0\n      minimumZ = 0\n      maximumX = 0\n      maximumZ = 0\n    } else {\n      if position.chunkX == minimumX {\n        minimumX = Int.max\n        for chunk in chunks where chunk.chunkX < minimumX {\n          minimumX = chunk.chunkX\n        }\n      } else if position.chunkX == maximumX {\n        maximumX = Int.min\n        for chunk in chunks where chunk.chunkX > maximumX {\n          maximumX = chunk.chunkX\n        }\n      }\n\n      if position.chunkZ == minimumZ {\n        minimumZ = Int.max\n        for chunk in chunks where chunk.chunkZ < minimumZ {\n          minimumZ = chunk.chunkZ\n        }\n      } else if position.chunkZ == maximumZ {\n        maximumZ = Int.min\n        for chunk in chunks where chunk.chunkZ > maximumZ {\n          maximumZ = chunk.chunkZ\n        }\n      }\n    }\n\n    // Remove the chunk's sections from the graph.\n    for y in 0..<Chunk.numSections {\n      sections.removeValue(forKey: ChunkSectionPosition(position, sectionY: y))\n    }\n  }\n\n  /// Updates a chunk in the visibility graph.\n  /// - Parameters:\n  ///   - chunk: Chunk to update.\n  ///   - position: Position of chunk to update.\n  public mutating func updateChunk(_ chunk: Chunk, at position: ChunkPosition) {\n    addChunk(chunk, at: position)\n  }\n\n  /// Gets whether the visibility graph contains the given chunk.\n  /// - Parameter position: The position of the chunk to check.\n  public func containsChunk(at position: ChunkPosition) -> Bool {\n    lock.acquireReadLock()\n    defer { lock.unlock() }\n\n    return chunks.contains(position)\n  }\n\n  /// Recomputes the connectivity of the given section.\n  /// - Parameters:\n  ///   - position: The position of the section.\n  ///   - chunk: The chunk that the section is in.\n  public mutating func updateSection(at position: ChunkSectionPosition, in chunk: Chunk) {\n    lock.acquireWriteLock()\n    defer { lock.unlock() }\n\n    guard let section = chunk.getSection(at: position.sectionY) else {\n      return\n    }\n\n    var connectivityGraph = ChunkSectionVoxelGraph(for: section, blockModelPalette: blockModelPalette)\n    sections[position] = (connectivity: connectivityGraph.calculateConnectivity(), isEmpty: section.isEmpty)\n  }\n\n  /// Gets whether a ray could possibly pass through the given chunk section, entering through a given face and exiting out another given face.\n  /// - Parameters:\n  ///   - entryFace: Face to enter through.\n  ///   - exitFace: Face to exit through.\n  ///   - section: Section to check for.\n  ///   - acquireLock: Whether to acquire a read lock or not. Only set to `false` if you know what you're doing.\n  /// - Returns: Whether is it possibly to see through the section looking through `entryFace` and out `exitFace`.\n  public func canPass(from entryFace: Direction, to exitFace: Direction, through section: ChunkSectionPosition, acquireLock: Bool = true) -> Bool {\n    if acquireLock { lock.acquireReadLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    let first = ChunkSectionFace.forDirection(entryFace)\n    let second = ChunkSectionFace.forDirection(exitFace)\n    if let connectivity = sections[section] {\n      return connectivity.connectivity.areConnected(first, second)\n    } else {\n      return isWithinPaddedBounds(section)\n    }\n  }\n\n  /// Gets whether the given section is within the bounds of the visibility graph, including 1 section of padding around the entire graph.\n  /// - Parameter section: The position of the section.\n  /// - Returns: `true` if the section is within the bounds of this graph.\n  public func isWithinPaddedBounds(_ section: ChunkSectionPosition) -> Bool {\n    return\n      section.sectionX >= minimumX - 1 && section.sectionX <= maximumX + 1 &&\n      section.sectionY >= -1 && section.sectionY <= Chunk.numSections + 1 &&\n      section.sectionZ >= minimumZ - 1 && section.sectionZ <= maximumZ + 1\n  }\n\n  /// Gets the positions of all chunk sections that are possibly visible from the given chunk.\n  /// - Parameter position: Position of the chunk section that the world is being viewed from.\n  /// - Parameter camera: Used for frustum culling.\n  /// - Returns: The positions of all possibly visible chunk sections. Does not include empty sections.\n  public func chunkSectionsVisible(from camera: Camera, renderDistance: Int) -> [ChunkSectionPosition] {\n    lock.acquireReadLock()\n    defer { lock.unlock() }\n\n    // Get the camera's position and swap it for an equivalent position to minimise the number of empty sections traversed if possible.\n    let position = simplifyCameraPosition(camera.entityPosition.chunkSection)\n    let cameraChunk = camera.entityPosition.chunk\n\n    // Traverse the graph to find all potentially visible sections\n    var visible: [ChunkSectionPosition] = []\n    visible.reserveCapacity(sectionCount)\n\n    if sections[position]?.isEmpty == false && position.isValid {\n      visible.append(position)\n    }\n\n    var visited = Set<ChunkSectionPosition>(minimumCapacity: sectionCount)\n    var queue: Deque = [SearchQueueEntry(position: position, entryFace: nil, directions: [])]\n\n    while let current = queue.popFirst() {\n      let entryFace = current.entryFace\n      let position = current.position\n\n      for exitFace in Direction.allDirections where exitFace != entryFace {\n        let neighbourPosition = position.neighbour(inDirection: exitFace)\n\n        // Avoids doubling back. If a chunk has been exited from the top face, any chunks after that shouldn't be exited from the bottom face.\n        if current.directions.contains(exitFace.opposite) {\n          continue\n        }\n\n        // Chunks outside render distance aren't visible\n        let neighbourChunk = neighbourPosition.chunk\n        if !neighbourChunk.isWithinRenderDistance(renderDistance, of: cameraChunk) {\n          continue\n        }\n\n        // Don't visit the same section twice\n        guard !visited.contains(neighbourPosition) else {\n          continue\n        }\n\n        if let entryFace = entryFace, !canPass(from: entryFace, to: exitFace, through: position, acquireLock: false) {\n          continue\n        }\n\n        // Frustum culling\n        if !camera.isChunkSectionVisible(at: neighbourPosition) {\n          continue\n        }\n\n        visited.insert(neighbourPosition)\n\n        var directions = current.directions\n        directions.insert(exitFace)\n\n        queue.append(SearchQueueEntry(\n          position: neighbourPosition,\n          entryFace: exitFace.opposite,\n          directions: directions\n        ))\n\n        if sections[neighbourPosition]?.isEmpty == false && neighbourPosition.isValid {\n          visible.append(neighbourPosition)\n        }\n      }\n    }\n\n    return visible\n  }\n\n  /// Moves the camera to an equivalent position which requires less traversing of empty chunk sections.\n  ///\n  /// If the camera is within the bounding box containing all non-empty chunks, it isn't moved. Otherwise,\n  /// the camera is moved to be in a chunk adjacent to the bounding box (including diagonally adjacent).\n  private func simplifyCameraPosition(_ position: ChunkSectionPosition) -> ChunkSectionPosition {\n    var position = position\n\n    if position.sectionX < minimumX - 1 {\n      position.sectionX = minimumX - 1\n    } else if position.sectionX > maximumX + 1 {\n      position.sectionX = maximumX + 1\n    }\n\n    if position.sectionY < -1 {\n      position.sectionY = -1\n    } else if position.sectionY > Chunk.numSections + 1 {\n      position.sectionY = Chunk.numSections + 1\n    }\n\n    if position.sectionZ < minimumZ - 1 {\n      position.sectionZ = minimumZ - 1\n    } else if position.sectionZ > maximumZ + 1 {\n      position.sectionZ = maximumZ + 1\n    }\n\n    return position\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/World/WorldMesh.swift",
    "content": "import DeltaCore\n\n/// Holds all the meshes for a world. Completely threadsafe.\npublic struct WorldMesh {\n  // MARK: Private properties\n\n  /// The world this mesh is for.\n  private var world: World\n  /// Used to determine which chunk sections should be rendered.\n  private var visibilityGraph: VisibilityGraph\n\n  /// A lock used to make the mesh threadsafe.\n  private var lock = ReadWriteLock()\n\n  /// A worker that handles the preparation and updating of meshes.\n  private var meshWorker: WorldMeshWorker\n  /// All chunk section meshes.\n  private var meshes: [ChunkSectionPosition: ChunkSectionMesh] = [:]\n  /// Positions of all chunk sections that need to have their meshes prepared again when they next become visible.\n  private var chunkSectionsToPrepare: Set<ChunkSectionPosition> = []\n  /// Positions of all currently visible chunk sections (updated when ``update(_:camera:)`` is called.\n  private var visibleSections: [ChunkSectionPosition] = []\n\n  /// The maximum number of chunk section preparation jobs that will be queued at any given time.\n  ///\n  /// This allows sections to be prepared in a more sensible order because they are prepared closer\n  /// to the time they were queued which means that the ordering is more likely to be valid. For example,\n  /// if the size of the job queue was not limited and the player turns around while sections are being\n  /// prepared, all of the sections that were previously visible would be prepared before the ones that\n  /// were actually visible.\n  private var maximumQueuedJobCount = 8\n\n  // MARK: Init\n\n  /// Creates a new world mesh. Prepares any chunks already loaded in the world.\n  public init(_ world: World, resources: ResourcePack.Resources) {\n    self.world = world\n    meshWorker = WorldMeshWorker(world: world, resources: resources)\n    visibilityGraph = VisibilityGraph(blockModelPalette: resources.blockModelPalette)\n\n    let chunks = world.loadedChunkPositions\n\n    for position in chunks {\n      addChunk(at: position)\n    }\n  }\n\n  // MARK: Public methods\n\n  /// Adds a chunk to the mesh.\n  /// - Parameter position: Position of the newly added chunk.\n  public mutating func addChunk(at position: ChunkPosition) {\n    lock.acquireWriteLock()\n    defer { lock.unlock() }\n\n    guard world.isChunkComplete(at: position) else {\n      return\n    }\n\n    // The chunks required to prepare this chunk is the same as the chunks that require this chunk to prepare.\n    // Adding this chunk may have made some of the chunks that require it preparable so here we check if any of\n    // those can now by prepared. `chunksRequiredToPrepare` includes the chunk itself as well.\n    for position in Self.chunksRequiredToPrepare(chunkAt: position) {\n      if !visibilityGraph.containsChunk(at: position) && canPrepareChunk(at: position) {\n        if let chunk = world.chunk(at: position) {\n          visibilityGraph.addChunk(chunk, at: position)\n\n          // Mark any non-empty sections of the chunk for preparation.\n          for (y, section) in chunk.getSections().enumerated() where !section.isEmpty {\n            let sectionPosition = ChunkSectionPosition(position, sectionY: y)\n            chunkSectionsToPrepare.insert(sectionPosition)\n          }\n        }\n      }\n    }\n  }\n\n  /// Removes the given chunk from the mesh.\n  ///\n  /// This method has an issue where any chunk already queued to be prepared will still be prepared and stored.\n  /// However, it will not be rendered, so this isn't much of an issue. And it probably won't happen often anyway.\n  /// - Parameter position: The position of the chunk to remove.\n  public mutating func removeChunk(at position: ChunkPosition) {\n    lock.acquireWriteLock()\n    defer { lock.unlock() }\n\n    for position in Self.chunksRequiredToPrepare(chunkAt: position) {\n      visibilityGraph.removeChunk(at: position)\n\n      for y in 0..<Chunk.numSections {\n        let sectionPosition = ChunkSectionPosition(position, sectionY: y)\n        chunkSectionsToPrepare.remove(sectionPosition)\n        meshes.removeValue(forKey: sectionPosition)\n      }\n    }\n  }\n\n  /// Schedules a chunk to have its meshes updated next time it is visible.\n  /// - Parameter position: The position of the chunk to update.\n  public mutating func updateChunk(at position: ChunkPosition) {\n    lock.acquireWriteLock()\n    defer { lock.unlock() }\n\n    guard let chunk = world.chunk(at: position) else {\n      log.warning(\"Chunk update received for non-existent chunk \\(position)\")\n      return\n    }\n\n    guard visibilityGraph.containsChunk(at: position) else {\n      return\n    }\n\n    visibilityGraph.updateChunk(chunk, at: position)\n\n    for (y, section) in chunk.getSections().enumerated() {\n      if !section.isEmpty {\n        let position = ChunkSectionPosition(position, sectionY: y)\n        chunkSectionsToPrepare.insert(position)\n      }\n    }\n  }\n\n  /// Schedules a chunk section to have its mesh updated next time it is visible. Doesn't lock the\n  /// world mesh lock. May still lock the visibility graph et al.\n  /// - Parameters:\n  ///   - position: The position of the chunk section to update.\n  public mutating func updateSection(at position: ChunkSectionPosition) {\n    lock.acquireWriteLock()\n    defer { lock.unlock() }\n\n    locklessUpdateSection(at: position)\n  }\n\n  /// Same as ``updateSection`` but for an array of positions. The benefit of this method when\n  /// applicable is that it doesn't acquire intermediate locks like a for loop containing\n  /// `updateSection` would.\n  /// - Parameters:\n  ///   - positions: The positions of the chunk sections to update.\n  public mutating func updateSections(at positions: [ChunkSectionPosition]) {\n    lock.acquireWriteLock()\n    defer { lock.unlock() }\n\n    for position in positions {\n      locklessUpdateSection(at: position)\n    }\n  }\n\n  /// The private implementation of ``updateSection`` that doesn't acquire ``lock``.\n  private mutating func locklessUpdateSection(at position: ChunkSectionPosition) {\n    guard let chunk = world.chunk(at: position.chunk) else {\n      // It is expected for this to happen because the method for determining which sections to\n      // update is conservative and will queue sections even if they're not getting rendered.\n      return\n    }\n\n    guard visibilityGraph.containsChunk(at: position.chunk) else {\n      return\n    }\n\n    visibilityGraph.updateSection(at: position, in: chunk)\n    chunkSectionsToPrepare.insert(position)\n  }\n\n  /// Updates the world mesh (should ideally be called once per frame).\n  /// - Parameters:\n  ///   - cameraPosition: The current position of the camera.\n  ///   - camera: The camera the world is being viewed from.\n  public mutating func update(camera: Camera, renderDistance: Int) {\n    lock.acquireWriteLock()\n    defer { lock.unlock() }\n\n    visibleSections = visibilityGraph.chunkSectionsVisible(from: camera, renderDistance: renderDistance)\n\n    for section in visibleSections {\n      if meshWorker.jobCount == maximumQueuedJobCount {\n        return\n      }\n\n      if shouldPrepareChunkSection(at: section) {\n        prepareChunkSection(at: section, acquireWriteLock: false)\n      }\n    }\n  }\n\n  /// Perform an arbitrary action that mutates each of the world's visible meshes.\n  /// - Parameter action: Action to perform on each visible mesh.\n  /// - Parameter shouldReverseOrder: If `true`, the sections will be mutated from furthest to closest.\n  public mutating func mutateVisibleMeshes(\n    fromBackToFront shouldReverseOrder: Bool = false,\n    _ action: (ChunkSectionPosition, inout ChunkSectionMesh) throws -> Void\n  ) rethrows {\n    lock.acquireWriteLock()\n    defer { lock.unlock() }\n\n    let updatedMeshes = meshWorker.getUpdatedMeshes()\n    for (position, mesh) in updatedMeshes {\n      meshes[position] = mesh\n    }\n\n    let sections = shouldReverseOrder ? visibleSections.reversed() : visibleSections\n    for position in sections {\n      if meshes[position] != nil {\n        // It was done this way to prevent copies\n        // swiftlint:disable force_unwrapping\n        try action(position, &meshes[position]!)\n        // swiftlint:enable force_unwrapping\n      }\n    }\n  }\n\n  /// Gets an array containing the position of each section affected by the update (including the section itself).\n  ///\n  /// This function shouldn't need to be `mutating`, but it causes crashes if it is not.\n  /// - Parameter position: The position of the chunk section.\n  /// - Parameter onlyLighting: If true, the update will be treated as if only lighting has changed.\n  /// - Returns: The affected chunk sections.\n  public mutating func sectionsAffectedBySectionUpdate(at position: ChunkSectionPosition, onlyLighting: Bool = false) -> [ChunkSectionPosition] {\n    lock.acquireReadLock()\n    defer { lock.unlock() }\n\n    var sections = [\n      position,\n      position.validNeighbour(inDirection: .north),\n      position.validNeighbour(inDirection: .south),\n      position.validNeighbour(inDirection: .east),\n      position.validNeighbour(inDirection: .west),\n      position.validNeighbour(inDirection: .up),\n      position.validNeighbour(inDirection: .down)\n    ].compactMap { $0 }\n\n    if onlyLighting {\n      return sections\n    }\n\n    // The following sections are only affected if they contain fluids\n    let potentiallyAffected = [\n      position.validNeighbour(inDirection: .north)?.validNeighbour(inDirection: .east),\n      position.validNeighbour(inDirection: .north)?.validNeighbour(inDirection: .east)?.validNeighbour(inDirection: .down),\n      position.validNeighbour(inDirection: .north)?.validNeighbour(inDirection: .west),\n      position.validNeighbour(inDirection: .north)?.validNeighbour(inDirection: .west)?.validNeighbour(inDirection: .down),\n      position.validNeighbour(inDirection: .south)?.validNeighbour(inDirection: .west),\n      position.validNeighbour(inDirection: .south)?.validNeighbour(inDirection: .west)?.validNeighbour(inDirection: .down),\n      position.validNeighbour(inDirection: .south)?.validNeighbour(inDirection: .west),\n      position.validNeighbour(inDirection: .south)?.validNeighbour(inDirection: .west)?.validNeighbour(inDirection: .down),\n      position.validNeighbour(inDirection: .north)?.validNeighbour(inDirection: .down),\n      position.validNeighbour(inDirection: .east)?.validNeighbour(inDirection: .down),\n      position.validNeighbour(inDirection: .south)?.validNeighbour(inDirection: .down),\n      position.validNeighbour(inDirection: .west)?.validNeighbour(inDirection: .down)\n    ].compactMap { $0 }\n\n    for section in potentiallyAffected {\n      if let mesh = meshes[section] {\n        if mesh.containsFluids {\n          sections.append(section)\n        }\n      }\n    }\n\n    return sections\n  }\n\n  /// Gets the list of chunks that must be present to prepare a chunk, including the chunk itself.\n  /// - Parameter position: Chunk to get dependencies of.\n  /// - Returns: Chunks that must be present to prepare the given chunk, including the chunk itself.\n  public static func chunksRequiredToPrepare(chunkAt position: ChunkPosition) -> [ChunkPosition] {\n    return [\n      position,\n      position.neighbour(inDirection: .north),\n      position.neighbour(inDirection: .north).neighbour(inDirection: .east),\n      position.neighbour(inDirection: .east),\n      position.neighbour(inDirection: .south).neighbour(inDirection: .east),\n      position.neighbour(inDirection: .south),\n      position.neighbour(inDirection: .south).neighbour(inDirection: .west),\n      position.neighbour(inDirection: .west),\n      position.neighbour(inDirection: .north).neighbour(inDirection: .west)\n    ]\n  }\n\n  // MARK: Private methods\n\n  /// Prepares the mesh for a chunk section. Threadsafe.\n  /// - Parameters:\n  ///   - position: The position of the section to prepare.\n  ///   - acquireWriteLock: If false, a write lock for `lock` must be acquired prior to calling this method.\n  private mutating func prepareChunkSection(at position: ChunkSectionPosition, acquireWriteLock: Bool) {\n    if acquireWriteLock { lock.acquireWriteLock() }\n    defer { if acquireWriteLock { lock.unlock() } }\n\n    let chunkPosition = position.chunk\n    chunkSectionsToPrepare.remove(position)\n\n    // TODO: This should possibly throw an error instead of failing silently\n    guard let chunk = world.chunk(at: chunkPosition), let neighbours = world.allNeighbours(ofChunkAt: chunkPosition) else {\n      log.warning(\"Failed to get chunk and neighbours of section at \\(position)\")\n      visibilityGraph.removeChunk(at: chunkPosition)\n      return\n    }\n\n    meshWorker.createMeshAsync(\n      at: position,\n      in: chunk,\n      neighbours: neighbours\n    )\n  }\n\n  /// Checks whether a chunk section should be prepared when it next becomes visible.\n  ///\n  /// Does not perform any locking (isn't threadsafe).\n  /// - Parameter position: The position of the chunk section to check.\n  /// - Returns: Whether the chunk section should be prepared or not.\n  private func shouldPrepareChunkSection(at position: ChunkSectionPosition) -> Bool {\n    return chunkSectionsToPrepare.contains(position)\n  }\n\n  /// Checks whether a chunk has all the neighbours required to prepare it (including lighting).\n  ///\n  /// Does not perform any locking (isn't threadsafe).\n  /// - Parameter position: The position of the chunk to check.\n  /// - Returns: Whether the chunk can be prepared or not.\n  private func canPrepareChunk(at position: ChunkPosition) -> Bool {\n    for position in Self.chunksRequiredToPrepare(chunkAt: position) {\n      if !world.isChunkComplete(at: position) {\n        return false\n      }\n    }\n\n    return true\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/World/WorldMeshWorker+Job.swift",
    "content": "import Collections\nimport DeltaCore\n\nextension WorldMeshWorker {\n  /// A chunk section mesh creation job.\n  struct Job {\n    /// The chunk that the section is in.\n    var chunk: Chunk\n    /// The position of the section to prepare.\n    var position: ChunkSectionPosition\n    /// The neighbouring chunks of ``chunk``.\n    var neighbours: ChunkNeighbours\n  }\n\n  /// Handles queueing jobs and prioritisation. Completely threadsafe.\n  struct JobQueue {\n    /// The number of jobs currently in the queue.\n    public var count: Int {\n      lock.acquireReadLock()\n      defer { lock.unlock() }\n      return jobs.count\n    }\n\n    /// The queue of current jobs.\n    private var jobs: Deque<Job> = []\n    /// A lock used to make the job queue threadsafe.\n    private var lock = ReadWriteLock()\n\n    /// Creates an empty job queue.\n    init() {}\n\n    /// Adds a job to the queue.\n    mutating func add(_ job: Job) {\n      lock.acquireWriteLock()\n      defer { lock.unlock() }\n      jobs.append(job)\n    }\n\n    /// Returns the next job to complete.\n    mutating func next() -> Job? {\n      lock.acquireWriteLock()\n      defer { lock.unlock() }\n      if !jobs.isEmpty {\n        return jobs.removeFirst()\n      } else {\n        return nil\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/World/WorldMeshWorker.swift",
    "content": "import Foundation\nimport Atomics\nimport Metal\nimport DeltaCore\n\n/// A multi-threaded worker that creates and updates the world's meshes. Completely threadsafe.\npublic class WorldMeshWorker {\n  // MARK: Public properties\n\n  /// The number of jobs currently queued.\n  public var jobCount: Int {\n    jobQueue.count\n  }\n\n  // MARK: Private properties\n\n  /// World that chunks are in.\n  private var world: World\n  /// Resources to prepare chunks with.\n  private let resources: ResourcePack.Resources\n\n  /// A lock used to make the worker threadsafe.\n  private var lock = ReadWriteLock()\n\n  /// Meshes that the worker has created or updated and the ``WorldRenderer`` hasn't taken back yet.\n  private var updatedMeshes: [ChunkSectionPosition: ChunkSectionMesh] = [:]\n\n  /// Mesh creation jobs.\n  private var jobQueue = JobQueue()\n  /// Serial dispatch queue for executing jobs on.\n  private var executionQueue = DispatchQueue(label: \"dev.stackotter.delta-client.WorldMeshWorker\", attributes: [.concurrent])\n  /// Whether the execution loop is currently running or not.\n  private var executingThreadsCount = ManagedAtomic<Int>(0)\n  /// The maximum number of execution loops allowed to run at once (for performance reasons).\n  private let maxExecutingThreadsCount = 1 // TODO: Scale max threads and executionQueue qos with size of job queue\n\n  // MARK: Init\n\n  /// Creates a new world mesh worker.\n  public init(world: World, resources: ResourcePack.Resources) {\n    self.world = world\n    self.resources = resources\n  }\n\n  // MARK: Public methods\n\n  /// Creates a new mesh for the specified chunk section.\n  public func createMeshAsync(\n    at position: ChunkSectionPosition,\n    in chunk: Chunk,\n    neighbours: ChunkNeighbours\n  ) {\n    let job = Job(\n      chunk: chunk,\n      position: position,\n      neighbours: neighbours)\n    jobQueue.add(job)\n    startExecutionLoop()\n  }\n\n  /// Gets the meshes that have been updated since the last call to this function.\n  /// - Returns: The updated meshes and their positions.\n  public func getUpdatedMeshes() -> [ChunkSectionPosition: ChunkSectionMesh] {\n    lock.acquireWriteLock()\n\n    defer {\n      updatedMeshes = [:]\n      lock.unlock()\n    }\n\n    return updatedMeshes\n  }\n\n  // MARK: Private methods\n\n  /// Starts an asynchronous loop that executes all jobs on the queue until there are none left.\n  private func startExecutionLoop() {\n    if executingThreadsCount.load(ordering: .relaxed) >= maxExecutingThreadsCount {\n      return\n    }\n\n    executingThreadsCount.wrappingIncrement(ordering: .relaxed)\n\n    executionQueue.async {\n      while true {\n        let jobWasExecuted = self.executeNextJob()\n\n        // If no job was executed, the job queue is empty and this execution loop can stop\n        if !jobWasExecuted {\n          if self.executingThreadsCount.wrappingDecrementThenLoad(ordering: .relaxed) < 0 {\n            log.warning(\"Error in WorldMeshWorker thread management, number of executing threads is below 0\")\n          }\n          return\n        }\n      }\n    }\n  }\n\n  /// Executes the next job.\n  /// - Returns: `false` if there were no jobs to execute.\n  private func executeNextJob() -> Bool {\n    guard let job = jobQueue.next() else {\n      return false\n    }\n\n    var affectedChunks: [Chunk] = []\n\n    for position in WorldMesh.chunksRequiredToPrepare(chunkAt: job.position.chunk) {\n      if let chunk = world.chunk(at: position) {\n        chunk.acquireReadLock()\n        affectedChunks.append(chunk)\n      }\n    }\n\n    let meshBuilder = ChunkSectionMeshBuilder(\n      forSectionAt: job.position,\n      in: job.chunk,\n      withNeighbours: job.neighbours,\n      world: world,\n      resources: resources\n    )\n\n    // TODO: implement buffer recycling\n    let mesh = meshBuilder.build()\n\n    for chunk in affectedChunks {\n      chunk.unlock()\n    }\n\n    lock.acquireWriteLock()\n    updatedMeshes[job.position] = mesh\n    lock.unlock()\n\n    return true\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Renderer/World/WorldRenderer.swift",
    "content": "import DeltaCore\nimport FirebladeMath\nimport Foundation\nimport MetalKit\n\n/// A renderer that renders a `World` along with its associated entities (from `Game.nexus`).\npublic final class WorldRenderer: Renderer {\n  // MARK: Private properties\n\n  /// Internal renderer for rendering entities.\n  private var entityRenderer: EntityRenderer\n\n  /// Render pipeline used for rendering world geometry.\n  private var renderPipelineState: MTLRenderPipelineState\n  #if !os(tvOS)\n    /// Render pipeline used for rendering translucent world geometry.\n    private var transparencyRenderPipelineState: MTLRenderPipelineState\n    /// Render pipeline used for compositing translucent geometry onto the screen buffer.\n    private var compositingRenderPipelineState: MTLRenderPipelineState\n  #endif\n\n  /// The device used for rendering.\n  private var device: MTLDevice\n  /// The resources to use for rendering blocks.\n  private var resources: ResourcePack.Resources\n  /// The command queue used for rendering.\n  private var commandQueue: MTLCommandQueue\n  /// The Metal texture palette containing the array-texture and animation-related buffers.\n  private var texturePalette: MetalTexturePalette\n  /// The light map texture used to calculate rendered brightness.\n  private var lightMap: LightMap\n\n  /// The client to render for.\n  private var client: Client\n\n  /// Manages the world's meshes.\n  private var worldMesh: WorldMesh\n\n  /// The rendering profiler.\n  private var profiler: Profiler<RenderingMeasurement>\n\n  /// A buffer containing uniforms containing the identity matrix (no-op).\n  private var identityUniformsBuffer: MTLBuffer\n\n  /// A buffer containing vertices for a block outline.\n  private let blockOutlineVertexBuffer: MTLBuffer\n  /// A buffer containing indices for a block outline.\n  private let blockOutlineIndexBuffer: MTLBuffer\n\n  /// A buffer containing the light map (updated each frame).\n  private var lightMapBuffer: MTLBuffer?\n\n  #if !os(tvOS)\n    /// The depth stencil state used for order independent transparency (which requires read-only\n    /// depth).\n    private let readOnlyDepthState: MTLDepthStencilState\n  #endif\n  /// The depth stencil state used when order independent transparency is disabled.\n  private let depthState: MTLDepthStencilState\n\n  /// The buffer for the uniforms used to render distance fog.\n  private let fogUniformsBuffer: MTLBuffer\n\n  private let destroyOverlayRenderPipelineState: MTLRenderPipelineState\n\n  // MARK: Init\n\n  /// Creates a new world renderer.\n  public init(\n    client: Client,\n    device: MTLDevice,\n    commandQueue: MTLCommandQueue,\n    profiler: Profiler<RenderingMeasurement>\n  ) throws {\n    self.client = client\n    self.device = device\n    self.commandQueue = commandQueue\n    self.profiler = profiler\n\n    // Load shaders\n    let library = try MetalUtil.loadDefaultLibrary(device)\n    let vertexFunction = try MetalUtil.loadFunction(\"chunkVertexShader\", from: library)\n    let fragmentFunction = try MetalUtil.loadFunction(\"chunkFragmentShader\", from: library)\n    let transparentFragmentFunction = try MetalUtil.loadFunction(\n      \"chunkOITFragmentShader\",\n      from: library\n    )\n    let transparentCompositingVertexFunction = try MetalUtil.loadFunction(\n      \"chunkOITCompositingVertexShader\",\n      from: library\n    )\n    let transparentCompositingFragmentFunction = try MetalUtil.loadFunction(\n      \"chunkOITCompositingFragmentShader\",\n      from: library\n    )\n\n    // Create block palette array texture.\n    resources = client.resourcePack.vanillaResources\n    texturePalette = try MetalTexturePalette(\n      palette: resources.blockTexturePalette,\n      device: device,\n      commandQueue: commandQueue\n    )\n\n    // Create light map\n    lightMap = LightMap(ambientLight: Double(client.game.world.dimension.ambientLight))\n\n    // TODO: Have another copy of this pipeline without blending enabled (to use when OIT is enabled)\n    // Create opaque pipeline (which also handles translucent geometry when OIT is disabled)\n    renderPipelineState = try MetalUtil.makeRenderPipelineState(\n      device: device,\n      label: \"WorldRenderer.renderPipelineState\",\n      vertexFunction: vertexFunction,\n      fragmentFunction: fragmentFunction,\n      blendingEnabled: true\n    )\n\n    destroyOverlayRenderPipelineState = try MetalUtil.makeRenderPipelineState(\n      device: device,\n      label: \"WorldRenderer.destroyOverlayRenderPipelineState\",\n      vertexFunction: vertexFunction,\n      fragmentFunction: fragmentFunction,\n      blendingEnabled: true,\n      editDescriptor: { descriptor in\n        descriptor.colorAttachments[0].sourceRGBBlendFactor = .destinationColor\n        descriptor.colorAttachments[0].sourceAlphaBlendFactor = .one\n        descriptor.colorAttachments[0].destinationRGBBlendFactor = .sourceColor\n        descriptor.colorAttachments[0].destinationAlphaBlendFactor = .zero\n      }\n    )\n\n    #if !os(tvOS)\n      // Create OIT pipeline\n      transparencyRenderPipelineState = try MetalUtil.makeRenderPipelineState(\n        device: device,\n        label: \"WorldRenderer.oit\",\n        vertexFunction: vertexFunction,\n        fragmentFunction: transparentFragmentFunction,\n        blendingEnabled: true,\n        editDescriptor: { (descriptor: MTLRenderPipelineDescriptor) in\n          // Accumulation texture\n          descriptor.colorAttachments[1].isBlendingEnabled = true\n          descriptor.colorAttachments[1].rgbBlendOperation = .add\n          descriptor.colorAttachments[1].alphaBlendOperation = .add\n          descriptor.colorAttachments[1].sourceRGBBlendFactor = .one\n          descriptor.colorAttachments[1].sourceAlphaBlendFactor = .one\n          descriptor.colorAttachments[1].destinationRGBBlendFactor = .one\n          descriptor.colorAttachments[1].destinationAlphaBlendFactor = .one\n\n          // Revealage texture\n          descriptor.colorAttachments[2].isBlendingEnabled = true\n          descriptor.colorAttachments[2].rgbBlendOperation = .add\n          descriptor.colorAttachments[2].alphaBlendOperation = .add\n          descriptor.colorAttachments[2].sourceRGBBlendFactor = .zero\n          descriptor.colorAttachments[2].sourceAlphaBlendFactor = .zero\n          descriptor.colorAttachments[2].destinationRGBBlendFactor = .oneMinusSourceColor\n          descriptor.colorAttachments[2].destinationAlphaBlendFactor = .oneMinusSourceAlpha\n        }\n      )\n\n      // Create OIT compositing pipeline\n      compositingRenderPipelineState = try MetalUtil.makeRenderPipelineState(\n        device: device,\n        label: \"WorldRenderer.compositing\",\n        vertexFunction: transparentCompositingVertexFunction,\n        fragmentFunction: transparentCompositingFragmentFunction,\n        blendingEnabled: true\n      )\n\n      // Create the depth state used for order independent transparency\n      readOnlyDepthState = try MetalUtil.createDepthState(device: device, readOnly: true)\n    #endif\n\n    // Create the regular depth state.\n    // TODO: Is this meant to be read only? I would assume not\n    depthState = try MetalUtil.createDepthState(device: device, readOnly: true)\n\n    // Create entity renderer\n    entityRenderer = try EntityRenderer(\n      client: client,\n      device: device,\n      commandQueue: commandQueue,\n      profiler: profiler,\n      blockTexturePalette: texturePalette\n    )\n\n    // Create world mesh\n    worldMesh = WorldMesh(client.game.world, resources: resources)\n\n    // TODO: Improve storage mode selection\n    #if os(macOS)\n      let storageMode = MTLResourceOptions.storageModeManaged\n    #elseif os(iOS) || os(tvOS)\n      let storageMode = MTLResourceOptions.storageModeShared\n    #else\n      #error(\"Unsupported platform\")\n    #endif\n\n    var identityUniforms = ChunkUniforms()\n    identityUniformsBuffer = try MetalUtil.makeBuffer(\n      device,\n      bytes: &identityUniforms,\n      length: MemoryLayout<ChunkUniforms>.stride,\n      options: storageMode\n    )\n\n    let maxOutlinePartCount =\n      RegistryStore.shared.blockRegistry.blocks.map { block in\n        return block.shape.outlineShape.aabbs.count\n      }.max() ?? 1\n\n    let geometry = Self.generateOutlineGeometry(position: .zero, size: [1, 1, 1], baseIndex: 0)\n\n    blockOutlineIndexBuffer = try MetalUtil.makeBuffer(\n      device,\n      length: MemoryLayout<UInt32>.stride * geometry.indices.count * maxOutlinePartCount,\n      options: storageMode,\n      label: \"blockOutlineIndexBuffer\"\n    )\n\n    blockOutlineVertexBuffer = try MetalUtil.makeBuffer(\n      device,\n      length: MemoryLayout<BlockVertex>.stride * geometry.vertices.count * maxOutlinePartCount,\n      options: storageMode,\n      label: \"blockOutlineVertexBuffer\"\n    )\n\n    fogUniformsBuffer = try MetalUtil.makeBuffer(\n      device,\n      length: MemoryLayout<FogUniforms>.stride,\n      options: storageMode,\n      label: \"fogUniformsBuffer\"\n    )\n\n    // Register event handler\n    client.eventBus.registerHandler { [weak self] event in\n      guard let self = self else { return }\n      self.handle(event)\n    }\n  }\n\n  // MARK: Public methods\n\n  /// Renders the world's blocks.\n  public func render(\n    view: MTKView,\n    encoder: MTLRenderCommandEncoder,\n    commandBuffer: MTLCommandBuffer,\n    worldToClipUniformsBuffer: MTLBuffer,\n    camera: Camera\n  ) throws {\n    // Update world mesh\n    profiler.push(.updateWorldMesh)\n    worldMesh.update(\n      camera: camera,\n      renderDistance: client.configuration.render.renderDistance\n    )\n    profiler.pop()\n\n    // Update animated textures\n    profiler.push(.updateAnimatedTextures)\n    texturePalette.update()\n    profiler.pop()\n\n    // Get light map buffer\n    profiler.push(.updateLightMap)\n    lightMap.update(\n      tick: client.game.tickScheduler.tickNumber,\n      sunAngleRadians: client.game.world.getSunAngleRadians(),\n      ambientLight: Double(client.game.world.dimension.ambientLight),\n      dimensionHasSkyLight: client.game.world.dimension.hasSkyLight\n    )\n    lightMapBuffer = try lightMap.getBuffer(device, reusing: lightMapBuffer)\n    profiler.pop()\n\n    profiler.push(.updateFogUniforms)\n    var fogUniforms = Self.fogUniforms(client: client, camera: camera)\n    fogUniformsBuffer.contents().copyMemory(\n      from: &fogUniforms,\n      byteCount: MemoryLayout<FogUniforms>.stride\n    )\n    profiler.pop()\n\n    // Setup render pass. The instance uniforms (vertex buffer index 3) are set to the identity\n    // matrix because this phase of the renderer doesn't use instancing although the chunk shader\n    // does support it.\n    encoder.setRenderPipelineState(renderPipelineState)\n    encoder.setVertexBuffer(texturePalette.textureStatesBuffer, offset: 0, index: 3)\n    encoder.setFragmentTexture(texturePalette.arrayTexture, index: 0)\n    encoder.setFragmentBuffer(lightMapBuffer, offset: 0, index: 0)\n    encoder.setFragmentBuffer(texturePalette.timeBuffer, offset: 0, index: 1)\n    encoder.setFragmentBuffer(fogUniformsBuffer, offset: 0, index: 2)\n\n    // Render transparent and opaque geometry\n    profiler.push(.encodeOpaque)\n    var visibleChunks: Set<ChunkPosition> = []\n    try worldMesh.mutateVisibleMeshes { position, mesh in\n      visibleChunks.insert(position.chunk)\n      try mesh.renderTransparentAndOpaque(\n        renderEncoder: encoder,\n        device: device,\n        commandQueue: commandQueue\n      )\n    }\n    profiler.pop()\n\n    if client.game.currentGamemode() != .spectator {\n      // Render selected block outline\n      profiler.push(.encodeBlockOutline)\n      if let targetedBlock = client.game.targetedBlock() {\n        let targetedBlockPosition = targetedBlock.target\n        var indices: [UInt32] = []\n        var vertices: [BlockVertex] = []\n        let block = client.game.world.getBlock(at: targetedBlockPosition)\n        let boundingBox = block.shape.outlineShape.offset(by: targetedBlockPosition.doubleVector)\n\n        if !boundingBox.aabbs.isEmpty {\n          for aabb in boundingBox.aabbs {\n            let geometry = Self.generateOutlineGeometry(\n              position: Vec3f(aabb.position),\n              size: Vec3f(aabb.size),\n              baseIndex: UInt32(indices.count)\n            )\n            indices.append(contentsOf: geometry.indices)\n            vertices.append(contentsOf: geometry.vertices)\n          }\n\n          blockOutlineVertexBuffer.contents().copyMemory(\n            from: &vertices,\n            byteCount: MemoryLayout<BlockVertex>.stride * vertices.count\n          )\n          blockOutlineIndexBuffer.contents().copyMemory(\n            from: &indices,\n            byteCount: MemoryLayout<UInt32>.stride * indices.count\n          )\n\n          encoder.setVertexBuffer(blockOutlineVertexBuffer, offset: 0, index: 0)\n          encoder.setVertexBuffer(identityUniformsBuffer, offset: 0, index: 2)\n\n          encoder.drawIndexedPrimitives(\n            type: .triangle,\n            indexCount: indices.count,\n            indexType: .uint32,\n            indexBuffer: blockOutlineIndexBuffer,\n            indexBufferOffset: 0\n          )\n        }\n      }\n      profiler.pop()\n    }\n\n    for breakingBlock in client.game.world.getBreakingBlocks() {\n      guard let stage = breakingBlock.stage else {\n        continue\n      }\n      let block = client.game.world.getBlock(at: breakingBlock.position)\n      if var model = resources.blockModelPalette.model(for: block.id, at: breakingBlock.position) {\n        let textureId = client.resourcePack.vanillaResources.blockTexturePalette.textureIndex(\n          for: Identifier(namespace: \"minecraft\", name: \"block/destroy_stage_\\(stage)\"))!\n        for (i, part) in model.parts.enumerated() {\n          for (j, element) in part.elements.enumerated() {\n            model.parts[i].elements[j].shade = false\n            for k in 0..<element.faces.count {\n              model.parts[i].elements[j].faces[k].texture = textureId\n              model.parts[i].elements[j].faces[k].isTinted = false\n            }\n          }\n        }\n        model.textureType = .transparent\n        let lightLevel = LightLevel(\n          sky: 15,\n          block: 0\n        )\n        var neighbourLightLevels: [Direction: LightLevel] = [:]\n        for direction in Direction.allDirections {\n          neighbourLightLevels[direction] = LightLevel(\n            sky: 15,\n            block: 0\n          )\n        }\n        let offset = block.getModelOffset(at: breakingBlock.position)\n        let modelToWorld = MatrixUtil.translationMatrix(breakingBlock.position.floatVector + offset)\n        let builder = BlockMeshBuilder(\n          model: model,\n          position: breakingBlock.position,\n          modelToWorld: modelToWorld,\n          culledFaces: [],\n          lightLevel: lightLevel,\n          neighbourLightLevels: neighbourLightLevels,\n          tintColor: Vec3f(repeating: 0),\n          blockTexturePalette: resources.blockTexturePalette\n        )\n        var dummyGeometry = Geometry<BlockVertex>()\n        var geometry = SortableMeshElement()\n        builder.build(into: &dummyGeometry, translucentGeometry: &geometry)\n        for i in 0..<geometry.vertices.count {\n          geometry.vertices[i].isTransparent = true\n        }\n        let vertexBuffer = device.makeBuffer(\n          bytes: &geometry.vertices,\n          length: MemoryLayout<BlockVertex>.stride * geometry.vertices.count)\n        guard\n          let indexBuffer = device.makeBuffer(\n            bytes: &geometry.indices, length: MemoryLayout<UInt32>.stride * geometry.indices.count)\n        else {\n          // No geometry to render\n          continue\n        }\n\n        encoder.setRenderPipelineState(destroyOverlayRenderPipelineState)\n        encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)\n        encoder.setVertexBuffer(identityUniformsBuffer, offset: 0, index: 2)\n\n        encoder.drawIndexedPrimitives(\n          type: .triangle,\n          indexCount: geometry.indices.count,\n          indexType: .uint32,\n          indexBuffer: indexBuffer,\n          indexBufferOffset: 0\n        )\n      }\n    }\n\n    // Entities are rendered before translucent geometry for correct alpha blending behaviour.\n    profiler.push(.entities)\n    entityRenderer.setVisibleChunks(visibleChunks)\n    try entityRenderer.render(\n      view: view,\n      encoder: encoder,\n      commandBuffer: commandBuffer,\n      worldToClipUniformsBuffer: worldToClipUniformsBuffer,\n      camera: camera\n    )\n    profiler.pop()\n\n    // Setup render pass for encoding translucent geometry after entity rendering pass\n    #if os(tvOS)\n      encoder.setRenderPipelineState(renderPipelineState)\n    #else\n      if client.configuration.render.enableOrderIndependentTransparency {\n        encoder.setRenderPipelineState(transparencyRenderPipelineState)\n        encoder.setDepthStencilState(readOnlyDepthState)\n      } else {\n        encoder.setRenderPipelineState(renderPipelineState)\n      }\n    #endif\n\n    encoder.setVertexBuffer(texturePalette.textureStatesBuffer, offset: 0, index: 3)\n\n    encoder.setFragmentTexture(texturePalette.arrayTexture, index: 0)\n    encoder.setFragmentBuffer(lightMapBuffer, offset: 0, index: 0)\n    encoder.setFragmentBuffer(texturePalette.timeBuffer, offset: 0, index: 1)\n\n    // Render translucent geometry\n    profiler.push(.encodeTranslucent)\n    try worldMesh.mutateVisibleMeshes(fromBackToFront: true) { _, mesh in\n      try mesh.renderTranslucent(\n        viewedFrom: camera.position,\n        sortTranslucent: !client.configuration.render.enableOrderIndependentTransparency,\n        renderEncoder: encoder,\n        device: device,\n        commandQueue: commandQueue\n      )\n    }\n\n    #if !os(tvOS)\n      // Composite translucent geometry onto the screen buffer. No vertices need to be supplied, the\n      // shader has the screen's corners hardcoded for simplicity.\n      if client.configuration.render.enableOrderIndependentTransparency {\n        encoder.setRenderPipelineState(compositingRenderPipelineState)\n        encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6)\n        encoder.setDepthStencilState(depthState)\n      }\n    #endif\n    profiler.pop()\n  }\n\n  // MARK: Private methods\n\n  private func handle(_ event: Event) {\n    // TODO: Optimize this the section updating algorithms to minimise unnecessary updates\n    switch event {\n      case let event as World.Event.AddChunk:\n        worldMesh.addChunk(at: event.position)\n\n      case let event as World.Event.RemoveChunk:\n        worldMesh.removeChunk(at: event.position)\n\n      case let event as World.Event.UpdateChunkLighting:\n        let updatedSections = event.data.updatedSections\n        var affectedSections: Set<ChunkSectionPosition> = []\n        for y in updatedSections {\n          let position = ChunkSectionPosition(event.position, sectionY: y)\n          let affected = worldMesh.sectionsAffectedBySectionUpdate(at: position, onlyLighting: true)\n          affectedSections.formUnion(affected)\n        }\n        worldMesh.updateSections(at: Array(affectedSections))\n\n      case let event as World.Event.SingleBlockUpdate:\n        let affectedSections = worldMesh.sectionsAffectedBySectionUpdate(\n          at: event.position.chunkSection)\n        worldMesh.updateSections(at: Array(affectedSections))\n\n      case let event as World.Event.MultiBlockUpdate:\n        var affectedSections: Set<ChunkSectionPosition> = []\n        for update in event.updates {\n          affectedSections.formUnion(\n            worldMesh.sectionsAffectedBySectionUpdate(at: update.position.chunkSection))\n        }\n        worldMesh.updateSections(at: Array(affectedSections))\n\n      case let event as World.Event.UpdateChunk:\n        var affectedSections: Set<ChunkSectionPosition> = []\n\n        for sectionY in event.updatedSections {\n          let position = ChunkSectionPosition(event.position, sectionY: sectionY)\n          affectedSections.formUnion(worldMesh.sectionsAffectedBySectionUpdate(at: position))\n        }\n        worldMesh.updateSections(at: Array(affectedSections))\n\n      case _ as JoinWorldEvent:\n        // TODO: this has the possibility to cause crashes\n        worldMesh = WorldMesh(client.game.world, resources: resources)\n\n      default:\n        return\n    }\n  }\n\n  static func fogUniforms(client: Client, camera: Camera) -> FogUniforms {\n    // When the render distance is above 2, move the fog 1 chunk closer to conceal\n    // more of the world edge.\n    let renderDistance = max(client.configuration.render.renderDistance - 1, 2)\n    let fog = client.game.world.getFog(\n      forViewerWithRay: camera.ray,\n      withRenderDistance: renderDistance\n    )\n\n    let isLinear: Bool\n    let fogDensity: Float\n    let fogStart: Float\n    let fogEnd: Float\n    switch fog.style {\n      case let .exponential(density):\n        isLinear = false\n        fogDensity = density\n        // Start and end are ignored by exponential fog\n        fogStart = 0\n        fogEnd = 0\n      case let .linear(start, end):\n        isLinear = true\n        // Density is ignored by linear fog\n        fogDensity = 0\n        fogStart = start\n        fogEnd = end\n    }\n\n    return FogUniforms(\n      fogColor: fog.color,\n      fogStart: fogStart,\n      fogEnd: fogEnd,\n      fogDensity: fogDensity,\n      isLinear: isLinear\n    )\n  }\n\n  private static func generateOutlineGeometry(\n    position: Vec3f,\n    size: Vec3f,\n    baseIndex: UInt32\n  ) -> Geometry<BlockVertex> {\n    let thickness: Float = 0.004\n    let padding: Float = -thickness + 0.001\n\n    // swiftlint:disable:next large_tuple\n    var boxes: [(position: Vec3f, size: Vec3f, axis: Axis, faces: [Direction])] = []\n    for side: Direction in [.north, .east, .south, .west] {\n      // Create up-right edge between this side and the next\n      let adjacentSide = side.rotated(1, clockwiseFacing: .down)\n\n      var position = side.vector + adjacentSide.vector\n      position *= size / 2 + Vec3f(padding + thickness / 2, 0, padding + thickness / 2)\n      position += Vec3f(size.x - thickness, 0, size.z - thickness) / 2\n      position.y -= padding\n      boxes.append(\n        (\n          position: position,\n          size: [thickness, size.component(along: .y) + padding * 2, thickness],\n          axis: .y,\n          faces: [side, adjacentSide]\n        ))\n\n      // Create the edges above and below this side\n      for direction: Direction in [.up, .down] {\n        let edgeDirection = adjacentSide.axis.positiveDirection.vector\n        var edgeSize = size.component(along: adjacentSide.axis) + (padding + thickness) * 2\n        if adjacentSide.axis == .x {\n          edgeSize -= thickness * 2\n        }\n        let edge = abs(adjacentSide.vector * edgeSize)\n\n        var position = position\n        if direction == .up {\n          position.y += size.component(along: .y) + padding * 2\n        } else {\n          position.y -= thickness\n        }\n        if position.component(along: adjacentSide.axis) > 0 {\n          if adjacentSide.axis == .x {\n            position.x -= size.component(along: .x) + padding * 2\n          } else {\n            position.z -= size.component(along: .z) + padding * 2 + thickness\n          }\n        } else if adjacentSide.axis == .x {\n          position.x += thickness\n        }\n        var faces = [side, direction]\n        if adjacentSide.axis != .x {\n          faces.append(contentsOf: [adjacentSide, adjacentSide.opposite])\n        }\n        boxes.append(\n          (\n            position: position,\n            size: (Vec3f(1, 1, 1) - edgeDirection) * thickness + edge,\n            axis: adjacentSide.axis,\n            faces: faces\n          ))\n      }\n    }\n\n    var blockOutlineVertices: [BlockVertex] = []\n    var blockOutlineIndices: [UInt32] = []\n\n    let translation = MatrixUtil.translationMatrix(position)\n    for box in boxes {\n      for face in box.faces {\n        let offset = UInt32(blockOutlineVertices.count) + baseIndex\n        let winding = CubeGeometry.faceWinding.map { index in\n          return index + offset\n        }\n\n        // Render both front and back faces\n        blockOutlineIndices.append(contentsOf: winding)\n        blockOutlineIndices.append(contentsOf: winding.reversed())\n\n        let transformation =\n          MatrixUtil.scalingMatrix(box.size) * MatrixUtil.translationMatrix(box.position)\n          * translation\n        for vertex in CubeGeometry.faceVertices[face.rawValue] {\n          let vertexPosition = (Vec4f(vertex, 1) * transformation).xyz\n          blockOutlineVertices.append(\n            BlockVertex(\n              x: vertexPosition.x,\n              y: vertexPosition.y,\n              z: vertexPosition.z,\n              u: 0,\n              v: 0,\n              r: 0,\n              g: 0,\n              b: 0,\n              a: 0.6,\n              skyLightLevel: UInt8(LightLevel.maximumLightLevel),\n              blockLightLevel: 0,\n              textureIndex: UInt16.max,\n              isTransparent: false\n            ))\n        }\n      }\n    }\n\n    return Geometry(vertices: blockOutlineVertices, indices: blockOutlineIndices)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Account.swift",
    "content": "import Foundation\n\n/// An account which can be a Microsoft, Mojang or offline account.\npublic enum Account: Codable, Identifiable, Hashable {\n  case microsoft(MicrosoftAccount)\n  case mojang(MojangAccount)\n  case offline(OfflineAccount)\n\n  /// The account's id.\n  public var id: String {\n    switch self {\n      case .microsoft(let account as OnlineAccount), .mojang(let account as OnlineAccount):\n        return account.id\n      case .offline(let account):\n        return account.id\n    }\n  }\n\n  /// The account type to display to users.\n  public var type: String {\n    switch self {\n      case .microsoft:\n        return \"Microsoft\"\n      case .mojang:\n        return \"Mojang\"\n      case .offline:\n        return \"Offline\"\n    }\n  }\n\n  /// The account's username.\n  public var username: String {\n    switch self {\n      case .microsoft(let account as OnlineAccount), .mojang(let account as OnlineAccount):\n        return account.username\n      case .offline(let account):\n        return account.username\n    }\n  }\n\n  /// The online version of this account if the account supports online mode.\n  public var online: OnlineAccount? {\n    switch self {\n      case .microsoft(let account as OnlineAccount), .mojang(let account as OnlineAccount):\n        return account\n      case .offline:\n        return nil\n    }\n  }\n\n  /// The offline version of this account.\n  public var offline: OfflineAccount {\n    switch self {\n      case .microsoft(let account as OnlineAccount), .mojang(let account as OnlineAccount):\n        return OfflineAccount(username: account.username)\n      case .offline(let account):\n        return account\n    }\n  }\n\n  /// Refreshes the account's access token (if it has one).\n  /// - Parameter clientToken: The client token to use when refreshing the account.\n  public func refreshed(withClientToken clientToken: String) async throws -> Self {\n    switch self {\n      case .microsoft(let account):\n        let account = try await MicrosoftAPI.refreshMinecraftAccount(account)\n        return .microsoft(account)\n      case .mojang(let account):\n        let account = try await MojangAPI.refresh(account, with: clientToken)\n        return .mojang(account)\n      case .offline:\n        return self\n    }\n  }\n\n  /// Refreshes the account's access token in place (if it has one).\n  /// - Parameter clientToken: The client token to use when refreshing the account.\n  public mutating func refresh(withClientToken clientToken: String) async throws {\n    self = try await refreshed(withClientToken: clientToken)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/MicrosoftAPI.swift",
    "content": "import Foundation\n\npublic enum MicrosoftAPIError: LocalizedError {\n  case noUserHashInResponse\n  case failedToDeserializeResponse\n  case xstsAuthenticationFailed\n  case expiredAccessToken\n  case failedToGetXboxLiveToken\n  case failedToGetXSTSToken\n  case failedToGetMinecraftAccessToken\n  case failedToGetAttachedLicenses\n  case accountDoesntOwnMinecraft\n  case failedToGetMinecraftAccount\n\n  public var errorDescription: String? {\n    switch self {\n      case .noUserHashInResponse:\n        return \"No user hash in response.\"\n      case .failedToDeserializeResponse:\n        return \"Failed to deserialize response.\"\n      case .xstsAuthenticationFailed:\n        return \"XSTS authentication failed.\"\n      case .expiredAccessToken:\n        return \"Expired access token.\"\n      case .failedToGetXboxLiveToken:\n        return \"Failed to get Xbox Live token.\"\n      case .failedToGetXSTSToken:\n        return \"Failed to get XSTS token.\"\n      case .failedToGetMinecraftAccessToken:\n        return \"Failed to get Minecraft access token.\"\n      case .failedToGetAttachedLicenses:\n        return \"Failed to get attached licenses.\"\n      case .accountDoesntOwnMinecraft:\n        return \"Account doesnt own Minecraft.\"\n      case .failedToGetMinecraftAccount:\n        return \"Failed to get Minecraft account.\"\n    }\n  }\n}\n\n/// A utility for interacting with the Microsoft authentication API.\n///\n/// ## Overview\n///\n/// First, device authorization is obtained via ``authorizeDevice()``. The user must then visit the\n/// verification url provided in the ``MicrosoftDeviceAuthorizationResponse`` and enter the\n/// `userCode` also provided in the response.\n///\n/// Once the user has completed the interactive part of logging in, ``getMicrosoftAccessToken(_:)``\n/// is used to convert the device code into a Microsoft access token.\n///\n/// ``getMinecraftAccount(_:)`` can then be called with the access token, resulting in an\n/// authenticated Microsoft-based Minecraft account.\npublic enum MicrosoftAPI {\n  // swiftlint:disable force_unwrapping\n  /// The client id used for Microsoft authentication.\n  public static let clientId = \"e5c1b05f-4e94-4747-90bf-3e9d40f830f1\"\n\n  private static let xstsSandboxId = \"RETAIL\"\n  private static let xstsRelyingParty = \"rp://api.minecraftservices.com/\"\n\n  private static let authorizationURL = URL(string: \"https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode\")!\n  private static let authenticationURL = URL(string: \"https://login.microsoftonline.com/consumers/oauth2/v2.0/token\")!\n\n  private static let xboxLiveAuthenticationURL = URL(string: \"https://user.auth.xboxlive.com/user/authenticate\")!\n  private static let xstsAuthenticationURL = URL(string: \"https://xsts.auth.xboxlive.com/xsts/authorize\")!\n  private static let minecraftXboxAuthenticationURL = URL(string: \"https://api.minecraftservices.com/authentication/login_with_xbox\")!\n\n  private static let gameOwnershipURL = URL(string: \"https://api.minecraftservices.com/entitlements/mcstore\")!\n  private static let minecraftProfileURL = URL(string: \"https://api.minecraftservices.com/minecraft/profile\")!\n  // swiftlint:enable force_unwrapping\n\n  // MARK: Public methods\n\n  /// Authorizes this device (metaphorically). This is the first step in authenticating a user.\n  /// - Returns: A device authorization response.\n  public static func authorizeDevice() async throws -> MicrosoftDeviceAuthorizationResponse {\n    let (_, data) = try await RequestUtil.performFormRequest(\n      url: authorizationURL,\n      body: [\n        \"client_id\": clientId,\n        \"scope\": \"XboxLive.signin offline_access\"\n      ],\n      method: .post\n    )\n\n    let response: MicrosoftDeviceAuthorizationResponse = try decodeResponse(data)\n    // TODO: extract the error type if the response isn't a valid access token response\n\n    return response\n  }\n\n  /// Fetches an access token for the user after they have completed the interactive login process.\n  /// - Parameter deviceCode: The device code used during authentication.\n  /// - Returns: An authenticated Microsoft access token.\n  public static func getMicrosoftAccessToken(_ deviceCode: String) async throws -> MicrosoftAccessToken {\n    let (_, data) = try await RequestUtil.performFormRequest(\n      url: authenticationURL,\n      body: [\n        \"tenant\": \"consumers\",\n        \"grant_type\": \"urn:ietf:params:oauth:grant-type:device_code\",\n        \"client_id\": clientId,\n        \"device_code\": deviceCode\n      ],\n      method: .post\n    )\n\n    let response: MicrosoftAccessTokenResponse = try decodeResponse(data)\n    // TODO: extract the error type if the response isn't a valid access token response\n\n    let accessToken = MicrosoftAccessToken(\n      token: response.accessToken,\n      expiresIn: response.expiresIn,\n      refreshToken: response.refreshToken\n    )\n\n    return accessToken\n  }\n\n  /// Gets the user's Minecraft account.\n  /// - Parameters:\n  ///   - minecraftAccessToken: The user's Minecraft access token.\n  ///   - microsoftAccessToken: The user's Microsoft access token.\n  /// - Returns: The user's Minecraft account.\n  public static func getMinecraftAccount(\n    _ minecraftAccessToken: MinecraftAccessToken,\n    _ microsoftAccessToken: MicrosoftAccessToken\n  ) async throws -> MicrosoftAccount {\n    var request = Request(minecraftProfileURL)\n    request.method = .get\n    request.headers[\"Authorization\"] = \"Bearer \\(minecraftAccessToken.token)\"\n\n    let (_, data) = try await RequestUtil.performRequest(request)\n\n    let response: MicrosoftMinecraftProfileResponse = try decodeResponse(data)\n\n    return MicrosoftAccount(\n      id: response.id,\n      username: response.name,\n      minecraftAccessToken: minecraftAccessToken,\n      microsoftAccessToken: microsoftAccessToken\n    )\n  }\n\n  /// Gets the user's Microsoft-based Minecraft account from their Microsoft access token.\n  /// - Parameter microsoftAccessToken: The user's Microsoft access token with suitable scope\n  ///   `XboxLive.signin` and `offline_access`.\n  /// - Returns: An authenticated and authorized Microsoft-based Minecraft account.\n  public static func getMinecraftAccount(_ microsoftAccessToken: MicrosoftAccessToken) async throws -> MicrosoftAccount {\n    // Get Xbox live token\n    let xboxLiveToken: XboxLiveToken\n    do {\n      xboxLiveToken = try await MicrosoftAPI.getXBoxLiveToken(microsoftAccessToken)\n    } catch {\n      throw MicrosoftAPIError.failedToGetXboxLiveToken.becauseOf(error)\n    }\n\n    // Get XSTS token\n    let xstsToken: String\n    do {\n      xstsToken = try await MicrosoftAPI.getXSTSToken(xboxLiveToken)\n    } catch {\n      throw MicrosoftAPIError.failedToGetXSTSToken.becauseOf(error)\n    }\n\n    // Get Minecraft access token\n    let minecraftAccessToken: MinecraftAccessToken\n    do {\n      minecraftAccessToken = try await MicrosoftAPI.getMinecraftAccessToken(xstsToken, xboxLiveToken)\n    } catch {\n      throw MicrosoftAPIError.failedToGetMinecraftAccessToken.becauseOf(error)\n    }\n\n    // Get a list of the user's licenses\n    let licenses: [GameOwnershipResponse.License]\n    do {\n      licenses = try await MicrosoftAPI.getAttachedLicenses(minecraftAccessToken)\n    } catch {\n      throw MicrosoftAPIError.failedToGetAttachedLicenses.becauseOf(error)\n    }\n\n    if licenses.isEmpty {\n      throw MicrosoftAPIError.accountDoesntOwnMinecraft\n    }\n\n    // Get the user's account\n    let account: MicrosoftAccount\n    do {\n      account = try await MicrosoftAPI.getMinecraftAccount(minecraftAccessToken, microsoftAccessToken)\n    } catch {\n      throw MicrosoftAPIError.failedToGetMinecraftAccount.becauseOf(error)\n    }\n\n    return account\n  }\n\n  /// Refreshes a Minecraft account which is attached to a Microsoft account.\n  /// - Parameter account: The account to refresh.\n  /// - Returns: The refreshed account.\n  public static func refreshMinecraftAccount(_ account: MicrosoftAccount) async throws -> MicrosoftAccount {\n    log.debug(\"Start refresh microsoft account\")\n    var account = account\n    if account.microsoftAccessToken.hasExpired {\n      account.microsoftAccessToken = try await MicrosoftAPI.refreshMicrosoftAccessToken(account.microsoftAccessToken)\n    }\n\n    account = try await MicrosoftAPI.getMinecraftAccount(account.microsoftAccessToken)\n    log.debug(\"Finish refresh microsoft account\")\n    return account\n  }\n\n  // MARK: Private methods\n\n  /// Acquires a new access token for the user's account using an existing refresh token.\n  /// - Parameter token: The access token to refresh.\n  /// - Returns: The refreshed access token.\n  private static func refreshMicrosoftAccessToken(_ token: MicrosoftAccessToken) async throws -> MicrosoftAccessToken {\n    let formData = [\n      \"client_id\": clientId,\n      \"refresh_token\": token.refreshToken,\n      \"grant_type\": \"refresh_token\",\n      \"scope\": \"service::user.auth.xboxlive.com::MBI_SSL\"\n    ]\n\n    let (_, data) = try await RequestUtil.performFormRequest(\n      url: authenticationURL,\n      body: formData,\n      method: .post\n    )\n\n    let response: MicrosoftAccessTokenResponse = try decodeResponse(data)\n\n    let accessToken = MicrosoftAccessToken(\n      token: response.accessToken,\n      expiresIn: response.expiresIn,\n      refreshToken: response.refreshToken\n    )\n\n    return accessToken\n  }\n\n  /// Gets the user's Xbox Live token from their Microsoft access token.\n  /// - Parameters:\n  ///   - accessToken: The user's Microsoft access token.\n  /// - Returns: The user's Xbox Live token.\n  private static func getXBoxLiveToken(_ accessToken: MicrosoftAccessToken) async throws -> XboxLiveToken {\n    guard !accessToken.hasExpired else {\n      throw MicrosoftAPIError.expiredAccessToken\n    }\n\n    let payload = XboxLiveAuthenticationRequest(\n      properties: XboxLiveAuthenticationRequest.Properties(\n        authMethod: \"RPS\",\n        siteName: \"user.auth.xboxlive.com\",\n        accessToken: \"d=\\(accessToken.token)\"\n      ),\n      relyingParty: \"http://auth.xboxlive.com\",\n      tokenType: \"JWT\"\n    )\n\n    let (_, data) = try await RequestUtil.performJSONRequest(\n      url: xboxLiveAuthenticationURL,\n      body: payload,\n      method: .post\n    )\n\n    let response: XboxLiveAuthenticationResponse = try decodeResponse(data)\n\n    guard let userHash = response.displayClaims.xui.first?.userHash else {\n      throw MicrosoftAPIError.noUserHashInResponse\n    }\n\n    return XboxLiveToken(token: response.token, userHash: userHash)\n  }\n\n  /// Gets the user's XSTS token from their Xbox Live token.\n  /// - Parameters:\n  ///   - xboxLiveToken: The user's Xbox Live token.\n  /// - Returns: The user's XSTS token.\n  private static func getXSTSToken(_ xboxLiveToken: XboxLiveToken) async throws -> String {\n    let payload = XSTSAuthenticationRequest(\n      properties: XSTSAuthenticationRequest.Properties(\n        sandboxId: xstsSandboxId,\n        userTokens: [xboxLiveToken.token]\n      ),\n      relyingParty: xstsRelyingParty,\n      tokenType: \"JWT\"\n    )\n\n    let (_, data) = try await RequestUtil.performJSONRequest(\n      url: xstsAuthenticationURL,\n      body: payload,\n      method: .post\n    )\n\n    guard let response: XSTSAuthenticationResponse = try? decodeResponse(data) else {\n      let error: XSTSAuthenticationError\n      do {\n        error = try decodeResponse(data)\n      } catch {\n        throw MicrosoftAPIError.failedToDeserializeResponse\n          .with(\"Content\", String(decoding: data, as: UTF8.self))\n          .becauseOf(error)\n      }\n\n      // Decode Microsoft's cryptic error codes\n      let message: String\n      switch error.code {\n        case 2148916233:\n          message = \"This Microsoft account does not have an attached Xbox Live account (\\(error.redirect))\"\n        case 2148916238:\n          message = \"Child accounts must first be added to a family (\\(error.redirect))\"\n        default:\n          message = error.message\n      }\n\n      throw MicrosoftAPIError.xstsAuthenticationFailed\n        .with(\"Reason\", message)\n        .with(\"Code\", error.code)\n        .with(\"Identity\", error.identity)\n    }\n\n    return response.token\n  }\n\n  /// Gets the Minecraft access token from the user's XSTS token.\n  /// - Parameters:\n  ///   - xstsToken: The user's XSTS token.\n  ///   - xboxLiveToken: The user's Xbox Live token.\n  /// - Returns: The user's Minecraft access token.\n  private static func getMinecraftAccessToken(_ xstsToken: String, _ xboxLiveToken: XboxLiveToken) async throws -> MinecraftAccessToken {\n    let payload = MinecraftXboxAuthenticationRequest(\n      identityToken: \"XBL3.0 x=\\(xboxLiveToken.userHash);\\(xstsToken)\"\n    )\n\n    let (_, data) = try await RequestUtil.performJSONRequest(\n      url: minecraftXboxAuthenticationURL,\n      body: payload,\n      method: .post\n    )\n\n    let response: MinecraftXboxAuthenticationResponse = try decodeResponse(data)\n\n    let token = MinecraftAccessToken(\n      token: response.accessToken,\n      expiresIn: response.expiresIn\n    )\n\n    return token\n  }\n\n  /// Gets the license attached to an account.\n  /// - Parameters:\n  ///   - accessToken: The user's Minecraft access token.\n  /// - Returns: The licenses attached to the user's Microsoft account.\n  private static func getAttachedLicenses(_ accessToken: MinecraftAccessToken) async throws -> [GameOwnershipResponse.License] {\n    var request = Request(gameOwnershipURL)\n    request.method = .get\n    request.headers[\"Authorization\"] = \"Bearer \\(accessToken.token)\"\n\n    let (_, data) = try await RequestUtil.performRequest(request)\n\n    let response: GameOwnershipResponse = try decodeResponse(data)\n\n    return response.items\n  }\n\n  /// A helper function for decoding JSON responses.\n  /// - Parameter data: The JSON data.\n  /// - Returns: The decoded response.\n  private static func decodeResponse<Response: Decodable>(_ data: Data) throws -> Response {\n    do {\n      return try CustomJSONDecoder().decode(Response.self, from: data)\n    } catch {\n      throw MicrosoftAPIError.failedToDeserializeResponse\n        .with(\"Content\", String(decoding: data, as: UTF8.self))\n        .becauseOf(error)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/MicrosoftAccessToken.swift",
    "content": "import Foundation\nimport CoreFoundation\n\n/// An access token used for refreshing Minecraft access tokens attached to Microsoft accounts.\npublic struct MicrosoftAccessToken: Codable, Hashable {\n  /// The access token.\n  public var token: String\n  /// The time that the token will expire at in system absolute time.\n  public var expiry: Int\n  /// The token used to acquire a new access token when it expires.\n  public var refreshToken: String\n\n  /// Whether the access token has expired or not. Includes a leeway of 10 seconds.\n  public var hasExpired: Bool {\n    return Int(CFAbsoluteTimeGetCurrent()) > expiry - 10\n  }\n\n  /// Creates a new access token with the given properties.\n  /// - Parameters:\n  ///   - token: The access token.\n  ///   - expiry: The time that the access token will expire at in system absolute time.\n  ///   - refreshToken: The refresh token.\n  public init(token: String, expiry: Int, refreshToken: String) {\n    self.token = token\n    self.expiry = expiry\n    self.refreshToken = refreshToken\n  }\n\n  /// Creates a new access token with the given properties.\n  /// - Parameters:\n  ///   - token: The access token.\n  ///   - secondsToLive: The number of seconds until the access token expires.\n  ///   - refreshToken: The refresh token.\n  public init(token: String, expiresIn secondsToLive: Int, refreshToken: String) {\n    self.token = token\n    self.expiry = Int(CFAbsoluteTimeGetCurrent()) + secondsToLive\n    self.refreshToken = refreshToken\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/MicrosoftAccount.swift",
    "content": "import Foundation\n\n/// A user account that authenticates using the new Microsoft method.\npublic struct MicrosoftAccount: Codable, OnlineAccount, Hashable {\n  /// The account's id as a uuid.\n  public var id: String\n  /// The account's username.\n  public var username: String\n  /// The access token used to connect to servers.\n  public var accessToken: MinecraftAccessToken\n  /// The Microsoft access token and refresh token pair.\n  public var microsoftAccessToken: MicrosoftAccessToken\n  \n  /// Creates an account with the given properties.\n  public init(\n    id: String,\n    username: String,\n    minecraftAccessToken: MinecraftAccessToken,\n    microsoftAccessToken: MicrosoftAccessToken\n  ) {\n    self.id = id\n    self.username = username\n    accessToken = minecraftAccessToken\n    self.microsoftAccessToken = microsoftAccessToken\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/Request/MinecraftXboxAuthenticationRequest.swift",
    "content": "import Foundation\n\nstruct MinecraftXboxAuthenticationRequest: Codable {\n  var identityToken: String\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/Request/XSTSAuthenticationRequest.swift",
    "content": "import Foundation\n\nstruct XSTSAuthenticationRequest: Codable {\n  struct Properties: Codable {\n    var sandboxId: String\n    var userTokens: [String]\n    \n    // swiftlint:disable nesting\n    enum CodingKeys: String, CodingKey {\n      case sandboxId = \"SandboxId\"\n      case userTokens = \"UserTokens\"\n    }\n    // swiftlint:enable nesting\n  }\n  \n  var properties: Properties\n  var relyingParty: String\n  var tokenType: String\n  \n  enum CodingKeys: String, CodingKey {\n    case properties = \"Properties\"\n    case relyingParty = \"RelyingParty\"\n    case tokenType = \"TokenType\"\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/Request/XboxLiveAuthenticationRequest.swift",
    "content": "import Foundation\n\nstruct XboxLiveAuthenticationRequest: Codable {\n  struct Properties: Codable {\n    var authMethod: String\n    var siteName: String\n    var accessToken: String\n    \n    // swiftlint:disable nesting\n    enum CodingKeys: String, CodingKey {\n      case authMethod = \"AuthMethod\"\n      case siteName = \"SiteName\"\n      case accessToken = \"RpsTicket\"\n    }\n    // swiftlint:enable nesting\n  }\n  \n  var properties: Properties\n  var relyingParty: String\n  var tokenType: String\n  \n  enum CodingKeys: String, CodingKey {\n    case properties = \"Properties\"\n    case relyingParty = \"RelyingParty\"\n    case tokenType = \"TokenType\"\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/Response/GameOwnershipResponse.swift",
    "content": "import Foundation\n\nstruct GameOwnershipResponse: Codable {\n  struct License: Codable {\n    var name: String\n    var signature: String\n  }\n  \n  var items: [License]\n  var signature: String\n  var keyId: String\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/Response/MicrosoftAccessTokenResponse.swift",
    "content": "import Foundation\n\nstruct MicrosoftAccessTokenResponse: Decodable {\n  var tokenType: String\n  var expiresIn: Int\n  var scope: String\n  var accessToken: String\n  var refreshToken: String\n\n  enum CodingKeys: String, CodingKey {\n    case tokenType = \"token_type\"\n    case expiresIn = \"expires_in\"\n    case scope\n    case accessToken = \"access_token\"\n    case refreshToken = \"refresh_token\"\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/Response/MicrosoftDeviceAuthorizationResponse.swift",
    "content": "import Foundation\n\npublic struct MicrosoftDeviceAuthorizationResponse: Decodable {\n  public var deviceCode: String\n  public var userCode: String\n  public var verificationURI: URL\n  public var expiresIn: Int\n  public var interval: Int\n  public var message: String\n\n  private enum CodingKeys: String, CodingKey {\n    case deviceCode = \"device_code\"\n    case userCode = \"user_code\"\n    case verificationURI = \"verification_uri\"\n    case expiresIn = \"expires_in\"\n    case interval\n    case message = \"message\"\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/Response/MicrosoftMinecraftProfileResponse.swift",
    "content": "import Foundation\n\nstruct MicrosoftMinecraftProfileResponse: Decodable {\n  var id: String\n  var name: String\n  var skins: [Skin]\n  var capes: [Cape]?\n  \n  struct Skin: Decodable {\n    var id: String\n    var state: String\n    var url: URL\n    var variant: String\n    var alias: String?\n  }\n  \n  struct Cape: Decodable {\n    var id: String\n    var state: String\n    var url: URL\n    var alias: String?\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/Response/MinecraftXboxAuthenticationResponse.swift",
    "content": "import Foundation\n\nstruct MinecraftXboxAuthenticationResponse: Codable {\n  var username: String\n  var roles: [String]\n  var accessToken: String\n  var tokenType: String\n  var expiresIn: Int\n  \n  enum CodingKeys: String, CodingKey {\n    case username\n    case roles\n    case accessToken = \"access_token\"\n    case tokenType = \"token_type\"\n    case expiresIn = \"expires_in\"\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/Response/XSTSAuthenticationError.swift",
    "content": "import Foundation\n\npublic struct XSTSAuthenticationError: Codable {\n  public let identity: String\n  public let code: Int\n  public let message: String\n  public let redirect: String\n  \n  enum CodingKeys: String, CodingKey {\n    case identity = \"Identity\"\n    case code = \"XErr\"\n    case message = \"Message\"\n    case redirect = \"Redirect\"\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/Response/XSTSAuthenticationResponse.swift",
    "content": "import Foundation\n\nstruct XSTSAuthenticationResponse: Codable {\n  struct Claims: Codable {\n    var xui: [XUIClaim]\n  }\n  \n  struct XUIClaim: Codable {\n    var userHash: String\n    \n    private enum CodingKeys: String, CodingKey {\n      case userHash = \"uhs\"\n    }\n  }\n  \n  var issueInstant: String\n  var notAfter: String\n  var token: String\n  var displayClaims: Claims\n  \n  enum CodingKeys: String, CodingKey {\n    case issueInstant = \"IssueInstant\"\n    case notAfter = \"NotAfter\"\n    case token = \"Token\"\n    case displayClaims = \"DisplayClaims\"\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/Response/XboxLiveAuthenticationResponse.swift",
    "content": "import Foundation\n\nstruct XboxLiveAuthenticationResponse: Codable {\n  struct Claims: Codable {\n    var xui: [XUIClaim]\n  }\n  \n  struct XUIClaim: Codable {\n    var userHash: String\n    \n    // swiftlint:disable nesting\n    enum CodingKeys: String, CodingKey {\n      case userHash = \"uhs\"\n    }\n    // swiftlint:enable nesting\n  }\n  \n  var issueInstant: String\n  var notAfter: String\n  var token: String\n  var displayClaims: Claims\n  \n  enum CodingKeys: String, CodingKey {\n    case issueInstant = \"IssueInstant\"\n    case notAfter = \"NotAfter\"\n    case token = \"Token\"\n    case displayClaims = \"DisplayClaims\"\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Microsoft/XboxLiveToken.swift",
    "content": "struct XboxLiveToken {\n  var token: String\n  var userHash: String\n  \n  init(token: String, userHash: String) {\n    self.token = token\n    self.userHash = userHash\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/MinecraftAccessToken.swift",
    "content": "import Foundation\nimport CoreFoundation\n\n/// An access token attached to an online account. Used for connecting to online-mode servers.\npublic struct MinecraftAccessToken: Codable, Hashable {\n  /// The access token.\n  public var token: String\n  /// The time that the token will expire at in system absolute time. If `nil`, the token won't expire.\n  public var expiry: Int?\n\n  /// Whether the access token has expired of not. The access token is valid for 10 more seconds after this changes to `true`.\n  public var hasExpired: Bool {\n    guard let expiry = expiry else {\n      return false\n    }\n\n    return Int(CFAbsoluteTimeGetCurrent()) > expiry - 10\n  }\n\n  /// Creates a new access token with the given properties.\n  /// - Parameters:\n  ///   - token: The access token.\n  ///   - expiry: The time that the access token will expire at in system absolute time.\n  public init(token: String, expiry: Int?) {\n    self.token = token\n    self.expiry = expiry\n  }\n\n  /// Creates a new access token with the given properties.\n  /// - Parameters:\n  ///   - token: The access token.\n  ///   - secondsToLive: The number of seconds that the access token is valid for.\n  public init(token: String, expiresIn secondsToLive: Int) {\n    self.token = token\n    self.expiry = Int(CFAbsoluteTimeGetCurrent()) + secondsToLive\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Mojang/MojangAPI.swift",
    "content": "import Foundation\n\npublic enum MojangAPIError: LocalizedError {\n  case failedToDeserializeResponse(String)\n\n  public var errorDescription: String? {\n    switch self {\n      case .failedToDeserializeResponse(let string):\n        return \"\"\"\n        Failed to deserialize response.\n        Data: \\(string)\n        \"\"\"\n    }\n  }\n}\n\n/// Used to interface with Mojang's authentication API.\npublic enum MojangAPI {\n  // swiftlint:disable force_unwrapping\n  private static let authenticationURL = URL(string: \"https://authserver.mojang.com/authenticate\")!\n  private static let joinServerURL = URL(string: \"https://sessionserver.mojang.com/session/minecraft/join\")!\n  private static let refreshURL = URL(string: \"https://authserver.mojang.com/refresh\")!\n  // swiftlint:enable force_unwrapping\n\n  /// Log into a Mojang account using an email and password.\n  /// - Parameters:\n  ///   - email: User's email.\n  ///   - password: User's password.\n  ///   - clientToken: The client's unique token (different for each user).\n  public static func login(\n    email: String,\n    password: String,\n    clientToken: String\n  ) async throws -> Account {\n    let payload = MojangAuthenticationRequest(\n      username: email,\n      password: password,\n      clientToken: clientToken,\n      requestUser: true)\n\n    let (_, data) = try await RequestUtil.performJSONRequest(url: authenticationURL, body: payload, method: .post)\n\n    guard let response = try? CustomJSONDecoder().decode(MojangAuthenticationResponse.self, from: data) else {\n      throw MojangAPIError.failedToDeserializeResponse(String(decoding: data, as: UTF8.self))\n    }\n\n    let accessToken = MinecraftAccessToken(\n      token: response.accessToken,\n      expiry: nil)\n\n    let selectedProfile = response.selectedProfile\n    let account = MojangAccount(\n      id: selectedProfile.id,\n      username: response.selectedProfile.name,\n      accessToken: accessToken)\n\n    return Account.mojang(account)\n  }\n\n  /// Contacts the Mojang auth servers as part of the join game handshake.\n  /// - Parameters:\n  ///   - accessToken: User's access token.\n  ///   - selectedProfile: UUID of the user's selected profile (one account can have multiple profiles).\n  ///   - serverHash: The hash received from the server that is being joined.\n  public static func join(\n    accessToken: String,\n    selectedProfile: String,\n    serverHash: String\n  ) async throws {\n    let payload = MojangJoinRequest(\n      accessToken: accessToken,\n      selectedProfile: selectedProfile,\n      serverId: serverHash)\n\n    _ = try await RequestUtil.performJSONRequest(url: joinServerURL, body: payload, method: .post)\n  }\n\n  /// Refreshes the access token of a Mojang account.\n  /// - Parameters:\n  ///   - account: The account to refresh.\n  ///   - clientToken: The client's 'unique' token (Delta Client just uses Mojang's).\n  public static func refresh(\n    _ account: MojangAccount,\n    with clientToken: String\n  ) async throws -> MojangAccount {\n    let accessToken = String(account.accessToken.token.split(separator: \".\")[1])\n    let payload = MojangRefreshTokenRequest(\n      accessToken: accessToken,\n      clientToken: clientToken)\n\n    let (_, data) = try await RequestUtil.performJSONRequest(url: refreshURL, body: payload, method: .post)\n\n    guard let response = try? CustomJSONDecoder().decode(MojangRefreshTokenResponse.self, from: data) else {\n      throw MojangAPIError.failedToDeserializeResponse(String(decoding: data, as: UTF8.self))\n    }\n\n    var refreshedAccount = account\n    refreshedAccount.accessToken = MinecraftAccessToken(token: response.accessToken, expiry: nil)\n\n    return refreshedAccount\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Mojang/MojangAccount.swift",
    "content": "import Foundation\n\n/// A user account that authenticates using the old Mojang method.\npublic struct MojangAccount: Codable, OnlineAccount, Hashable {\n  public var id: String\n  public var username: String\n  public var accessToken: MinecraftAccessToken\n  \n  public init(\n    id: String,\n    username: String,\n    accessToken: MinecraftAccessToken\n  ) {\n    self.id = id\n    self.username = username\n    self.accessToken = accessToken\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Mojang/Request/MojangAuthenticationRequest.swift",
    "content": "import Foundation\n\nstruct MojangAuthenticationRequest: Encodable {\n  struct MojangAgent: Codable {\n    var name = \"Minecraft\"\n    var version = 1\n  }\n  \n  var agent = MojangAgent()\n  var username: String\n  var password: String\n  var clientToken: String\n  var requestUser: Bool\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Mojang/Request/MojangJoinRequest.swift",
    "content": "import Foundation\n\nstruct MojangJoinRequest: Codable {\n  var accessToken: String\n  var selectedProfile: String\n  var serverId: String\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Mojang/Request/MojangRefreshTokenRequest.swift",
    "content": "import Foundation\n\nstruct MojangRefreshTokenRequest: Codable {\n  var accessToken: String\n  var clientToken: String\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Mojang/Response/MojangAuthenticationResponse.swift",
    "content": "import Foundation\n\nstruct MojangAuthenticationResponse: Decodable {\n  struct MojangUser: Codable {\n    var id: String\n    var username: String\n  }\n  \n  struct MojangProfile: Codable {\n    var name: String\n    var id: String\n  }\n  \n  var user: MojangUser\n  var clientToken: String\n  var accessToken: String\n  var selectedProfile: MojangProfile\n  var availableProfiles: [MojangProfile]\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/Mojang/Response/MojangRefreshTokenResponse.swift",
    "content": "import Foundation\n\nstruct MojangRefreshTokenResponse: Codable {\n  var accessToken: String\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/OfflineAccount.swift",
    "content": "import Foundation\n\n/// A user account that can only connect to offline mode servers.\npublic struct OfflineAccount: Codable, Hashable {\n  public var id: String\n  public var username: String\n  \n  /// Creates an offline account.\n  ///\n  /// The account's UUID is generated based on the username.\n  /// - Parameter username: The username for the account.\n  public init(username: String) {\n    self.username = username\n    \n    let generatedUUID = UUID.fromString(\"OfflinePlayer: \\(username)\")?.uuidString\n    id = generatedUUID ?? UUID().uuidString\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Account/OnlineAccount.swift",
    "content": "/// An account that can be used to join online servers.\npublic protocol OnlineAccount {\n  /// The account id (a UUID).\n  var id: String { get }\n  /// The username.\n  var username: String { get }\n  /// The access token for joining servers.\n  var accessToken: MinecraftAccessToken { get }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Cache/BinaryCacheable.swift",
    "content": "import Foundation\n\n/// Allows values of conforming types to easily be cached.\npublic protocol BinaryCacheable: Cacheable, RootBinarySerializable {}\n\n/// An error thrown by a type conforming to ``BinaryCacheable``.\npublic enum BinaryCacheableError: LocalizedError {\n  /// Failed to load a value from a binary cache file.\n  case failedToLoadFromCache(BinaryCacheable.Type, Error)\n  /// Failed to cache a value to a binary cache file.\n  case failedToCache(BinaryCacheable.Type, Error)\n\n  public var errorDescription: String? {\n    switch self {\n      case .failedToLoadFromCache(let type, let error):\n        return \"\"\"\n        Failed to load a value from a binary cache file.\n        Type: \\(String(describing: type))\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n      case .failedToCache(let type, let error):\n        return \"\"\"\n        Failed to cache a value to a binary cache file.\n        Type: \\(String(describing: type))\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n    }\n  }\n}\n\npublic extension BinaryCacheable {\n  static func loadCached(from file: URL) throws -> Self {\n    do {\n      return try deserialize(fromFile: file)\n    } catch {\n      throw BinaryCacheableError.failedToLoadFromCache(Self.self, error)\n    }\n  }\n\n  func cache(to file: URL) throws {\n    do {\n      try serialize(toFile: file)\n    } catch {\n      throw BinaryCacheableError.failedToCache(Self.self, error)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Cache/BinarySerialization.swift",
    "content": "import Foundation\n\n/// An error thrown during serialization implemented via ``BinarySerializable``.\npublic enum BinarySerializationError: LocalizedError {\n  case invalidSerializationFormatVersion(Int, expectedVersion: Int)\n  case invalidCaseId(Int, type: String)\n\n  public var errorDescription: String? {\n    switch self {\n      case .invalidSerializationFormatVersion(let version, expectedVersion: let expectedVersion):\n        return \"\"\"\n        Invalid serialization format version.\n        Expected: \\(expectedVersion)\n        Received: \\(version)\n        \"\"\"\n      case .invalidCaseId(let id, let type):\n        return \"\"\"\n        Invalid enum case id.\n        Id: \\(id)\n        Enum: \\(type)\n        \"\"\"\n    }\n  }\n}\n\n/// An error thrown during deserialization implemented via ``BinarySerializable``.\npublic enum DeserializationError: LocalizedError {\n  case invalidRawValue\n\n  public var errorDescription: String? {\n    switch self {\n      case .invalidRawValue:\n        return \"Encountered an invalid raw value while deserializing a RawRepresentable value.\"\n    }\n  }\n}\n\n/// A conforming type is able to be serialized and deserialized using Delta Client's custom binary\n/// caching mechanism.\npublic protocol BinarySerializable {\n  /// Serializes the value into a byte buffer.\n  func serialize(into buffer: inout Buffer)\n  /// Deserializes a value from a byte buffer.\n  static func deserialize(from buffer: inout Buffer) throws -> Self\n}\n\npublic extension BinarySerializable {\n  /// Serializes the value into a byte buffer.\n  func serialize() -> Buffer {\n    var buffer = Buffer()\n    serialize(into: &buffer)\n    return buffer\n  }\n\n  /// Deserializes a value from a byte buffer.\n  static func deserialize(from buffer: Buffer) throws -> Self {\n    var buffer = buffer\n    return try deserialize(from: &buffer)\n  }\n}\n\n/// A conforming type is intended to be the root type of a binary cache and includes a validated\n/// format version that should be bumped after each change that would effect the format of the cache.\npublic protocol RootBinarySerializable: BinarySerializable {\n  static var serializationFormatVersion: Int { get }\n}\n\npublic extension RootBinarySerializable {\n  /// Serializes the value into a byte buffer prefixed by its format version.\n  func serialize() -> Buffer {\n    var buffer = Buffer()\n    Self.serializationFormatVersion.serialize(into: &buffer)\n    serialize(into: &buffer)\n    return buffer\n  }\n\n  /// Deserializes a value from a byte buffer and verifies that it has the correct format version\n  /// before continuing.\n  static func deserialize(from buffer: Buffer) throws -> Self {\n    var buffer = buffer\n    let incomingVersion = try Int.deserialize(from: &buffer)\n    guard incomingVersion == serializationFormatVersion else {\n      throw BinarySerializationError.invalidSerializationFormatVersion(incomingVersion, expectedVersion: serializationFormatVersion)\n    }\n    return try deserialize(from: &buffer)\n  }\n\n  /// Serializes this value into a file.\n  func serialize(toFile file: URL) throws {\n    let buffer = serialize()\n    try Data(buffer.bytes).write(to: file)\n  }\n\n  /// Deserializes a value from a file.\n  static func deserialize(fromFile file: URL) throws -> Self {\n    let data = try Data(contentsOf: file)\n    return try deserialize(from: Buffer([UInt8](data)))\n  }\n}\n\n/// A bitwise copyable type is one that is a value type that takes up contiguous memory with no\n/// indirection (i.e. doesn't include properties that are arrays, strings, etc.). For your safety, a\n/// runtime check is performed that ensures that conforming types are actually bitwise copyable\n/// types (a.k.a. a plain ol' datatypes, see [_isPOD](https://github.com/apple/swift/blob/5a7b8c7922348179cc6dbc7281108e59d94ccecb/stdlib/public/core/Builtin.swift#L709))\n///\n/// The ``BinarySerializable`` implementation provided by conforming to this marker protocol essentially\n/// just copies the raw bytes of a type into the output for serialization, and reverses the process\n/// extremely efficiently when deserializing using unsafe pointers. Bitwise copyable types are the\n/// fastest types to serialize and deserialize.\npublic protocol BitwiseCopyable: BinarySerializable {}\n\npublic extension BitwiseCopyable {\n  @inline(__always)\n  func serialize(into buffer: inout Buffer) {\n    precondition(_isPOD(Self.self), \"\\(type(of: self)) must be a bitwise copyable datatype to conform to BitwiseCopyable\")\n    var value = self\n    withUnsafeBytes(of: &value) { bufferPointer in\n      let pointer = bufferPointer.assumingMemoryBound(to: UInt8.self).baseAddress!\n      for i in 0..<MemoryLayout<Self>.size {\n        buffer.writeByte(pointer.advanced(by: i).pointee)\n      }\n\n      if MemoryLayout<Self>.size == MemoryLayout<Self>.stride {\n        return\n      }\n\n      // Padding with zeroes is required to avoid segfaults that would occur if the last type\n      // serialized into a buffer was a type that had mismatching size and stride and the length of\n      // the buffer happened to be aligned with the end of a page. Using a for loop seems to be the\n      // fastest way to do this with Swift's array API. There would definitely be faster ways in C.\n      for _ in MemoryLayout<Self>.size..<MemoryLayout<Self>.stride {\n        buffer.writeByte(0)\n      }\n    }\n  }\n\n  @inline(__always)\n  static func deserialize(from buffer: inout Buffer) throws -> Self {\n    precondition(_isPOD(Self.self), \"\\(type(of: self)) must be a bitwise copyable datatype to conform to BitwiseCopyable\")\n    let index = buffer.index\n    buffer.index += MemoryLayout<Self>.stride\n    return buffer.bytes.withUnsafeBytes { pointer in\n      return pointer.loadUnaligned(fromByteOffset: index, as: Self.self)\n    }\n  }\n}\n\nextension Bool: BitwiseCopyable {}\n\nextension Int: BitwiseCopyable {}\nextension UInt: BitwiseCopyable {}\nextension Int64: BitwiseCopyable {}\nextension UInt64: BitwiseCopyable {}\nextension Int32: BitwiseCopyable {}\nextension UInt32: BitwiseCopyable {}\nextension Int16: BitwiseCopyable {}\nextension UInt16: BitwiseCopyable {}\nextension Int8: BitwiseCopyable {}\nextension UInt8: BitwiseCopyable {}\n\nextension Float: BitwiseCopyable {}\nextension Double: BitwiseCopyable {}\n\nextension Matrix2x2: BitwiseCopyable, BinarySerializable {}\nextension Matrix3x3: BitwiseCopyable, BinarySerializable {}\nextension Matrix4x4: BitwiseCopyable, BinarySerializable {}\n\nextension SIMD2: BitwiseCopyable, BinarySerializable {}\nextension SIMD3: BitwiseCopyable, BinarySerializable {}\nextension SIMD4: BitwiseCopyable, BinarySerializable {}\nextension SIMD8: BitwiseCopyable, BinarySerializable {}\nextension SIMD16: BitwiseCopyable, BinarySerializable {}\nextension SIMD32: BitwiseCopyable, BinarySerializable {}\nextension SIMD64: BitwiseCopyable, BinarySerializable {}\n\nextension String: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    count.serialize(into: &buffer)\n    buffer.writeString(self)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> Self {\n    let count = try Int.deserialize(from: &buffer)\n    return try buffer.readString(length: count)\n  }\n}\n\nextension Character: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    self.utf8.count.serialize(into: &buffer)\n    buffer.writeBytes([UInt8](self.utf8))\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> Self {\n    let count = try Int.deserialize(from: &buffer)\n    let bytes = try buffer.readBytes(count)\n    guard let string = String(bytes: bytes, encoding: .utf8) else {\n      throw BufferError.invalidByteInUTF8String\n    }\n    return Character(string)\n  }\n}\n\npublic extension BinarySerializable where Self: RawRepresentable, RawValue: BinarySerializable {\n  func serialize(into buffer: inout Buffer) {\n    rawValue.serialize(into: &buffer)\n  }\n\n  static func deserialize(from buffer: inout Buffer) throws -> Self {\n    guard let value = Self(rawValue: try .deserialize(from: &buffer)) else {\n      throw DeserializationError.invalidRawValue\n    }\n\n    return value\n  }\n}\n\nextension Array: BinarySerializable where Element: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    count.serialize(into: &buffer)\n    for element in self {\n      element.serialize(into: &buffer)\n    }\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> [Element] {\n    let count = try Int.deserialize(from: &buffer)\n    var array: [Element] = []\n    array.reserveCapacity(count)\n\n    for _ in 0..<count {\n      array.append(try .deserialize(from: &buffer))\n    }\n\n    return array\n  }\n}\n\nextension Set: BinarySerializable where Element: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    count.serialize(into: &buffer)\n    for element in self {\n      element.serialize(into: &buffer)\n    }\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> Set<Element> {\n    let count = try Int.deserialize(from: &buffer)\n    var set: Set<Element> = []\n    set.reserveCapacity(count)\n\n    for _ in 0..<count {\n      set.insert(try .deserialize(from: &buffer))\n    }\n\n    return set\n  }\n}\n\nextension Optional: BinarySerializable where Wrapped: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    if let value = self {\n      true.serialize(into: &buffer)\n      value.serialize(into: &buffer)\n    } else {\n      false.serialize(into: &buffer)\n    }\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> Wrapped? {\n    let isPresent = try Bool.deserialize(from: &buffer)\n    if isPresent {\n      return try Wrapped.deserialize(from: &buffer)\n    } else {\n      return nil\n    }\n  }\n}\n\nextension Dictionary: BinarySerializable where Key: BinarySerializable, Value: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    count.serialize(into: &buffer)\n    for (key, value) in self {\n      key.serialize(into: &buffer)\n      value.serialize(into: &buffer)\n    }\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> Dictionary<Key, Value> {\n    let count = try Int.deserialize(from: &buffer)\n    var dictionary: [Key: Value] = [:]\n    dictionary.reserveCapacity(count)\n    for _ in 0..<count {\n      let key = try Key.deserialize(from: &buffer)\n      let value = try Value.deserialize(from: &buffer)\n      dictionary[key] = value\n    }\n    return dictionary\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Cache/BlockModelPalette+BinaryCacheable.swift",
    "content": "extension DirectionSet: BitwiseCopyable {}\nextension TextureType: BitwiseCopyable {}\nextension Direction: BitwiseCopyable {}\nextension ModelDisplayTransforms: BitwiseCopyable {}\n\nextension BlockModelPalette: BinaryCacheable {\n  public static var serializationFormatVersion: Int {\n    return 0\n  }\n\n  public func serialize(into buffer: inout Buffer) {\n    models.serialize(into: &buffer)\n    displayTransforms.serialize(into: &buffer)\n    identifierToIndex.serialize(into: &buffer)\n    fullyOpaqueBlocks.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> BlockModelPalette {\n    return BlockModelPalette(\n      models: try .deserialize(from: &buffer),\n      displayTransforms: try .deserialize(from: &buffer),\n      identifierToIndex: try .deserialize(from: &buffer),\n      fullyOpaqueBlocks: try [Bool].deserialize(from: &buffer)\n    )\n  }\n}\n\nextension Identifier: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    namespace.serialize(into: &buffer)\n    name.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> Identifier {\n    return Identifier(\n      namespace: try .deserialize(from: &buffer),\n      name: try .deserialize(from: &buffer)\n    )\n  }\n}\n\nextension BlockModel: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    parts.serialize(into: &buffer)\n    cullingFaces.serialize(into: &buffer)\n    cullableFaces.serialize(into: &buffer)\n    nonCullableFaces.serialize(into: &buffer)\n    textureType.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> BlockModel {\n    return BlockModel(\n      parts: try .deserialize(from: &buffer),\n      cullingFaces: try .deserialize(from: &buffer),\n      cullableFaces: try .deserialize(from: &buffer),\n      nonCullableFaces: try .deserialize(from: &buffer),\n      textureType: try .deserialize(from: &buffer)\n    )\n  }\n}\n\nextension BlockModelPart: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    ambientOcclusion.serialize(into: &buffer)\n    displayTransformsIndex.serialize(into: &buffer)\n    elements.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> BlockModelPart {\n    return BlockModelPart(\n      ambientOcclusion: try .deserialize(from: &buffer),\n      displayTransformsIndex: try .deserialize(from: &buffer),\n      elements: try .deserialize(from: &buffer)\n    )\n  }\n}\n\nextension BlockModelElement: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    transformation.serialize(into: &buffer)\n    shade.serialize(into: &buffer)\n    faces.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> BlockModelElement {\n    return BlockModelElement(\n      transformation: try .deserialize(from: &buffer),\n      shade: try .deserialize(from: &buffer),\n      faces: try .deserialize(from: &buffer)\n    )\n  }\n}\n\nextension BlockModelFace: BitwiseCopyable {}\n"
  },
  {
    "path": "Sources/Core/Sources/Cache/Cacheable.swift",
    "content": "import Foundation\n\n/// Conforming types can be cached to disk with ease.\npublic protocol Cacheable {\n  /// The default cache file name for this type. Used for ``Cacheable/loadCached(fromDirectory:)``\n  /// and ``Cacheable/cache(toDirectory:)``.\n  static var defaultCacheFileName: String { get }\n  /// Loads a cached value from a cache directory. See ``cacheFileName`` for a type's default cache\n  /// file name.\n  static func loadCached(from file: URL) throws -> Self\n  /// Caches this value to a cache directory. See ``cacheFileName`` for a type's default cache file\n  /// name.\n  func cache(to file: URL) throws\n}\n\npublic extension Cacheable {\n  static var defaultCacheFileName: String {\n    let base = String(describing: Self.self)\n    let fileType: String\n    if (Self.self as? JSONCacheable.Type) != nil {\n      fileType = \"json\"\n    } else {\n      fileType = \"bin\"\n    }\n    return \"\\(base).\\(fileType)\"\n  }\n\n  static func loadCached(fromDirectory directory: URL) throws -> Self {\n    return try loadCached(from: directory.appendingPathComponent(defaultCacheFileName))\n  }\n\n  func cache(toDirectory directory: URL) throws {\n    try cache(to: directory.appendingPathComponent(Self.defaultCacheFileName))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Cache/FontPalette+BinaryCacheable.swift",
    "content": "extension FontPalette: BinaryCacheable {\n  public static var serializationFormatVersion: Int {\n    return 0\n  }\n\n  public func serialize(into buffer: inout Buffer) {\n    fonts.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> FontPalette {\n    return FontPalette(try .deserialize(from: &buffer))\n  }\n}\n\nextension Font: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    characters.serialize(into: &buffer)\n    asciiCharacters.serialize(into: &buffer)\n    textures.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> Font {\n    return Font(\n      characters: try .deserialize(from: &buffer),\n      asciiCharacters: try .deserialize(from: &buffer),\n      textures: try .deserialize(from: &buffer)\n    )\n  }\n}\n\nextension CharacterDescriptor: BitwiseCopyable {}\n"
  },
  {
    "path": "Sources/Core/Sources/Cache/ItemModelPalette+BinaryCacheable.swift",
    "content": "extension ItemModelPalette: BinaryCacheable {\n  public static var serializationFormatVersion: Int {\n    return 0\n  }\n\n  public func serialize(into buffer: inout Buffer) {\n    models.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> ItemModelPalette {\n    return ItemModelPalette(\n      try .deserialize(from: &buffer)\n    )\n  }\n}\n\nextension ItemModel: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    switch self {\n      case .layered(let textureIndices, let transforms):\n        0.serialize(into: &buffer)\n        textureIndices.serialize(into: &buffer)\n        transforms.serialize(into: &buffer)\n      case .blockModel(let id):\n        1.serialize(into: &buffer)\n        id.serialize(into: &buffer)\n      case .entity(let identifier, let transforms):\n        2.serialize(into: &buffer)\n        identifier.serialize(into: &buffer)\n        transforms.serialize(into: &buffer)\n      case .empty:\n        3.serialize(into: &buffer)\n    }\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> ItemModel {\n    let caseId = try Int.deserialize(from: &buffer)\n    switch caseId {\n      case 0:\n        return .layered(\n          textureIndices: try .deserialize(from: &buffer),\n          transforms: try .deserialize(from: &buffer)\n        )\n      case 1:\n        return .blockModel(id: try .deserialize(from: &buffer))\n      case 2:\n        return .entity(\n          try .deserialize(from: &buffer),\n          transforms: try .deserialize(from: &buffer)\n        )\n      case 3:\n        return .empty\n      default:\n        throw BinarySerializationError.invalidCaseId(caseId, type: String(describing: Self.self))\n    }\n  }\n}\n\nextension ItemModelTexture: BitwiseCopyable {}\n"
  },
  {
    "path": "Sources/Core/Sources/Cache/Registry/BiomeRegistry+JSONCacheable.swift",
    "content": "extension BiomeRegistry: JSONCacheable {}\n"
  },
  {
    "path": "Sources/Core/Sources/Cache/Registry/BlockRegistry+BinaryCacheable.swift",
    "content": "extension Block.Tint: BitwiseCopyable {}\nextension Block.Offset: BitwiseCopyable {}\nextension Block.PhysicalMaterial: BitwiseCopyable {}\nextension Block.LightMaterial: BitwiseCopyable {}\nextension Block.SoundMaterial: BitwiseCopyable {}\nextension FluidState: BitwiseCopyable {}\nextension AxisAlignedBoundingBox: BitwiseCopyable {}\nextension Block.StateProperties: BitwiseCopyable {}\n\nextension BlockRegistry: BinaryCacheable {\n  public static var serializationFormatVersion: Int {\n    return 0\n  }\n\n  public func serialize(into buffer: inout Buffer) {\n    blocks.serialize(into: &buffer)\n    renderDescriptors.serialize(into: &buffer)\n    selfCullingBlocks.serialize(into: &buffer)\n    airBlocks.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> BlockRegistry {\n    return BlockRegistry(\n      blocks: try .deserialize(from: &buffer),\n      renderDescriptors: try .deserialize(from: &buffer),\n      selfCullingBlocks: try .deserialize(from: &buffer),\n      airBlocks: try .deserialize(from: &buffer)\n    )\n  }\n}\n\nextension Block: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    id.serialize(into: &buffer)\n    vanillaParentBlockId.serialize(into: &buffer)\n    identifier.serialize(into: &buffer)\n    className.serialize(into: &buffer)\n    fluidState.serialize(into: &buffer)\n    tint.serialize(into: &buffer)\n    offset.serialize(into: &buffer)\n    vanillaMaterialIdentifier.serialize(into: &buffer)\n    physicalMaterial.serialize(into: &buffer)\n    lightMaterial.serialize(into: &buffer)\n    soundMaterial.serialize(into: &buffer)\n    shape.serialize(into: &buffer)\n    stateProperties.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> Block {\n    return Block(\n      id: try .deserialize(from: &buffer),\n      vanillaParentBlockId: try .deserialize(from: &buffer),\n      identifier: try .deserialize(from: &buffer),\n      className: try .deserialize(from: &buffer),\n      fluidState: try .deserialize(from: &buffer),\n      tint: try .deserialize(from: &buffer),\n      offset: try .deserialize(from: &buffer),\n      vanillaMaterialIdentifier: try .deserialize(from: &buffer),\n      physicalMaterial: try .deserialize(from: &buffer),\n      lightMaterial: try .deserialize(from: &buffer),\n      soundMaterial: try .deserialize(from: &buffer),\n      shape: try .deserialize(from: &buffer),\n      stateProperties: try .deserialize(from: &buffer)\n    )\n  }\n}\n\nextension Block.Shape: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    isDynamic.serialize(into: &buffer)\n    isLarge.serialize(into: &buffer)\n    collisionShape.serialize(into: &buffer)\n    outlineShape.serialize(into: &buffer)\n    occlusionShapeIds.serialize(into: &buffer)\n    isSturdy.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> Block.Shape {\n    return Block.Shape(\n      isDynamic: try .deserialize(from: &buffer),\n      isLarge: try .deserialize(from: &buffer),\n      collisionShape: try .deserialize(from: &buffer),\n      outlineShape: try .deserialize(from: &buffer),\n      occlusionShapeIds: try .deserialize(from: &buffer),\n      isSturdy: try .deserialize(from: &buffer)\n    )\n  }\n}\n\nextension CompoundBoundingBox: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    aabbs.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> CompoundBoundingBox {\n    return CompoundBoundingBox(try .deserialize(from: &buffer))\n  }\n}\n\nextension BlockModelRenderDescriptor: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    model.serialize(into: &buffer)\n    xRotationDegrees.serialize(into: &buffer)\n    yRotationDegrees.serialize(into: &buffer)\n    uvLock.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> BlockModelRenderDescriptor {\n    return BlockModelRenderDescriptor(\n      model: try .deserialize(from: &buffer),\n      xRotationDegrees: try .deserialize(from: &buffer),\n      yRotationDegrees: try .deserialize(from: &buffer),\n      uvLock: try .deserialize(from: &buffer)\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Cache/Registry/EntityRegistry+JSONCacheable.swift",
    "content": "extension EntityRegistry: JSONCacheable {}\n"
  },
  {
    "path": "Sources/Core/Sources/Cache/Registry/FluidRegistry+JSONCacheable.swift",
    "content": "extension FluidRegistry: JSONCacheable {}\n"
  },
  {
    "path": "Sources/Core/Sources/Cache/Registry/ItemRegistry+JSONCacheable.swift",
    "content": "extension ItemRegistry: JSONCacheable {}\n"
  },
  {
    "path": "Sources/Core/Sources/Cache/Registry/JSONCacheable.swift",
    "content": "import Foundation\n\n/// Allows ``Codable`` types to easily conform to the ``Cacheable`` protocol.\npublic protocol JSONCacheable: Cacheable, Codable {}\n\npublic enum JSONCacheableError: LocalizedError {\n  /// Failed to load a value from a JSON cache file.\n  case failedToLoadFromCache(JSONCacheable.Type, Error)\n  /// Failed to cache a value to a JSON cache file.\n  case failedToCache(JSONCacheable.Type, Error)\n\n  public var errorDescription: String? {\n    switch self {\n      case .failedToLoadFromCache(let type, let error):\n        return \"\"\"\n        Failed to load a value from a JSON cache file.\n        Type: \\(String(describing: type))\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n      case .failedToCache(let type, let error):\n        return \"\"\"\n        Failed to cache a value to a binary cache file.\n        Type: \\(String(describing: type))\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n    }\n  }\n}\n\npublic extension JSONCacheable {\n  static func loadCached(from file: URL) throws -> Self {\n    do {\n      let data = try Data(contentsOf: file)\n      return try CustomJSONDecoder().decode(Self.self, from: data)\n    } catch {\n      throw JSONCacheableError.failedToLoadFromCache(Self.self, error)\n    }\n  }\n\n  func cache(to file: URL) throws {\n    do {\n      let data = try JSONEncoder().encode(self)\n      try data.write(to: file)\n    } catch {\n      throw JSONCacheableError.failedToCache(Self.self, error)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Cache/TexturePalette+BinaryCacheable.swift",
    "content": "import SwiftImage\n\nextension TexturePalette: BinaryCacheable {\n  public static var serializationFormatVersion: Int {\n    return 0\n  }\n\n  public func serialize(into buffer: inout Buffer) {\n    textures.serialize(into: &buffer)\n    width.serialize(into: &buffer)\n    height.serialize(into: &buffer)\n    identifierToIndex.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> TexturePalette {\n    return TexturePalette(\n      textures: try .deserialize(from: &buffer),\n      width: try .deserialize(from: &buffer),\n      height: try .deserialize(from: &buffer),\n      identifierToIndex: try .deserialize(from: &buffer)\n    )\n  }\n}\n\nextension Texture: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    type.serialize(into: &buffer)\n    image.serialize(into: &buffer)\n    animation.serialize(into: &buffer)\n    frameCount.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> Texture {\n    return Texture(\n      type: try .deserialize(from: &buffer),\n      image: try .deserialize(from: &buffer),\n      animation: try .deserialize(from: &buffer),\n      frameCount: try .deserialize(from: &buffer)\n    )\n  }\n}\n\nextension Texture.BGRA: BitwiseCopyable {}\n\nextension Image: BinarySerializable where Pixel: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    width.serialize(into: &buffer)\n    height.serialize(into: &buffer)\n    (width * height).serialize(into: &buffer) // Simplifies deserialization\n    self.withUnsafeBytes { pixelBuffer in\n      let pointer = pixelBuffer.assumingMemoryBound(to: UInt8.self).baseAddress!\n      for i in 0..<pixelBuffer.count {\n        buffer.writeByte(pointer.advanced(by: i).pointee)\n      }\n    }\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> Self {\n    return Self(\n      width: try .deserialize(from: &buffer),\n      height: try .deserialize(from: &buffer),\n      pixels: try .deserialize(from: &buffer)\n    )\n  }\n}\n\nextension Texture.Animation: BinarySerializable {\n  public func serialize(into buffer: inout Buffer) {\n    interpolate.serialize(into: &buffer)\n    frames.serialize(into: &buffer)\n  }\n\n  public static func deserialize(from buffer: inout Buffer) throws -> Texture.Animation {\n    return Texture.Animation(\n      interpolate: try .deserialize(from: &buffer),\n      frames: try .deserialize(from: &buffer)\n    )\n  }\n}\n\nextension Texture.Animation.Frame: BitwiseCopyable {}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/Chat.swift",
    "content": "import Collections\n\n/// Storage for a game's chat.\npublic struct Chat {\n  public static let maximumScrollback = 200\n\n  /// All messages sent and received.\n  public var messages: Deque<ChatMessage> = []\n\n  /// Creates an empty chat.\n  public init() {}\n\n  /// Add a message to the chat.\n  /// - Parameter message: The message to add.\n  public mutating func add(_ message: ChatMessage) {\n    messages.append(message)\n    if messages.count > Self.maximumScrollback {\n      messages.removeFirst()\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/ChatComponent/ChatComponent.swift",
    "content": "/// A component of a chat message.\npublic struct ChatComponent: Decodable, Equatable {\n  /// The component's style.\n  var style: Style\n  /// The component's content.\n  var content: Content\n  /// The component's children (displayed directly after ``content``).\n  var children: [ChatComponent]\n\n  private enum CodingKeys: String, CodingKey {\n    case children = \"extra\"\n    case text\n    case translationIdentifier = \"translate\"\n    case translationContent = \"with\"\n    case score\n    case keybind\n  }\n\n  /// Creates a new chat component.\n  /// - Parameters:\n  ///   - style: The component's style (inherited by children).\n  ///   - content: The component's content.\n  ///   - children: The component's children (dsplayed directly after ``content``).\n  public init(style: Style, content: Content, children: [ChatComponent] = []) {\n    self.style = style\n    self.content = content\n    self.children = children\n  }\n\n  public init(from decoder: Decoder) throws {\n    let container: KeyedDecodingContainer<CodingKeys>\n    do {\n      container = try decoder.container(keyedBy: CodingKeys.self)\n    } catch {\n      let content = try decoder.singleValueContainer().decode(String.self)\n      style = Style()\n      self.content = Content.string(content)\n      children = []\n      return\n    }\n    style = try Style(from: decoder)\n\n    if container.contains(.children) {\n      children = try container.decode([ChatComponent].self, forKey: .children)\n    } else {\n      children = []\n    }\n\n    if container.contains(.text) {\n      let string: String\n      if let integer = try? container.decode(Int.self, forKey: .text) {\n        string = String(integer)\n      } else if let boolean = try? container.decode(Bool.self, forKey: .text) {\n        string = String(boolean)\n      } else {\n        string = try container.decode(String.self, forKey: .text)\n      }\n      content = .string(string)\n    } else if container.contains(.score) {\n      let score = try container.decode(ScoreContent.self, forKey: .score)\n      content = .score(score)\n    } else if container.contains(.keybind) {\n      let keybind = try container.decode(String.self, forKey: .keybind)\n      content = .keybind(keybind)\n    } else if container.contains(.translationIdentifier) {\n      let identifier = try container.decode(String.self, forKey: .translationIdentifier)\n      let translationContent: [ChatComponent]\n      if container.contains(.translationContent) {\n        translationContent = try container.decode([ChatComponent].self, forKey: .translationContent)\n      } else {\n        translationContent = []\n      }\n      content = .translation(LocalizedContent(translateKey: identifier, content: translationContent))\n    } else {\n      throw ChatComponentError.invalidChatComponentType\n    }\n  }\n\n  /// Converts the chat component to plain text.\n  /// - Parameter locale: The locale to use when resolving localized components.\n  /// - Returns: The component's contents as plain text.\n  public func toText(with locale: MinecraftLocale) -> String {\n    var output = content.toText(with: locale)\n    for child in children {\n      output += child.toText(with: locale)\n    }\n\n    // Remove legacy formatted text style codes\n    let legacy = LegacyFormattedText(output)\n    return legacy.toString()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/ChatComponent/ChatComponentColor.swift",
    "content": "import Foundation\n\nextension ChatComponent {\n  /// A component's color.\n  public enum Color: String, Codable, Equatable {\n    // TODO: handle hex code colors\n    case white\n    case black\n    case gray\n    case darkGray = \"dark_gray\"\n    case red\n    case darkRed = \"dark_red\"\n    case gold\n    case yellow\n    case green\n    case darkGreen = \"dark_green\"\n    case aqua\n    case darkAqua = \"dark_aqua\"\n    case blue\n    case darkBlue = \"dark_blue\"\n    case purple = \"light_purple\"\n    case darkPurple = \"dark_purple\"\n    case reset\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/ChatComponent/ChatComponentContent.swift",
    "content": "extension ChatComponent {\n  /// The content of a chat component.\n  public enum Content: Equatable {\n    case string(String)\n    case keybind(String)\n    case score(ScoreContent)\n    case translation(LocalizedContent)\n\n    /// Converts the content to plain text.\n    /// - Parameter locale: The locale to use when resolving localized content.\n    /// - Returns: The content as plain text.\n    public func toText(with locale: MinecraftLocale) -> String {\n      switch self {\n        case .string(let string):\n          return string\n        case .keybind(let keybind):\n          // TODO: read the keybind's value from configuration\n          return keybind\n        case .score(let score):\n          // TODO: load score value in `score.value` is nil\n          return \"\\(score.name):\\(score.objective):\\(score.value ?? \"unknown_value\")\"\n        case .translation(let localizedContent):\n          return locale.getTranslation(for: localizedContent.translateKey, with: localizedContent.content.map { component in\n            return component.toText(with: locale)\n          })\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/ChatComponent/ChatComponentError.swift",
    "content": "import Foundation\n\n/// An error thrown by ``ChatComponent`` and related types.\npublic enum ChatComponentError: LocalizedError {\n  case invalidChatComponentType\n  \n  public var errorDescription: String? {\n    switch self {\n      case .invalidChatComponentType:\n        return \"Invalid chat component type.\"\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/ChatComponent/ChatComponentLocalizedContent.swift",
    "content": "extension ChatComponent {\n  /// The content of a localized component.\n  public struct LocalizedContent: Equatable {\n    /// The identifier of the localized template to use.\n    public var translateKey: String\n    /// Content to replace placeholders in the localized template with.\n    public var content: [ChatComponent]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/ChatComponent/ChatComponentScoreContent.swift",
    "content": "import Foundation\n\nextension ChatComponent {\n  /// The content of a score component.\n  public struct ScoreContent: Codable, Equatable {\n    /// The name of the user to display the score of. `*` indicates the current user.\n    public var name: String\n    /// The objective to display the score for.\n    public var objective: String\n    /// The score's value. If `nil`, the score value should be loaded from the world.\n    public var value: String?\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/ChatComponent/ChatComponentStyle.swift",
    "content": "import Foundation\n\nextension ChatComponent {\n  public struct Style: Decodable, Equatable {\n    /// If `true`, the text is **bold**.\n    public var bold: Bool?\n    /// If `true`, the text is *italic*.\n    public var italic: Bool?\n    /// If `true`, the text has an underline.\n    public var underlined: Bool?\n    /// If `true`, the text has a ~~line through the middle~~.\n    public var strikethrough: Bool?\n    /// If `true`, each character of the text cycles through characters of the same width to hide the message.\n    public var obfuscated: Bool?\n    /// The color of text.\n    public var color: Color?\n\n    private enum CodingKeys: String, CodingKey {\n      case bold\n      case italic\n      case underlined\n      case strikethrough\n      case obfuscated\n      case color\n    }\n\n    /// Creates a chat style. Defaults to white text with no decoration.\n    public init(\n      bold: Bool? = nil,\n      italic: Bool? = nil,\n      underlined: Bool? = nil,\n      strikethrough: Bool? = nil,\n      obfuscated: Bool? = nil,\n      color: Color? = nil\n    ) {\n      self.bold = bold\n      self.italic = italic\n      self.underlined = underlined\n      self.strikethrough = strikethrough\n      self.obfuscated = obfuscated\n      self.color = color\n    }\n\n    public init(from decoder: Decoder) throws {\n      let container = try decoder.container(keyedBy: CodingKeys.self)\n      bold = (try? container.decode(Bool.self, forKey: .bold))\n      italic = (try? container.decode(Bool.self, forKey: .italic))\n      underlined = (try? container.decode(Bool.self, forKey: .underlined))\n      strikethrough = (try? container.decode(Bool.self, forKey: .strikethrough))\n      obfuscated = (try? container.decode(Bool.self, forKey: .obfuscated))\n      color = (try? container.decode(Color.self, forKey: .color))\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/ChatMessage.swift",
    "content": "import Foundation\nimport CoreFoundation\n\n/// A chat message.\npublic struct ChatMessage {\n  /// The content of the message.\n  public var content: ChatComponent\n  /// The uuid of the message's sender.\n  public var sender: UUID\n  /// The time at which the message was received.\n  public var timeReceived: CFAbsoluteTime\n\n  /// Creates a new chat message.\n  /// - Parameters:\n  ///   - content: The message of the message (includes `<sender>` when received from\n  ///     vanilla server).\n  ///   - sender: The uuid of the message's sender.\n  ///   - timeReceived: The time at which the message was received. Defaults to the current time.\n  public init(\n    content: ChatComponent,\n    sender: UUID,\n    timeReceived: CFAbsoluteTime = CFAbsoluteTimeGetCurrent()\n  ) {\n    self.content = content\n    self.sender = sender\n    self.timeReceived = timeReceived\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/LegacyFormattedText/LegacyFormattedText.swift",
    "content": "import Foundation\n\n#if canImport(AppKit)\nimport AppKit\n#elseif canImport(UIKit)\nimport CoreGraphics\nimport UIKit\n#endif\n\n/// Text formatted using legacy formatting codes.\n///\n/// [See the wiki for more information on legacy formatting codes](https://minecraft.fandom.com/wiki/Formatting_codes)\npublic struct LegacyFormattedText {\n  /// The styled tokens that form the text.\n  public var tokens: [Token]\n\n  /// Parses a string containing legacy formatting codes into styled tokens.\n  public init(_ string: String) {\n    // This will always succeed because lenient parsing doesn't throw any errors\n    // swiftlint:disable force_try\n    self = try! Self.parse(string, strict: false)\n    // swiftlint:enable force_try\n  }\n\n  /// Creates legacy formatted text from tokens.\n  public init(_ tokens: [Token]) {\n    self.tokens = tokens\n  }\n\n  /// When `strict` is `true`, no errors are thrown.\n  public static func parse(_ string: String, strict: Bool) throws -> LegacyFormattedText {\n    var tokens: [Token] = []\n\n    var currentColor: Color?\n    var currentStyle: Style?\n    let parts = string.split(separator: \"§\", omittingEmptySubsequences: false)\n    for (i, part) in parts.enumerated() {\n      // First token always has no formatting code\n      if i == 0 {\n        guard part != \"\" else {\n          continue\n        }\n        tokens.append(Token(string: String(part), color: nil, style: nil))\n        continue\n      }\n\n      // First character is formatting code\n      guard let character = part.first else {\n        if strict {\n          throw LegacyFormattedTextError.missingFormattingCodeCharacter\n        }\n        continue\n      }\n\n      // Rest is content\n      let content = String(part.dropFirst())\n\n      guard let code = FormattingCode(rawValue: character) else {\n        if strict {\n          throw LegacyFormattedTextError.invalidFormattingCodeCharacter(character)\n        }\n        tokens.append(Token(string: content, color: nil, style: nil))\n        continue\n      }\n\n      // Update formatting state\n      switch code {\n        case .style(.reset):\n          currentStyle = nil\n          currentColor = nil\n        case let .color(color):\n          currentColor = color\n          // Using a color code resets the style in Java Edition\n          currentStyle = nil\n        case let .style(style):\n          currentStyle = style\n      }\n\n      guard !content.isEmpty else {\n        continue\n      }\n\n      tokens.append(Token(string: content, color: currentColor, style: currentStyle))\n    }\n\n    return LegacyFormattedText(tokens)\n  }\n\n  /// - Returns: The string as plaintext (with styling removed)\n  public func toString() -> String {\n    return tokens.map(\\.string).joined()\n  }\n\n  #if canImport(Darwin)\n  /// Creates an attributed string representing the formatted text.\n  /// - Parameter fontSize: The size of font to use.\n  /// - Returns: The formatted string.\n  public func attributedString(fontSize: CGFloat) -> NSAttributedString {\n    let font = FontUtil.systemFont(ofSize: fontSize)\n\n    let output = NSMutableAttributedString(string: \"\")\n    for token in tokens {\n      var attributes: [NSAttributedString.Key: Any] = [:]\n      var font = font\n\n      if let color = token.color {\n        let color = ColorUtil.color(fromHexString: color.hex)\n        attributes[.foregroundColor] = color\n        attributes[.strikethroughColor] = color\n        attributes[.underlineColor] = color\n      }\n\n      if let style = token.style {\n        switch style {\n          case .bold:\n            font = font.bold() ?? font\n          case .strikethrough:\n            attributes[.strikethroughStyle] = NSUnderlineStyle.single.rawValue\n          case .underline:\n            attributes[.underlineStyle] = NSUnderlineStyle.single.rawValue\n          case .italic:\n            font = font.italics() ?? font\n          case .reset:\n            break\n        }\n      }\n\n      attributes[.font] = font\n\n      output.append(NSAttributedString(\n        string: token.string,\n        attributes: attributes\n      ))\n    }\n\n    return output\n  }\n  #endif\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/LegacyFormattedText/LegacyFormattedTextColor.swift",
    "content": "extension LegacyFormattedText {\n  /// A legacy text color formatting code.\n  public enum Color: Character {\n    case black = \"0\"\n    case darkBlue = \"1\"\n    case darkGreen = \"2\"\n    case darkAqua = \"3\"\n    case darkRed = \"4\"\n    case darkPurple = \"5\"\n    case gold = \"6\"\n    case gray = \"7\"\n    case darkGray = \"8\"\n    case blue = \"9\"\n    case green = \"a\"\n    case aqua = \"b\"\n    case red = \"c\"\n    case lightPurple = \"d\"\n    case yellow = \"e\"\n    case white = \"f\"\n    \n    /// The hex value of the color.\n    public var hex: String {\n      switch self {\n        case .black: return \"#000000\"\n        case .darkBlue: return \"#0000AA\"\n        case .darkGreen: return \"#00AA00\"\n        case .darkAqua: return \"#00AAAA\"\n        case .darkRed: return \"#AA0000\"\n        case .darkPurple: return \"#AA00AA\"\n        case .gold: return \"#FFAA00\"\n        case .gray: return \"#AAAAAA\"\n        case .darkGray: return \"#555555\"\n        case .blue: return \"#5555FF\"\n        case .green: return \"#55FF55\"\n        case .aqua: return \"#55FFFF\"\n        case .red: return \"#FF5555\"\n        case .lightPurple: return \"#FF55FF\"\n        case .yellow: return \"#FFFF55\"\n        case .white: return \"#FFFFFF\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/LegacyFormattedText/LegacyFormattedTextError.swift",
    "content": "import Foundation\n\npublic enum LegacyFormattedTextError: Error {\n  case missingFormattingCodeCharacter\n  case invalidFormattingCodeCharacter(Character)\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/LegacyFormattedText/LegacyFormattedTextFormattingCode.swift",
    "content": "extension LegacyFormattedText {\n  /// A legacy text formatting code.\n  ///\n  /// [Reference](https://minecraft.fandom.com/wiki/Formatting_codes)\n  public enum FormattingCode: RawRepresentable {\n    case color(Color)\n    case style(Style)\n\n    public var rawValue: Character {\n      switch self {\n        case let .color(color):\n          return color.rawValue\n        case let .style(style):\n          return style.rawValue\n      }\n    }\n    \n    public init?(rawValue: Character) {\n      if let color = Color(rawValue: rawValue) {\n        self = .color(color)\n      } else if let style = Style(rawValue: rawValue) {\n        self = .style(style)\n      } else {\n        return nil\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/LegacyFormattedText/LegacyFormattedTextStyle.swift",
    "content": "extension LegacyFormattedText {\n  /// A legacy text style formatting code.\n  public enum Style: Character {\n    case bold = \"l\"\n    case strikethrough = \"m\"\n    case underline = \"n\"\n    case italic = \"o\"\n    case reset = \"r\"\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Chat/LegacyFormattedText/LegacyFormattedTextToken.swift",
    "content": "extension LegacyFormattedText {\n  /// A formatted text token.\n  public struct Token: Equatable {\n    let string: String\n    let color: Color?\n    let style: Style?\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Client.swift",
    "content": "import Foundation\n\n// TODO: Make client actually Sendable\n\n/// A client creates and maintains a connection to a server and handles the received packets.\npublic final class Client: @unchecked Sendable {\n  // MARK: Public properties\n\n  /// The resource pack to use.\n  public var resourcePack: ResourcePack\n  /// The account this client uses to join servers.\n  public var account: Account?\n\n  /// The game this client is playing in.\n  public var game: Game\n  /// The client's configuration.\n  public let configuration: ClientConfiguration\n\n  /// The connection to the current server.\n  public var connection: ServerConnection?\n  /// An event bus shared with ``game``.\n  public var eventBus = EventBus()\n  /// Whether the client has finished downloading terrain yet.\n  public var hasFinishedDownloadingTerrain = false\n\n  // MARK: Init\n\n  /// Creates a new client instance.\n  /// - Parameter resourcePack: The resources to use.\n  /// - Parameter configuration: The clientside configuration.\n  public init(resourcePack: ResourcePack, configuration: ClientConfiguration) {\n    self.resourcePack = resourcePack\n    self.configuration = configuration\n    game = Game(\n      eventBus: eventBus,\n      configuration: configuration,\n      font: resourcePack.vanillaResources.fontPalette.defaultFont,\n      locale: resourcePack.getDefaultLocale()\n    )\n  }\n\n  deinit {\n    game.tickScheduler.cancel()\n    connection?.close()\n  }\n\n  // MARK: Connection lifecycle\n\n  /// Join the specified server. Throws if the packets fail to send.\n  public func joinServer(\n    describedBy descriptor: ServerDescriptor,\n    with account: Account\n  ) async throws {\n    self.account = account\n\n    // Create a connection to the server\n    let connection = try await ServerConnection(\n      descriptor: descriptor,\n      eventBus: eventBus\n    )\n    connection.setPacketHandler { [weak self] packet in\n      guard let self = self else { return }\n      self.handlePacket(packet)\n    }\n    game.stopTickScheduler()\n    game = Game(\n      eventBus: eventBus,\n      configuration: configuration,\n      connection: connection,\n      font: resourcePack.vanillaResources.fontPalette.defaultFont,\n      locale: resourcePack.getDefaultLocale()\n    )\n    hasFinishedDownloadingTerrain = false\n    try connection.login(username: account.username)\n    self.connection = connection\n  }\n\n  /// Disconnect from the currently connected server if any.\n  public func disconnect() {\n    // Close connection\n    connection?.close()\n    connection = nil\n    // Reset chunk storage\n    game.changeWorld(to: World(eventBus: eventBus))\n    // Stop ticking\n    game.tickScheduler.cancel()\n  }\n\n  // MARK: Networking\n\n  /// Send a packet to the server currently connected to (if any).\n  public func sendPacket(_ packet: ServerboundPacket) throws {\n    try connection?.sendPacket(packet)\n  }\n\n  /// The client's packet handler.\n  public func handlePacket(_ packet: ClientboundPacket) {\n    do {\n      if let entityPacket = packet as? ClientboundEntityPacket {\n        game.handleDuringTick(entityPacket, client: self)\n      } else {\n        try packet.handle(for: self)\n      }\n    } catch {\n      disconnect()\n      log.error(\"Failed to handle packet: \\(error)\")\n      eventBus.dispatch(PacketHandlingErrorEvent(packetId: type(of: packet).id, error: \"\\(error)\"))\n    }\n  }\n\n  // MARK: Input\n\n  /// Handles a key press.\n  /// - Parameters:\n  ///   - key: The pressed key.\n  ///   - characters: The characters typed by pressing the key.\n  public func press(_ key: Key, _ characters: [Character] = []) {\n    let input = configuration.keymap.getInput(for: key)\n    game.press(key: key, input: input, characters: characters)\n  }\n\n  /// Handles an input press.\n  /// - Parameter input: The pressed input.\n  public func press(_ input: Input) {\n    game.press(key: nil, input: input)\n  }\n\n  /// Handles a key release.\n  /// - Parameter key: The released key.\n  public func release(_ key: Key) {\n    let input = configuration.keymap.getInput(for: key)\n    game.release(key: key, input: input)\n  }\n\n  /// Handles an input release.\n  /// - Parameter input: The released input.\n  public func release(_ input: Input) {\n    game.release(key: nil, input: input)\n  }\n\n  /// Releases all inputs.\n  public func releaseAllInputs() {\n    game.releaseAllInputs()\n  }\n\n  /// Moves the mouse.\n  ///\n  /// `deltaX` and `deltaY` aren't just the difference between the current and\n  /// previous values of `x` and `y` because there are ways for the mouse to\n  /// appear at a new position without causing in-game movement (e.g. if the\n  /// user opens the in-game menu, moves the mouse, and then closes the in-game\n  /// menu).\n  /// - Parameters:\n  ///   - x: The absolute mouse x (relative to the play area's top left corner).\n  ///   - y: The absolute mouse y (relative to the play area's top left corner).\n  ///   - deltaX: The change in mouse x.\n  ///   - deltaY: The change in mouse y.\n  public func moveMouse(x: Float, y: Float, deltaX: Float, deltaY: Float) {\n    // TODO: Update this API (and everything else reliant on it) so that DeltaCore\n    //   is the one that decides which input events to ignore (instead of InputView\n    //   (and similar) deciding whether an event should be given to Client or not).\n    //   This will allow the deltaX and deltaY parameters to be removed.\n    game.moveMouse(x: x, y: y, deltaX: deltaX, deltaY: deltaY)\n  }\n\n  /// Moves the left thumbstick.\n  /// - Parameters:\n  ///   - x: The x position.\n  ///   - y: The y position.\n  public func moveLeftThumbstick(_ x: Float, _ y: Float) {\n    game.moveLeftThumbstick(x, y)\n  }\n\n  /// Moves the right thumbstick.\n  /// - Parameters:\n  ///   - x: The x position.\n  ///   - y: The y position.\n  public func moveRightThumbstick(_ x: Float, _ y: Float) {\n    game.moveRightThumbstick(x, y)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ClientConfiguration.swift",
    "content": "/// Clientside configuration such as keymap and clientside render distance.\n/// Any package creating a Client instance should implement this protocol to allow DeltaCore to access configuration values.\npublic protocol ClientConfiguration {\n  /// The configuration related to rendering.\n  var render: RenderConfiguration { get }\n  /// The configured keymap.\n  var keymap: Keymap { get }\n  /// Whether to use the sprint key as a toggle.\n  var toggleSprint: Bool { get }\n  /// Whether to use the sneak key as a toggle.\n  var toggleSneak: Bool { get }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Constants.swift",
    "content": "import Foundation\n\npublic enum Constants {\n  public static let protocolVersion = 736\n  public static let versionString = \"1.16.1\"\n  public static let locale = \"en_us\"\n  public static let textureSize = 16\n  public static let packFormatVersion = 5\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/Axis.swift",
    "content": "import Foundation\n\n/// An axis\npublic enum Axis: CaseIterable {\n  case x\n  case y\n  case z\n\n  /// The positive direction along this axis in Minecraft's coordinate system.\n  public var positiveDirection: Direction {\n    switch self {\n      case .x:\n        return .east\n      case .y:\n        return .up\n      case .z:\n        return .south\n    }\n  }\n\n  /// The negative direction along this axis in Minecraft's coordinate system.\n  public var negativeDirection: Direction {\n    switch self {\n      case .x:\n        return .west\n      case .y:\n        return .down\n      case .z:\n        return .north\n    }\n  }\n\n  /// The conventional indices assigned to the axis, i.e. x -> 0, y -> 1, z -> 2\n  public var index: Int {\n    switch self {\n      case .x:\n        return 0\n      case .y:\n        return 1\n      case .z:\n        return 2\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/BlockPosition.swift",
    "content": "import FirebladeMath\nimport Foundation\n\n/// A block position.\npublic struct BlockPosition {\n  /// The origin.\n  public static let zero = BlockPosition(x: 0, y: 0, z: 0)\n\n  /// The x component.\n  public var x: Int\n  /// The y component.\n  public var y: Int\n  /// The z component.\n  public var z: Int\n\n  /// The position of the ``Chunk`` this position is in\n  public var chunk: ChunkPosition {\n    let chunkX = x >> 4  // divides by 16 and rounds down\n    let chunkZ = z >> 4\n    return ChunkPosition(chunkX: chunkX, chunkZ: chunkZ)\n  }\n\n  /// The position of the ``Chunk/Section`` this position is in\n  public var chunkSection: ChunkSectionPosition {\n    let sectionX = x >> 4  // divides by 16 and rounds down\n    let sectionY = y >> 4\n    let sectionZ = z >> 4\n    return ChunkSectionPosition(sectionX: sectionX, sectionY: sectionY, sectionZ: sectionZ)\n  }\n\n  /// This position relative to the ``Chunk`` it's in\n  public var relativeToChunk: BlockPosition {\n    let relativeX = x &- chunk.chunkX &* Chunk.Section.width\n    let relativeZ = z &- chunk.chunkZ &* Chunk.Section.depth\n    return BlockPosition(x: relativeX, y: y, z: relativeZ)\n  }\n\n  /// This position relative to the ``Chunk/Section`` it's in\n  public var relativeToChunkSection: BlockPosition {\n    let relativeX = x &- chunk.chunkX &* Chunk.Section.width\n    let relativeZ = z &- chunk.chunkZ &* Chunk.Section.depth\n    let relativeY = y &- sectionIndex &* Chunk.Section.height\n    return BlockPosition(x: relativeX, y: relativeY, z: relativeZ)\n  }\n\n  /// This position as a vector of floats.\n  public var floatVector: Vec3f {\n    return Vec3f(\n      Float(x),\n      Float(y),\n      Float(z)\n    )\n  }\n\n  /// This position as a vector of doubles.\n  public var doubleVector: Vec3d {\n    return Vec3d(\n      Double(x),\n      Double(y),\n      Double(z)\n    )\n  }\n\n  /// This position as a vector of ints.\n  public var intVector: Vec3i {\n    return Vec3i(x, y, z)\n  }\n\n  /// The positions neighbouring this position.\n  public var neighbours: [BlockPosition] {\n    Direction.allDirections.map { self + $0.intVector }\n  }\n\n  /// The block index of the position.\n  ///\n  /// Block indices are in order of increasing x-coordinate, in rows of increasing\n  /// z-coordinate, in layers of increasing y. If that doesn't make sense read the\n  /// implementation.\n  public var blockIndex: Int {\n    return (y &* Chunk.depth &+ z) &* Chunk.width &+ x\n  }\n\n  /// The biomes index of the position.\n  ///\n  /// Biome indices are in order of increasing x-coordinate, in rows of increasing\n  /// z-coordinate, in layers of increasing y. If that doesn't make sense read the\n  /// implementation. Each biome is a 4x4x4 area, and that's the only reason that\n  /// this calculation differs from ``blockIndex``.\n  public var biomeIndex: Int {\n    return (y / 4 &* Chunk.depth / 4 &+ z / 4) &* Chunk.width / 4 &+ x / 4\n  }\n\n  /// The section Y of the section this position is in\n  public var sectionIndex: Int {\n    let index = y / Chunk.Section.height\n    if y < 0 {\n      return index - 1\n    } else {\n      return index\n    }\n  }\n\n  /// Create a new block position.\n  ///\n  /// Coordinates are not validated.\n  /// - Parameters:\n  ///   - x: The x coordinate.\n  ///   - y: The y coordinate.\n  ///   - z: The z coordinate.\n  public init(x: Int, y: Int, z: Int) {\n    self.x = x\n    self.y = y\n    self.z = z\n  }\n\n  /// Component-wise addition of two block positions.\n  public static func + (lhs: BlockPosition, rhs: Vec3i) -> BlockPosition {\n    return BlockPosition(x: lhs.x &+ rhs.x, y: lhs.y &+ rhs.y, z: lhs.z &+ rhs.z)\n  }\n\n  /// Gets the position of the neighbouring block in the given direction.\n  public func neighbour(_ direction: Direction) -> BlockPosition {\n    return self + direction.intVector\n  }\n}\n\nextension BlockPosition: Hashable {\n  public func hash(into hasher: inout Hasher) {\n    hasher.combine(x)\n    hasher.combine(y)\n    hasher.combine(z)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/CardinalDirection.swift",
    "content": "import Foundation\n\n/// An axis aligned compass direction (i.e. either North, East, South or West).\npublic enum CardinalDirection: CaseIterable {\n  case north\n  case east\n  case south\n  case west\n\n  /// The opposite direction.\n  public var opposite: CardinalDirection {\n    let oppositeMap: [CardinalDirection: CardinalDirection] = [\n      .north: .south,\n      .south: .north,\n      .east: .west,\n      .west: .east]\n    return oppositeMap[self]!\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/Difficulty.swift",
    "content": "import Foundation\n\npublic enum Difficulty: UInt8 {\n  case peaceful = 0\n  case easy = 1\n  case normal = 2\n  case hard = 3\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/Direction.swift",
    "content": "import FirebladeMath\nimport Foundation\n\n/// A direction enum where the raw value is the same as in some of the Minecraft packets.\npublic enum Direction: Int, CustomStringConvertible {\n  case down = 0\n  case up = 1\n  case north = 2\n  case south = 3\n  case west = 4\n  case east = 5\n\n  /// The array of all directions.\n  public static let allDirections: [Direction] = [\n    .north,\n    .south,\n    .east,\n    .west,\n    .up,\n    .down,\n  ]\n\n  /// All directions excluding up and down.\n  public static let sides: [Direction] = [\n    .north,\n    .east,\n    .south,\n    .west,\n  ]\n\n  public var description: String {\n    switch self {\n      case .down:\n        return \"down\"\n      case .up:\n        return \"up\"\n      case .north:\n        return \"north\"\n      case .south:\n        return \"south\"\n      case .west:\n        return \"west\"\n      case .east:\n        return \"east\"\n    }\n  }\n\n  /// Returns the directions on the xz plane that are perpendicular to a direction.\n  public var perpendicularXZ: [Direction] {\n    switch self {\n      case .north, .south:\n        return [.east, .west]\n      case .east, .west:\n        return [.north, .south]\n      case .up, .down:\n        return [.north, .east, .south, .west]\n    }\n  }\n\n  /// The axis this direction lies on.\n  public var axis: Axis {\n    switch self {\n      case .west, .east:\n        return .x\n      case .up, .down:\n        return .y\n      case .north, .south:\n        return .z\n    }\n  }\n\n  /// Whether the direction is a positive direction in Minecraft's coordinate system or not.\n  public var isPositive: Bool {\n    switch self {\n      case .up, .south, .east:\n        return true\n      case .down, .north, .west:\n        return false\n    }\n  }\n\n  /// The direction pointing the opposite direction to this one.\n  public var opposite: Direction {\n    switch self {\n      case .down:\n        return .up\n      case .up:\n        return .down\n      case .north:\n        return .south\n      case .south:\n        return .north\n      case .east:\n        return .west\n      case .west:\n        return .east\n    }\n  }\n\n  /// Creates a direction from a string such as `\"down\"`.\n  public init?(string: String) {\n    switch string {\n      case \"down\":\n        self = .down\n      case \"up\":\n        self = .up\n      case \"north\":\n        self = .north\n      case \"south\":\n        self = .south\n      case \"west\":\n        self = .west\n      case \"east\":\n        self = .east\n      default:\n        return nil\n    }\n  }\n\n  /// A normalized vector representing this direction.\n  public var vector: Vec3f {\n    Vec3f(intVector)\n  }\n\n  /// A normalized vector representing this direction.\n  public var doubleVector: Vec3d {\n    Vec3d(intVector)\n  }\n\n  /// A normalized vector representing this direction.\n  public var intVector: Vec3i {\n    switch self {\n      case .down:\n        return Vec3i(0, -1, 0)\n      case .up:\n        return Vec3i(0, 1, 0)\n      case .north:\n        return Vec3i(0, 0, -1)\n      case .south:\n        return Vec3i(0, 0, 1)\n      case .west:\n        return Vec3i(-1, 0, 0)\n      case .east:\n        return Vec3i(1, 0, 0)\n    }\n  }\n\n  /// Returns the direction `n` 90 degree clockwise rotations around the axis while facing `referenceDirection`.\n  public func rotated(_ n: Int, clockwiseFacing referenceDirection: Direction) -> Direction {\n    // The three 'loops' of directions around the three axes. The directions are listed clockwise when looking in the negative direction along the axis.\n    let loops: [Axis: [Direction]] = [\n      .x: [.up, .north, .down, .south],\n      .y: [.north, .east, .south, .west],\n      .z: [.up, .east, .down, .west],\n    ]\n    switch self {\n      case referenceDirection, referenceDirection.opposite:\n        return self\n      default:\n        // This is safe because all axes are defined in the dictionary\n        let loop = loops[referenceDirection.axis]!\n        // This is safe because all four directions that are handled by this case will be in the loop\n        let index = loop.firstIndex(of: self)!\n        var newIndex: Int\n        if referenceDirection.isPositive {\n          newIndex = index - n\n        } else {\n          newIndex = index + n\n        }\n        newIndex = MathUtil.mod(newIndex, loop.count)\n        return loop[newIndex]\n    }\n  }\n}\n\nextension Direction: Codable {\n  public init(from decoder: Decoder) throws {\n    let string = try decoder.singleValueContainer().decode(String.self)\n    guard let direction = Direction(string: string) else {\n      throw DecodingError.dataCorrupted(\n        .init(\n          codingPath: decoder.codingPath,\n          debugDescription: \"Invalid direction '\\(string)'\"\n        )\n      )\n    }\n    self = direction\n  }\n\n  public func encode(to encoder: Encoder) throws {\n    var container = encoder.singleValueContainer()\n    try container.encode(description)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/DirectionSet.swift",
    "content": "/// A set of directions.\npublic struct DirectionSet: SetAlgebra, Equatable {\n  public typealias Element = Direction\n\n  public var rawValue: UInt8\n\n  public static let north = DirectionSet(containing: .north)\n  public static let south = DirectionSet(containing: .south)\n  public static let east = DirectionSet(containing: .east)\n  public static let west = DirectionSet(containing: .west)\n  public static let up = DirectionSet(containing: .up)\n  public static let down = DirectionSet(containing: .down)\n\n  /// The set of all directions.\n  public static let all: DirectionSet = [\n    .north,\n    .south,\n    .east,\n    .west,\n    .up,\n    .down\n  ]\n\n  public var isEmpty: Bool {\n    return rawValue == 0\n  }\n\n  public init() {\n    rawValue = 0\n  }\n\n  public init(rawValue: UInt8) {\n    self.rawValue = rawValue\n  }\n\n  public init(containing direction: Direction) {\n    rawValue = Self.directionMask(direction)\n  }\n\n  public init<S: Sequence>(_ sequence: S) where S.Element == Direction {\n    self.init()\n    for element in sequence {\n      insert(element)\n    }\n  }\n\n  private static func directionMask(_ direction: Direction) -> UInt8 {\n    return 1 << direction.rawValue\n  }\n\n  public func contains(_ direction: Direction) -> Bool {\n    return rawValue & Self.directionMask(direction) != 0\n  }\n\n  @discardableResult public mutating func insert(_ direction: Direction) -> (inserted: Bool, memberAfterInsert: Direction) {\n    let oldRawValue = rawValue\n    rawValue |= Self.directionMask(direction)\n    return (inserted: oldRawValue != rawValue, memberAfterInsert: direction)\n  }\n\n  @discardableResult public mutating func update(with direction: Direction) -> Direction? {\n    let oldRawValue = rawValue\n    rawValue |= Self.directionMask(direction)\n    return oldRawValue == rawValue ? nil : direction\n  }\n\n  @discardableResult public mutating func remove(_ direction: Direction) -> Direction? {\n    let oldRawValue = rawValue\n    rawValue &= ~Self.directionMask(direction)\n    return oldRawValue == rawValue ? nil : direction\n  }\n\n  public func union(_ other: DirectionSet) -> DirectionSet {\n    return DirectionSet(rawValue: rawValue | other.rawValue)\n  }\n\n  public mutating func formUnion(_ other: DirectionSet) {\n    rawValue |= other.rawValue\n  }\n\n  public func intersection(_ other: DirectionSet) -> DirectionSet {\n    return DirectionSet(rawValue: rawValue & other.rawValue)\n  }\n\n  public mutating func formIntersection(_ other: DirectionSet) {\n    rawValue &= other.rawValue\n  }\n\n  public func symmetricDifference(_ other: DirectionSet) -> DirectionSet {\n    return DirectionSet(rawValue: rawValue ^ other.rawValue)\n  }\n\n  public mutating func formSymmetricDifference(_ other: DirectionSet) {\n    rawValue ^= other.rawValue\n  }\n\n  public func isStrictSubset(of other: DirectionSet) -> Bool {\n    return rawValue != other.rawValue && intersection(other).rawValue == rawValue\n  }\n\n  public func isStrictSuperset(of other: DirectionSet) -> Bool {\n    return other.isStrictSubset(of: self)\n  }\n\n  public func isDisjoint(with other: DirectionSet) -> Bool {\n    return subtracting(other).rawValue == rawValue\n  }\n\n  public func isSubset(of other: DirectionSet) -> Bool {\n    return intersection(other).rawValue == rawValue\n  }\n\n  public func isSuperset(of other: DirectionSet) -> Bool {\n    return other.isSubset(of: self)\n  }\n\n  public mutating func subtract(_ other: DirectionSet) {\n    rawValue &= ~other.rawValue\n  }\n\n  public func subtracting(_ other: DirectionSet) -> DirectionSet {\n    return DirectionSet(rawValue: rawValue & (~other.rawValue))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/DominantHand.swift",
    "content": "import Foundation\n\npublic enum DominantHand: Int32 {\n  case left = 0\n  case right = 1\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/Equipment.swift",
    "content": "import Foundation\n\npublic struct Equipment {\n  public var slot: UInt8\n  public var item: Slot\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/Gamemode.swift",
    "content": "import Foundation\n\n/// The player's gamemode. Each gamemode gives the player different abilities.\npublic enum Gamemode: Int8 {\n  case survival = 0\n  case creative = 1\n  case adventure = 2\n  case spectator = 3\n  \n  /// Whether the player is always flying when in this gamemode.\n  public var isAlwaysFlying: Bool {\n    return self == .spectator\n  }\n\n  /// Whether the player collides with the world or not when in this gamemode.\n  public var hasCollisions: Bool {\n    return self != .spectator\n  }\n\n  /// Whether the gamemode has visible health.\n  public var hasHealth: Bool {\n    switch self {\n      case .survival, .adventure:\n        return true\n      case .creative, .spectator:\n        return false\n    }\n  }\n\n  public var canBreakBlocks: Bool {\n    switch self {\n      case .survival, .creative:\n        return true\n      case .adventure, .spectator:\n        return false\n    }\n  }\n\n  public var canPlaceBlocks: Bool {\n    switch self {\n      case .survival, .creative:\n        return true\n      case .adventure, .spectator:\n        return false\n    }\n  }\n\n  /// The lowercase string representation of the gamemode.\n  public var string: String {\n    switch self {\n      case .survival: return \"survival\"\n      case .creative: return \"creative\"\n      case .adventure: return \"adventure\"\n      case .spectator: return \"spectator\"\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/Hand.swift",
    "content": "import Foundation\n\npublic enum Hand: Int32 {\n  case mainHand = 0\n  case offHand = 1\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/Identifier.swift",
    "content": "import Foundation\nimport Parsing\n\n/// An identifier consists of a namespace and a name, they are used by Mojang a lot.\npublic struct Identifier: Hashable, Equatable, Codable, CustomStringConvertible {\n  // MARK: Public properties\n\n  /// The namespace of the identifier.\n  public var namespace: String\n  /// The name of the identifier.\n  public var name: String\n\n  /// The string representation of this identifier.\n  public var description: String {\n    return \"\\(namespace):\\(name)\"\n  }\n\n  // MARK: Private properties\n\n  /// The set of characters allowed in an identifier namespace.\n  private static let namespaceAllowedCharacters = Set(\"0123456789abcdefghijklmnopqrstuvwxyz-_\")\n  /// The set of characters allowed in an identifier name.\n  private static let nameAllowedCharacters = Set(\"0123456789abcdefghijklmnopqrstuvwxyz-_./\")\n\n  /// The parser used to parse identifiers from strings.\n  private static let identifierParser = OneOf<Substring, Identifier, _> {\n    Parse {\n      Prefix(1...) { namespaceAllowedCharacters.contains($0) }\n\n      Optionally {\n        \":\"\n        Prefix(1...) { nameAllowedCharacters.contains($0) }\n      }\n\n      End()\n    }.map { tuple -> Identifier in\n      if let name = tuple.1 {\n        return Identifier(namespace: String(tuple.0), name: String(name))\n      } else {\n        return Identifier(name: String(tuple.0))\n      }\n    }\n\n    // Required for the case where an identifier has no namespace and the name contains characters not allowed in a namespace\n    Parse {\n      Prefix { nameAllowedCharacters.contains($0) }\n      End()\n    }.map { name -> Identifier in\n      return Identifier(name: String(name))\n    }\n  }\n\n  // MARK: Init\n\n  /// Creates an identifier with the given name and namespace.\n  /// - Parameters:\n  ///   - namespace: The namespace for the identifier.\n  ///   - name: The name for the identifier.\n  public init(namespace: String, name: String) {\n    self.namespace = namespace\n    self.name = name\n  }\n\n  /// Creates an identifier with the given name and the `\"minecraft\"` namespace.\n  ///\n  /// This is separate from ``Identifier/init(namespace:name:)`` so that it can be used it expressions\n  /// such as `[\"dirt\", \"wood\"].map(Identifier.init(name:))` (which a single init with a default argument\n  /// wouldn't enable).\n  /// - Parameters:\n  ///   - name: The name for the identifier.\n  public init(name: String) {\n    self.namespace = \"minecraft\"\n    self.name = name\n  }\n\n  /// Creates an identifier from the given string. Throws if the string is not a valid identifier.\n  /// - Parameter string: String of the form `\"namespace:name\"` or `\"name\"`.\n  public init(_ string: String) throws {\n    do {\n      let identifier = try Self.identifierParser.parse(string)\n      name = identifier.name\n      namespace = identifier.namespace\n    } catch {\n      throw IdentifierError.invalidIdentifier(string, error)\n    }\n  }\n\n  public init(from decoder: Decoder) throws {\n    do {\n      // Decode identifiers in the form [\"namespace\", \"name\"] (it's a lot faster than string form)\n      var container = try decoder.unkeyedContainer()\n      let namespace = try container.decode(String.self)\n      let name = try container.decode(String.self)\n      self.init(namespace: namespace, name: name)\n    } catch {\n      // If the previous decoding method files the value is likely stored as a single string (of the form \"namespace:name\")\n      let container = try decoder.singleValueContainer()\n      let string = try container.decode(String.self)\n      try self.init(string)\n    }\n  }\n\n  // MARK: Public methods\n\n  /// Hashes the identifier.\n  public func hash(into hasher: inout Hasher) {\n    hasher.combine(namespace)\n    hasher.combine(name)\n  }\n\n  public static func == (lhs: Identifier, rhs: Identifier) -> Bool {\n    return lhs.namespace == rhs.namespace && lhs.name == rhs.name\n  }\n\n  /// Encodes the identifier in the form `[\"namespace\", \"name\"]` (because it's the most efficient way).\n  public func encode(to encoder: Encoder) throws {\n    var container = encoder.unkeyedContainer()\n    try container.encode(namespace)\n    try container.encode(name)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/IdentifierError.swift",
    "content": "import Foundation\n\npublic enum IdentifierError: LocalizedError {\n  case invalidIdentifier(String, Error)\n  \n  public var errorDescription: String? {\n    switch self {\n      case .invalidIdentifier(let string, let error):\n        return \"\"\"\n        Invalid identifier for string: \\(string).\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/ItemStack.swift",
    "content": "import Foundation\n\n// TODO: Refactor item stacks and recipes\n/// A stack of items.\npublic struct ItemStack {\n  /// The item that is stacked.\n  public var itemId: Int\n  /// The number of items in the stack.\n  public var count: Int\n  /// Any extra properties the items have.\n  public var nbt: NBT.Compound // TODO: refactor this away\n\n  /// Creates a new item stack.\n  public init(itemId: Int, itemCount: Int, nbt: NBT.Compound? = nil) {\n    self.itemId = itemId\n    self.count = itemCount\n    self.nbt = nbt ?? NBT.Compound()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/NBT/NBT.swift",
    "content": "/// NBT is essentially Mojang's take on protocol buffers.\n///\n/// It stands for 'Named Binary Tags' and is basically what it sounds like.\n/// In my NBT system it should be assumed all tags are big endian and signed\n/// unless a type name specifies otherwise.\npublic enum NBT { }\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/NBT/NBTCompound.swift",
    "content": "import Foundation\n\npublic enum NBTError: LocalizedError {\n  case emptyList\n  case invalidListType\n  case invalidTagType\n  case rootTagNotCompound\n  case failedToGetList(_ key: String)\n  case failedToGetTag(_ key: String)\n  case failedToOpenURL\n\n  public var errorDescription: String? {\n    switch self {\n      case .emptyList:\n        return \"Empty list.\"\n      case .invalidListType:\n        return \"Invalid list type.\"\n      case .invalidTagType:\n        return \"Invalid tag type.\"\n      case .rootTagNotCompound:\n        return \"Root tag not compound.\"\n      case .failedToGetList(let key):\n        return \"Failed to get list for key: \\(key).\"\n      case .failedToGetTag(let key):\n        return \"Failed to get tag for key: \\(key).\"\n      case .failedToOpenURL:\n        return \"Failed to open URL.\"\n    }\n  }\n}\n\n// all tags are assumed to be big endian and signed unless otherwise specified\n// TODO: Clean up NBT decoder\n\nextension NBT {\n  /// An container for NBT tags. A bit like an object in JSON.\n  public struct Compound: CustomStringConvertible {\n    public var buffer: Buffer\n    public var tags: [String: Tag] = [:]\n    public var name: String = \"\"\n    public var numBytes = -1\n    public var isRoot: Bool\n\n    public var description: String {\n      return \"\\(tags)\"\n    }\n\n    // MARK: Init\n\n    public init(name: String = \"\", isRoot: Bool = false) {\n      self.buffer = Buffer()\n      self.isRoot = isRoot\n      self.name = name\n    }\n\n    public init(fromBytes bytes: [UInt8], isRoot: Bool = true) throws {\n      try self.init(fromBuffer: Buffer(bytes), isRoot: isRoot)\n    }\n\n    public init(fromBuffer buffer: Buffer, withName name: String = \"\", isRoot: Bool = true) throws {\n      let initialBufferIndex = buffer.index\n\n      self.buffer = buffer\n      self.isRoot = isRoot\n      self.name = name\n\n      try unpack()\n\n      if isRoot && !tags.isEmpty {\n        let root: Compound = try get(\"\")\n        tags = root.tags\n        self.name = root.name\n      }\n\n      let numBytesRead = self.buffer.index - initialBufferIndex\n      numBytes = numBytesRead\n    }\n\n    public init(fromURL url: URL) throws {\n      let data: Data\n      do {\n        data = try Data(contentsOf: url)\n      } catch {\n        throw NBTError.failedToOpenURL\n      }\n      let bytes = [UInt8](data)\n      try self.init(fromBytes: bytes)\n    }\n\n    // MARK: Getters\n\n    public func get<T>(_ key: String) throws -> T {\n      guard let value = tags[key]?.value, let tag = value as? T else {\n        throw NBTError.failedToGetTag(key)\n      }\n      return tag\n    }\n\n    public func getList<T>(_ key: String) throws -> T {\n      guard let nbtList = tags[key]?.value as? List else {\n        throw NBTError.failedToGetList(key)\n      }\n      guard let list = nbtList.list as? T else {\n        throw NBTError.failedToGetList(key)\n      }\n      return list\n    }\n\n    // MARK: Unpacking\n\n    public mutating func unpack() throws {\n      var n = 0\n      while true {\n        let typeId = try buffer.readByte()\n        if let type = TagType(rawValue: typeId) {\n          if type == .end {\n            break\n          }\n          let nameLength = Int(try buffer.readShort(endianness: .big))\n          let name = try buffer.readString(length: nameLength)\n\n          tags[name] = try readTag(ofType: type, withId: n, andName: name)\n        } else { // type not valid\n          throw NBTError.invalidTagType\n        }\n\n        // the root tag should only contain one command\n        if isRoot {\n          break\n        }\n        n += 1\n      }\n    }\n\n    private mutating func readTag(ofType type: TagType, withId id: Int = 0, andName name: String = \"\") throws -> Tag {\n      var value: Any?\n      switch type {\n        case .end:\n          break\n        case .byte:\n          value = try buffer.readSignedByte()\n        case .short:\n          value = try buffer.readSignedShort(endianness: .big)\n        case .int:\n          value = try buffer.readSignedInteger(endianness: .big)\n        case .long:\n          value = try buffer.readSignedLong(endianness: .big)\n        case .float:\n          value = try buffer.readFloat(endianness: .big)\n        case .double:\n          value = try buffer.readDouble(endianness: .big)\n        case .byteArray:\n          let length = Int(try buffer.readSignedInteger(endianness: .big))\n          value = try buffer.readSignedBytes(length)\n        case .string:\n          let length = Int(try buffer.readShort(endianness: .big))\n          value = try buffer.readString(length: length)\n        case .list:\n          let typeId = try buffer.readByte()\n          if let listType = TagType(rawValue: typeId) {\n            let length = try buffer.readSignedInteger(endianness: .big)\n            if length < 0 {\n              throw NBTError.emptyList\n            }\n\n            var list = List(type: listType)\n            if length != 0 {\n              for _ in 0..<length {\n                let elem = try readTag(ofType: listType)\n                list.append(elem.value!)\n              }\n            }\n            value = list\n          } else {\n            throw NBTError.invalidListType\n          }\n        case .compound:\n          let compound = try Compound(fromBuffer: buffer, withName: name, isRoot: false)\n          try buffer.skip(compound.numBytes)\n          value = compound\n        case .intArray:\n          let count = try buffer.readSignedInteger(endianness: .big)\n          var array: [Int] = []\n          for _ in 0..<count {\n            let integer = Int(try buffer.readSignedInteger(endianness: .big))\n            array.append(integer)\n          }\n          value = array\n        case .longArray:\n          let count = try buffer.readSignedInteger(endianness: .big)\n          var array: [Int] = []\n          for _ in 0..<count {\n            let long = Int(try buffer.readSignedLong(endianness: .big))\n            array.append(long)\n          }\n          value = array\n      }\n      return Tag(id: id, name: name, type: type, value: value!)\n    }\n\n    // MARK: Packing\n\n    public mutating func pack() -> [UInt8] {\n      buffer = Buffer()\n      let tags = self.tags.values\n\n      if isRoot {\n        buffer.writeByte(TagType.compound.rawValue)\n        writeName(self.name)\n      }\n      for tag in tags {\n        buffer.writeByte(tag.type.rawValue)\n        writeName(tag.name!)\n        writeTag(tag)\n      }\n      writeTag(Tag(id: 0, type: .end, value: nil))\n\n      return buffer.bytes\n    }\n\n    private mutating func writeName(_ name: String) {\n      buffer.writeShort(UInt16(name.utf8.count), endianness: .big)\n      buffer.writeString(name)\n    }\n\n    // TODO: Remove force casts\n    // swiftlint:disable force_cast\n    private mutating func writeTag(_ tag: Tag) {\n      switch tag.type {\n        case .end:\n          buffer.writeByte(0)\n        case .byte:\n          buffer.writeSignedByte(tag.value as! Int8)\n        case .short:\n          buffer.writeSignedShort(tag.value as! Int16, endianness: .big)\n        case .int:\n          buffer.writeSignedInt(tag.value as! Int32, endianness: .big)\n        case .long:\n          buffer.writeSignedLong(tag.value as! Int64, endianness: .big)\n        case .float:\n          buffer.writeFloat(tag.value as! Float, endianness: .big)\n        case .double:\n          buffer.writeDouble(tag.value as! Double, endianness: .big)\n        case .byteArray:\n          buffer.writeBytes(tag.value as! [UInt8])\n        case .string:\n          let string = tag.value as! String\n          buffer.writeShort(UInt16(string.utf8.count), endianness: .big)\n          buffer.writeString(string)\n        case .list:\n          let list = tag.value as! List\n          let listType = list.type\n          let listLength = list.count\n\n          buffer.writeByte(listType.rawValue)\n          buffer.writeSignedInt(Int32(listLength), endianness: .big)\n\n          for elem in list.list {\n            let value = Tag(id: 0, type: listType, value: elem)\n            writeTag(value)\n          }\n        case .compound:\n          var compound = tag.value as! Compound\n          buffer.writeBytes(compound.pack())\n        case .intArray:\n          let array = tag.value as! [Int32]\n          let length = Int32(array.count)\n          buffer.writeSignedInt(length, endianness: .big)\n          for int in array {\n            buffer.writeSignedInt(int, endianness: .big)\n          }\n        case .longArray:\n          let array = tag.value as! [Int64]\n          let length = Int32(array.count)\n          buffer.writeSignedInt(length, endianness: .big)\n          for int in array {\n            buffer.writeSignedLong(int, endianness: .big)\n          }\n      }\n    }\n    // swiftlint:enable force_cast\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/NBT/NBTList.swift",
    "content": "import Foundation\n\n// TODO: figure out how to not use Any\nextension NBT {\n  /// An array of unnamed NBT tags of the same type.\n  public struct List: CustomStringConvertible {\n    public var type: TagType\n    public var list: [Any] = []\n    \n    public var description: String {\n      return \"\\(list)\"\n    }\n    \n    public var count: Int {\n      return list.count\n    }\n    \n    public mutating func append(_ elem: Any) {\n      list.append(elem)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/NBT/NBTTag.swift",
    "content": "import Foundation\n\nextension NBT {\n  /// An NBT tag holds a name (unless in an NBT list), a type, and a value.\n  public struct Tag: CustomStringConvertible {\n    public var id: Int\n    public var name: String?\n    public var type: TagType\n    public var value: Any?\n    \n    public var description: String {\n      if value != nil {\n        if value is Compound {\n          return \"\\(value!)\"\n        }\n        return \"\\\"\\(value!)\\\"\"\n      } else {\n        return \"nil\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/NBT/NBTTagType.swift",
    "content": "import Foundation\n\nextension NBT {\n  /// All possible NBT tag types\n  public enum TagType: UInt8 {\n    case end = 0\n    case byte = 1\n    case short = 2\n    case int = 3\n    case long = 4\n    case float = 5\n    case double = 6\n    case byteArray = 7\n    case string = 8\n    case list = 9\n    case compound = 10\n    case intArray = 11\n    case longArray = 12\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/PlayerEntityAction.swift",
    "content": "import Foundation\n\npublic enum PlayerEntityAction: Int32 {\n  case startSneaking = 0\n  case stopSneaking = 1\n  case leaveBed = 2\n  case startSprinting = 3\n  case stopSprinting = 4\n  case startHorseJump = 5\n  case stopHorseJump = 6\n  case openHorseInventory = 7\n  case startElytraFlying = 8\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/PlayerFlags.swift",
    "content": "import Foundation\n\npublic struct PlayerFlags: OptionSet {\n  public let rawValue: UInt8\n  \n  public init(rawValue: UInt8) {\n    self.rawValue = rawValue\n  }\n  \n  public static let invulnerable = PlayerFlags(rawValue: 0x01)\n  public static let flying = PlayerFlags(rawValue: 0x02)\n  public static let canFly = PlayerFlags(rawValue: 0x04)\n  public static let instantBreak = PlayerFlags(rawValue: 0x08)\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/Slot.swift",
    "content": "/// An inventory slot.\npublic struct Slot {\n  /// The slot's content if any.\n  public var stack: ItemStack?\n\n  /// Creates a new slot.\n  /// - Parameter stack: The slot's content.\n  public init(_ stack: ItemStack? = nil) {\n    self.stack = stack\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/Statistic.swift",
    "content": "import Foundation\n\npublic struct Statistic {\n  public var categoryId: Int\n  public var statisticId: Int\n  public var value: Int\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Datatypes/UUID.swift",
    "content": "import Foundation\n\n// adding a convenience method to more consistently get a UUID from a string (cause minecraft randomly gives you ones without hyphens)\nextension UUID {\n  static func fromString(_ string: String) -> UUID? {\n    let cleanedString: String\n    var matches = string.range(of: \"^[a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12}$\", options: .regularExpression)\n    if matches != nil {\n      cleanedString = string\n    } else {\n      matches = string.range(of: \"^[a-fA-F0-9]{32}\", options: .regularExpression)\n      if matches != nil {\n        let tempString = NSMutableString(string: string)\n        tempString.insert(\"-\", at: 20)\n        tempString.insert(\"-\", at: 16)\n        tempString.insert(\"-\", at: 12)\n        tempString.insert(\"-\", at: 8)\n        cleanedString = tempString as String\n      } else {\n        return nil\n      }\n    }\n    \n    return UUID(uuidString: cleanedString)\n  }\n  \n  func toBytes() -> [UInt8] {\n    let (u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11, u12, u13, u14, u15, u16) = self.uuid\n    return [u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11, u12, u13, u14, u15, u16]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Duration.swift",
    "content": "/// A duration of time.\npublic struct Duration: CustomStringConvertible {\n  /// The duration in seconds.\n  public var seconds: Double\n\n  /// The duration in milliseconds.\n  public var milliseconds: Double {\n    seconds * 1_000\n  }\n\n  /// The duration in microseconds.\n  public var microseconds: Double {\n    seconds * 1_000_000\n  }\n\n  /// The duration in nanoseconds.\n  public var nanoseconds: Double {\n    seconds * 1_000_000_000\n  }\n\n  /// A description of the duration with units appropriate for its magnitude.\n  public var description: String {\n    if seconds >= 1 {\n      return String(format: \"%.04fs\", seconds)\n    } else if milliseconds >= 1 {\n      return String(format: \"%.04fms\", milliseconds)\n    } else if microseconds >= 1 {\n      return String(format: \"%.04fμs\", microseconds)\n    } else {\n      return String(format: \"%.04fns\", nanoseconds)\n    }\n  }\n\n  /// Creates a duration measured in seconds.\n  public static func seconds(_ seconds: Double) -> Self {\n    Self(seconds: seconds)\n  }\n\n  /// Creates a duration measured in milliseconds.\n  public static func milliseconds(_ milliseconds: Double) -> Self {\n    Self(seconds: milliseconds / 1_000)\n  }\n\n  /// Creates a duration measured in microseconds.\n  public static func microseconds(_ microseconds: Double) -> Self {\n    Self(seconds: microseconds / 1_000_000)\n  }\n\n  /// Creates a duration measured in nanoseconds.\n  public static func nanoseconds(_ nanoseconds: Double) -> Self {\n    Self(seconds: nanoseconds / 1_000_000_000)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/BlockEntity.swift",
    "content": "import Foundation\n\n/// Metadata about a block.\npublic struct BlockEntity {\n  /// The position of the block that this metadata applies to.\n  public let position: BlockPosition\n  /// Identifier of the block that this metadata is for.\n  public let identifier: Identifier\n  /// Metadata stored in the nbt format.\n  public let nbt: NBT.Compound\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EnderDragonParts.swift",
    "content": "import FirebladeECS\n\npublic class EnderDragonParts: Component {\n  public var head = Part(\n    .head,\n    size: Vec2d(1, 1),\n    entityIdOffset: 1,\n    relativePosition: Vec3d(-0.5, 0, 1)\n  )\n  public var neck = Part(\n    .neck,\n    size: Vec2d(3, 3),\n    entityIdOffset: 2,\n    relativePosition: Vec3d(-1.5, -1, -2)\n  )\n  public var body = Part(\n    .body,\n    size: Vec2d(5, 3),\n    entityIdOffset: 3,\n    relativePosition: Vec3d(-2.5, -1, -4)\n  )\n  // TODO: Verify the entity id offsets of these\n  public var upperTail = Part(\n    .tail,\n    size: Vec2d(2, 2),\n    entityIdOffset: 4,\n    relativePosition: Vec3d(-1, -0.5, -5)\n  )\n  public var midTail = Part(\n    .tail,\n    size: Vec2d(2, 2),\n    entityIdOffset: 5,\n    relativePosition: Vec3d(-1, -0.5, -6)\n  )\n  public var lowerTail = Part(\n    .tail,\n    size: Vec2d(2, 2),\n    entityIdOffset: 6,\n    relativePosition: Vec3d(-1, -0.5, -7)\n  )\n  public var leftWing = Part(\n    .wing,\n    size: Vec2d(4, 2),\n    entityIdOffset: 7,\n    relativePosition: Vec3d(2, 0, -4)\n  )\n  public var rightWing = Part(\n    .wing,\n    size: Vec2d(4, 2),\n    entityIdOffset: 8,\n    relativePosition: Vec3d(-6, 0, -4)\n  )\n\n  /// All parts.\n  public var parts: [Part] {\n    [head, neck, body, upperTail, midTail, lowerTail, leftWing, rightWing]\n  }\n\n  public init() {}\n\n  public struct Part {\n    /// Group that this part is a member of (e.g. tail).\n    public var group: Group\n    /// The size of the part's hitbox as a width (x and z) and a height (y).\n    public var size: Vec2d\n    /// The offset of this part's entity id from the parent entity's id.\n    public var entityIdOffset: Int\n    /// Position relative to parent entity.\n    public var relativePosition: Vec3d\n\n    public enum Group {\n      case head\n      case neck\n      case body\n      case tail\n      case wing\n    }\n\n    public init(\n      _ group: Group,\n      size: Vec2d,\n      entityIdOffset: Int,\n      relativePosition: Vec3d\n    ) {\n      self.group = group\n      self.size = size\n      self.entityIdOffset = entityIdOffset\n      self.relativePosition = relativePosition\n    }\n\n    public func aabb(withParentPosition parentPosition: Vec3d) -> AxisAlignedBoundingBox {\n      AxisAlignedBoundingBox(\n        position: parentPosition + relativePosition,\n        size: Vec3d(size.x, size.y, size.x)\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityAcceleration.swift",
    "content": "import FirebladeECS\nimport FirebladeMath\n\n/// A component storing an entity's acceleration.\npublic class EntityAcceleration: Component {\n  // MARK: Public properties\n\n  /// The vector representing this acceleration.\n  public var vector: Vec3d\n\n  /// x component in blocks per tick.\n  public var x: Double {\n    get { vector.x }\n    set { vector.x = newValue }\n  }\n\n  /// y component in blocks per tick.\n  public var y: Double {\n    get { vector.y }\n    set { vector.y = newValue }\n  }\n\n  /// z component in blocks per tick.\n  public var z: Double {\n    get { vector.z }\n    set { vector.z = newValue }\n  }\n\n  // MARK: Init\n\n  /// Creates an entity's acceleration.\n  /// - Parameter vector: Vector representing the acceleration.\n  public init(_ vector: Vec3d) {\n    self.vector = vector\n  }\n\n  /// Creates an entity's acceleration.\n  /// - Parameters:\n  ///   - x: x component.\n  ///   - y: y component.\n  ///   - z: z component.\n  public convenience init(_ x: Double, _ y: Double, _ z: Double) {\n    self.init(Vec3d(x, y, z))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityAttributes.swift",
    "content": "import FirebladeECS\n\n/// A component storing an entity's attributes. See ``EntityMetadata`` for a\n/// discussion on the difference between metadata and attributes.\npublic class EntityAttributes: Component {\n  /// The attributes as key-value pairs.\n  private var attributes: [EntityAttributeKey: EntityAttributeValue] = [:]\n\n  /// Creates a new component with the default value for each attribute.\n  public init() {}\n\n  /// Gets and sets the value of an attribute. If the attribute has no value, the default value is returned.\n  public subscript(_ attribute: EntityAttributeKey) -> EntityAttributeValue {\n    get {\n      return attributes[attribute] ?? EntityAttributeValue(baseValue: attribute.defaultValue)\n    }\n    set(newValue) {\n      attributes[attribute] = newValue\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityCamera.swift",
    "content": "import FirebladeECS\n\n/// A component storing an entity's camera properties.\npublic class EntityCamera: Component {\n  /// A camera perspective.\n  public enum Perspective: Int, Codable, CaseIterable {\n    /// Rendered as if looking from the entity's eyes.\n    case firstPerson\n    /// Rendered from behind the entity's head, looking at the back of the entity's head.\n    case thirdPersonRear\n    /// Rendered in front of the entity's head, looking at the entity's face.\n    case thirdPersonFront\n  }\n  \n  /// The current perspective.\n  public var perspective: Perspective\n  \n  /// Creates an entity's camera.\n  /// - Parameter perspective: Defaults to ``Perspective-swift.enum/firstPerson``.\n  public init(perspective: Perspective = .firstPerson) {\n    self.perspective = perspective\n  }\n  \n  /// Changes to the next perspective.\n  public func cyclePerspective() {\n    let index = (perspective.rawValue + 1) % Perspective.allCases.count\n    perspective = Perspective.allCases[index]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityExperience.swift",
    "content": "import FirebladeECS\n\n/// A component storing an entity's experience level.\npublic class EntityExperience: Component {\n  /// The entity's total xp.\n  public var experience: Int\n  /// The entity's xp level (displayed above the xp bar).\n  public var experienceLevel: Int\n  /// The entity's experience bar progress.\n  public var experienceBarProgress: Float\n  \n  /// Creates an entity's xp state.\n  /// - Parameters:\n  ///   - experience: Defaults to 0.\n  ///   - experienceLevel: Defaults to 0.\n  ///   - experienceBarProgress: Defaults to 0.\n  public init(experience: Int = 0, experienceLevel: Int = 0, experienceBarProgress: Float = 0) {\n    self.experience = experience\n    self.experienceLevel = experienceLevel\n    self.experienceBarProgress = experienceBarProgress\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityFlying.swift",
    "content": "import FirebladeECS\n\n/// A component storing whether an entity is flying.\npublic class EntityFlying: Component {\n  /// Whether the entity is flying or not.\n  public var isFlying: Bool\n\n  /// Creates an entity's flying state.\n  /// - Parameter isFlying: Defaults to false.\n  public init(_ isFlying: Bool = false) {\n    self.isFlying = isFlying\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityHeadYaw.swift",
    "content": "import FirebladeECS\n\n/// A component storing the yaw of an entity's head.\npublic class EntityHeadYaw: Component {\n  /// The yaw of an entity's head (counter clockwise starting at the positive z axis).\n  public var yaw: Float\n  \n  /// Creates a new entity head yaw component.\n  public init(_ yaw: Float) {\n    self.yaw = yaw\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityHealth.swift",
    "content": "import FirebladeECS\n\n/// A component storing an entity's health (hp).\npublic class EntityHealth: Component {\n  /// The entity's health measured in half hearts.\n  public var health: Float\n  \n  /// Creates an entity's health value.\n  /// - Parameter health: Defaults to 20 hp.\n  public init(_ health: Float = 20) {\n    self.health = health\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityHitBox.swift",
    "content": "import FirebladeECS\nimport FirebladeMath\n\n/// A component storing an entity's hit box.\n///\n/// Entity hit boxes are axis aligned and their width and depth are always equal (``width``).\npublic class EntityHitBox: Component {\n  /// The width (and depth) of the entity's hit box.\n  public var width: Double\n  /// The height of the entity's hit box.\n  public var height: Double\n\n  /// The size of the hit box as a vector.\n  public var size: Vec3d {\n    Vec3d(width, height, width)\n  }\n\n  /// Creates a new hit box component.\n  public init(width: Double, height: Double) {\n    self.width = width\n    self.height = height\n  }\n\n  /// Creates a new hit box component.\n  public init(width: Float, height: Float) {\n    self.width = Double(width)\n    self.height = Double(height)\n  }\n\n  /// The bounding box for this hitbox if it was at the given position.\n  /// - Parameter position: The position of the hitbox.\n  /// - Returns: A bounding box with the same size as the hitbox and the given position.\n  public func aabb(at position: Vec3d) -> AxisAlignedBoundingBox {\n    let position = position - 0.5 * Vec3d(size.x, 0, size.z)\n    return AxisAlignedBoundingBox(position: position, size: size)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityId.swift",
    "content": "import FirebladeECS\n\n/// A component storing an entity's id.\npublic class EntityId: Component {\n  public var id: Int\n  \n  public init(_ id: Int) {\n    self.id = id\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityKindId.swift",
    "content": "import FirebladeECS\n\n/// A component storing the id of an entity's kind.\npublic class EntityKindId: Component {\n  public var id: Int\n\n  public var entityKind: EntityKind? {\n    RegistryStore.shared.entityRegistry.entity(withId: id)\n  }\n  \n  public init(_ id: Int) {\n    self.id = id\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityLerpState.swift",
    "content": "import CoreFoundation\nimport FirebladeECS\nimport FirebladeMath\nimport Foundation\n\n/// A component storing the lerp (if any) that an entity is currently undergoing; lerp is short\n/// for linear interpolation.\npublic class EntityLerpState: Component {\n  public var currentLerp: Lerp?\n\n  public struct Lerp {\n    public var targetPosition: Vec3d\n    public var targetPitch: Float\n    public var targetYaw: Float\n    public var ticksRemaining: Int\n\n    public init(\n      targetPosition: Vec3d,\n      targetPitch: Float,\n      targetYaw: Float,\n      ticksRemaining: Int\n    ) {\n      self.targetPosition = targetPosition\n      self.targetPitch = targetPitch\n      self.targetYaw = targetYaw\n      self.ticksRemaining = ticksRemaining\n    }\n  }\n\n  public init() {}\n\n  /// Initiate a lerp to the given position and rotation.\n  /// - Parameters:\n  ///   - position: Target position.\n  ///   - pitch: Target pitch.\n  ///   - yaw: Target yaw.\n  ///   - duration: Lerp duration in ticks.\n  public func lerp(to position: Vec3d, pitch: Float, yaw: Float, duration: Int) {\n    currentLerp = Lerp(\n      targetPosition: position,\n      targetPitch: pitch,\n      targetYaw: yaw,\n      ticksRemaining: duration\n    )\n  }\n\n  /// Ticks an entities current lerp returning the entity's new position, pitch, and yaw. If there's no current\n  /// lerp, then `nil` is returned.\n  public func tick(\n    position: Vec3d,\n    pitch: Float,\n    yaw: Float\n  ) -> (\n    position: Vec3d,\n    pitch: Float,\n    yaw: Float\n  )? {\n    guard var lerp = currentLerp, lerp.ticksRemaining > 0 else {\n      currentLerp = nil\n      return nil\n    }\n\n    let progress = 1 / Double(lerp.ticksRemaining)\n\n    lerp.ticksRemaining -= 1\n    if lerp.ticksRemaining == 0 {\n      currentLerp = nil\n    } else {\n      currentLerp = lerp\n    }\n\n    let targetYaw: Float\n    if yaw - lerp.targetYaw > .pi {\n      targetYaw = lerp.targetYaw + 2 * .pi\n    } else if yaw - lerp.targetYaw < -.pi {\n      targetYaw = lerp.targetYaw - 2 * .pi\n    } else {\n      targetYaw = lerp.targetYaw\n    }\n\n    return (\n      MathUtil.lerp(from: position, to: lerp.targetPosition, progress: progress),\n      MathUtil.lerp(from: pitch, to: lerp.targetPitch, progress: Float(progress)),\n      MathUtil.lerp(from: yaw, to: targetYaw, progress: Float(progress))\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityMetadata.swift",
    "content": "import FirebladeECS\n\n/// The distinction between entity metadata and entity attributes is that entity\n/// attributes are for properties that can have modifiers applied (e.g. speed,\n/// max health, etc).\npublic class EntityMetadata: Component {\n  /// Metadata specific to a certain kind of entity.\n  public var specializedMetadata: SpecializedMetadata?\n\n  public var itemMetadata: ItemMetadata? {\n    switch specializedMetadata {\n      case let .item(metadata): return metadata\n      default: return nil\n    }\n  }\n\n  public var mobMetadata: MobMetadata? {\n    switch specializedMetadata {\n      case let .mob(metadata): return metadata\n      default: return nil\n    }\n  }\n\n  public enum SpecializedMetadata {\n    case item(ItemMetadata)\n    case mob(MobMetadata)\n  }\n\n  public struct ItemMetadata {\n    public var slot = Slot()\n    /// The phase (in the periodic motion sense) of the item entity's bobbing animation.\n    /// Not part of the vanilla entity metadata, this is just a sensible place to store\n    /// this item entity property.\n    public var bobbingPhaseOffset: Float\n\n    public init() {\n      bobbingPhaseOffset = Float.random(in: 0...1) * 2 * .pi\n    }\n  }\n\n  public struct MobMetadata {\n    /// If an entity doesn't have AI, we should ignore its velocity. For some reason the\n    /// server still sends us the velocity even when the entity isn't moving.\n    public var noAI = false\n  }\n\n  /// Creates a\n  public init(inheritanceChain: [String]) {\n    if inheritanceChain.contains(\"ItemEntity\") {\n      specializedMetadata = .item(ItemMetadata())\n    } else if inheritanceChain.contains(\"MobEntity\") {\n      specializedMetadata = .mob(MobMetadata())\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityNutrition.swift",
    "content": "import FirebladeECS\n\n/// A component storing an entity's food and saturation levels.\npublic class EntityNutrition: Component {\n  public var food: Int\n  public var saturation: Float\n  \n  /// Creates an entity's nutrition with the provided values.\n  /// - Parameters:\n  ///   - food: Defaults to 20.\n  ///   - saturation: Defaults to 0.\n  public init(food: Int = 20, saturation: Float = 0) {\n    self.food = food\n    self.saturation = saturation\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityOnGround.swift",
    "content": "import FirebladeECS\n\n/// A component storing whether an entity is on the ground (walking or swimming) or not on the ground (jumping/falling).\npublic class EntityOnGround: Component {\n  /// Whether the entity is touching the ground or not.\n  public var onGround: Bool\n  /// The most recently saved value of ``onGround`` (see ``save()``).\n  public var previousOnGround: Bool\n  \n  public init(_ onGround: Bool) {\n    self.onGround = onGround\n    previousOnGround = onGround\n  }\n\n  /// Saves the current value to ``previousOnGround``.\n  public func save() {\n    previousOnGround = onGround\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityPosition.swift",
    "content": "import Foundation\nimport CoreFoundation\nimport FirebladeECS\nimport FirebladeMath\n\n/// A component storing an entity's position relative to the world.\n///\n/// Use ``smoothVector`` to get a vector that changes smoothly (positions are usually only updated once per tick).\npublic class EntityPosition: Component {\n  // MARK: Public properties\n\n  /// The underlying vector.\n  public var vector: Vec3d\n\n  /// The previous position.\n  public var previousVector: Vec3d\n\n  /// The amount of time taken (in seconds) for ``smoothVector`` to transition from one position to the next.\n  public var smoothingAmount: Double\n\n  /// A vector that smoothly interpolates from the previous position (set by calling ``save()``)\n  /// to the next position in an amount of time described by ``smoothingAmount``.\n  public var smoothVector: Vec3d {\n    let delta = CFAbsoluteTimeGetCurrent() - lastUpdated\n    let tickProgress = MathUtil.clamp(delta / smoothingAmount, 0, 1)\n    return tickProgress * (vector - previousVector) + previousVector\n  }\n\n  /// The raw x component (not smoothed).\n  public var x: Double {\n    get { vector.x }\n    set { vector.x = newValue }\n  }\n\n  /// The raw y component (not smoothed).\n  public var y: Double {\n    get { vector.y }\n    set { vector.y = newValue }\n  }\n\n  /// The raw z component (not smoothed).\n  public var z: Double {\n    get { vector.z }\n    set { vector.z = newValue }\n  }\n\n  /// The position of the chunk this position is in.\n  public var chunk: ChunkPosition {\n    return ChunkPosition(\n      chunkX: Int((x / 16).rounded(.down)),\n      chunkZ: Int((z / 16).rounded(.down))\n    )\n  }\n\n  /// The position of the chunk section this position is in.\n  public var chunkSection: ChunkSectionPosition {\n    return ChunkSectionPosition(\n      sectionX: Int((x / 16).rounded(.down)),\n      sectionY: Int((y / 16).rounded(.down)),\n      sectionZ: Int((z / 16).rounded(.down))\n    )\n  }\n\n  /// The block at the entity position.\n  public var block: BlockPosition {\n    return BlockPosition(\n      x: Int(x.rounded(.down)),\n      y: Int(y.rounded(.down)),\n      z: Int(z.rounded(.down))\n    )\n  }\n\n  /// The block underneath the entity position.\n  public var blockUnderneath: BlockPosition {\n    return BlockPosition(\n      x: Int(x.rounded(.down)),\n      y: Int((y - 0.5).rounded(.down)),\n      z: Int(z.rounded(.down))\n    )\n  }\n\n  // MARK: Private properties\n\n  /// The time the vector was last updated. Used for smoothing.\n  private var lastUpdated: CFAbsoluteTime\n\n  // MARK: Init\n\n  /// Creates an entity position from a vector.\n  /// - Parameters:\n  ///   - vector: A vector representing the position.\n  ///   - smoothingAmount: The amount of time (in seconds) for ``smoothVector`` to transition from one position to the next. Defaults to one 15th of a second.\n  public init(_ vector: Vec3d, smoothingAmount: Double = 1/15) {\n    self.vector = vector\n    previousVector = vector\n    lastUpdated = CFAbsoluteTimeGetCurrent()\n    self.smoothingAmount = smoothingAmount\n  }\n\n  /// Creates an entity position from coordinates.\n  /// - Parameters:\n  ///   - x: x coordinate.\n  ///   - y: y coordinate.\n  ///   - z: z coordinate.\n  ///   - smoothingAmount: The amount of time (in seconds) for ``smoothVector`` to transition from one position to the next. Defaults to one 15th of a second.\n  public convenience init(_ x: Double, _ y: Double, _ z: Double, smoothingAmount: Double = 1/15) {\n    self.init(Vec3d(x, y, z), smoothingAmount: smoothingAmount)\n  }\n\n  // MARK: Updating\n\n  /// Moves the position to a new position.\n  public func move(to position: EntityPosition) {\n    vector = position.vector\n  }\n\n  /// Moves the position to a new position.\n  public func move(to position: Vec3d) {\n    vector = position\n  }\n\n  /// Offsets the position by a specified amount.\n  public func move(by offset: Vec3d) {\n    vector += offset\n  }\n\n  // MARK: Smoothing\n\n  /// Saves the current value as the value to smooth from.\n  public func save() {\n    previousVector = smoothVector\n    lastUpdated = CFAbsoluteTimeGetCurrent()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityRotation.swift",
    "content": "import FirebladeECS\nimport Foundation\nimport CoreFoundation\n\n/// A component storing an entity's rotation in radians.\npublic class EntityRotation: Component {\n  /// The amount of time taken (in seconds) for ``smoothVector`` to transition from one position to the next.\n  public var smoothingAmount: Float\n\n  /// Pitch in radians.\n  public var pitch: Float\n\n  /// Yaw in radians.\n  public var yaw: Float\n\n  /// The previous pitch.\n  public var previousPitch: Float\n\n  /// The previous yaw.\n  public var previousYaw: Float\n\n  /// The smoothly interpolated pitch.\n  public var smoothPitch: Float {\n    let delta = Float(CFAbsoluteTimeGetCurrent() - lastUpdated)\n    let progress = MathUtil.clamp(delta / smoothingAmount, 0, 1)\n    return MathUtil.lerpAngle(from: previousPitch, to: pitch, progress: progress)\n  }\n\n  /// The smoothly interpolated yaw.\n  public var smoothYaw: Float {\n    let delta = Float(CFAbsoluteTimeGetCurrent() - lastUpdated)\n    let progress = MathUtil.clamp(delta / smoothingAmount, 0, 1)\n    return MathUtil.lerpAngle(from: previousYaw, to: yaw, progress: progress)\n  }\n\n  /// The compass heading.\n  public var heading: Direction {\n    let index = Int((yaw / (.pi / 2)).rounded()) % 4\n    return [.south, .west, .north, .east][index]\n  }\n\n  // MARK: Private properties\n\n  /// The time that the rotation was last updated. Used for smoothing. Set by ``save()``.\n  private var lastUpdated: CFAbsoluteTime\n\n  // MARK: Init\n\n  /// Creates an entity's rotation.\n  /// - Parameters:\n  ///   - pitch: The pitch in radians. -pi/2 is straight up, 0 is straight ahead, and pi/2 is straight down.\n  ///   - yaw: The yaw in radians. Measured counterclockwise from the positive z axis.\n  ///   - smoothingAmount: The amount of time (in seconds) for ``smoothYaw`` and ``smoothPitch``\n  ///     to transition from one position to the next. Defaults to 0 seconds.\n  public init(pitch: Float, yaw: Float, smoothingAmount: Float = 0) {\n    self.pitch = pitch\n    self.yaw = yaw\n    self.previousPitch = pitch\n    self.previousYaw = yaw\n    self.smoothingAmount = smoothingAmount\n    lastUpdated = CFAbsoluteTimeGetCurrent()\n  }\n\n  // MARK: Public methods\n\n  /// Saves the current pitch and yaw as the values to smooth from.\n  public func save() {\n    previousPitch = pitch\n    previousYaw = yaw\n    lastUpdated = CFAbsoluteTimeGetCurrent()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntitySneaking.swift",
    "content": "import FirebladeECS\n\n/// A component storing whether an entity is sneaking or not.\npublic class EntitySneaking: Component {\n  /// Whether the entity is sneaking or not.\n  public var isSneaking: Bool\n\n  /// Creates an entity's sneaking state.\n  /// - Parameter isSneaking: Defaults to false.\n  public init(_ isSneaking: Bool = false) {\n    self.isSneaking = isSneaking\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntitySprinting.swift",
    "content": "import FirebladeECS\n\n/// A component storing whether an entity is sprinting or not.\npublic class EntitySprinting: Component {\n  /// Whether the entity is sprinting or not.\n  public var isSprinting: Bool\n\n  /// Creates an entity's sprinting state.\n  /// - Parameter isSprinting: Defaults to false.\n  public init(_ isSprinting: Bool = false) {\n    self.isSprinting = isSprinting\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityUUID.swift",
    "content": "import Foundation\nimport FirebladeECS\n\n/// A component storing an entity's UUID. Not to be confused with ``EntityId``.\npublic class EntityUUID: Component {\n  public var uuid: UUID\n  \n  public init(_ uuid: UUID) {\n    self.uuid = uuid\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/EntityVelocity.swift",
    "content": "import FirebladeECS\nimport FirebladeMath\n\n/// A component storing an entity's velocity in blocks per tick.\npublic class EntityVelocity: Component {\n  // MARK: Public properties\n\n  /// The vector representing this velocity.\n  public var vector: Vec3d\n\n  /// x component in blocks per tick.\n  public var x: Double {\n    get { vector.x }\n    set { vector.x = newValue }\n  }\n\n  /// y component in blocks per tick.\n  public var y: Double {\n    get { vector.y }\n    set { vector.y = newValue }\n  }\n\n  /// z component in blocks per tick.\n  public var z: Double {\n    get { vector.z }\n    set { vector.z = newValue }\n  }\n\n  // MARK: Init\n\n  /// Creates an entity's velocity.\n  /// - Parameter vector: Vector representing the velocity.\n  public init(_ vector: Vec3d) {\n    self.vector = vector\n  }\n\n  /// Creates an entity's velocity.\n  /// - Parameters:\n  ///   - x: x component.\n  ///   - y: y component.\n  ///   - z: z component.\n  public convenience init(_ x: Double, _ y: Double, _ z: Double) {\n    self.init(Vec3d(x, y, z))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/Marker/ClientPlayerEntity.swift",
    "content": "import FirebladeECS\n\n/// A marker component for the client's player's entity.\npublic class ClientPlayerEntity: Component {}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/Marker/LivingEntity.swift",
    "content": "import FirebladeECS\n\n/// A marker component for living entities.\npublic class LivingEntity: Component {}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/Marker/NonLivingEntity.swift",
    "content": "import FirebladeECS\n\n/// A marker component for non-living entities.\npublic class NonLivingEntity: Component {}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/Marker/PlayerEntity.swift",
    "content": "import FirebladeECS\n\n/// A marker component for player entities.\npublic class PlayerEntity: Component {}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/ObjectData.swift",
    "content": "import FirebladeECS\n\n/// A component storing an object's data value.\npublic class ObjectData: Component {\n  /// The object's data value (don't ask me what this does, I don't know yet).\n  public var data: Int\n  \n  /// Creates an object data component.\n  public init(_ data: Int) {\n    self.data = data\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/ObjectUUID.swift",
    "content": "import Foundation\nimport FirebladeECS\n\n/// A component storing an object's UUID. Not to be confused with.\npublic class ObjectUUID: Component {\n  /// An object's UUID.\n  public var uuid: UUID\n  \n  /// Creates an object UUID component.\n  public init(_ uuid: UUID) {\n    self.uuid = uuid\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/PlayerAttributes.swift",
    "content": "import FirebladeECS\n\n/// A component storing attributes specific to the client's player.\npublic class PlayerAttributes: Component {\n  /// The player's spawn position.\n  public var spawnPosition: BlockPosition\n  /// The player's maximum flying speed (set by server).\n  public var flyingSpeed: Float\n  /// The player's current fov modifier.\n  public var fovModifier: Float\n  /// Whether the player is invulnerable to damage or not. In creative mode this is `true`.\n  public var isInvulnerable: Bool\n  /// Whether the player is allowed to fly or not.\n  public var canFly: Bool\n  /// Whether the player can instantly break blocks.\n  public var canInstantBreak: Bool\n  /// Whether the player is in hardcore mode or not. Affects respawn screen and rendering of hearts.\n  public var isHardcore: Bool\n  /// The player's previous gamemode. Likely used in vanilla by the F3+F4 gamemode switcher.\n  public var previousGamemode: Gamemode?\n\n  /// Creates the player's attributes.\n  /// - Parameters:\n  ///   - spawnPosition: Defaults to (0, 0, 0).\n  ///   - flyingSpeed: Defaults to 0.\n  ///   - fovModifier: Defaults to 0.\n  ///   - isInvulnerable: Defaults to false.\n  ///   - canFly: Defaults to false.\n  ///   - canInstantBreak: Defaults to false.\n  ///   - isHardcore: Defaults to false.\n  ///   - previousGamemode: Defaults to `nil`.\n  public init(\n    spawnPosition: BlockPosition = BlockPosition(x: 0, y: 0, z: 0),\n    flyingSpeed: Float = 0,\n    fovModifier: Float = 0,\n    isInvulnerable: Bool = false,\n    canFly: Bool = false,\n    canInstantBreak: Bool = false,\n    isHardcore: Bool = false,\n    previousGamemode: Gamemode? = nil\n  ) {\n    self.spawnPosition = spawnPosition\n    self.flyingSpeed = flyingSpeed\n    self.fovModifier = fovModifier\n    self.isInvulnerable = isInvulnerable\n    self.canFly = canFly\n    self.canInstantBreak = canInstantBreak\n    self.isHardcore = isHardcore\n    self.previousGamemode = previousGamemode\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/PlayerCollisionState.swift",
    "content": "import FirebladeECS\n\n/// A component which stores the state of collisions from the latest tick.\npublic class PlayerCollisionState: Component {\n  public var collidingHorizontally = false\n  public var collidingVertically = false\n\n  public init() {}\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/PlayerFOV.swift",
    "content": "import CoreFoundation\nimport FirebladeECS\nimport FirebladeMath\n\n/// A component storing the player's FOV.\npublic class PlayerFOV: Component {\n  /// The FOV multiplier from the previous tick.\n  public var previousMultiplier: Float\n  /// The FOV multiplier from the next tick.\n  public var multiplier: Float\n  /// The time at which the FOV multiplier was last calculated.\n  public var lastUpdated: CFAbsoluteTime\n\n  /// The FOV multiplier smoothed over the course of the current tick. Smooths from\n  /// the latest value saved using ``PlayerFOV/save``.\n  public var smoothMultiplier: Float {\n    let delta = Float(CFAbsoluteTimeGetCurrent() - lastUpdated)\n    let tickProgress = MathUtil.clamp(delta * Float(TickScheduler.defaultTicksPerSecond), 0, 1)\n    return tickProgress * (multiplier - previousMultiplier) + previousMultiplier\n  }\n\n  /// Creates a player FOV multiplier component.\n  public init(multiplier: Float = 1) {\n    self.previousMultiplier = multiplier\n    self.multiplier = multiplier\n    lastUpdated = CFAbsoluteTimeGetCurrent()\n  }\n\n  /// Saves the current value as the value to smooth from.\n  public func save() {\n    previousMultiplier = multiplier\n    lastUpdated = CFAbsoluteTimeGetCurrent()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/PlayerGamemode.swift",
    "content": "import FirebladeECS\n\n/// A component storing a player's gamemode.\npublic class PlayerGamemode: Component {\n  /// The player's gamemode.\n  public var gamemode: Gamemode\n  \n  /// Creates a player's gamemode.\n  /// - Parameter gamemode: Defaults to survival.\n  public init(gamemode: Gamemode = .survival) {\n    self.gamemode = gamemode\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Components/PlayerInventory.swift",
    "content": "import FirebladeECS\n\n/// A component storing the player's inventory. Simply wraps a ``Window`` with some\n/// inventory-specific properties and helper methods.\npublic class PlayerInventory: Component {\n  /// The number of slots in a player inventory (including armor and off hand).\n  public static let slotCount = 46\n  /// The player's inventory's window id.\n  public static let windowId = 0\n\n  /// The index of the crafting result slot.\n  public static let craftingResultIndex = craftingResultArea.startIndex\n  /// The index of the player's off-hand slot.\n  public static let offHandIndex = offHandArea.startIndex\n\n  public static let mainArea = WindowArea(\n    startIndex: 9,\n    width: 9,\n    height: 3,\n    position: Vec2i(8, 84)\n  )\n\n  public static let hotbarArea = WindowArea(\n    startIndex: 36,\n    width: 9,\n    height: 1,\n    position: Vec2i(8, 142)\n  )\n\n  public static let craftingInputArea = WindowArea(\n    startIndex: 1,\n    width: 2,\n    height: 2,\n    position: Vec2i(98, 18),\n    kind: .smallCraftingRecipeInput\n  )\n\n  public static let craftingResultArea = WindowArea(\n    startIndex: 0,\n    width: 1,\n    height: 1,\n    position: Vec2i(154, 28),\n    kind: .recipeResult\n  )\n\n  public static let armorArea = WindowArea(\n    startIndex: 5,\n    width: 1,\n    height: 4,\n    position: Vec2i(8, 8),\n    kind: .armor\n  )\n\n  public static let offHandArea = WindowArea(\n    startIndex: 45,\n    width: 1,\n    height: 1,\n    position: Vec2i(77, 62)\n  )\n\n  /// The inventory's window; contains the underlying slots.\n  public var window: Window\n  /// The player's currently selected hotbar slot.\n  public var selectedHotbarSlot: Int\n\n  /// The inventory's main 3 row 9 column area.\n  public var mainArea: [[Slot]] {\n    window.slots(for: Self.mainArea)\n  }\n\n  /// The inventory's crafting input slots.\n  public var craftingInputs: [[Slot]] {\n    window.slots(for: Self.craftingInputArea)\n  }\n\n  // TODO: Choose a casing for hotbar and stick to it (hotbar vs hotBar)\n  /// The inventory's hotbar slots.\n  public var hotbar: [Slot] {\n    window.slots(for: Self.hotbarArea)[0]\n  }\n\n  /// The result slot of the inventory's crafting area.\n  public var craftingResult: Slot {\n    window.slots[Self.craftingResultIndex]\n  }\n\n  /// The armor slots.\n  public var armorSlots: [Slot] {\n    window.slots(for: Self.armorArea).map { row in\n      row[0]\n    }\n  }\n\n  /// The off-hand slot.\n  public var offHand: Slot {\n    window.slots[Self.offHandIndex]\n  }\n\n  /// The item in the currently selected hotbar slot, `nil` if the slot is empty\n  /// or the item stack is invalid.\n  public var mainHandItem: Item? {\n    guard let stack = hotbar[selectedHotbarSlot].stack else {\n      return nil\n    }\n\n    guard let item = RegistryStore.shared.itemRegistry.item(withId: stack.itemId) else {\n      log.warning(\"Non-existent item with id \\(stack.itemId) selected in hotbar\")\n      return nil\n    }\n\n    return item\n  }\n\n  /// Creates the player's inventory state.\n  /// - Parameter selectedHotbarSlot: Defaults to 0 (the first slot from the left in the main hotbar).\n  /// - Precondition: The length of `slots` must match ``PlayerInventory/slotCount``.\n  public init(slots: [Slot]? = nil, selectedHotbarSlot: Int = 0) {\n    window = Window(\n      id: Self.windowId,\n      type: .inventory,\n      slots: slots\n    )\n\n    self.selectedHotbarSlot = selectedHotbarSlot\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Singles/ClientboundEntityPacketStore.swift",
    "content": "import FirebladeECS\n\n/// Used to save entity-related packets until a tick occurs. Entity packets must be handled during\n/// the game tick.\npublic final class ClientboundEntityPacketStore: SingleComponent {\n  /// Packets are stored as closures to avoid systems needing access to the ``Client`` when running\n  /// the packet handlers.\n  public var packets: [() throws -> Void]\n\n  /// Creates an empty store.\n  public init() {\n    packets = []\n  }\n\n  /// Adds a packet to be handled during the next tick.\n  public func add(_ packet: ClientboundEntityPacket, client: Client) {\n    packets.append({ [weak client] in\n      guard let client = client else { return }\n      try packet.handle(for: client)\n    })\n  }\n\n  /// Handles all stored packets and removes them.\n  public func handleAll() throws {\n    for packet in packets {\n      try packet()\n    }\n    packets = []\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Singles/GUIStateStorage.swift",
    "content": "import FirebladeECS\n\n@dynamicMemberLookup\npublic final class GUIStateStorage: SingleComponent {\n  public var inner = GUIState()\n\n  public init() {}\n\n  subscript<T>(dynamicMember member: WritableKeyPath<GUIState, T>) -> T {\n    get {\n      inner[keyPath: member]\n    }\n    set {\n      inner[keyPath: member] = newValue\n    }\n  }\n\n  subscript<T>(dynamicMember member: KeyPath<GUIState, T>) -> T {\n    inner[keyPath: member]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Singles/InputState.swift",
    "content": "import FirebladeECS\nimport FirebladeMath\n\n/// The game's input state.\npublic final class InputState: SingleComponent {\n  /// The maximum number of ticks between consecutive inputs to count as a\n  /// double tap.\n  public static let maximumDoubleTapDelay = 6\n\n  /// The newly pressed keys in the order that they were pressed. Only includes\n  /// presses since last call to ``flushInputs()``.\n  public private(set) var newlyPressed: [KeyPressEvent] = []\n  /// The newly released keys in the order that they were released. Only includes\n  /// releases since last call to ``flushInputs()``.\n  public private(set) var newlyReleased: [KeyReleaseEvent] = []\n\n  /// The currently pressed keys.\n  public private(set) var keys: Set<Key> = []\n  /// The currently pressed inputs.\n  public private(set) var inputs: Set<Input> = []\n\n  /// The current absolute mouse position relative to the play area's top left corner.\n  /// Measured in true pixels (not scaled down by the screen's scaling factor).\n  public private(set) var mousePosition: Vec2f = Vec2f(0, 0)\n  /// The mouse delta since the last call to ``resetMouseDelta()``.\n  public private(set) var mouseDelta: Vec2f = Vec2f(0, 0)\n  /// The position of the left thumbstick.\n  public private(set) var leftThumbstick: Vec2f = Vec2f(0, 0)\n  /// The position of the right thumbstick.\n  public private(set) var rightThumbstick: Vec2f = Vec2f(0, 0)\n\n  /// The time since forwards last changed from not pressed to pressed.\n  public private(set) var ticksSinceForwardsPressed: Int = 0\n  /// Whether the sprint was triggered by double tapping forwards.\n  public private(set) var sprintIsFromDoubleTap: Bool = false\n\n  /// The time since jump last changed from not pressed to pressed.\n  public private(set) var ticksSinceJumpPressed: Int = 0\n\n  /// Whether sprint is currently toggled. Only used if toggle sprint is enabled.\n  public private(set) var isSprintToggled: Bool = false\n  /// Whether sneak is currently toggled. Only used if toggle sneak is enabled.\n  public private(set) var isSneakToggled: Bool = false\n\n  /// Creates an empty input state.\n  public init() {}\n\n  /// Presses an input.\n  /// - Parameters:\n  ///   - key: The key pressed.\n  ///   - input: The input bound to the pressed key if any.\n  ///   - characters: Characters associated with the input.\n  public func press(key: Key?, input: Input?, characters: [Character] = []) {\n    newlyPressed.append(KeyPressEvent(key: key, input: input, characters: characters))\n  }\n\n  /// Releases an input.\n  /// - Parameters:\n  ///   - key: The key released.\n  ///   - input: The input released.\n  public func release(key: Key?, input: Input?) {\n    newlyReleased.append(KeyReleaseEvent(key: key, input: input))\n  }\n\n  /// Releases all inputs. Doesn't clear ``newlyPressed``, so if used when disabling\n  /// a certain set of (or all) inputs, wait to call this until after all relevant handlers\n  /// know to ignore said inputs.\n  public func releaseAll(clearNewlyPressed: Bool = true) {\n    for key in keys {\n      newlyReleased.append(KeyReleaseEvent(key: key, input: nil))\n    }\n\n    for input in inputs {\n      newlyReleased.append(KeyReleaseEvent(key: nil, input: input))\n    }\n  }\n\n  /// Clears ``newlyPressed`` and ``newlyReleased``.\n  public func flushInputs() {\n    newlyPressed = []\n    newlyReleased = []\n  }\n\n  /// Updates the current mouse delta by adding the given delta.\n  ///\n  /// See ``Client/moveMouse(x:y:deltaX:deltaY:)`` for the reasoning behind\n  /// having both absolute and relative parameters (it's currently necessary\n  /// but could be fixed by cleaning up the input handling architecture).\n  /// - Parameters:\n  ///   - x: The absolute mouse x (relative to the play area's top left corner).\n  ///   - y: The absolute mouse y (relative to the play area's top left corner).\n  ///   - deltaX: The change in mouse x.\n  ///   - deltaY: The change in mouse y.\n  public func moveMouse(x: Float, y: Float, deltaX: Float, deltaY: Float) {\n    mouseDelta += Vec2f(deltaX, deltaY)\n    mousePosition = Vec2f(x, y)\n  }\n\n  /// Updates the current position of the left thumbstick.\n  /// - Parameters:\n  ///   - x: The x position.\n  ///   - y: The y position.\n  public func moveLeftThumbstick(_ x: Float, _ y: Float) {\n    leftThumbstick = [x, y]\n  }\n\n  /// Updates the current position of the right thumbstick.\n  /// - Parameters:\n  ///   - x: The x position.\n  ///   - y: The y position.\n  public func moveRightThumbstick(_ x: Float, _ y: Float) {\n    rightThumbstick = [x, y]\n  }\n\n  /// Resets the mouse delta to 0.\n  public func resetMouseDelta() {\n    mouseDelta = Vec2f(0, 0)\n  }\n\n  /// Ticks the input state by flushing ``newlyPressed`` into ``keys`` and ``inputs``, and clearing\n  /// ``newlyReleased``. Also emits events to the given ``EventBus``.\n  func tick(_ isInputSuppressed: [Bool], _ eventBus: EventBus, _ configuration: ClientConfiguration) {\n    precondition(isInputSuppressed.count == newlyPressed.count, \"`isInputSuppressed` should be the same length as `newlyPressed`\")\n\n    ticksSinceForwardsPressed += 1\n    ticksSinceJumpPressed += 1\n\n    // Reset toggles if they're disabled\n    if (!configuration.toggleSprint && isSprintToggled) {\n      inputs.remove(.sprint)\n      isSprintToggled = false\n    }\n    if (!configuration.toggleSneak && isSneakToggled) {\n      inputs.remove(.sneak)\n      isSneakToggled = false\n    }\n\n    for (var event, suppressInput) in zip(newlyPressed, isInputSuppressed) {\n      if suppressInput {\n        event.input = nil\n      }\n\n      // Detect double pressing forwards (to activate sprint).\n      if event.input == .moveForward && !inputs.contains(.moveForward) {\n        // If the forwards key has been pressed within 6 ticks, press sprint.\n        if !inputs.contains(.sprint) && ticksSinceForwardsPressed <= Self.maximumDoubleTapDelay {\n          inputs.insert(.sprint)\n\n          // Mark the sprint input for removal once forwards is pressed.\n          sprintIsFromDoubleTap = true\n        }\n        ticksSinceForwardsPressed = 0\n      }\n\n      // Detect double pressing jump (to fly).\n      if event.input == .jump && !inputs.contains(.jump) {\n        if ticksSinceJumpPressed <= Self.maximumDoubleTapDelay {\n          inputs.insert(.fly)\n        }\n        ticksSinceJumpPressed = 0\n      }\n\n      // Make sure that sprint isn't removed when forwards is released if it was pressed by the user.\n      if event.input == .sprint {\n        sprintIsFromDoubleTap = false\n      }\n\n      eventBus.dispatch(event)\n\n      if let key = event.key {\n        keys.insert(key)\n      }\n\n      if let input = event.input {\n        // Toggle sprint if enabled\n        if configuration.toggleSprint && input == .sprint {\n          isSprintToggled = !isSprintToggled\n          if !isSprintToggled {\n            inputs.remove(input)\n            continue\n          }\n        }\n\n        // Toggle sneak if enabled\n        if configuration.toggleSneak && input == .sneak {\n          isSneakToggled = !isSneakToggled\n          if !isSneakToggled {\n            inputs.remove(input)\n            continue\n          }\n        }\n        \n        inputs.insert(input)\n      }\n    }\n\n    for event in newlyReleased {\n      // Remove sprint if the forwards key is released and the sprint came from a double tap.\n      if event.input == .moveForward && sprintIsFromDoubleTap {\n        inputs.remove(.sprint)\n      }\n      \n      // TODO: The release event of any inputs that were suppressed should probably also be suppressed\n      eventBus.dispatch(event)\n\n      // Don't remove sprint or sneak if toggling is enabled\n      if (event.input == .sprint && configuration.toggleSprint) || (event.input == .sneak && configuration.toggleSneak) {\n        continue\n      }\n\n      if let key = event.key {\n        keys.remove(key)\n      }\n      if let input = event.input {\n        inputs.remove(input)\n      }\n    }\n\n    flushInputs()\n\n    // `fly` is a synthetic input and is always immediately released.\n    if inputs.contains(.fly) {\n      release(key: nil, input: .fly)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/EntityMovementSystem.swift",
    "content": "import FirebladeECS\n\n/// Updates the position of each entity according to its velocity (excluding the player,\n/// because velocity for the player is handled by the ``PlayerVelocitySystem``). Also handles\n/// position/rotation lerping.\npublic struct EntityMovementSystem: System {\n  public func update(_ nexus: Nexus, _ world: World) {\n    let physicsEntities = nexus.family(\n      requiresAll: EntityPosition.self,\n      EntityVelocity.self,\n      EntityRotation.self,\n      EntityLerpState.self,\n      EntityMetadata.self,\n      EntityKindId.self,\n      EntityOnGround.self,\n      excludesAll: ClientPlayerEntity.self\n    )\n\n    for (position, velocity, rotation, lerpState, metadata, kind, onGround) in physicsEntities {\n      guard let kind = RegistryStore.shared.entityRegistry.entity(withId: kind.id) else {\n        log.warning(\"Unknown entity kind '\\(kind.id)'\")\n        continue\n      }\n\n      if let (newPosition, newPitch, newYaw) = lerpState.tick(\n        position: position.vector,\n        pitch: rotation.pitch,\n        yaw: rotation.yaw\n      ) {\n        position.vector = newPosition\n        rotation.pitch = newPitch\n        rotation.yaw = newYaw\n        continue\n      }\n\n      velocity.vector *= 0.98\n\n      if onGround.onGround {\n        velocity.vector.y = 0\n      } else {\n        if kind.identifier == Identifier(name: \"item\") {\n          velocity.vector.y *= 0.98\n          velocity.vector.y -= 0.04\n        }\n      }\n\n      if abs(velocity.vector.x) < 0.003 {\n        velocity.vector.x = 0\n      }\n      if abs(velocity.vector.y) < 0.003 {\n        velocity.vector.y = 0\n      }\n      if abs(velocity.vector.z) < 0.003 {\n        velocity.vector.z = 0\n      }\n\n      if metadata.mobMetadata?.noAI != true {\n        position.move(by: velocity.vector)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/EntitySmoothingSystem.swift",
    "content": "import FirebladeECS\n\n/// Saves the position of each entity before it is modified so that interpolation can be performed\n/// when rendering.\npublic struct EntitySmoothingSystem: System {\n  public func update(_ nexus: Nexus, _ world: World) {\n    let physicsEntities = nexus.family(\n      requiresAll: EntityPosition.self,\n      EntityVelocity.self,\n      excludesAll: ClientPlayerEntity.self\n    )\n\n    for (position, _) in physicsEntities {\n      position.save()\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PacketHandlingSystem.swift",
    "content": "import FirebladeECS\n\n/// Handles all packets in the ``ClientboundEntityPacketStore``. Mostly the packets in the store are\n/// entity movement packets. Handling them during the game tick helps position smoothing work\n/// better.\npublic struct EntityPacketHandlingSystem: System {\n  public func update(_ nexus: Nexus, _ world: World) throws {\n    let packetStore = nexus.single(ClientboundEntityPacketStore.self).component\n    do {\n      try packetStore.handleAll()\n    } catch {\n      log.error(\"Failed to handle entity-related packets during tick: \\(error)\")\n      throw error\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PlayerAccelerationSystem.swift",
    "content": "import Foundation\nimport FirebladeECS\nimport FirebladeMath\n\npublic struct PlayerAccelerationSystem: System {\n  static let sneakMultiplier: Double = 0.3\n  static let sprintingFoodLevel = 6\n\n  static let sprintingModifier = EntityAttributeModifier(\n    uuid: UUID(uuidString: \"662A6B8D-DA3E-4C1C-8813-96EA6097278D\")!,\n    amount: 0.3,\n    operation: .addPercent\n  )\n\n  public func update(_ nexus: Nexus, _ world: World) {\n    let guiState = nexus.single(GUIStateStorage.self).component\n\n    var family = nexus.family(\n      requiresAll: EntityNutrition.self,\n      EntityFlying.self,\n      EntityOnGround.self,\n      EntityRotation.self,\n      EntityPosition.self,\n      EntityAcceleration.self,\n      EntitySprinting.self,\n      EntitySneaking.self,\n      PlayerAttributes.self,\n      EntityAttributes.self,\n      PlayerCollisionState.self,\n      ClientPlayerEntity.self\n    ).makeIterator()\n\n    guard let (\n      nutrition,\n      flying,\n      onGround,\n      rotation,\n      position,\n      acceleration,\n      sprinting,\n      sneaking,\n      playerAttributes,\n      entityAttributes,\n      collisionState,\n      _\n    ) = family.next() else {\n      log.error(\"PlayerAccelerationSystem failed to get player to tick\")\n      return\n    }\n\n    // This should just act as an optimization, movement inputs shouldn't get here\n    // in the first place when movement isn't allowed so this function would just\n    // zero the acceleration anyway.\n    guard guiState.movementAllowed else {\n      acceleration.vector = .zero\n      return\n    }\n\n    let inputState = nexus.single(InputState.self).component\n    let inputs = Self.addControllerMovement(inputState.inputs, inputState.leftThumbstick)\n\n    let forwardsImpulse: Double = inputs.contains(.moveForward) ? 1 : 0\n    let backwardsImpulse: Double = inputs.contains(.moveBackward) ? 1 : 0\n    let leftImpulse: Double = inputs.contains(.strafeLeft) ? 1 : 0\n    let rightImpulse: Double = inputs.contains(.strafeRight) ? 1 : 0\n\n    var impulse = Vec3d(\n      leftImpulse - rightImpulse,\n      0,\n      forwardsImpulse - backwardsImpulse\n    )\n\n    if !flying.isFlying && inputs.contains(.sneak) {\n      impulse *= Self.sneakMultiplier\n      sneaking.isSneaking = true\n    } else {\n      sneaking.isSneaking = false\n    }\n\n    let canSprint = (nutrition.food > Self.sprintingFoodLevel || playerAttributes.canFly)\n    if !sprinting.isSprinting && impulse.z >= 0.8 && canSprint && inputs.contains(.sprint) {\n      sprinting.isSprinting = true\n    }\n\n    if sprinting.isSprinting {\n      if !inputs.contains(.moveForward) || collisionState.collidingHorizontally {\n        sprinting.isSprinting = false\n      }\n    }\n\n    let hasModifier = entityAttributes[.movementSpeed].hasModifier(Self.sprintingModifier.uuid)\n    if sprinting.isSprinting == true && !hasModifier {\n      entityAttributes[.movementSpeed].apply(Self.sprintingModifier)\n    } else if sprinting.isSprinting == false && hasModifier {\n      entityAttributes[.movementSpeed].remove(Self.sprintingModifier.uuid)\n    }\n\n    if impulse.magnitude < 0.0000001 {\n      impulse = .zero\n    } else if impulse.magnitudeSquared > 1 {\n      impulse = normalize(impulse)\n    }\n\n    impulse.x *= 0.98\n    impulse.z *= 0.98\n\n    let speed = Self.calculatePlayerSpeed(\n      position.vector,\n      world,\n      entityAttributes[.movementSpeed].value,\n      sprinting.isSprinting,\n      onGround.onGround,\n      flying.isFlying,\n      playerAttributes.flyingSpeed\n    )\n\n    impulse *= speed\n\n    let rotationMatrix = MatrixUtil.rotationMatrix(y: Double(rotation.yaw))\n    impulse = (Vec4d(impulse, 1) * rotationMatrix).xyz\n\n    acceleration.vector = impulse\n  }\n\n  private static func addControllerMovement(\n    _ inputs: Set<Input>,\n    _ thumbstick: Vec2f\n  ) -> Set<Input> {\n    let x = thumbstick.x\n    let y = thumbstick.y\n\n    guard x != 0 || y != 0 else {\n      return inputs\n    }\n\n    var inputs = inputs\n\n    let angle = FirebladeMath.atan2(y, x)\n    let sector = Int((angle / (.pi / 4)).rounded())\n    if sector >= 1 && sector <= 3 {\n      inputs.insert(.moveForward)\n    }\n    if sector >= -3 && sector <= -1 {\n      inputs.insert(.moveBackward)\n    }\n    if sector == -4 || sector == -3 || sector == 3 || sector == 4 {\n      inputs.insert(.strafeLeft)\n    }\n    if sector >= -1 && sector <= 1 {\n      inputs.insert(.strafeRight)\n    }\n\n    return inputs\n  }\n\n  private static func calculatePlayerSpeed(\n    _ position: Vec3d,\n    _ world: World,\n    _ movementSpeed: Double,\n    _ isSprinting: Bool,\n    _ onGround: Bool,\n    _ isFlying: Bool,\n    _ flightSpeed: Float\n  ) -> Double {\n    var speed: Double\n    if onGround {\n      // TODO: make get block below function once there is a Position protocol (and make vectors conform to it)\n      let blockPosition = BlockPosition(\n        x: Int(Foundation.floor(position.x)),\n        y: Int(Foundation.floor(position.y - 0.5)),\n        z: Int(Foundation.floor(position.z))\n      )\n      let block = world.getBlock(at: blockPosition)\n      let slipperiness = block.physicalMaterial.slipperiness\n\n      speed = movementSpeed * 0.216 / (slipperiness * slipperiness * slipperiness)\n    } else if isFlying {\n      speed = Double(flightSpeed)\n      if isSprinting {\n        speed *= 2\n      }\n    } else {\n      speed = 0.02\n      if isSprinting {\n        speed += 0.006\n      }\n    }\n    return speed\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PlayerBlockBreakingSystem.swift",
    "content": "import FirebladeECS\n\npublic final class PlayerBlockBreakingSystem: System {\n  var connection: ServerConnection?\n  weak var game: Game?\n  var lastDestroyCompletionTick: Int?\n\n  public init(_ connection: ServerConnection?, _ game: Game) {\n    self.connection = connection\n    self.game = game\n  }\n\n  public func update(_ nexus: Nexus, _ world: World) throws {\n    guard let game = game else {\n      return\n    }\n\n    // TODO: Cancel digging when hotbar slot changes\n\n    var family = nexus.family(\n      requiresAll: PlayerInventory.self,\n      PlayerGamemode.self,\n      PlayerAttributes.self,\n      EntityId.self,\n      ClientPlayerEntity.self\n    ).makeIterator()\n\n    guard let (inventory, gamemode, attributes, playerEntityId, _) = family.next() else {\n      log.error(\"PlayerInputSystem failed to get player to tick\")\n      return\n    }\n\n    guard gamemode.gamemode.canPlaceBlocks else {\n      return\n    }\n\n    let guiState = nexus.single(GUIStateStorage.self).component\n\n    guard guiState.movementAllowed else {\n      return\n    }\n\n    // 5tick delay between successfully breaking a block and starting to break the next one.\n    if let completionTick = lastDestroyCompletionTick, game.tickScheduler.tickNumber - completionTick <= 5 {\n      return\n    }\n\n    let inputState = nexus.single(InputState.self).component\n\n    guard let targetedBlock = game.targetedBlock(acquireLock: false) else {\n      if let block = world.getBreakingBlocks().first(where: { $0.perpetratorEntityId == playerEntityId.id }) {\n        world.endBlockBreaking(for: playerEntityId.id)\n\n        try self.connection?.sendPacket(PlayerDiggingPacket(\n          status: .cancelledDigging,\n          location: block.position,\n          face: .up // TODO: Figure out what value to use in this situation\n        ))\n      }\n      return\n    }\n\n    let position = targetedBlock.target\n\n    let notifyServer = { status in\n      try self.connection?.sendPacket(PlayerDiggingPacket(\n        status: status,\n        location: position,\n        face: targetedBlock.face\n      ))\n    }\n\n    guard !attributes.canInstantBreak else {\n      if inputState.newlyPressed.contains(where: { $0.input == .destroy }) {\n        try notifyServer(.startedDigging)\n      }\n      return\n    }\n\n    let newlyReleased = inputState.newlyReleased.contains(where: { $0.input == .destroy })\n    if newlyReleased {\n      world.endBlockBreaking(for: playerEntityId.id)\n      try notifyServer(.cancelledDigging)\n    }\n\n    // Technically possible to release and press again within the same tick.\n    if inputState.newlyPressed.contains(where: { $0.input == .destroy }) {\n      world.startBreakingBlock(at: position, for: playerEntityId.id)\n      try notifyServer(.startedDigging)\n    } else if inputState.inputs.contains(.destroy) && !newlyReleased {\n      let ourBreakingBlocks = world.getBreakingBlocks().filter { block in\n        block.perpetratorEntityId == playerEntityId.id\n      }\n\n      var alreadyMiningTargetedBlock = false\n      for block in ourBreakingBlocks {\n        if block.position != position {\n          world.endBlockBreaking(at: block.position)\n          try notifyServer(.cancelledDigging)\n        } else {\n          alreadyMiningTargetedBlock = true\n        }\n      }\n\n      if alreadyMiningTargetedBlock {\n        // TODO: This may be off by one tick, worth double checking\n        let block = world.getBlock(at: position)\n        let heldItem = inventory.mainHandItem\n        world.addBreakingProgress(Self.computeDestroyProgressDelta(block, heldItem), toBlockAt: position)\n        let progress = world.getBlockBreakingProgress(at: position) ?? 0\n        if progress >= 1 {\n          world.endBlockBreaking(for: playerEntityId.id)\n          world.setBlockId(at: position, to: 0)\n          try notifyServer(.finishedDigging)\n          lastDestroyCompletionTick = game.tickScheduler.tickNumber\n        }\n      } else {\n        world.startBreakingBlock(at: position, for: playerEntityId.id)\n        try notifyServer(.startedDigging)\n      }\n    }\n  }\n\n  public static func computeDestroyProgressDelta(_ block: Block, _ heldItem: Item?) -> Double {\n    let isBamboo = block.className == \"BambooBlock\" || block.className == \"BambooSaplingBlock\"\n    let holdingSword = heldItem?.properties?.toolProperties?.kind == .sword\n    if isBamboo && holdingSword {\n      return 1\n    } else {\n      let hardness = block.physicalMaterial.hardness\n\n      // TODO: Sentinel values are gross, we could probably just make hardness an optional\n      //   (don't have to copy vanilla). I would do that right now but we need cache versioning\n      //   first cause I'm pretty sure that change is subtle enough that it might just crash cache\n      //   loading instead of forcing it to fail and retry from pixlyzer.\n      guard hardness != -1 else {\n        return 0\n      }\n\n      let defaultSpeed = block.physicalMaterial.requiresTool ? 0.01 : (1 / 30)\n      let toolSpeed = heldItem?.properties?.toolProperties?.destroySpeedMultiplier(for: block) ?? defaultSpeed\n      return toolSpeed / hardness\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PlayerClimbSystem.swift",
    "content": "import FirebladeECS\n\npublic struct PlayerClimbSystem: System {\n  public func update(_ nexus: Nexus, _ world: World) {\n    var family = nexus.family(\n      requiresAll: EntityPosition.self,\n      EntityVelocity.self,\n      PlayerGamemode.self,\n      PlayerCollisionState.self,\n      ClientPlayerEntity.self\n    ).makeIterator()\n\n    guard let (position, velocity, gamemode, collisionState, _) = family.next() else {\n      log.error(\"PlayerClimbSystem failed to get player to tick\")\n      return\n    }\n\n    let isOnLadder = Self.isOnLadder(position, world, gamemode.gamemode)\n\n    guard isOnLadder else {\n      return\n    }\n\n    let inputState = nexus.single(InputState.self).component\n\n    // Limit horizontal velocity while on ladder.\n    velocity.vector.x = MathUtil.clamp(velocity.vector.x, -0.15, 0.15)\n    velocity.vector.z = MathUtil.clamp(velocity.vector.z, -0.15, 0.15)\n\n    // Limit falling speed while on ladder.\n    velocity.vector.y = max(velocity.vector.y, -0.15)\n    \n    // Ascending ladders takes precedence over sneaking on ladders.\n    if collisionState.collidingHorizontally || inputState.inputs.contains(.jump) {\n      velocity.vector.y = 0.2\n    } else if Self.isStoppedOnLadder(position, world, gamemode.gamemode, inputState, collisionState) {\n      velocity.vector.y = 0\n    }\n  }\n\n  static func isOnLadder(_ position: EntityPosition, _ world: World, _ gamemode: Gamemode) -> Bool {\n    guard gamemode != .spectator else {\n      return false\n    }\n\n    let block = world.getBlock(at: position.block)\n\n    if block.isClimbable {\n      return true\n    } else {\n      // If the player is on an open trapdoor above a ladder then they're also counted as being on a ladder\n      // as long as the ladder and trapdoor are facing the same way.\n      let blockBelow = world.getBlock(at: position.block.neighbour(.down))\n      let blockIdentifierBelow = blockBelow.identifier\n\n      let ladder = Identifier(name: \"block/ladder\")\n      let onTrapdoorAboveLadder = block.className == \"TrapdoorBlock\" && blockIdentifierBelow == ladder\n      let blocksFacingSameDirection = block.stateProperties.facing == blockBelow.stateProperties.facing\n      let trapdoorIsOpen = block.stateProperties.isOpen == true\n      return onTrapdoorAboveLadder && blocksFacingSameDirection && trapdoorIsOpen\n    }\n  }\n\n  static func isStoppedOnLadder(\n    _ position: EntityPosition,\n    _ world: World,\n    _ gamemode: Gamemode,\n    _ inputState: InputState,\n    _ collisionState: PlayerCollisionState\n  ) -> Bool {\n    let isClimbing = collisionState.collidingHorizontally || inputState.inputs.contains(.jump)\n    return isOnLadder(position, world, gamemode) && !isClimbing && inputState.inputs.contains(.sneak)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PlayerCollisionSystem.swift",
    "content": "import FirebladeECS\nimport FirebladeMath\n\n// TODO: Implement pushoutofblocks method (see decompiled vanilla sources)\npublic struct PlayerCollisionSystem: System {\n  static let stepHeight: Double = 0.6\n  \n  public func update(_ nexus: Nexus, _ world: World) {\n    var family = nexus.family(\n      requiresAll: EntityPosition.self,\n      EntityVelocity.self,\n      EntityHitBox.self,\n      EntityOnGround.self,\n      PlayerGamemode.self,\n      PlayerCollisionState.self,\n      ClientPlayerEntity.self\n    ).makeIterator()\n\n    guard let (position, velocity, hitbox, onGround, gamemode, collisionState, _) = family.next() else {\n      log.error(\"PlayerCollisionSystem failed to get player to tick\")\n      return\n    }\n\n    guard gamemode.gamemode.hasCollisions else {\n      return\n    }\n\n    let original = velocity.vector\n\n    let (adjustedVelocity, step) = Self.getAdjustedVelocityWithStepping(\n      position.vector,\n      velocity.vector,\n      hitbox.aabb(at: position.vector),\n      world,\n      onGround.onGround\n    )\n\n    velocity.vector = adjustedVelocity\n    position.vector.y += step\n\n    onGround.onGround = original.y < 0 && original.y != velocity.y\n\n    collisionState.collidingVertically = original.y != velocity.y\n    collisionState.collidingHorizontally = original.x != velocity.x || original.z != velocity.z\n  }\n\n  /// Adjusts the player's velocity to avoid collisions while automatically going up blocks under\n  /// 0.5 tall (e.g. slabs or carpets).\n  /// - Parameters:\n  ///   - position: The player's position.\n  ///   - velocity: The player's velocity.\n  ///   - aabb: The player's hitbox.\n  ///   - world: The world the player is colliding with.\n  /// - Returns: The adjusted velocity, the magnitude will be between 0 and the magnitude of the original velocity.\n  private static func getAdjustedVelocityWithStepping(\n    _ position: Vec3d,\n    _ velocity: Vec3d,\n    _ aabb: AxisAlignedBoundingBox,\n    _ world: World,\n    _ onGround: Bool\n  ) -> (velocity: Vec3d, step: Double) {\n    // TODO: Rewrite to be more delta clienty perhaps (currently quite similar to vanilla's impl)\n\n    let adjustedVelocity = getAdjustedVelocity(position, velocity, aabb, world)\n\n    let willBeOnGround = adjustedVelocity.y != velocity.y && velocity.y < 0\n    let onGround = onGround || willBeOnGround\n    let wasHorizontallyRestricted = adjustedVelocity.x != velocity.x || adjustedVelocity.z != velocity.z\n\n    // Check if the player could step up to move further\n    if onGround && wasHorizontallyRestricted {\n      var velocityWithStep = getAdjustedVelocity(\n        position,\n        [velocity.x, Self.stepHeight, velocity.z],\n        aabb,\n        world\n      )\n\n      let maximumVerticalVelocity = getAdjustedVelocity(\n        position,\n        [0, Self.stepHeight, 0],\n        aabb.extend(by: [velocity.x, 0, velocity.z]),\n        world\n      ).y\n\n      if maximumVerticalVelocity < Self.stepHeight {\n        // If the player would hit their head while ascending a full stepHeight, check if stepping as\n        // high as possible without hitting their head would avoid other collisions.\n        let velocityWithSmallerStep = getAdjustedVelocity(\n          position,\n          [velocity.x, 0, velocity.z],\n          aabb.offset(by: maximumVerticalVelocity, along: .y),\n          world\n        )\n\n        if velocityWithSmallerStep.horizontalMagnitude > velocityWithStep.horizontalMagnitude {\n          velocityWithStep = velocityWithSmallerStep\n          velocityWithStep.y = maximumVerticalVelocity\n        }\n      }\n\n      if velocityWithStep.horizontalMagnitude > adjustedVelocity.horizontalMagnitude {\n        // Recalculate the y velocity required to get up the 'step'.\n        velocityWithStep += getAdjustedVelocity(position, [0, -Self.stepHeight, 0], aabb.offset(by: velocityWithStep), world)\n        let step = velocityWithStep.y\n        velocityWithStep.y = 0\n        return (velocity: velocityWithStep, step: step)\n      }\n    }\n\n    return (velocity: adjustedVelocity, step: 0)\n  }\n\n  /// Adjusts the player's velocity to avoid collisions.\n  /// - Parameters:\n  ///   - position: The player's position.\n  ///   - velocity: The player's velocity.\n  ///   - aabb: The player's hitbox.\n  ///   - world: The world the player is colliding with.\n  /// - Returns: The adjusted velocity, the magnitude will be between 0 and the magnitude of the original velocity.\n  private static func getAdjustedVelocity(\n    _ position: Vec3d,\n    _ velocity: Vec3d,\n    _ aabb: AxisAlignedBoundingBox,\n    _ world: World\n  ) -> Vec3d {\n    let collisionVolume = getCollisionVolume(position, velocity, aabb, world)\n\n    var adjustedVelocity = velocity\n\n    adjustedVelocity.y = adjustComponent(adjustedVelocity.y, onAxis: .y, collisionVolume: collisionVolume, aabb: aabb)\n    var adjustedAABB = aabb.offset(by: adjustedVelocity.y, along: .y)\n\n    let prioritizeZ = abs(velocity.z) > abs(velocity.x)\n\n    if prioritizeZ {\n      adjustedVelocity.z = adjustComponent(adjustedVelocity.z, onAxis: .z, collisionVolume: collisionVolume, aabb: adjustedAABB)\n      adjustedAABB = adjustedAABB.offset(by: adjustedVelocity.z, along: .z)\n    }\n\n    adjustedVelocity.x = adjustComponent(adjustedVelocity.x, onAxis: .x, collisionVolume: collisionVolume, aabb: adjustedAABB)\n    adjustedAABB = adjustedAABB.offset(by: adjustedVelocity.x, along: .x)\n\n    if !prioritizeZ {\n      adjustedVelocity.z = adjustComponent(adjustedVelocity.z, onAxis: .z, collisionVolume: collisionVolume, aabb: adjustedAABB)\n    }\n\n    if adjustedVelocity.magnitudeSquared > velocity.magnitudeSquared {\n      adjustedVelocity = .zero\n    }\n\n    return adjustedVelocity\n  }\n\n  /// Adjusts the specified component of the player velocity to avoid any collisions along that axis.\n  /// - Parameters:\n  ///   - value: The value of the velocity component.\n  ///   - axis: The axis the velocity component lies on.\n  ///   - collisionVolume: The volume to avoid collisions with.\n  ///   - aabb: The player aabb.\n  /// - Returns: The adjusted velocity along the given axis. The adjusted value will be between 0 and the original velocity.\n  private static func adjustComponent(\n    _ value: Double,\n    onAxis axis: Axis,\n    collisionVolume: CompoundBoundingBox,\n    aabb: AxisAlignedBoundingBox\n  ) -> Double {\n    if abs(value) < 0.0000001 {\n      return 0\n    }\n\n    var value = value\n    for otherAABB in collisionVolume.aabbs {\n      if !aabb.offset(by: value, along: axis).shrink(by: 0.001).intersects(with: otherAABB) {\n        continue\n      }\n\n      let aabbMin = aabb.minimum.component(along: axis)\n      let aabbMax = aabb.maximum.component(along: axis)\n      let otherMin = otherAABB.minimum.component(along: axis)\n      let otherMax = otherAABB.maximum.component(along: axis)\n\n      if value > 0 && otherMin <= aabbMax + value {\n        let newValue = otherMin - aabbMax\n        if newValue >= -0.0000001 {\n          value = min(newValue, value)\n        }\n      } else if value < 0 && otherMax >= aabbMin + value {\n        let newValue = otherMax - aabbMin\n        if newValue <= 0.0000001 {\n          value = max(newValue, value)\n        }\n      }\n    }\n    return value\n  }\n\n  /// Gets a compound shape of all blocks the player could possibly be colliding with.\n  ///\n  /// It creates the smallest bounding box containing the current player AABB and the player AABB\n  /// after adding the current velocity (pre-collisions). It then creates a compound bounding box\n  /// containing all blocks within that volume.\n  private static func getCollisionVolume(\n    _ position: Vec3d,\n    _ velocity: Vec3d,\n    _ aabb: AxisAlignedBoundingBox,\n    _ world: World\n  ) -> CompoundBoundingBox {\n    let nextAABB = aabb.offset(by: velocity)\n    let minimum = MathUtil.min(aabb.minimum, nextAABB.minimum)\n    let maximum = MathUtil.max(aabb.maximum, nextAABB.maximum)\n\n    // Extend the AABB down one block to account for blocks such as fences\n    let bigAABB = AxisAlignedBoundingBox(minimum: minimum, maximum: maximum).extend(.down, amount: 1)\n    let blockPositions = bigAABB.blockPositions\n\n    var collisionShape = CompoundBoundingBox()\n    for blockPosition in blockPositions {\n      guard world.isChunkComplete(at: blockPosition.chunk) else {\n        continue\n      }\n\n      let block = world.getBlock(at: blockPosition)\n      let blockShape = block.shape.collisionShape.offset(by: blockPosition.doubleVector)\n\n      collisionShape.formUnion(blockShape)\n    }\n\n    return collisionShape\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PlayerFOVSystem.swift",
    "content": "import Foundation\nimport FirebladeECS\n\npublic struct PlayerFOVSystem: System {\n  public func update(_ nexus: Nexus, _ world: World) {\n    var family = nexus.family(\n      requiresAll: PlayerFOV.self,\n      EntityAttributes.self,\n      EntityFlying.self,\n      ClientPlayerEntity.self\n    ).makeIterator()\n\n    guard let (fov, attributes, flying, _) = family.next() else {\n      log.error(\"PlayerFOVSystem failed to get player to tick\")\n      return\n    }\n\n    // Save the current fov as the fov to smooth from over the course of the next tick.\n    fov.save()\n\n    let speedAttribute = attributes[EntityAttributeKey.movementSpeed]\n    let speed = speedAttribute.value\n    let baseSpeed = speedAttribute.baseValue\n\n    var targetMultiplier: Float\n    if baseSpeed != 0 && speed != 0 {\n      targetMultiplier = Float((speed / baseSpeed + 1) / 2)\n      if flying.isFlying {\n        targetMultiplier *= 1.1\n      }\n    } else {\n      targetMultiplier = 1\n    }\n\n    // The effective multiplier will creep up to the value of `targetMultiplier` over the\n    // course of a few ticks.\n    fov.multiplier += (targetMultiplier - fov.multiplier) / 2\n\n    if fov.multiplier > 1.5 {\n      fov.multiplier = 1.5\n    } else if fov.multiplier < 0.1 {\n      fov.multiplier = 0.1\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PlayerFlightSystem.swift",
    "content": "import FirebladeECS\n\npublic struct PlayerFlightSystem: System {\n  public func update(_ nexus: Nexus, _ world: World) {\n    var family = nexus.family(\n      requiresAll: PlayerGamemode.self,\n      EntityOnGround.self,\n      EntityFlying.self,\n      EntityVelocity.self,\n      PlayerAttributes.self,\n      ClientPlayerEntity.self\n    ).makeIterator()\n    \n    guard let (gamemode, onGround, flying, velocity, attributes, _) = family.next() else {\n      log.error(\"PlayerFlightSystem failed to get player to tick\")\n      return\n    }\n    \n    let inputState = nexus.single(InputState.self).component\n\n    if gamemode.gamemode.isAlwaysFlying {\n      onGround.onGround = false\n      flying.isFlying = true\n    } else if onGround.onGround || !attributes.canFly {\n      flying.isFlying = false\n    }\n\n    if inputState.inputs.contains(.fly) && !flying.isFlying && attributes.canFly {\n      flying.isFlying = true\n    } else if inputState.inputs.contains(.fly) && flying.isFlying && !gamemode.gamemode.isAlwaysFlying {\n      flying.isFlying = false\n    }\n    \n    if flying.isFlying {\n      let sneakPressed = inputState.inputs.contains(.sneak)\n      let jumpPressed = inputState.inputs.contains(.jump)\n      if sneakPressed != jumpPressed {\n        if sneakPressed {\n          velocity.y -= Double(attributes.flyingSpeed * 3)\n        } else {\n          velocity.y += Double(attributes.flyingSpeed * 3)\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PlayerFrictionSystem.swift",
    "content": "import FirebladeECS\n\npublic struct PlayerFrictionSystem: System {\n  public func update(_ nexus: Nexus, _ world: World) {\n    var family = nexus.family(\n      requiresAll: EntityPosition.self,\n      EntityVelocity.self,\n      EntityOnGround.self,\n      ClientPlayerEntity.self\n    ).makeIterator()\n    \n    guard let (position, velocity, onGround, _) = family.next() else {\n      log.error(\"PlayerFrictionSystem failed to get player to tick\")\n      return\n    }\n    \n    var multiplier: Double = 0.91\n    if onGround.previousOnGround {\n      let blockPosition = position.blockUnderneath\n      let material = world.getBlock(at: blockPosition).physicalMaterial\n\n      multiplier *= material.slipperiness\n    }\n\n    velocity.x *= multiplier\n    velocity.y *= 0.98\n    velocity.z *= multiplier\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PlayerGravitySystem.swift",
    "content": "import FirebladeECS\nimport Foundation\n\npublic struct PlayerGravitySystem: System {\n  public func update(_ nexus: Nexus, _ world: World) {\n    var family = nexus.family(\n      requiresAll: EntityFlying.self,\n      EntityVelocity.self,\n      EntityPosition.self,\n      PlayerGamemode.self,\n      PlayerCollisionState.self,\n      ClientPlayerEntity.self\n    ).makeIterator()\n    \n    guard let (flying, velocity, position, gamemode, collisionState, _) = family.next() else {\n      log.error(\"PlayerGravitySystem failed to get player to tick\")\n      return\n    }\n\n    let inputState = nexus.single(InputState.self).component\n\n    guard !PlayerClimbSystem.isStoppedOnLadder(position, world, gamemode.gamemode, inputState, collisionState) else {\n      return\n    }\n    \n    guard !flying.isFlying else {\n      return\n    }\n\n    if world.chunk(at: position.chunk) != nil {\n      velocity.y -= 0.08\n    }\n\n    velocity.y *= 0.98\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PlayerInputSystem.swift",
    "content": "import FirebladeECS\n\n#if os(macOS)\n  // Used to access clipboard\n  import AppKit\n#endif\n\n/// Handles all player input except for input related to player movement (see ``PlayerAccelerationSystem``).\npublic final class PlayerInputSystem: System {\n  var connection: ServerConnection?\n  weak var game: Game?\n  var eventBus: EventBus\n  let configuration: ClientConfiguration\n  let font: Font\n  let locale: MinecraftLocale\n\n  public init(\n    _ connection: ServerConnection?,\n    _ game: Game,\n    _ eventBus: EventBus,\n    _ configuration: ClientConfiguration,\n    _ font: Font,\n    _ locale: MinecraftLocale\n  ) {\n    self.connection = connection\n    self.game = game\n    self.eventBus = eventBus\n    self.configuration = configuration\n    self.font = font\n    self.locale = locale\n  }\n\n  public func update(_ nexus: Nexus, _ world: World) throws {\n    guard let game = game else {\n      return\n    }\n\n    var family = nexus.family(\n      requiresAll: EntityRotation.self,\n      PlayerInventory.self,\n      EntityCamera.self,\n      PlayerGamemode.self,\n      EntitySneaking.self,\n      ClientPlayerEntity.self\n    ).makeIterator()\n\n    guard let (rotation, inventory, camera, gamemode, sneaking, _) = family.next() else {\n      log.error(\"PlayerInputSystem failed to get player to tick\")\n      return\n    }\n\n    let inputState = nexus.single(InputState.self).component\n    let guiState = nexus.single(GUIStateStorage.self).component\n\n    let mousePosition = Vec2i(inputState.mousePosition / guiState.drawableScalingFactor)\n\n    // Handle non-movement inputs\n    var isInputSuppressed: [Bool] = []\n    for event in inputState.newlyPressed {\n      var suppressInput = false\n\n      // TODO: Formalize 'mouse targeted interactions are allowed', seems a bit hacky this way\n      if !suppressInput && !guiState.movementAllowed {\n        // Recompute the gui everytime that we get it to handle a new interaction because e.g. if previous\n        // interactions within the same tick have closed the inventory and opened the chat, then the old\n        // gui would still be handling the input for the inventory. That's a bit contrived, but I'm sure\n        // other edge cases are possible, and recomputing the GUI is relatively cheap (and multiple inputs\n        // within a single tick should be uncommon anyway).\n\n        // Be careful not to acquire a nexus lock here (passing the guiState parameter ensures this)\n        let gui = game.compileGUI(\n          withFont: font,\n          locale: locale,\n          connection: connection,\n          guiState: guiState\n        )\n        suppressInput = gui.handleInteraction(.press(event), at: mousePosition)\n      }\n\n      if !suppressInput {\n        suppressInput = try handleChat(event, inputState, guiState)\n      }\n\n      if !suppressInput {\n        suppressInput = try handleInventory(event, inventory, guiState, eventBus, connection)\n      }\n\n      if !suppressInput {\n        suppressInput = try handleWindow(event, guiState, eventBus, connection)\n      }\n\n      if !suppressInput {\n        switch event.input {\n          case .changePerspective:\n            camera.cyclePerspective()\n          case .toggleHUD:\n            guiState.showHUD = !guiState.showHUD\n          case .toggleDebugHUD:\n            guiState.showDebugScreen = !guiState.showDebugScreen\n          case .toggleInventory:\n            // Closing the inventory is handled by `handleInventory`\n            guiState.showInventory = true\n            inputState.releaseAll()\n            eventBus.dispatch(ReleaseCursorEvent())\n          case .slot1:\n            inventory.selectedHotbarSlot = 0\n          case .slot2:\n            inventory.selectedHotbarSlot = 1\n          case .slot3:\n            inventory.selectedHotbarSlot = 2\n          case .slot4:\n            inventory.selectedHotbarSlot = 3\n          case .slot5:\n            inventory.selectedHotbarSlot = 4\n          case .slot6:\n            inventory.selectedHotbarSlot = 5\n          case .slot7:\n            inventory.selectedHotbarSlot = 6\n          case .slot8:\n            inventory.selectedHotbarSlot = 7\n          case .slot9:\n            inventory.selectedHotbarSlot = 8\n          case .nextSlot:\n            inventory.selectedHotbarSlot = (inventory.selectedHotbarSlot + 1) % 9\n          case .previousSlot:\n            inventory.selectedHotbarSlot = (inventory.selectedHotbarSlot + 8) % 9\n          case .dropItem:\n            // TODO: Implement a similar check on other desktop platforms (Linux, Windows)\n            #if os(macOS)\n              let isQuitKeyboardShortcut =\n                event.key == .q\n                && (inputState.keys.contains(where: \\.isCommand)\n                  || inputState.newlyPressed.contains { $0.key?.isCommand == true })\n              guard !isQuitKeyboardShortcut else {\n                break\n              }\n            #endif\n\n            let slotIndex = PlayerInventory.hotbarArea.startIndex + inventory.selectedHotbarSlot\n            inventory.window.dropItemFromSlot(\n              slotIndex,\n              mouseItemStack: nil,\n              connection: connection\n            )\n          case .place:\n            // Block breaking is handled by ``PlayerBlockBreakingSystem``, this just handles hand animation and\n            // other non breaking things for the `.destroy` input (e.g. attacking)\n            if inventory.hotbar[inventory.selectedHotbarSlot].stack != nil {\n              try connection?.sendPacket(UseItemPacket(hand: .mainHand))\n            }\n\n            guard let targetedThing = game.targetedThing(acquireLock: false) else {\n              break\n            }\n\n            switch targetedThing.target {\n              case let .block(blockPosition):\n                let cursor = targetedThing.cursor\n                try connection?.sendPacket(\n                  PlayerBlockPlacementPacket(\n                    hand: .mainHand,\n                    location: blockPosition,\n                    face: targetedThing.face,\n                    cursorPositionX: cursor.x,\n                    cursorPositionY: cursor.y,\n                    cursorPositionZ: cursor.z,\n                    insideBlock: targetedThing.distance < 0\n                  )\n                )\n\n                if gamemode.gamemode.canPlaceBlocks {\n                  // TODO: Predict the result of block placement so that we're not relying on the server\n                  //   (quite noticeable latency)\n                }\n              case let .entity(entityId):\n                let targetedPosition = targetedThing.targetedPosition\n                try connection?.sendPacket(\n                  InteractEntityPacket(\n                    entityId: Int32(entityId),\n                    interaction: .interactAt(\n                      targetX: targetedPosition.x,\n                      targetY: targetedPosition.y,\n                      targetZ: targetedPosition.z,\n                      hand: .mainHand,\n                      isSneaking: sneaking.isSneaking\n                    )\n                  )\n                )\n                try connection?.sendPacket(\n                  InteractEntityPacket(\n                    entityId: Int32(entityId),\n                    interaction: .interact(hand: .mainHand, isSneaking: sneaking.isSneaking)\n                  )\n                )\n            }\n          case .destroy:\n            guard let targetedEntity = game.targetedEntity(acquireLock: false) else {\n              try connection?.sendPacket(AnimationServerboundPacket(hand: .mainHand))\n              break\n            }\n\n            var entityId = targetedEntity.target\n            if let dragonParts = game.accessComponent(\n              entityId: targetedEntity.target,\n              EnderDragonParts.self,\n              acquireLock: false,\n              action: identity\n            ) {\n              guard\n                let dragonPosition = game.accessComponent(\n                  entityId: targetedEntity.target,\n                  EntityPosition.self,\n                  acquireLock: false,\n                  action: identity\n                )\n              else {\n                log.warning(\"Ender dragon missing position\")\n                break\n              }\n\n              let ray = game.accessPlayer(acquireLock: false, action: \\.ray)\n              var currentDistance: Float = Player.attackReach\n              for part in dragonParts.parts {\n                let aabb = part.aabb(withParentPosition: dragonPosition.vector)\n                if let (distance, _) = aabb.intersectionDistanceAndFace(with: ray),\n                  distance < currentDistance\n                {\n                  entityId = targetedEntity.target + part.entityIdOffset\n                  currentDistance = distance\n                }\n              }\n            }\n\n            try connection?.sendPacket(\n              InteractEntityPacket(\n                entityId: Int32(entityId),\n                interaction: .attack(isSneaking: sneaking.isSneaking)\n              )\n            )\n          default:\n            break\n        }\n\n        if event.key == .escape {\n          eventBus.dispatch(OpenInGameMenuEvent())\n        }\n      }\n\n      isInputSuppressed.append(suppressInput)\n    }\n\n    // Handle mouse input.\n    if guiState.movementAllowed {\n      updateRotation(inputState, rotation)\n    }\n\n    inputState.resetMouseDelta()\n    inputState.tick(isInputSuppressed, eventBus, configuration)\n  }\n\n  /// - Returns: Whether to suppress the input associated with the event or not. `true` while user is typing.\n  private func handleChat(\n    _ event: KeyPressEvent,\n    _ inputState: InputState,\n    _ guiState: GUIStateStorage\n  ) throws -> Bool {\n    if var message = guiState.messageInput {\n      var newCharacters: [Character] = []\n      if event.key == .enter {\n        if !message.isEmpty {\n          try connection?.sendPacket(ChatMessageServerboundPacket(message))\n          guiState.playerMessageHistory.append(message)\n          guiState.currentMessageIndex = nil\n        }\n        guiState.messageInput = nil\n        eventBus.dispatch(CaptureCursorEvent())\n        return true\n      } else if event.key == .escape {\n        guiState.messageInputCursor = 0\n        guiState.messageInput = nil\n        guiState.currentMessageIndex = nil\n        eventBus.dispatch(CaptureCursorEvent())\n        return true\n      } else if event.key == .delete {\n        if !message.isEmpty && guiState.messageInputCursor < guiState.messageInput?.count ?? 0 {\n          guiState.messageInput?.remove(at: message.index(before: guiState.messageInputCursorIndex))\n        }\n      } else if event.key == .upArrow {\n        // If no message is selected, select the above message\n        if let index = guiState.currentMessageIndex, index > 0 {\n          guiState.currentMessageIndex = index - 1\n          guiState.messageInput = guiState.playerMessageHistory[index - 1]\n        } else if guiState.currentMessageIndex == nil && !guiState.playerMessageHistory.isEmpty {\n          guiState.stashedMessageInput = guiState.messageInput\n          let index = guiState.playerMessageHistory.count - 1\n          guiState.currentMessageIndex = index\n          guiState.messageInput = guiState.playerMessageHistory[index]\n        }\n      } else if event.key == .downArrow {\n        // If there is a message selected, index down a message\n        if let index = guiState.currentMessageIndex {\n          if index < guiState.playerMessageHistory.count - 1 {\n            guiState.currentMessageIndex = index + 1\n            guiState.messageInput = guiState.playerMessageHistory[index + 1]\n          } else {\n            // If there is no message to index down to, go back to what the user was typing originally\n            guiState.currentMessageIndex = nil\n            guiState.messageInput = guiState.stashedMessageInput ?? \"\"\n          }\n        }\n      } else if event.key == .leftArrow\n        && guiState.messageInput?.count ?? 0 > guiState.messageInputCursor\n      {\n        guiState.messageInputCursor += 1\n      } else if event.key == .rightArrow && guiState.messageInputCursor > 0 {\n        guiState.messageInputCursor -= 1\n      } else {\n        #if os(macOS)\n          if event.key == .v\n            && !inputState.keys.intersection([.leftCommand, .rightCommand]).isEmpty\n          {\n            // Handle paste keyboard shortcut\n            if let content = NSPasteboard.general.string(forType: .string) {\n              newCharacters = Array(content)\n            }\n          } else if message.utf8.count < InGameGUI.maximumMessageLength {\n            newCharacters = event.characters\n          }\n        #else\n          if message.utf8.count < InGameGUI.maximumMessageLength {\n            newCharacters = event.characters\n          }\n        #endif\n\n        // Ensure that the message doesn't exceed 256 bytes (including if multi-byte characters are entered).\n        var count = 0\n        for character in newCharacters {\n          guard character.isPrintable, character != \"\\t\" else {\n            // TODO: Make this check less restrictive, it's currently over-cautious\n            continue\n          }\n          guard character.utf8.count + message.utf8.count <= InGameGUI.maximumMessageLength else {\n            break\n          }\n\n          let index = message.index(guiState.messageInputCursorIndex, offsetBy: count)\n          message.insert(character, at: index)\n          count += 1\n        }\n        guiState.messageInput = message\n      }\n    } else if event.input == .openChat {\n      guiState.messageInput = \"\"\n      // TODO: Refactor input handling to be a bit more declarative so that something like\n      //   issue #192 is less likely to happen again. Besides, this input handling code is\n      //   pretty spaghetti and could do with a makeover anyway.\n      inputState.releaseAll()\n      eventBus.dispatch(ReleaseCursorEvent())\n    } else if event.key == .forwardSlash {\n      guiState.messageInput = \"/\"\n      inputState.releaseAll()\n      eventBus.dispatch(ReleaseCursorEvent())\n    }\n\n    // Suppress inputs while the user is typing.\n    return guiState.showChat\n  }\n\n  /// - Returns: Whether to suppress the input associated with the event or not.\n  private func handleInventory(\n    _ event: KeyPressEvent,\n    _ inventory: PlayerInventory,\n    _ guiState: GUIStateStorage,\n    _ eventBus: EventBus,\n    _ connection: ServerConnection?\n  ) throws -> Bool {\n    guard guiState.showInventory else {\n      return false\n    }\n\n    if event.key == .escape || event.input == .toggleInventory {\n      // Weirdly enough, the vanilla client sends a close window packet when closing the player's\n      // inventory even though it never tells the server that it opened the inventory in the first\n      // place. Likely just for the server to verify the slots and chuck out anything in the crafting\n      // area.\n      try inventory.window.close(\n        mouseStack: &guiState.mouseItemStack,\n        eventBus: eventBus,\n        connection: connection\n      )\n      guiState.showInventory = false\n    }\n\n    return true\n  }\n\n  /// - Returns: Whether to suppress the input associated with the event or not.\n  private func handleWindow(\n    _ event: KeyPressEvent,\n    _ guiState: GUIStateStorage,\n    _ eventBus: EventBus,\n    _ connection: ServerConnection?\n  ) throws -> Bool {\n    guard let window = guiState.window else {\n      return false\n    }\n\n    if event.key == .escape || event.input == .toggleInventory {\n      try window.close(\n        mouseStack: &guiState.mouseItemStack,\n        eventBus: eventBus,\n        connection: connection\n      )\n      guiState.window = nil\n    }\n\n    return true\n  }\n\n  /// Updates the direction which the player is looking.\n  /// - Parameters:\n  ///   - inputState: The current input state.\n  ///   - rotation: The player's rotation component.\n  private func updateRotation(_ inputState: InputState, _ rotation: EntityRotation) {\n    let thumbstickSensitivity: Float = 0.2\n    let stick = inputState.rightThumbstick * thumbstickSensitivity\n\n    let mouseDelta = inputState.mouseDelta\n    var yaw = rotation.yaw + mouseDelta.x + stick.x\n    var pitch = rotation.pitch + mouseDelta.y - stick.y\n\n    // Clamp pitch between -90 and 90\n    pitch = MathUtil.clamp(pitch, -.pi / 2, .pi / 2)\n\n    // Wrap yaw to be between 0 and 360\n    let remainder = yaw.truncatingRemainder(dividingBy: .pi * 2)\n    yaw = remainder < 0 ? .pi * 2 + remainder : remainder\n\n    rotation.yaw = yaw\n    rotation.pitch = pitch\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PlayerJumpSystem.swift",
    "content": "import Foundation\nimport FirebladeECS\n\npublic struct PlayerJumpSystem: System {\n  public func update(_ nexus: Nexus, _ world: World) {\n    var family = nexus.family(\n      requiresAll: EntityOnGround.self,\n      EntitySprinting.self,\n      EntityVelocity.self,\n      EntityPosition.self,\n      EntityRotation.self,\n      ClientPlayerEntity.self\n    ).makeIterator()\n\n    guard let (onGround, sprinting, velocity, position, rotation, _) = family.next() else {\n      log.error(\"PlayerJumpSystem failed to get player to tick\")\n      return\n    }\n\n    let inputState = nexus.single(InputState.self).component\n\n    guard onGround.onGround && inputState.inputs.contains(.jump) else {\n      return\n    }\n\n    let blockPosition = BlockPosition(\n      x: Int(position.x.rounded(.down)),\n      y: Int((position.y - 0.5).rounded(.down)),\n      z: Int(position.z.rounded(.down))\n    )\n    let block = world.getBlock(at: blockPosition)\n\n    let jumpPower = 0.42 * Double(block.physicalMaterial.jumpVelocityMultiplier)\n    velocity.y = jumpPower\n\n    // Add a bit of extra acceleration if the player is sprinting (this makes sprint jumping faster than sprinting)\n    if sprinting.isSprinting {\n      let yaw = Double(rotation.yaw)\n      velocity.x -= Foundation.sin(yaw) * 0.2\n      velocity.z += Foundation.cos(yaw) * 0.2\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PlayerPacketSystem.swift",
    "content": "import FirebladeECS\nimport FirebladeMath\n\n/// Sends update packets to the server depending on which client and player properties have changed.\n/// Mostly sends movement packets.\npublic struct PlayerPacketSystem: System {\n  var connection: ServerConnection\n\n  var state = State()\n\n  class State {\n    var previousHotbarSlot = -1\n    var wasSprinting = false\n    var wasSneaking = false\n    var wasFlying = false\n    var ticksUntilForcedPositionUpdate = 20\n    var lastPositionSent = Vec3d.zero\n  }\n\n  public init(_ connection: ServerConnection) {\n    self.connection = connection\n  }\n\n  public func update(_ nexus: Nexus, _ world: World) throws {\n    guard connection.hasJoined else {\n      return\n    }\n\n    var family = nexus.family(\n      requiresAll: PlayerInventory.self,\n      EntityId.self,\n      EntitySprinting.self,\n      EntitySneaking.self,\n      EntityPosition.self,\n      EntityRotation.self,\n      EntityOnGround.self,\n      EntityFlying.self,\n      ClientPlayerEntity.self\n    ).makeIterator()\n\n    guard let (inventory, entityId, sprinting, sneaking, position, rotation, onGround, flying, _) = family.next() else {\n      log.error(\"PlayerPacketSystem failed to get player to tick\")\n      return\n    }\n\n    // Send hotbar slot update\n    if inventory.selectedHotbarSlot != state.previousHotbarSlot {\n      try connection.sendPacket(HeldItemChangeServerboundPacket(slot: Int16(inventory.selectedHotbarSlot)))\n      state.previousHotbarSlot = inventory.selectedHotbarSlot\n    }\n\n    // Send sprinting update\n    let isSprinting = sprinting.isSprinting\n    if isSprinting != state.wasSprinting {\n      try connection.sendPacket(EntityActionPacket(\n        entityId: Int32(entityId.id),\n        action: isSprinting ? .startSprinting : .stopSprinting\n      ))\n      state.wasSprinting = isSprinting\n    }\n\n    // Send sneaking update\n    let isSneaking = sneaking.isSneaking\n    if isSneaking != state.wasSneaking {\n      try connection.sendPacket(EntityActionPacket(\n        entityId: Int32(entityId.id),\n        action: isSneaking ? .startSneaking : .stopSneaking\n      ))\n      state.wasSneaking = isSneaking\n    }\n\n    // Send position update if player has moved fast enough\n    let positionDelta = (position.vector - state.lastPositionSent).magnitudeSquared\n    state.ticksUntilForcedPositionUpdate -= 1\n    let mustSendPositionUpdate = positionDelta > 0.0009 || state.ticksUntilForcedPositionUpdate == 0\n    let hasRotated = rotation.previousPitch != rotation.pitch || rotation.previousYaw != rotation.yaw\n    var positionUpdateSent = true\n    if mustSendPositionUpdate && hasRotated {\n      try connection.sendPacket(PlayerPositionAndRotationServerboundPacket(\n        position: position.vector,\n        yaw: MathUtil.degrees(from: rotation.yaw),\n        pitch: MathUtil.degrees(from: rotation.pitch),\n        onGround: onGround.onGround\n      ))\n    } else if mustSendPositionUpdate {\n      try connection.sendPacket(PlayerPositionPacket(\n        position: position.vector,\n        onGround: onGround.onGround\n      ))\n    } else if hasRotated {\n      try connection.sendPacket(PlayerRotationPacket(\n        yaw: MathUtil.degrees(from: rotation.yaw),\n        pitch: MathUtil.degrees(from: rotation.pitch),\n        onGround: onGround.onGround\n      ))\n    } else if onGround.onGround != onGround.previousOnGround {\n      try connection.sendPacket(PlayerMovementPacket(onGround: onGround.onGround))\n    } else {\n      positionUpdateSent = false\n    }\n\n    if flying.isFlying != state.wasFlying {\n      state.wasFlying = flying.isFlying\n      // TODO: Ensure other player flags are preserved\n      try connection.sendPacket(PlayerAbilitiesServerboundPacket(flags: flying.isFlying ? [.flying] : []))\n    }\n\n    if positionUpdateSent {\n      state.ticksUntilForcedPositionUpdate = 20\n      state.lastPositionSent = position.vector\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PlayerPositionSystem.swift",
    "content": "import FirebladeECS\n\npublic struct PlayerPositionSystem: System {\n  public func update(_ nexus: Nexus, _ world: World) {\n    var family = nexus.family(\n      requiresAll: EntityPosition.self,\n      EntityVelocity.self,\n      EntityFlying.self,\n      ClientPlayerEntity.self\n    ).makeIterator()\n    \n    guard let (position, velocity, flying, _) = family.next() else {\n      log.error(\"PlayerPositionSystem failed to get player to tick\")\n      return\n    }\n\n    position.vector += velocity.vector\n    \n    position.x = MathUtil.clamp(position.x, -29_999_999, 29_999_999)\n    position.z = MathUtil.clamp(position.z, -29_999_999, 29_999_999)\n\n    if flying.isFlying {\n      velocity.y *= 0.6\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PlayerSmoothingSystem.swift",
    "content": "import FirebladeECS\n\npublic struct PlayerSmoothingSystem: System {\n  public func update(_ nexus: Nexus, _ world: World) {\n    var family = nexus.family(\n      requiresAll: EntityPosition.self,\n      EntityRotation.self,\n      EntityOnGround.self,\n      ClientPlayerEntity.self\n    ).makeIterator()\n    \n    guard let (position, rotation, onGround, _) = family.next() else {\n      log.error(\"PlayerSmoothingSystem failed to get player to tick\")\n      return\n    }\n    \n    position.save()\n    rotation.save()\n    onGround.save()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/PlayerVelocitySystem.swift",
    "content": "import FirebladeECS\n\npublic struct PlayerVelocitySystem: System {\n  public func update(_ nexus: Nexus, _ world: World) {\n    var family = nexus.family(\n      requiresAll: EntityPosition.self,\n      EntityVelocity.self,\n      EntityAcceleration.self,\n      EntityOnGround.self,\n      ClientPlayerEntity.self\n    ).makeIterator()\n\n    guard let (position, velocity, acceleration, onGround, _) = family.next() else {\n      log.error(\"PlayerVelocitySystem failed to get player to tick\")\n      return\n    }\n\n    if abs(velocity.x) < 0.003 {\n      velocity.x = 0\n    }\n    if abs(velocity.y) < 0.003 {\n      velocity.y = 0\n    }\n    if abs(velocity.z) < 0.003 {\n      velocity.z = 0\n    }\n\n    velocity.vector += acceleration.vector\n\n    if onGround.onGround {\n      let blockPosition = BlockPosition(\n        x: Int(position.x.rounded(.down)),\n        y: Int((position.y - 0.5).rounded(.down)),\n        z: Int(position.z.rounded(.down)))\n      let material = world.getBlock(at: blockPosition).physicalMaterial\n\n      velocity.x *= material.velocityMultiplier\n      velocity.z *= material.velocityMultiplier\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/ECS/Systems/System.swift",
    "content": "import FirebladeECS\n\npublic protocol System {\n  func update(_ nexus: Nexus, _ world: World) throws\n}\n"
  },
  {
    "path": "Sources/Core/Sources/FileSystem.swift",
    "content": "import Foundation\n\n/// An interface to the file system (a better version of `FileManager.default` from `Foundation`).\npublic enum FileSystem {\n  /// Gets whether a file or directory exists at the specified URL.\n  public static func itemExists(_ item: URL) -> Bool {\n    return FileManager.default.fileExists(atPath: item.path)\n  }\n\n  /// Gets whether a given directory exists.\n  public static func directoryExists(_ directory: URL) -> Bool {\n    var isDirectory = ObjCBool(false)\n    let itemExists = FileManager.default.fileExists(atPath: directory.path, isDirectory: &isDirectory)\n    return itemExists && isDirectory.boolValue\n  }\n\n  /// Gets whether a given file exists (`false` if the URL points to a directory).\n  public static func fileExists(_ file: URL) -> Bool {\n    var isDirectory = ObjCBool(false)\n    let itemExists = FileManager.default.fileExists(atPath: file.path, isDirectory: &isDirectory)\n    return itemExists && !isDirectory.boolValue\n  }\n\n  /// Gets the direct descendants of a directory.\n  public static func children(of directory: URL) throws -> [URL] {\n    return try FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil)\n  }\n\n  /// Deletes the given file or directory.\n  public static func remove(_ item: URL) throws {\n    try FileManager.default.removeItem(at: item)\n  }\n\n  /// Creates a directory (including any required intermediate directories).\n  public static func createDirectory(_ directory: URL) throws {\n    try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/BossBar.swift",
    "content": "import Foundation\n\npublic struct BossBar {\n  public var id: UUID\n  public var title: ChatComponent\n  /// The boss's health as a value from 0 to 1.\n  public var health: Float\n  public var color: Color\n  public var style: Style\n  public var flags: Flags\n\n  public enum Color: Int {\n    case pink = 0\n    case blue = 1\n    case red = 2\n    case green = 3\n    case yellow = 4\n    case purple = 5\n    case white = 6\n\n    /// The background and foreground sprites used when rendering a boss bar of\n    /// this color.\n    public var sprites: (background: GUISprite, foreground: GUISprite) {\n      switch self {\n        case .pink:\n          return (.pinkBossBarBackground, .pinkBossBarForeground)\n        case .blue:\n          return (.blueBossBarBackground, .blueBossBarForeground)\n        case .red:\n          return (.redBossBarBackground, .redBossBarForeground)\n        case .green:\n          return (.greenBossBarBackground, .greenBossBarForeground)\n        case .yellow:\n          return (.yellowBossBarBackground, .yellowBossBarForeground)\n        case .purple:\n          return (.purpleBossBarBackground, .purpleBossBarForeground)\n        case .white:\n          return (.whiteBossBarBackground, .whiteBossBarForeground)\n      }\n    }\n  }\n\n  public enum Style: Int {\n    case noNotches = 0\n    case sixNotches = 1\n    case tenNotches = 2\n    case twelveNotches = 3\n    case twentyNotches = 4\n\n    /// The bar overlay sprite used when rendering a boss bar of this style.\n    public var overlay: GUISprite {\n      switch self {\n        case .noNotches:\n          return .bossBarNoNotchOverlay\n        case .sixNotches:\n          return .bossBarSixNotchOverlay\n        case .tenNotches:\n          return .bossBarTenNotchOverlay\n        case .twelveNotches:\n          return .bossBarTwelveNotchOverlay\n        case .twentyNotches:\n          return .bossBarTwentyNotchOverlay\n      }\n    }\n  }\n\n  public struct Flags {\n    public var darkenSky: Bool\n    public var createFog: Bool\n    public var isEnderDragonHealthBar: Bool\n\n    public init(darkenSky: Bool, createFog: Bool, isEnderDragonHealthBar: Bool) {\n      self.darkenSky = darkenSky\n      self.createFog = createFog\n      self.isEnderDragonHealthBar = isEnderDragonHealthBar\n    }\n  }\n\n  public init(\n    id: UUID,\n    title: ChatComponent,\n    health: Float,\n    color: BossBar.Color,\n    style: BossBar.Style,\n    flags: BossBar.Flags\n  ) {\n    self.id = id\n    self.title = title\n    self.health = health\n    self.color = color\n    self.style = style\n    self.flags = flags\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/Constraints.swift",
    "content": "import FirebladeMath\n\npublic struct Constraints {\n  public var vertical: VerticalConstraint\n  public var horizontal: HorizontalConstraint\n\n  public init(_ vertical: VerticalConstraint, _ horizontal: HorizontalConstraint) {\n    self.vertical = vertical\n    self.horizontal = horizontal\n  }\n\n  public static let center = Constraints(.center, .center)\n\n  public static func position(_ x: Int, _ y: Int) -> Constraints {\n    return Constraints(.top(y), .left(x))\n  }\n\n  public func solve(innerSize: Vec2i, outerSize: Vec2i) -> Vec2i {\n    let x: Int\n    switch horizontal {\n      case .left(let distance):\n        x = distance\n      case .center(let offset):\n        x = (outerSize.x - innerSize.x) / 2 + (offset?.value ?? 0)\n      case .right(let distance):\n        x = outerSize.x - innerSize.x - distance\n    }\n\n    let y: Int\n    switch vertical {\n      case .top(let distance):\n        y = distance\n      case .center(let offset):\n        y = (outerSize.y - innerSize.y) / 2 + (offset?.value ?? 0)\n      case .bottom(let distance):\n        y = outerSize.y - innerSize.y - distance\n    }\n\n    return [x, y]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/GUIBuilder.swift",
    "content": "@resultBuilder\npublic struct GUIBuilder {\n  public static func buildBlock(_ elements: GUIElement...) -> GUIElement {\n    .list(spacing: 0, elements: elements)\n  }\n\n  public static func buildEither(first component: GUIElement) -> GUIElement {\n    component\n  }\n\n  public static func buildEither(second component: GUIElement) -> GUIElement {\n    component\n  }\n\n  public static func buildOptional(_ component: GUIElement?) -> GUIElement {\n    if let component = component {\n      return component\n    } else {\n      return .spacer(width: 0, height: 0)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/GUIElement.swift",
    "content": "// TODO: Update container related modifier methods to avoid unnecessary nesting where possible,\n//   e.g. `.expand().padding(2)` should only result in a single container being added instead of\n//   two levels of nested containers.\npublic indirect enum GUIElement {\n  public enum Direction {\n    case vertical\n    case horizontal\n  }\n\n  public struct DirectionSet: ExpressibleByArrayLiteral, Equatable {\n    public var horizontal: Bool\n    public var vertical: Bool\n\n    public static let both: Self = [.horizontal, .vertical]\n    public static let neither: Self = []\n    public static let horizontal: Self = [.horizontal]\n    public static let vertical: Self = [.vertical]\n\n    public init(arrayLiteral elements: Direction...) {\n      vertical = elements.contains(.vertical)\n      horizontal = elements.contains(.horizontal)\n    }\n  }\n\n  public enum Edge {\n    case top\n    case bottom\n    case left\n    case right\n  }\n\n  public struct EdgeSet: ExpressibleByArrayLiteral {\n    public var edges: Set<Edge>\n\n    public static let top: Self = [.top]\n    public static let bottom: Self = [.bottom]\n    public static let left: Self = [.left]\n    public static let right: Self = [.right]\n    public static let vertical: Self = [.top, .bottom]\n    public static let horizontal: Self = [.left, .right]\n    public static let all: Self = [.top, .bottom, .left, .right]\n\n    public init(arrayLiteral elements: Edge...) {\n      edges = Set(elements)\n    }\n\n    public func contains(_ edge: Edge) -> Bool {\n      edges.contains(edge)\n    }\n  }\n\n  public struct Padding {\n    public var top: Int\n    public var bottom: Int\n    public var left: Int\n    public var right: Int\n\n    /// The total padding along each axis.\n    public var axisTotals: Vec2i {\n      Vec2i(\n        left + right,\n        top + bottom\n      )\n    }\n\n    public static let zero: Self = Padding(top: 0, bottom: 0, left: 0, right: 0)\n\n    public init(top: Int, bottom: Int, left: Int, right: Int) {\n      self.top = top\n      self.bottom = bottom\n      self.left = left\n      self.right = right\n    }\n\n    public init(edges: EdgeSet, amount: Int) {\n      self.top = edges.contains(.top) ? amount : 0\n      self.bottom = edges.contains(.bottom) ? amount : 0\n      self.left = edges.contains(.left) ? amount : 0\n      self.right = edges.contains(.right) ? amount : 0\n    }\n  }\n\n  case text(_ content: String, wrap: Bool = false, color: Vec4f = Vec4f(1, 1, 1, 1))\n  case message(_ message: ChatComponent, wrap: Bool = true)\n  case interactable(_ element: GUIElement, handleInteraction: (Interaction) -> Bool)\n  case sprite(GUISprite)\n  case customSprite(GUISpriteDescriptor)\n  /// Stacks elements in the specified direction. Aligns elements to the top left by default.\n  case list(direction: Direction = .vertical, spacing: Int, elements: [GUIElement])\n  /// Stacks elements in the z direction. Non-positioned elements default to the top-left corner.\n  /// Elements appear on top of the elements that come before them.\n  case stack(elements: [GUIElement])\n  case positioned(element: GUIElement, constraints: Constraints)\n  case sized(element: GUIElement, width: Int?, height: Int?)\n  case spacer(width: Int, height: Int)\n  /// Wraps an element with a background.\n  case container(\n    element: GUIElement,\n    background: Vec4f?,\n    padding: Padding,\n    paddingColor: Vec4f?,\n    expandDirections: DirectionSet = .neither\n  )\n  case floating(element: GUIElement)\n  case item(id: Int)\n\n  public var children: [GUIElement] {\n    switch self {\n      case let .list(_, _, elements), let .stack(elements):\n        return elements\n      case let .interactable(element, _), let .positioned(element, _),\n          let .sized(element, _, _), let .container(element, _, _, _, _),\n          let .floating(element):\n        return [element]\n      case .text, .message, .sprite, .customSprite, .spacer, .item:\n        return []\n    }\n  }\n\n  public static let textWrapIndent: Int = 4\n  public static let lineSpacing: Int = 1\n\n  public static func list(\n    direction: Direction = .vertical,\n    spacing: Int,\n    @GUIBuilder elements: () -> GUIElement\n  ) -> GUIElement {\n    .list(direction: direction, spacing: spacing, elements: elements().children)\n  }\n\n  public static func forEach<S: Sequence>(\n    in values: S,\n    direction: Direction = .vertical,\n    spacing: Int,\n    @GUIBuilder element: (S.Element) -> GUIElement\n  ) -> GUIElement {\n    let elements = values.map(element)\n    return .list(direction: direction, spacing: spacing, elements: elements)\n  }\n\n  public static func stack(@GUIBuilder elements: () -> GUIElement) -> GUIElement {\n    .stack(elements: elements().children)\n  }\n\n  public func center() -> GUIElement {\n    .positioned(element: self, constraints: .center)\n  }\n\n  public func positionInParent(_ x: Int, _ y: Int) -> GUIElement {\n    .positioned(element: self, constraints: .position(x, y))\n  }\n\n  public func positionInParent(_ position: Vec2i) -> GUIElement {\n    .positioned(element: self, constraints: .position(position.x, position.y))\n  }\n\n  public func constraints(\n    _ verticalConstraint: VerticalConstraint,\n    _ horizontalConstraint: HorizontalConstraint\n  ) -> GUIElement {\n    .positioned(element: self, constraints: Constraints(verticalConstraint, horizontalConstraint))\n  }\n\n  public func constraints(_ constraints: Constraints) -> GUIElement {\n    .positioned(element: self, constraints: constraints)\n  }\n\n  /// `nil` indicates to use the natural width/height (the default).\n  public func size(_ width: Int?, _ height: Int?) -> GUIElement {\n    .sized(element: self, width: width, height: height)\n  }\n\n  public func size(_ size: Vec2i) -> GUIElement {\n    .sized(element: self, width: size.x, height: size.y)\n  }\n\n  public func padding(_ amount: Int) -> GUIElement {\n    self.padding(.all, amount)\n  }\n\n  public func padding(_ edges: EdgeSet, _ amount: Int) -> GUIElement {\n    .container(element: self, background: nil, padding: Padding(edges: edges, amount: amount), paddingColor: nil)\n  }\n\n  public func border(_ amount: Int, _ color: Vec4f) -> GUIElement {\n    self.border(.all, amount, color)\n  }\n\n  public func border(_ edges: EdgeSet, _ amount: Int, _ color: Vec4f) -> GUIElement {\n    .container(element: self, background: nil, padding: Padding(edges: edges, amount: amount), paddingColor: color)\n  }\n\n  public func background(_ color: Vec4f) -> GUIElement {\n    // Sometimes we can just update the element instead of adding another layer.\n    switch self {\n      case let .container(element, background, padding, paddingColor, expandDirections):\n        if background == nil {\n          return .container(\n            element: element,\n            background: color,\n            padding: padding,\n            paddingColor: paddingColor,\n            expandDirections: expandDirections\n          )\n        }\n      default:\n        break\n    }\n    return .container(element: self, background: color, padding: .zero, paddingColor: nil)\n  }\n\n  public func expand(_ directions: DirectionSet = .both) -> GUIElement {\n    if case let .container(element, background, padding, paddingColor, .neither) = self {\n      return .container(\n        element: element,\n        background: background,\n        padding: padding,\n        paddingColor: paddingColor,\n        expandDirections: directions\n      )\n    }\n    return .container(element: self, background: .zero, padding: .zero, paddingColor: nil, expandDirections: directions)\n  }\n\n  public func onClick(_ action: @escaping () -> Void) -> GUIElement {\n    onHoverKeyPress(matching: .leftMouseButton, action)\n  }\n\n  public func onRightClick(_ action: @escaping () -> Void) -> GUIElement {\n    onHoverKeyPress(matching: .rightMouseButton, action)\n  }\n\n  public func onHoverKeyPress(matching key: Key, _ action: @escaping () -> Void) -> GUIElement {\n    .interactable(\n      self,\n      handleInteraction: { interaction in\n        switch interaction {\n          case let .press(event):\n            if event.key == key {\n              action()\n              return true\n            } else {\n              return false\n            }\n          case .release:\n            return false\n        }\n      }\n    )\n  }\n\n  public func onHoverKeyPress(_ action: @escaping (KeyPressEvent) -> Bool) -> GUIElement {\n    .interactable(\n      self,\n      handleInteraction: { interaction in\n        switch interaction {\n          case let .press(event):\n            return action(event)\n          case .release:\n            return false\n        }\n      }\n    )\n  }\n\n  /// Sets an element's apparent size to zero so that it doesn't partake in layout.\n  /// It will still get put exactly where it otherwise would, but for example if the\n  /// element is in a list, all following elements will be positioned as if the element\n  /// doesn't exist (except double spacing where the element would've been).\n  ///\n  /// Any constraints placed on an element after it has been floated will act as if the\n  /// element is of zero size. This probably isn't the best and could be fixed without\n  /// too much effort (by making element rendering logic understand floating instead of\n  /// just pretending the size is zero).\n  public func float() -> GUIElement {\n    .floating(element: self)\n  }\n\n  public enum Interaction {\n    case press(KeyPressEvent)\n    case release(KeyReleaseEvent)\n  }\n\n  public struct GUIRenderable {\n    public var relativePosition: Vec2i\n    public var size: Vec2i\n    public var content: Content?\n    public var children: [GUIRenderable]\n\n    public enum Content {\n      case text(wrappedLines: [String], hangingIndent: Int, color: Vec4f)\n      case interactable(handleInteraction: (Interaction) -> Bool)\n      case sprite(GUISpriteDescriptor)\n      /// Fills the renderable with the given background color. Goes behind\n      /// any children that the renderable may have.\n      case background(Vec4f)\n      case item(id: Int)\n    }\n\n    // Returns true if the click was handled by the renderable or any of its children.\n    public func handleInteraction(_ interaction: Interaction, at position: Vec2i) -> Bool {\n      guard Self.isHit(position, inBoxAt: relativePosition, ofSize: size) else {\n        return false\n      }\n\n      switch content {\n        case let .interactable(handleInteraction):\n          if handleInteraction(interaction) {\n            return true\n          }\n        case .text, .sprite, .background, .item, nil:\n          break\n      }\n\n      let relativeClickPosition = position &- relativePosition\n      for renderable in children.reversed() {\n        if renderable.handleInteraction(interaction, at: relativeClickPosition) {\n          return true\n        }\n      }\n\n      return false\n    }\n\n    private static func isHit(\n      _ position: Vec2i,\n      inBoxAt upperLeft: Vec2i,\n      ofSize size: Vec2i\n    ) -> Bool {\n      return\n        position.x > upperLeft.x && position.x < (upperLeft.x + size.x)\n        && position.y > upperLeft.y && position.y < (upperLeft.y + size.y)\n    }\n  }\n\n  public func resolveConstraints(\n    availableSize: Vec2i,\n    font: Font,\n    locale: MinecraftLocale\n  ) -> GUIRenderable {\n    let relativePosition: Vec2i\n    let size: Vec2i\n    let content: GUIRenderable.Content?\n    let children: [GUIRenderable]\n    switch self {\n      case let .text(text, wrap, color):\n        // Wrap the lines, but if wrapping is disabled wrap to a width of Int.max (so that we can\n        // still compute the width of the line).\n        let lines = Self.wrap(\n          text,\n          maximumWidth: wrap ? availableSize.x : .max,\n          indent: Self.textWrapIndent,\n          font: font\n        )\n        relativePosition = .zero\n        size = Vec2i(\n          lines.map(\\.width).max() ?? 0,\n          lines.count * Font.defaultCharacterHeight + (lines.count - 1) * Self.lineSpacing\n        )\n        content = .text(\n          wrappedLines: lines.map(\\.line),\n          hangingIndent: Self.textWrapIndent,\n          color: color\n        )\n        children = []\n      case let .message(message, wrap):\n        let text = message.toText(with: locale)\n        return GUIElement.text(text, wrap: wrap)\n          .resolveConstraints(availableSize: availableSize, font: font, locale: locale)\n      case let .interactable(label, handleInteraction):\n        let child = label.resolveConstraints(\n          availableSize: availableSize,\n          font: font,\n          locale: locale\n        )\n        relativePosition = .zero\n        size = child.size\n        content = .interactable(handleInteraction: handleInteraction)\n        children = [child]\n      case let .sprite(sprite):\n        let descriptor = sprite.descriptor\n        relativePosition = .zero\n        size = descriptor.size\n        content = .sprite(descriptor)\n        children = []\n      case let .customSprite(descriptor):\n        relativePosition = .zero\n        size = descriptor.size\n        content = .sprite(descriptor)\n        children = []\n      case let .list(direction, spacing, elements):\n        var availableSize = availableSize\n        var childPosition = Vec2i(0, 0)\n        let axisComponent = direction == .vertical ? 1 : 0\n\n        children = elements.map { element in\n          var renderable = element.resolveConstraints(\n            availableSize: availableSize,\n            font: font,\n            locale: locale\n          )\n          renderable.relativePosition[axisComponent] += childPosition[axisComponent]\n\n          let rowSize = renderable.size[axisComponent] + spacing\n          childPosition[axisComponent] += rowSize\n          availableSize[axisComponent] -= rowSize\n\n          return renderable\n        }\n\n        relativePosition = .zero\n\n        let lengthAlongAxis = elements.isEmpty ? 0 : childPosition[axisComponent] - spacing\n        switch direction {\n          case .vertical:\n            let width = children.map(\\.size.x).max() ?? 0\n            size = Vec2i(width, lengthAlongAxis)\n          case .horizontal:\n            let height = children.map(\\.size.y).max() ?? 0\n            size = Vec2i(lengthAlongAxis, height)\n        }\n\n        content = nil\n      case let .stack(elements):\n        children = elements.map { element in\n          element.resolveConstraints(\n            availableSize: availableSize,\n            font: font,\n            locale: locale\n          )\n        }\n        size = Vec2i(\n          children.map { renderable in\n            renderable.size.x + renderable.relativePosition.x\n          }.max() ?? 0,\n          children.map { renderable in\n            renderable.size.y + renderable.relativePosition.y\n          }.max() ?? 0\n        )\n        relativePosition = .zero\n        content = nil\n      case let .positioned(element, constraints):\n        let child = element.resolveConstraints(\n          availableSize: availableSize,\n          font: font,\n          locale: locale\n        )\n        children = [child]\n        relativePosition = constraints.solve(\n          innerSize: child.size,\n          outerSize: availableSize\n        )\n        size = child.size\n        content = nil\n      case let .sized(element, width, height):\n        let child = element.resolveConstraints(\n          availableSize: Vec2i(\n            width ?? availableSize.x,\n            height ?? availableSize.y\n          ),\n          font: font,\n          locale: locale\n        )\n        children = [child]\n        relativePosition = .zero\n        size = Vec2i(\n          width ?? child.size.x,\n          height ?? child.size.y\n        )\n        content = nil\n      case let .spacer(width, height):\n        children = []\n        relativePosition = .zero\n        size = Vec2i(width, height)\n        content = nil\n      case let .container(element, background, padding, paddingColor, expandDirections):\n        let paddingAxisTotals = padding.axisTotals\n        var child = element.resolveConstraints(\n          availableSize: availableSize &- paddingAxisTotals,\n          font: font,\n          locale: locale\n        )\n        child.relativePosition &+= Vec2i(padding.left, padding.top)\n        relativePosition = .zero\n        size = Vec2i(\n          expandDirections.horizontal\n            ? availableSize.x\n            : min(availableSize.x, child.size.x + paddingAxisTotals.x),\n          expandDirections.vertical\n            ? availableSize.y\n            : min(availableSize.y, child.size.y + paddingAxisTotals.y)\n        )\n\n        let expandedChildSize = size &- paddingAxisTotals\n\n        if let paddingColor = paddingColor {\n          // Something feels kinda dark about this variable name...\n          var borderChildren: [GUIRenderable] = []\n\n          // https://open.spotify.com/track/5hM5arv9KDbCHS0k9uqwjr?si=58df9b2231e848b8\n          func addBorderLine(position: Vec2i, size: Vec2i) {\n            borderChildren.append(GUIRenderable(\n              relativePosition: position,\n              size: size,\n              content: .background(paddingColor),\n              children: []\n            ))\n          }\n\n          if padding.left != 0 {\n            addBorderLine(\n              position: [0, 0],\n              size: [padding.left, expandedChildSize.y + paddingAxisTotals.y]\n            )\n          }\n          if padding.right != 0 {\n            addBorderLine(\n              position: [padding.left + expandedChildSize.x, 0],\n              size: [padding.right, expandedChildSize.y + paddingAxisTotals.y]\n            )\n          }\n          if padding.top != 0 {\n            addBorderLine(\n              position: [padding.left, 0],\n              size: [expandedChildSize.x, padding.top]\n            )\n          }\n          if padding.bottom != 0 {\n            addBorderLine(\n              position: [padding.left, padding.top + expandedChildSize.y],\n              size: [expandedChildSize.x, padding.bottom]\n            )\n          }\n\n          let backgroundChild: GUIRenderable?\n          if let background = background {\n            backgroundChild = GUIRenderable(\n              relativePosition: Vec2i(padding.left, padding.top),\n              size: child.size,\n              content: .background(background),\n              children: []\n            )\n          } else {\n            backgroundChild = nil\n          }\n\n          children = borderChildren + [\n            backgroundChild,\n            child\n          ].compactMap(identity)\n          content = nil\n        } else {\n          children = [child]\n          content = background.map(GUIRenderable.Content.background)\n        }\n      case let .floating(element):\n        let child = element.resolveConstraints(\n          availableSize: availableSize,\n          font: font,\n          locale: locale\n        )\n        children = [child]\n        relativePosition = .zero\n        size = .zero\n        content = nil\n      case let .item(id):\n        children = []\n        relativePosition = .zero\n        size = Vec2i(16, 16)\n        content = .item(id: id)\n    }\n\n    return GUIRenderable(\n      relativePosition: relativePosition,\n      size: size,\n      content: content,\n      children: children\n    )\n  }\n\n  /// `indent` must be less than `maximumWidth` and `maximumWidth` must greater than the width of\n  /// each individual character in the string.\n  static func wrap(_ text: String, maximumWidth: Int, indent: Int, font: Font) -> [(line: String, width: Int)] {\n    assert(indent < maximumWidth, \"indent must be smaller than maximumWidth\")\n\n    if text == \"\" {\n      return [(line: \"\", width: 0)]\n    }\n\n    var wrapIndex: String.Index? = nil\n    var latestSpace: String.Index? = nil\n    var width = 0\n    for i in text.indices {\n      let character = text[i]\n      guard let descriptor = Self.descriptor(for: character, from: font) else {\n        continue\n      }\n\n      assert(\n        descriptor.renderedWidth < maximumWidth,\n        \"maximumWidth must be greater than every individual character in the string\"\n      )\n\n      // Compute the width with the current character included\n      var nextWidth = width + descriptor.renderedWidth\n      if i != text.startIndex {\n        nextWidth += 1 // character spacing\n      }\n\n      // TODO: wrap on other characters such as '-' as well\n      if character == \" \" {\n        latestSpace = i\n      }\n\n      // Break before the current character if it'd bring the text over the maximum width.\n      // If it's the first character, never wrap because otherwise we enter an infinite loop.\n      if nextWidth > maximumWidth && i != text.startIndex {\n        if let spaceIndex = latestSpace {\n          wrapIndex = spaceIndex\n        } else {\n          wrapIndex = i\n        }\n        break\n      } else {\n        width = nextWidth\n      }\n    }\n\n    var lines: [(line: String, width: Int)] = []\n    if let wrapIndex = wrapIndex {\n      lines = [\n        (line: String(text[text.startIndex..<wrapIndex]), width: width)\n      ]\n\n      var startIndex = wrapIndex\n      while text[startIndex] == \" \" {\n        startIndex = text.index(after: startIndex)\n        if startIndex == text.endIndex {\n          return lines // NOTE: early return\n        }\n      }\n      let nonWrappedText = text[startIndex...]\n      lines.append(contentsOf: wrap(\n        String(nonWrappedText),\n        maximumWidth: maximumWidth - indent,\n        indent: 0,\n        font: font\n      ))\n    } else {\n      lines = [(line: text, width: width)]\n    }\n\n    return lines\n  }\n\n  static func descriptor(for character: Character, from font: Font) -> CharacterDescriptor? {\n    if let descriptor = font.descriptor(for: character) {\n      return descriptor\n    } else if let descriptor = font.descriptor(for: \"�\") {\n      return descriptor\n    } else {\n      log.warning(\"Failed to replace invalid character '\\(character)' with placeholder '�'.\")\n      return nil\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/GUISprite.swift",
    "content": "/// A sprite in the GUI texture palette.\npublic enum GUISprite {\n  case heartOutline\n  case fullHeart\n  case halfHeart\n  case foodOutline\n  case fullFood\n  case halfFood\n  case armorOutline\n  case fullArmor\n  case halfArmor\n  case crossHair\n  case hotbar\n  case selectedHotbarSlot\n  case xpBarBackground\n  case xpBarForeground\n  case inventory\n  case craftingTable\n  case furnace\n  case blastFurnace\n  case smoker\n  case anvil\n  case dispenser\n  case beacon\n\n  /// If positioned directly above ``GUISprite/genericInventory`` it forms\n  /// the background for a single chest window. The way the texture is made forces\n  /// these to be separate sprites.\n  case genericInventory // Inventory for most interfaces, its a part of the sprite\n  case generic9x1\n  case generic9x2\n  case generic9x3\n  case generic9x4\n  case generic9x5\n  case generic9x6\n\n  case pinkBossBarBackground\n  case pinkBossBarForeground\n  case blueBossBarBackground\n  case blueBossBarForeground\n  case redBossBarBackground\n  case redBossBarForeground\n  case greenBossBarBackground\n  case greenBossBarForeground\n  case yellowBossBarBackground\n  case yellowBossBarForeground\n  case purpleBossBarBackground\n  case purpleBossBarForeground\n  case whiteBossBarBackground\n  case whiteBossBarForeground\n\n  case bossBarNoNotchOverlay\n  case bossBarSixNotchOverlay\n  case bossBarTenNotchOverlay\n  case bossBarTwelveNotchOverlay\n  case bossBarTwentyNotchOverlay\n\n  /// The sprite for a connection strength in the range `0...5`.\n  case playerConnectionStrength(PlayerInfo.ConnectionStrength)\n\n  /// The descriptor for the sprite.\n  public var descriptor: GUISpriteDescriptor {\n    switch self {\n      case .heartOutline:\n        return .icon(0, 0)\n      case .fullHeart:\n        return .icon(4, 0)\n      case .halfHeart:\n        return .icon(5, 0)\n      case .foodOutline:\n        return .icon(0, 3)\n      case .fullFood:\n        return .icon(4, 3)\n      case .halfFood:\n        return .icon(5, 3)\n      case .armorOutline:\n        return .icon(0, 1)\n      case .fullArmor:\n        return .icon(2, 1)\n      case .halfArmor:\n        return .icon(1, 1)\n      case .crossHair:\n        return GUISpriteDescriptor(slice: .icons, position: [3, 3], size: [9, 9])\n      case .hotbar:\n        return GUISpriteDescriptor(slice: .widgets, position: [0, 0], size: [182, 22])\n      case .selectedHotbarSlot:\n        return GUISpriteDescriptor(slice: .widgets, position: [0, 22], size: [24, 24])\n      case .xpBarBackground:\n        return GUISpriteDescriptor(slice: .icons, position: [0, 64], size: [182, 5])\n      case .xpBarForeground:\n        return GUISpriteDescriptor(slice: .icons, position: [0, 69], size: [182, 5])\n      case .inventory:\n        return GUISpriteDescriptor(slice: .inventory, position: [0, 0], size: [176, 166])\n      case .craftingTable:\n        return GUISpriteDescriptor(slice: .craftingTable, position: [0, 0], size: [176, 166])\n      case .furnace:\n        return GUISpriteDescriptor(slice: .furnace, position: [0, 0], size: [176, 166])\n      case .blastFurnace:\n        return GUISpriteDescriptor(slice: .blastFurnace, position: [0, 0], size: [176, 166])\n      case .smoker:\n        return GUISpriteDescriptor(slice: .smoker, position: [0, 0], size: [176, 166])\n      case .anvil:\n        return GUISpriteDescriptor(slice: .anvil, position: [0, 0], size: [176, 166])\n      case .dispenser:\n        return GUISpriteDescriptor(slice: .dispenser, position: [0, 0],  size: [176, 166])\n      case .beacon:\n        return GUISpriteDescriptor(slice: .beacon, position: [0,0], size: [229, 218])\n      case .genericInventory:\n        return GUISpriteDescriptor(slice: .genericContainer, position: [0, 125], size: [176, 97])\n      case .generic9x1:\n        return GUISpriteDescriptor(slice: .genericContainer, position: [0, 0], size: [176, 35])\n      case .generic9x2:\n        return GUISpriteDescriptor(slice: .genericContainer, position: [0, 0], size: [176, 53])\n      case .generic9x3:\n        return GUISpriteDescriptor(slice: .genericContainer, position: [0, 0], size: [176, 71])\n      case .generic9x4:\n        return GUISpriteDescriptor(slice: .genericContainer, position: [0, 0], size: [176, 89])\n      case .generic9x5:\n        return GUISpriteDescriptor(slice: .genericContainer, position: [0, 0], size: [176, 107])\n      case .generic9x6:\n        return GUISpriteDescriptor(slice: .genericContainer, position: [0, 0], size: [176, 222])\n      case .pinkBossBarBackground:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 0], size: [182, 5])\n      case .pinkBossBarForeground:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 5], size: [182, 5])\n      case .blueBossBarBackground:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 10], size: [182, 5])\n      case .blueBossBarForeground:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 15], size: [182, 5])\n      case .redBossBarBackground:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 20], size: [182, 5])\n      case .redBossBarForeground:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 25], size: [182, 5])\n      case .greenBossBarBackground:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 30], size: [182, 5])\n      case .greenBossBarForeground:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 35], size: [182, 5])\n      case .yellowBossBarBackground:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 40], size: [182, 5])\n      case .yellowBossBarForeground:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 45], size: [182, 5])\n      case .purpleBossBarBackground:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 50], size: [182, 5])\n      case .purpleBossBarForeground:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 55], size: [182, 5])\n      case .whiteBossBarBackground:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 60], size: [182, 5])\n      case .whiteBossBarForeground:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 65], size: [182, 5])\n      case .bossBarNoNotchOverlay:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 70], size: [182, 5])\n      case .bossBarSixNotchOverlay:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 80], size: [182, 5])\n      case .bossBarTenNotchOverlay:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 90], size: [182, 5])\n      case .bossBarTwelveNotchOverlay:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 100], size: [182, 5])\n      case .bossBarTwentyNotchOverlay:\n        return GUISpriteDescriptor(slice: .bars, position: [0, 110], size: [182, 5])\n      case let .playerConnectionStrength(strength):\n        let y = 16 + (5 - strength.rawValue) * 8\n        return GUISpriteDescriptor(slice: .icons, position: [0, y], size: [10, 7])\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/GUISpriteDescriptor.swift",
    "content": "import FirebladeMath\n\n/// Describes how to render a specific sprite from a ``GUITexturePalette``.\npublic struct GUISpriteDescriptor {\n  /// The slice containing the sprite.\n  public var slice: GUITextureSlice\n  /// The position of the sprite in the texture. Origin is at the top left.\n  public var position: Vec2i\n  /// The size of the sprite.\n  public var size: Vec2i\n\n  /// Creates the descriptor for the specified icon. Icons start 16 pixels from the left of the\n  /// texture and are arranged as a grid of 9x9 icons.\n  /// - Parameters:\n  ///   - xIndex: The horizontal index of the sprite.\n  ///   - yIndex: The vertical index of the sprite.\n  /// - Returns: A sprite descriptor for the icon.\n  public static func icon(_ xIndex: Int, _ yIndex: Int) -> GUISpriteDescriptor {\n    return GUISpriteDescriptor(\n      slice: .icons,\n      position: [xIndex * 9 + 16, yIndex * 9],\n      size: [9, 9]\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/HorizontalConstraint.swift",
    "content": "public enum HorizontalConstraint {\n  case left(Int)\n  case center(HorizontalOffset?)\n  case right(Int)\n\n  public static let center = Self.center(nil)\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/HorizontalOffset.swift",
    "content": "public enum HorizontalOffset {\n  case left(Int)\n  case right(Int)\n\n  /// The offset value, negative for up, positive for down.\n  public var value: Int {\n    switch self {\n      case .left(let offset):\n        return -offset\n      case .right(let offset):\n        return offset\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/InGameGUI.swift",
    "content": "import Collections\nimport CoreFoundation\nimport SwiftCPUDetect\n\n/// Never acquires nexus locks.\npublic class InGameGUI {\n  // TODO: Figure out why anything greater than 252 breaks the protocol. Anything less than 256 should work afaict\n  public static let maximumMessageLength = 252\n\n  /// The number of seconds until messages should be hidden from the regular GUI.\n  static let messageHideDelay: Double = 10\n  /// The maximum number of messages displayed in the regular GUI.\n  static let maximumDisplayedMessages = 10\n  /// The width of the chat history.\n  static let chatHistoryWidth = 330\n\n  #if os(macOS)\n    /// The system's CPU display name.\n    static let cpuName = HWInfo.CPU.name()\n    /// The system's CPU architecture.\n    static let cpuArch = CpuArchitecture.current()?.rawValue\n    /// The system's total memory.\n    static let totalMem = (HWInfo.ramAmount() ?? 0) / (1024 * 1024 * 1024)\n    /// A string containing information about the system's default GPU.\n    static let gpuInfo = GPUDetection.mainMetalGPU()?.infoString()\n  #endif\n\n  static let xpLevelTextColor = Vec4f(126, 252, 31, 255) / 255\n  static let debugScreenRowBackgroundColor = Vec4f(80, 80, 80, 144) / 255\n\n  public init() {}\n\n  /// Gets the GUI's content. Doesn't acquire any locks.\n  public func content(\n    game: Game,\n    connection: ServerConnection?,\n    state: GUIStateStorage\n  ) -> GUIElement {\n    let (gamemode, inventory, perspective) = game.accessPlayer(acquireLock: false) { player in\n      (player.gamemode.gamemode, player.inventory, player.camera.perspective)\n    }\n\n    let inputState = game.accessInputState(acquireLock: false, action: identity)\n\n    if state.showHUD {\n      return GUIElement.stack {\n        if gamemode != .spectator {\n          GUIElement.stack {\n            hotbarArea(game: game, gamemode: gamemode)\n\n            if perspective == .firstPerson {\n              GUIElement.sprite(.crossHair)\n                .center()\n            }\n          }\n        }\n\n        GUIElement.forEach(in: state.bossBars, spacing: 3) { bossBar in\n          self.bossBar(bossBar)\n        }\n        .constraints(.top(2), .center)\n\n        if state.movementAllowed && inputState.keys.contains(.tab) {\n          tabList(game.tabList)\n            .constraints(.top(8), .center)\n        }\n\n        if state.showDebugScreen {\n          debugScreen(game: game, state: state)\n        }\n\n        chat(state: state)\n\n        if state.showInventory {\n          window(\n            window: inventory.window,\n            game: game,\n            connection: connection,\n            state: state\n          )\n        } else if let window = state.window {\n          self.window(\n            window: window,\n            game: game,\n            connection: connection,\n            state: state\n          )\n        }\n      }\n    } else {\n      return GUIElement.spacer(width: 0, height: 0)\n    }\n  }\n\n  public func bossBar(_ bossBar: BossBar) -> GUIElement {\n    let (background, foreground) = bossBar.color.sprites\n    return GUIElement.list(spacing: 1) {\n      GUIElement.message(bossBar.title, wrap: false)\n        .constraints(.top(0), .center)\n\n      GUIElement.stack {\n        continuousMeter(bossBar.health, background: background, foreground: foreground)\n        // TODO: Render both the background and foreground overlays separately (instead of assuming\n        //   that they're both the same like they are in the vanilla resource pack)\n        GUIElement.sprite(bossBar.style.overlay)\n      }\n    }\n    .size(GUISprite.xpBarBackground.descriptor.size.x, nil)\n  }\n\n  public func tabList(_ tabList: TabList) -> GUIElement {\n    // TODO: Resolve chat component content when building ui instead of when resolving it\n    //   (just too tricky to do stuff without knowing the chat component's content)\n    // TODO: Handle teams (changes sorting I think)\n    // TODO: Spectator players should go first, then sort by display name (with no-display-name players\n    //   coming first?) then sort by player name\n    let sortedPlayers = tabList.players.values.sorted { left, right in\n      // TODO: Sort by the name that's gonna be displayed (displayName ?? name),\n      //   requires the above TODO to be resolved first\n      left.name < right.name\n    }\n\n    let borderColor = Vec4f(0, 0, 0, 0.8)\n\n    // TODO: Render borders between rows (harder than it sounds lol, will require more advanced layout\n    //   controls in GUIElement).\n    return GUIElement.list(direction: .horizontal, spacing: 0) {\n      GUIElement.forEach(in: sortedPlayers, spacing: 0) { player in\n        // TODO: Add text shadow when that's supported for chat components\n        // TODO: Render spectator mode player names italic\n        GUIElement.list(direction: .horizontal, spacing: 2) {\n          if let displayName = player.displayName {\n            GUIElement.message(displayName)\n          } else {\n            textWithShadow(player.name)\n          }\n        }\n      }\n      .padding(.bottom, -1)\n      .background(Vec4f(0, 0, 0, 0.5))\n\n      GUIElement.forEach(in: sortedPlayers, spacing: 1) { player in\n        GUIElement.sprite(.playerConnectionStrength(player.connectionStrength))\n          .padding([.top, .right], 1)\n          .padding(.left, 2)\n      }\n      .background(Vec4f(0, 0, 0, 0.5))\n    }\n    .border([.top, .left, .bottom], 1, borderColor)\n  }\n\n  public func chat(state: GUIStateStorage) -> GUIElement {\n    // TODO: Implement scrollable chat history.\n\n    // Limit number of messages shown.\n    let index = max(\n      state.chat.messages.startIndex,\n      state.chat.messages.endIndex - Self.maximumDisplayedMessages\n    )\n    let latestMessages = state.chat.messages[index..<state.chat.messages.endIndex]\n\n    // If editing messages, only show messages sent/received within the last\n    // `Self.messageHideDelay` seconds.\n    let threshold = CFAbsoluteTimeGetCurrent() - Self.messageHideDelay\n    var visibleMessages: Deque<ChatMessage>.SubSequence\n    if state.showChat {\n      visibleMessages = latestMessages\n    } else {\n      let lastVisibleIndex =\n        latestMessages.lastIndex { message in\n          message.timeReceived < threshold\n        }?.advanced(by: 1) ?? latestMessages.startIndex\n      visibleMessages = latestMessages[lastVisibleIndex..<latestMessages.endIndex]\n    }\n\n    return GUIElement.stack {\n      if !visibleMessages.isEmpty {\n        GUIElement.forEach(in: visibleMessages, spacing: 1) { message in\n          // TODO: Chat message text shadows\n          GUIElement.message(message.content, wrap: true)\n        }\n        .constraints(.top(0), .left(1))\n        .padding(1)\n        .size(Self.chatHistoryWidth, nil)\n        .background(Vec4f(0, 0, 0, 0.5))\n        .constraints(.bottom(40), .left(0))\n      }\n\n      if let messageInput = state.messageInput {\n        textField(content: messageInput, cursorIndex: state.messageInputCursorIndex)\n          .padding(2)\n          .constraints(.bottom(0), .left(0))\n      }\n    }\n  }\n\n  public func textField(content: String, cursorIndex: String.Index) -> GUIElement {\n    let messageBeforeCursor = String(content.prefix(upTo: cursorIndex))\n    let messageAfterCursor = String(content.suffix(from: cursorIndex))\n\n    return GUIElement.list(direction: .horizontal, spacing: 0) {\n      textWithShadow(messageBeforeCursor)\n\n      if Int(CFAbsoluteTimeGetCurrent() * 10 / 3) % 2 == 1 {\n        if messageAfterCursor.isEmpty {\n          textWithShadow(\"_\")\n            .positionInParent(messageBeforeCursor.isEmpty ? 0 : 1, 0)\n        } else {\n          GUIElement.spacer(width: 1, height: 11)\n            .background(Vec4f(1, 1, 1, 1))\n            .positionInParent(0, -1)\n            .float()\n        }\n      }\n\n      textWithShadow(messageAfterCursor)\n    }\n    .size(nil, Font.defaultCharacterHeight + 1)\n    .expand(.horizontal)\n    .padding([.top, .left, .right], 2)\n    .padding(.bottom, 1)\n    .background(Vec4f(0, 0, 0, 0.5))\n  }\n\n  /// Gets the contents of the hotbar (and nearby stats if in a gamemode with health).\n  /// Doesn't acquire a nexus lock.\n  public func hotbarArea(game: Game, gamemode: Gamemode) -> GUIElement {\n    let (health, food, selectedSlot, xpBarProgress, xpLevel, hotbarSlots) =\n      game.accessPlayer(acquireLock: false) { player in\n        (\n          player.health.health,\n          player.nutrition.food,\n          player.inventory.selectedHotbarSlot,\n          player.experience.experienceBarProgress,\n          player.experience.experienceLevel,\n          player.inventory.hotbar\n        )\n      }\n\n    return GUIElement.list(spacing: 0) {\n      if gamemode.hasHealth {\n        stats(health: health, food: food, xpBarProgress: xpBarProgress, xpLevel: xpLevel)\n      }\n\n      hotbar(slots: hotbarSlots, selectedSlot: selectedSlot)\n    }\n    .size(GUISprite.hotbar.descriptor.size.x + 2, nil)\n    .constraints(.bottom(-1), .center)\n  }\n\n  public func hotbar(slots: [Slot], selectedSlot: Int) -> GUIElement {\n    GUIElement.stack {\n      GUIElement.sprite(.hotbar)\n        .padding(1)\n      GUIElement.sprite(.selectedHotbarSlot)\n        .positionInParent(selectedSlot * 20, 0)\n\n      GUIElement.forEach(in: slots, direction: .horizontal, spacing: 4) { slot in\n        inventorySlot(slot)\n      }\n      .positionInParent(4, 4)\n    }\n  }\n\n  public func window(\n    window: Window,\n    game: Game,\n    connection: ServerConnection?,\n    state: GUIStateStorage\n  ) -> GUIElement {\n    let mousePosition = game.accessInputState(acquireLock: false) { inputState in\n      Vec2i(inputState.mousePosition / state.drawableScalingFactor)\n    }\n\n    return GUIElement.stack {\n      GUIElement.spacer(width: 0, height: 0)\n        .expand()\n        .background(Vec4f(0, 0, 0, 0.702))\n        .onClick {\n          window.dropStackFromMouse(&state.mouseItemStack, connection: connection)\n        }\n        .onRightClick {\n          window.dropItemFromMouse(&state.mouseItemStack, connection: connection)\n        }\n\n      GUIElement.stack {\n        // Has a dummy click handler to prevent clicks within the inventory from propagating to the background\n        window.type.background.onHoverKeyPress { event in\n          return event.key == .leftMouseButton || event.key == .rightMouseButton\n        }\n\n        GUIElement.stack(\n          elements: window.type.areas.map { area in\n            windowArea(\n              area,\n              window,\n              game: game,\n              connection: connection,\n              state: state\n            )\n          }\n        )\n      }\n      .center()\n\n      if let mouseItemStack = state.mouseItemStack {\n        inventorySlot(Slot(mouseItemStack))\n          .positionInParent(mousePosition &- Vec2i(8, 8))\n      }\n    }\n  }\n\n  public func windowArea(\n    _ area: WindowArea,\n    _ window: Window,\n    game: Game,\n    connection: ServerConnection?,\n    state: GUIStateStorage\n  ) -> GUIElement {\n    GUIElement.forEach(in: 0..<area.height, spacing: 2) { y in\n      GUIElement.forEach(in: 0..<area.width, direction: .horizontal, spacing: 2) { x in\n        let index = area.startIndex + y * area.width + x\n        inventorySlot(window.slots[index])\n          .onClick {\n            window.leftClick(index, mouseStack: &state.mouseItemStack, connection: connection)\n          }\n          .onRightClick {\n            window.rightClick(index, mouseStack: &state.mouseItemStack, connection: connection)\n          }\n          .onHoverKeyPress { event in\n            let inputState = game.accessInputState(acquireLock: false, action: identity)\n            return window.pressKey(\n              over: index,\n              event: event,\n              mouseStack: &state.mouseItemStack,\n              inputState: inputState,\n              connection: connection\n            )\n          }\n      }\n    }\n    .positionInParent(area.position)\n  }\n\n  public func inventorySlot(_ slot: Slot) -> GUIElement {\n    // TODO: Make if blocks layout transparent (their children should be treated as children of the parent block)\n    if let stack = slot.stack {\n      return GUIElement.stack {\n        GUIElement.item(id: stack.itemId)\n\n        if stack.count != 1 {\n          textWithShadow(\"\\(stack.count)\")\n            .constraints(.bottom(-2), .right(-1))\n            .float()\n        }\n      }\n      .size(16, 16)\n    } else {\n      return GUIElement.spacer(width: 16, height: 16)\n    }\n  }\n\n  public func textWithShadow(\n    _ text: String,\n    textColor: Vec4f = Vec4f(1, 1, 1, 1),\n    shadowColor: Vec4f = Vec4f(62, 62, 62, 255) / 255\n  ) -> GUIElement {\n    if !text.isEmpty {\n      return GUIElement.stack {\n        GUIElement.text(text, color: shadowColor)\n          .positionInParent(1, 1)\n        GUIElement.text(text, color: textColor)\n      }\n    } else {\n      return GUIElement.spacer(width: 0, height: 0)\n    }\n  }\n\n  public enum ReadingDirection {\n    case leftToRight\n    case rightToLeft\n  }\n\n  public func stats(\n    health: Float,\n    food: Int,\n    xpBarProgress: Float,\n    xpLevel: Int\n  ) -> GUIElement {\n    GUIElement.list(spacing: 0) {\n      GUIElement.stack {\n        discreteMeter(\n          Int(health.rounded()),\n          fullIcon: .fullHeart,\n          halfIcon: .halfHeart,\n          outlineIcon: .heartOutline\n        )\n\n        discreteMeter(\n          food,\n          fullIcon: .fullFood,\n          halfIcon: .halfFood,\n          outlineIcon: .foodOutline,\n          direction: .rightToLeft\n        )\n        .constraints(.top(0), .right(0))\n      }\n      .size(GUISprite.hotbar.descriptor.size.x, nil)\n      .constraints(.top(0), .center)\n\n      GUIElement.stack {\n        continuousMeter(\n          xpBarProgress,\n          background: .xpBarBackground,\n          foreground: .xpBarForeground\n        )\n        .constraints(.top(0), .center)\n\n        outlinedText(\"\\(xpLevel)\", textColor: Self.xpLevelTextColor)\n          .constraints(.top(-7), .center)\n      }\n      .padding(1)\n      .constraints(.top(0), .center)\n    }\n  }\n\n  /// Gets the contents of the debug screen, doesn't acquire a nexus lock.\n  public func debugScreen(game: Game, state: GUIStateStorage) -> GUIElement {\n    var blockPosition = BlockPosition(x: 0, y: 0, z: 0)\n    var chunkSectionPosition = ChunkSectionPosition(sectionX: 0, sectionY: 0, sectionZ: 0)\n    var position: Vec3d = .zero\n    var pitch: Float = 0\n    var yaw: Float = 0\n    var heading: Direction = .north\n    var gamemode: Gamemode = .adventure\n    game.accessPlayer(acquireLock: false) { player in\n      position = player.position.vector\n      blockPosition = player.position.blockUnderneath\n      chunkSectionPosition = player.position.chunkSection\n      pitch = MathUtil.degrees(from: player.rotation.pitch)\n      yaw = MathUtil.degrees(from: player.rotation.yaw)\n      heading = player.rotation.heading\n      gamemode = player.gamemode.gamemode\n    }\n    blockPosition.y += 1\n\n    let x = String(format: \"%.06f\", position.x).prefix(7)\n    let y = String(format: \"%.06f\", position.y).prefix(7)\n    let z = String(format: \"%.06f\", position.z).prefix(7)\n\n    let relativePosition = blockPosition.relativeToChunkSection\n    let relativePositionString = \"\\(relativePosition.x) \\(relativePosition.y) \\(relativePosition.z)\"\n    let chunkSectionString =\n      \"\\(chunkSectionPosition.sectionX) \\(chunkSectionPosition.sectionY) \\(chunkSectionPosition.sectionZ)\"\n\n    let yawString = String(format: \"%.01f\", yaw)\n    let pitchString = String(format: \"%.01f\", pitch)\n    let axisHeading = \"\\(heading.isPositive ? \"positive\" : \"negative\") \\(heading.axis)\"\n\n    var lightPosition = blockPosition\n    lightPosition.y += 1\n    let skyLightLevel = game.world.getSkyLightLevel(at: lightPosition)\n    let blockLightLevel = game.world.getBlockLightLevel(at: lightPosition)\n\n    let biome = game.world.getBiome(at: blockPosition)\n\n    let targetedEntity = game.targetedEntity()\n\n    let leftSections: [[String]] = [\n      [\n        \"Minecraft \\(Constants.versionString) (Delta Client)\",\n        renderStatisticsString(state.inner.debouncedRenderStatistics()),\n        \"Dimension: \\(game.world.dimension.identifier)\",\n      ],\n      [\n        \"XYZ: \\(x) / \\(y) / \\(z)\",\n        // Block under feet\n        \"Block: \\(blockPosition.x) \\(blockPosition.y) \\(blockPosition.z)\",\n        \"Chunk: \\(relativePositionString) in \\(chunkSectionString)\",\n        \"Facing: \\(heading) (Towards \\(axisHeading)) (\\(yawString) / \\(pitchString))\",\n        // Lighting (at foot level)\n        \"Light: \\(skyLightLevel) sky, \\(blockLightLevel) block\",\n        \"Biome: \\(biome?.identifier.description ?? \"not loaded\")\",\n        \"Gamemode: \\(gamemode.string)\",\n      ],\n      // Custom Delta Client info\n      [\n        targetedEntity.map { \"Targeted entity: \\($0.target)\" }\n      ].compactMap(identity),\n    ]\n\n    #if os(macOS)\n      let rightSections: [[String]] = [\n        [\n          \"CPU: \\(Self.cpuName ?? \"unknown\") (\\(Self.cpuArch ?? \"n/a\"))\",\n          \"Total mem: \\(Self.totalMem)GB\",\n          \"GPU: \\(Self.gpuInfo ?? \"unknown\")\",\n        ]\n      ]\n    #else\n      let rightSections: [[String]] = []\n    #endif\n\n    return GUIElement.stack {\n      debugScreenList(leftSections, side: .left)\n      debugScreenList(rightSections, side: .right)\n    }\n  }\n\n  public enum Alignment {\n    case left\n    case right\n  }\n\n  public func debugScreenList(_ sections: [[String]], side: Alignment) -> GUIElement {\n    GUIElement.forEach(in: sections, spacing: 6) { section in\n      GUIElement.forEach(in: section, spacing: 0) { line in\n        GUIElement.text(line)\n          .padding([.left, .top], 1)\n          .padding([.right], 2)\n          .background(Self.debugScreenRowBackgroundColor)\n          .constraints(.top(0), side == .left ? .left(0) : .right(0))\n      }\n      .padding(1)\n    }\n  }\n\n  /// Converts the given render statistics into the format required by the debug screen;\n  ///\n  /// ```\n  /// XX fps (XX.XX theoretical) (XX.XXms cpu, XX.XXms gpu)\n  /// ```\n  ///\n  /// Theoretical FPS and GPU time are only included if being collected.\n  public func renderStatisticsString(_ renderStatistics: RenderStatistics) -> String {\n    let theoreticalFPSString = renderStatistics.averageTheoreticalFPS.map { theoreticalFPS in\n      \"(\\(theoreticalFPS) theoretical)\"\n    }\n    let gpuTimeString = renderStatistics.averageGPUTime.map { gpuTime in\n      String(format: \"%.02fms gpu\", gpuTime * 1000)\n    }\n    let cpuTimeString = String(format: \"%.02fms cpu\", renderStatistics.averageCPUTime * 1000)\n    let fpsString = String(format: \"%.00f fps\", renderStatistics.averageFPS)\n\n    let timingsString = [cpuTimeString, gpuTimeString].compactMap(identity).joined(separator: \", \")\n\n    return [fpsString, theoreticalFPSString, \"(\\(timingsString))\"]\n      .compactMap(identity)\n      .joined(separator: \" \")\n  }\n\n  public func outlinedText(\n    _ text: String,\n    textColor: Vec4f,\n    outlineColor: Vec4f = Vec4f(0, 0, 0, 1)\n  ) -> GUIElement {\n    let outlineText = GUIElement.text(text, color: outlineColor)\n    return GUIElement.stack {\n      outlineText.constraints(.top(0), .left(1))\n      outlineText.constraints(.top(1), .left(0))\n      outlineText.constraints(.top(1), .left(2))\n      outlineText.constraints(.top(2), .left(1))\n      GUIElement.text(text, color: textColor)\n        .constraints(.top(1), .left(1))\n    }\n  }\n\n  public func discreteMeter(\n    _ value: Int,\n    fullIcon: GUISprite,\n    halfIcon: GUISprite,\n    outlineIcon: GUISprite,\n    direction: ReadingDirection = .leftToRight\n  ) -> GUIElement {\n    let fullIconCount = value / 2\n    let hasHalfIcon = value % 2 == 0\n    var range = Array(0..<10)\n    if direction == .rightToLeft {\n      range = range.reversed()\n    }\n    return GUIElement.forEach(in: range, direction: .horizontal, spacing: -1) { i in\n      GUIElement.stack {\n        GUIElement.sprite(outlineIcon)\n        if i < fullIconCount {\n          GUIElement.sprite(fullIcon)\n        } else if hasHalfIcon && i == fullIconCount {\n          GUIElement.sprite(halfIcon)\n        }\n      }\n    }\n  }\n\n  public func continuousMeter(\n    _ value: Float,\n    background: GUISprite,\n    foreground: GUISprite\n  ) -> GUIElement {\n    var croppedForeground = foreground.descriptor\n    croppedForeground.size.x = Int(Float(croppedForeground.size.x) * value)\n    return GUIElement.stack {\n      GUIElement.sprite(background)\n      GUIElement.customSprite(croppedForeground)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/RenderStatistics.swift",
    "content": "/// Statistics related to a renderer's performance.\npublic struct RenderStatistics {\n  // MARK: Public properties\n\n  /// The average CPU time including waiting for vsync. Measured as the time between starting two consecutive frames.\n  public var averageFrameTime: Double {\n    frameTimes.average()\n  }\n\n  /// The average CPU time excluding waiting for vsync.\n  public var averageCPUTime: Double {\n    cpuTimes.average()\n  }\n\n  /// The average GPU time.\n  public var averageGPUTime: Double? {\n    gpuCountersEnabled ? gpuTimes.average() : nil\n  }\n\n  /// The average FPS.\n  public var averageFPS: Double {\n    return averageFrameTime == 0 ? 0 : 1 / averageFrameTime\n  }\n\n  /// The theoretical FPS if vsync was disabled.\n  public var averageTheoreticalFPS: Double? {\n    if let averageGPUTime = averageGPUTime {\n      let bottleneck = max(averageCPUTime, averageGPUTime)\n      return bottleneck == 0 ? 0 : 1 / bottleneck\n    } else {\n      return nil\n    }\n  }\n\n  public var gpuCountersEnabled: Bool\n\n  // MARK: Private properties\n\n  /// The most recent cpu times including waiting for vsync. Measured in seconds.\n  private var frameTimes: [Double] = []\n  /// The most recent cpu times excluding waiting for vsync. Measured in seconds.\n  private var cpuTimes: [Double] = []\n  /// The most recent gpu times. Measured in seconds.\n  private var gpuTimes: [Double] = []\n\n  /// The number of samples for the rolling average.\n  public let sampleSize = 20\n\n  // MARK: Init\n\n  /// Creates a new group of render statistics.\n  public init(gpuCountersEnabled: Bool = false) {\n    self.gpuCountersEnabled = gpuCountersEnabled\n  }\n\n  // MARK: Public methods\n\n  /// Updates the statistics with the measurements taken for a frame.\n  /// - Parameters:\n  ///   - frameTime: The CPU time taken for the frame measured in seconds including waiting for vsync.\n  ///   - cpuTime: The CPU time taken for the frame measured in seconds excluding waiting for vsync.\n  ///   - gpuTime: The GPU time taken for the frame measured in seconds.\n  public mutating func addMeasurement(frameTime: Double, cpuTime: Double, gpuTime: Double?) {\n    frameTimes.append(frameTime)\n    cpuTimes.append(cpuTime)\n    if let gpuTime = gpuTime {\n      gpuTimes.append(gpuTime)\n    }\n\n    if frameTimes.count > sampleSize {\n      frameTimes.removeFirst()\n      cpuTimes.removeFirst()\n      if gpuTime != nil {\n        gpuTimes.removeFirst()\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/VerticalConstraint.swift",
    "content": "public enum VerticalConstraint {\n  case top(Int)\n  case center(VerticalOffset?)\n  case bottom(Int)\n\n  public static let center = Self.center(nil)\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/VerticalOffset.swift",
    "content": "public enum VerticalOffset {\n  case up(Int)\n  case down(Int)\n\n  /// The offset value, negative for up, positive for down.\n  public var value: Int {\n    switch self {\n      case .up(let offset):\n        return -offset\n      case .down(let offset):\n        return offset\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/Window.swift",
    "content": "/// The slots behind a GUI window. Only a `class` because of the way it gets consumed by\n/// ``InGameGUI``, it gets too unergonomic short of wrapping it in a ``Box`` (which\n/// gets a little cumbersome sometimes, purely because we have to give inventory\n/// special treatment while also wanting to work generically over mutable references to\n/// windows).\npublic class Window {\n  public var id: Int\n  public var type: WindowType\n  public var slots: [Slot]\n\n  /// The action id to use for the next action performed on the inventory (used when sending\n  /// ``ClickWindowPacket``).\n  private var nextActionId = 0\n\n  public init(id: Int, type: WindowType, slots: [Slot]? = nil) {\n    if let count = slots?.count {\n      precondition(count == type.slotCount)\n    }\n\n    self.id = id\n    self.type = type\n    self.slots = slots ?? Array(repeating: Slot(), count: type.slotCount)\n    self.nextActionId = 0\n  }\n\n  /// Returns a unique window action id (counts up from 0 like vanilla does).\n  private func generateActionId() -> Int16 {\n    let id = nextActionId\n    nextActionId += 1\n    return Int16(id)\n  }\n\n  /// Gets the slots associated with a particular area of the window.\n  /// - Returns: The rows of the area, e.g. ``PlayerInventory/hotbarArea`` results in a single row, and\n  ///   ``PlayerInventory/armorArea`` results in 4 rows containing 1 element each.\n  public func slots(for area: WindowArea) -> [[Slot]] {\n    var rows: [[Slot]] = []\n    for y in 0..<area.height {\n      var row: [Slot] = []\n      for x in 0..<area.width {\n        let index = y * area.width + x + area.startIndex\n        row.append(slots[index])\n      }\n      rows.append(row)\n    }\n    return rows\n  }\n\n  /// Gets the window area corresponding to the given slot and the position of the slot in the area, if any.\n  public func area(containing slotIndex: Int) -> (area: WindowArea, position: Vec2i)? {\n    for area in type.areas {\n      guard let position = area.position(ofWindowSlot: slotIndex) else {\n        continue\n      }\n      return (area, position)\n    }\n    return nil\n  }\n\n  public func leftClick(\n    _ slotIndex: Int, mouseStack: inout ItemStack?, connection: ServerConnection?\n  ) {\n    guard let (area, slotPosition) = area(containing: slotIndex) else {\n      log.warning(\n        \"No area of window of type '\\(type.identifier)' contains the slot with index '\\(slotIndex)'\"\n      )\n      return\n    }\n\n    let clickedItem = slots[slotIndex]\n    if var slotStack = slots[slotIndex].stack,\n      var mouseStackCopy = mouseStack,\n      slotStack.itemId == mouseStackCopy.itemId\n    {\n      guard\n        let item = RegistryStore.shared.itemRegistry.item(withId: slotStack.itemId)\n      else {\n        log.warning(\"Failed to get maximum stack size for item with id '\\(slotStack.itemId)'\")\n        return\n      }\n\n      // If clicking on a recipe result, take result if possible.\n      if area.kind == .recipeResult {\n        // Only take if we can take the whole result\n        if slotStack.count <= item.maximumStackSize - mouseStackCopy.count {\n          slots[slotIndex].stack = nil\n          mouseStackCopy.count += slotStack.count\n          mouseStack = mouseStackCopy\n        }\n      } else {\n        let total = slotStack.count + mouseStackCopy.count\n        slotStack.count = min(total, item.maximumStackSize)\n        slots[slotIndex].stack = slotStack\n        if slotStack.count == total {\n          mouseStack = nil\n        } else {\n          mouseStackCopy.count = total - slotStack.count\n          mouseStack = mouseStackCopy\n        }\n      }\n    } else if area.kind != .recipeResult {\n      if area.kind == .armor, let mouseStackCopy = mouseStack {\n        guard\n          let item = RegistryStore.shared.itemRegistry.item(withId: mouseStackCopy.itemId)\n        else {\n          log.warning(\"Failed to get armor properties for item with id '\\(mouseStackCopy.itemId)'\")\n          return\n        }\n\n        // TODO: Allow heads and carved pumpkings to be warn (should be easy, just need an exhaustive\n        //   list).\n        // Ensure that armor of the correct kind (boots etc) can be put in an armor slot\n        let isValid = item.properties?.armorProperties?.equipmentSlot.index == slotPosition.y\n        if isValid {\n          swap(&slots[slotIndex].stack, &mouseStack)\n        }\n      } else {\n        swap(&slots[slotIndex].stack, &mouseStack)\n      }\n    }\n\n    do {\n      try connection?.sendPacket(\n        ClickWindowPacket(\n          windowId: UInt8(id),\n          actionId: generateActionId(),\n          action: .leftClick(slot: Int16(slotIndex)),\n          clickedItem: clickedItem\n        )\n      )\n    } catch {\n      log.warning(\"Failed to send click window packet for inventory left click: \\(error)\")\n    }\n  }\n\n  public func rightClick(\n    _ slotIndex: Int, mouseStack: inout ItemStack?, connection: ServerConnection?\n  ) {\n    let clickedItem = slots[slotIndex]\n    if var stack = slots[slotIndex].stack, mouseStack == nil {\n      let total = stack.count\n      var takenStack = stack\n      stack.count = total / 2\n      takenStack.count = total - stack.count\n      mouseStack = takenStack\n      if stack.count == 0 {\n        slots[slotIndex].stack = nil\n      } else {\n        slots[slotIndex].stack = stack\n      }\n    } else if var stack = mouseStack, slots[slotIndex].stack == nil {\n      stack.count -= 1\n      slots[slotIndex].stack = ItemStack(itemId: stack.itemId, itemCount: 1)\n      if stack.count == 0 {\n        mouseStack = nil\n      } else {\n        mouseStack = stack\n      }\n    } else if let slotStack = slots[slotIndex].stack,\n      slotStack.itemId == mouseStack?.itemId\n    {\n      slots[slotIndex].stack?.count += 1\n      mouseStack?.count -= 1\n      if mouseStack?.count == 0 {\n        mouseStack = nil\n      }\n    } else {\n      swap(&slots[slotIndex].stack, &mouseStack)\n    }\n\n    do {\n      try connection?.sendPacket(\n        ClickWindowPacket(\n          windowId: UInt8(id),\n          actionId: generateActionId(),\n          action: .rightClick(slot: Int16(slotIndex)),\n          clickedItem: clickedItem\n        ))\n    } catch {\n      log.warning(\"Failed to send click window packet for inventory right click: \\(error)\")\n    }\n  }\n\n  /// - Returns: `true` if the event was handled, otherwise `false` (indicating that the next relevant GUI\n  ///   element should given the event, and so on).\n  public func pressKey(\n    over slotIndex: Int,\n    event: KeyPressEvent,\n    mouseStack: inout ItemStack?,\n    inputState: InputState,\n    connection: ServerConnection?\n  ) -> Bool {\n    guard let input = event.input else {\n      return false\n    }\n\n    let slotInputs: [Input] = [\n      .slot1, .slot2, .slot3, .slot4, .slot5, .slot6, .slot7, .slot8, .slot9,\n    ]\n\n    if input == .dropItem {\n      let dropWholeStack = inputState.keys.contains(where: \\.isControl)\n      if dropWholeStack {\n        dropStackFromSlot(slotIndex, mouseItemStack: mouseStack, connection: connection)\n      } else {\n        dropItemFromSlot(slotIndex, mouseItemStack: mouseStack, connection: connection)\n      }\n    } else if let hotBarSlot = slotInputs.firstIndex(of: input) {\n      guard mouseStack == nil else {\n        return true\n      }\n\n      let clickedItem = slots[slotIndex]\n      let hotBarSlotslotIndex = PlayerInventory.hotbarArea.startIndex + hotBarSlot\n      if hotBarSlotslotIndex != slotIndex {\n        slots.swapAt(slotIndex, hotBarSlotslotIndex)\n      }\n\n      do {\n        try connection?.sendPacket(\n          ClickWindowPacket(\n            windowId: UInt8(id),\n            actionId: generateActionId(),\n            action: .numberKey(slot: Int16(slotIndex), number: Int8(hotBarSlot)),\n            clickedItem: clickedItem\n          ))\n      } catch {\n        log.warning(\"Failed to send click window packet for inventory right click: \\(error)\")\n      }\n    } else {\n      return false\n    }\n\n    return true\n  }\n\n  public func close(mouseStack: inout ItemStack?, eventBus: EventBus, connection: ServerConnection?)\n    throws\n  {\n    mouseStack = nil\n    eventBus.dispatch(CaptureCursorEvent())\n    try connection?.sendPacket(CloseWindowServerboundPacket(windowId: UInt8(id)))\n  }\n\n  public func dropItemFromSlot(\n    _ slotIndex: Int, mouseItemStack: ItemStack?, connection: ServerConnection?\n  ) {\n    dropFromSlot(\n      slotIndex, wholeStack: false, mouseItemStack: mouseItemStack, connection: connection)\n  }\n\n  public func dropStackFromSlot(\n    _ slotIndex: Int, mouseItemStack: ItemStack?, connection: ServerConnection?\n  ) {\n    dropFromSlot(\n      slotIndex, wholeStack: true, mouseItemStack: mouseItemStack, connection: connection)\n  }\n\n  public func dropItemFromMouse(_ mouseStack: inout ItemStack?, connection: ServerConnection?) {\n    dropFromMouse(wholeStack: false, mouseItemStack: &mouseStack, connection: connection)\n  }\n\n  public func dropStackFromMouse(_ mouseStack: inout ItemStack?, connection: ServerConnection?) {\n    dropFromMouse(wholeStack: true, mouseItemStack: &mouseStack, connection: connection)\n  }\n\n  public func dropFromMouse(\n    wholeStack: Bool,\n    mouseItemStack mouseStack: inout ItemStack?,\n    connection: ServerConnection?\n  ) {\n    let slot = Slot(mouseStack)\n    if wholeStack {\n      mouseStack = nil\n    } else if var stack = mouseStack {\n      stack.count -= 1\n      if stack.count == 0 {\n        mouseStack = nil\n      } else {\n        mouseStack = stack\n      }\n    }\n\n    do {\n      try connection?.sendPacket(\n        ClickWindowPacket(\n          windowId: UInt8(id),\n          actionId: generateActionId(),\n          action: wholeStack ? .leftClick(slot: nil) : .rightClick(slot: nil),\n          clickedItem: slot\n        ))\n    } catch {\n      log.warning(\"Failed to send click window packet for item drop: \\(error)\")\n    }\n  }\n\n  private func dropFromSlot(\n    _ slotIndex: Int,\n    wholeStack: Bool,\n    mouseItemStack: ItemStack?,\n    connection: ServerConnection?\n  ) {\n    if mouseItemStack == nil {\n      if wholeStack {\n        slots[slotIndex].stack = nil\n      } else if var stack = slots[slotIndex].stack {\n        stack.count -= 1\n        if stack.count == 0 {\n          slots[slotIndex].stack = nil\n        } else {\n          slots[slotIndex].stack = stack\n        }\n      }\n    }\n\n    do {\n      try connection?.sendPacket(\n        ClickWindowPacket(\n          windowId: UInt8(id),\n          actionId: generateActionId(),\n          action: wholeStack\n            ? .dropStack(slot: Int16(slotIndex)) : .dropOne(slot: Int16(slotIndex)),\n          clickedItem: Slot(ItemStack(itemId: -1, itemCount: 1))\n        ))\n    } catch {\n      log.warning(\"Failed to send click window packet for item drop: \\(error)\")\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/WindowArea.swift",
    "content": "/// An area of a GUI window; a grid of slots. Only handles areas where the rows\n/// are stored one after another in the window's slot array.\npublic struct WindowArea {\n  /// Index of the first slot in the area.\n  public var startIndex: Int\n  /// Number of slots wide.\n  public var width: Int\n  /// Number of slots high.\n  public var height: Int\n  /// The position of the area within its window.\n  public var position: Vec2i\n  /// The kind of window area (determines its behaviour).\n  public var kind: Kind?\n\n  public init(\n    startIndex: Int,\n    width: Int,\n    height: Int,\n    position: Vec2i,\n    kind: WindowArea.Kind? = nil\n  ) {\n    self.startIndex = startIndex\n    self.width = width\n    self.height = height\n    self.position = position\n    self.kind = kind\n  }\n\n  /// Gets the position of a slot (given as an index in the window's slots array)\n  /// if it lies within this area.\n  public func position(ofWindowSlot slotIndex: Int) -> Vec2i? {\n    let endIndex = startIndex + width * height\n    if slotIndex >= startIndex && slotIndex < endIndex {\n      let position = Vec2i(\n        (slotIndex - startIndex) % width,\n        (slotIndex - startIndex) / width\n      )\n      return position\n    }\n    return nil\n  }\n\n  public enum Kind {\n    /// A 9x3 area synced with the player's inventory.\n    case inventorySynced\n    /// A 9x1 area synced with the player's hotbar.\n    case hotbarSynced\n    /// A full 3x3 crafting recipe input area.\n    case fullCraftingRecipeInput\n    /// A small 2x2 crafting recipe input area.\n    case smallCraftingRecipeInput\n    /// A 1x1 heat recipe (e.g. furnace recipe) input area.\n    case heatRecipeInput\n    /// A 1x1 recipe result output area.\n    case recipeResult\n    /// A 1x1 heat recipe fuel input area (e.g. furnace fuel slot).\n    case heatRecipeFuel\n    /// The 1x1 anvil input slot on the left.\n    case firstAnvilInput\n    /// The 1x1 anvil input slot on the right.\n    case secondAnvilInput\n    /// The 1x4 armor area in the player inventory.\n    case armor\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUI/WindowType.swift",
    "content": "/// A type of GUI window (e.g. inventory, crafting table, chest, etc). Defines the layout\n/// and which slots go where.\npublic struct WindowType {\n  public var id: Id\n  public var identifier: Identifier\n  public var background: GUIElement\n  public var slotCount: Int\n  public var areas: [WindowArea]\n\n  public enum Id: Hashable, Equatable {\n    case inventory\n    case vanilla(Int)\n  }\n\n  /// The window types understood by vanilla.\n  public static let types = [Id: Self](\n    values: [\n      inventory,\n      craftingTable,\n      anvil,\n      furnace,\n      blastFurnace,\n      smoker,\n      beacon,\n      generic9x1,\n      generic9x2,\n      generic9x3,\n      generic9x4,\n      generic9x5,\n      generic9x6,\n      generic3x3,\n    ],\n    keyedBy: \\.id\n  )\n\n  /// The player's inventory.\n  public static let inventory = WindowType(\n    id: .inventory,\n    identifier: Identifier(namespace: \"minecraft\", name: \"inventory\"),\n    background: .sprite(.inventory),\n    slotCount: 46,\n    areas: [\n      PlayerInventory.mainArea,\n      PlayerInventory.hotbarArea,\n      PlayerInventory.craftingInputArea,\n      PlayerInventory.craftingResultArea,\n      PlayerInventory.armorArea,\n      PlayerInventory.offHandArea,\n    ]\n  )\n\n  /// A 3x3 crafting table.\n  public static let craftingTable = WindowType(\n    id: .vanilla(11),\n    identifier: Identifier(namespace: \"minecraft\", name: \"crafting\"),\n    background: .sprite(.craftingTable),\n    slotCount: 46,\n    areas: [\n      WindowArea(\n        startIndex: 0,\n        width: 1,\n        height: 1,\n        position: Vec2i(124, 35),\n        kind: .recipeResult\n      ),\n      WindowArea(\n        startIndex: 1,\n        width: 3,\n        height: 3,\n        position: Vec2i(30, 17),\n        kind: .fullCraftingRecipeInput\n      ),\n      WindowArea(\n        startIndex: 10,\n        width: 9,\n        height: 3,\n        position: Vec2i(8, 84),\n        kind: .inventorySynced\n      ),\n      WindowArea(\n        startIndex: 37,\n        width: 9,\n        height: 1,\n        position: Vec2i(8, 142),\n        kind: .hotbarSynced\n      ),\n    ]\n  )\n\n  /// An anvil.\n  public static let anvil = WindowType(\n    id: .vanilla(7),\n    identifier: Identifier(namespace: \"minecraft\", name: \"crafting\"),\n    background: .sprite(.anvil),\n    slotCount: 39,\n    areas: [\n      WindowArea(\n        startIndex: 0,\n        width: 1,\n        height: 1,\n        position: Vec2i(27, 47),\n        kind: .firstAnvilInput\n      ),\n      WindowArea(\n        startIndex: 1,\n        width: 1,\n        height: 1,\n        position: Vec2i(76, 47),\n        kind: .secondAnvilInput\n      ),\n      WindowArea(\n        startIndex: 2,\n        width: 1,\n        height: 1,\n        position: Vec2i(134, 47),\n        kind: .recipeResult\n      ),\n      WindowArea(\n        startIndex: 3,\n        width: 9,\n        height: 3,\n        position: Vec2i(8, 84),\n        kind: .inventorySynced\n      ),\n      WindowArea(\n        startIndex: 30,\n        width: 9,\n        height: 1,\n        position: Vec2i(8, 142),\n        kind: .hotbarSynced\n      ),\n    ]\n  )\n\n  /// The areas of a heat recipe window (e.g. furnace, smoker, etc).\n  public static let heatRecipeWindowAreas: [WindowArea] = [\n    WindowArea(\n      startIndex: 0,\n      width: 1,\n      height: 1,\n      position: Vec2i(56, 17),\n      kind: .heatRecipeInput\n    ),\n    WindowArea(\n      startIndex: 1,\n      width: 1,\n      height: 1,\n      position: Vec2i(56, 53),\n      kind: .heatRecipeFuel\n    ),\n    WindowArea(\n      startIndex: 2,\n      width: 1,\n      height: 1,\n      position: Vec2i(112, 31),\n      kind: .recipeResult\n    ),\n    WindowArea(\n      startIndex: 3,\n      width: 9,\n      height: 3,\n      position: Vec2i(8, 84),\n      kind: .inventorySynced\n    ),\n    WindowArea(\n      startIndex: 30,\n      width: 9,\n      height: 1,\n      position: Vec2i(8, 142),\n      kind: .hotbarSynced\n    ),\n  ]\n\n  /// A regular furnace.\n  public static let furnace = WindowType(\n    id: .vanilla(13),\n    identifier: Identifier(namespace: \"minecraft\", name: \"furnace\"),\n    background: .sprite(.furnace),\n    slotCount: 39,\n    areas: heatRecipeWindowAreas\n  )\n\n  /// A blast furnace.\n  public static let blastFurnace = WindowType(\n    id: .vanilla(9),\n    identifier: Identifier(namespace: \"minecraft\", name: \"blast_furnace\"),\n    background: .sprite(.blastFurnace),\n    slotCount: 39,\n    areas: heatRecipeWindowAreas\n  )\n\n  /// A smoker.\n  public static let smoker = WindowType(\n    id: .vanilla(21),\n    identifier: Identifier(namespace: \"minecraft\", name: \"smoker\"),\n    background: .sprite(.smoker),\n    slotCount: 39,\n    areas: heatRecipeWindowAreas\n  )\n\n  /// A beacon block interface.\n  public static let beacon = WindowType(\n    id: .vanilla(8),\n    identifier: Identifier(namespace: \"minecraft\", name: \"beacon\"),\n    background: .sprite(.beacon),\n    slotCount: 37,\n    areas: [\n      WindowArea(\n        startIndex: 0,\n        width: 1,\n        height: 1,\n        position: Vec2i(136, 110)\n      ),\n      WindowArea(\n        startIndex: 1,\n        width: 9,\n        height: 3,\n        position: Vec2i(36, 137),\n        kind: .inventorySynced\n      ),\n      WindowArea(\n        startIndex: 28,\n        width: 9,\n        height: 1,\n        position: Vec2i(36, 196),\n        kind: .hotbarSynced\n      ),\n    ]\n  )\n\n  // A dispenser or dropper.\n  public static let generic3x3 = WindowType(\n    id: .vanilla(6),\n    identifier: Identifier(namespace: \"minecraft\", name: \"generic_3x3\"),\n    background: .sprite(.dispenser),\n    slotCount: 45,\n    areas: [\n      WindowArea(\n        startIndex: 0,\n        width: 3,\n        height: 3,\n        position: Vec2i(62, 17)\n      ),\n      WindowArea(\n        startIndex: 9,\n        width: 9,\n        height: 3,\n        position: Vec2i(8, 84),\n        kind: .inventorySynced\n      ),\n      WindowArea(\n        startIndex: 36,\n        width: 9,\n        height: 1,\n        position: Vec2i(8, 142),\n        kind: .hotbarSynced\n      ),\n    ]\n  )\n\n  /// A 1-row container.\n  public static let generic9x1 = WindowType(\n    id: .vanilla(0),\n    identifier: Identifier(namespace: \"minecraft\", name: \"generic_9x1\"),\n    background: GUIElement.list(spacing: 0) {\n      GUIElement.sprite(.generic9x1)\n      GUIElement.sprite(.genericInventory)\n    },\n    slotCount: 45,\n    areas: [\n      WindowArea(\n        startIndex: 0,\n        width: 9,\n        height: 1,\n        position: Vec2i(8, 18)\n      ),\n      WindowArea(\n        startIndex: 9,\n        width: 9,\n        height: 3,\n        position: Vec2i(8, 50),\n        kind: .inventorySynced\n      ),\n      WindowArea(\n        startIndex: 36,\n        width: 9,\n        height: 1,\n        position: Vec2i(8, 108),\n        kind: .hotbarSynced\n      ),\n    ]\n  )\n\n  /// A 2-row container.\n  public static let generic9x2 = WindowType(\n    id: .vanilla(1),\n    identifier: Identifier(namespace: \"minecraft\", name: \"generic_9x2\"),\n    background: GUIElement.list(spacing: 0) {\n      GUIElement.sprite(.generic9x2)\n      GUIElement.sprite(.genericInventory)\n    },\n    slotCount: 54,\n    areas: [\n      WindowArea(\n        startIndex: 0,\n        width: 9,\n        height: 2,\n        position: Vec2i(8, 18)\n      ),\n      WindowArea(\n        startIndex: 18,\n        width: 9,\n        height: 3,\n        position: Vec2i(8, 68),\n        kind: .inventorySynced\n      ),\n      WindowArea(\n        startIndex: 45,\n        width: 9,\n        height: 1,\n        position: Vec2i(8, 126),\n        kind: .hotbarSynced\n      ),\n    ]\n  )\n\n  /// A 3-row container (e.g. a single chest).\n  public static let generic9x3 = WindowType(\n    id: .vanilla(2),\n    identifier: Identifier(namespace: \"minecraft\", name: \"generic_9x3\"),\n    background: GUIElement.list(spacing: 0) {\n      GUIElement.sprite(.generic9x3)\n      GUIElement.sprite(.genericInventory)\n    },\n    slotCount: 63,\n    areas: [\n      WindowArea(\n        startIndex: 0,\n        width: 9,\n        height: 3,\n        position: Vec2i(8, 18)\n      ),\n      WindowArea(\n        startIndex: 27,\n        width: 9,\n        height: 3,\n        position: Vec2i(8, 86),\n        kind: .inventorySynced\n      ),\n      WindowArea(\n        startIndex: 54,\n        width: 9,\n        height: 1,\n        position: Vec2i(8, 144),\n        kind: .hotbarSynced\n      ),\n    ]\n  )\n\n  /// A 4-row container.\n  public static let generic9x4 = WindowType(\n    id: .vanilla(3),\n    identifier: Identifier(namespace: \"minecraft\", name: \"generic_9x4\"),\n    background: GUIElement.list(spacing: 0) {\n      GUIElement.sprite(.generic9x4)\n      GUIElement.sprite(.genericInventory)\n    },\n    slotCount: 72,\n    areas: [\n      WindowArea(\n        startIndex: 0,\n        width: 9,\n        height: 4,\n        position: Vec2i(8, 18)\n      ),\n      WindowArea(\n        startIndex: 36,\n        width: 9,\n        height: 3,\n        position: Vec2i(8, 104),\n        kind: .inventorySynced\n      ),\n      WindowArea(\n        startIndex: 63,\n        width: 9,\n        height: 1,\n        position: Vec2i(8, 162),\n        kind: .hotbarSynced\n      ),\n    ]\n  )\n\n  /// A 4-row container.\n  public static let generic9x5 = WindowType(\n    id: .vanilla(4),\n    identifier: Identifier(namespace: \"minecraft\", name: \"generic_9x5\"),\n    background: GUIElement.list(spacing: 0) {\n      GUIElement.sprite(.generic9x5)\n      GUIElement.sprite(.genericInventory)\n    },\n    slotCount: 81,\n    areas: [\n      WindowArea(\n        startIndex: 0,\n        width: 9,\n        height: 5,\n        position: Vec2i(8, 18)\n      ),\n      WindowArea(\n        startIndex: 45,\n        width: 9,\n        height: 3,\n        position: Vec2i(8, 122),\n        kind: .inventorySynced\n      ),\n      WindowArea(\n        startIndex: 72,\n        width: 9,\n        height: 1,\n        position: Vec2i(8, 180),\n        kind: .hotbarSynced\n      ),\n    ]\n  )\n\n  /// A 6-row container (e.g. a double chest).\n  public static let generic9x6 = WindowType(\n    id: .vanilla(5),\n    identifier: Identifier(namespace: \"minecraft\", name: \"generic_9x6\"),\n    background: GUIElement.list(spacing: 0) {\n      GUIElement.sprite(.generic9x6)\n    },\n    slotCount: 90,\n    areas: [\n      WindowArea(\n        startIndex: 0,\n        width: 9,\n        height: 6,\n        position: Vec2i(8, 18)\n      ),\n      WindowArea(\n        startIndex: 54,\n        width: 9,\n        height: 3,\n        position: Vec2i(8, 140),\n        kind: .inventorySynced\n      ),\n      WindowArea(\n        startIndex: 81,\n        width: 9,\n        height: 1,\n        position: Vec2i(8, 198),\n        kind: .hotbarSynced\n      ),\n    ]\n  )\n}\n"
  },
  {
    "path": "Sources/Core/Sources/GUIState.swift",
    "content": "import CoreFoundation\n\npublic struct GUIState {\n  /// The interval between render statistics updates in the GUI. Used by\n  /// ``GUIState/debouncedRenderStatistics()``.\n  public static let renderStatisticsUpdateInterval = 0.4\n\n  /// Whether the HUD (health, hotbar, hunger, etc.) is visible or not.\n  public var showHUD = true\n  /// Whether the debug screen is visible or not.\n  public var showDebugScreen = false\n  /// Whether the inventory is open or not.\n  public var showInventory = false\n\n  /// The chat history including messages received from other players.\n  public var chat = Chat()\n  /// The current contents of the chat message input. Non-`nil` if and only if chat is open.\n  public var messageInput: String?\n  /// The message that the user was composing before they used the up arrow to replace it with\n  /// a historical message. Allows users to return to their message by hitting the down arrow a\n  /// sufficient number of times.\n  public var stashedMessageInput: String?\n  /// All messages that the player has sent.\n  public var playerMessageHistory: [String] = []\n  /// The index of the currently selected historical message (changed by using the up\n  /// and down arrow keys while composing a chat message). Indexes into ``playerMessageHistory``.\n  public var currentMessageIndex: Int?\n\n  /// A small default size would cause more text wrapping and slow down game\n  /// ticks (even if not rendering). Haven't measured, just a hunch. Renderers\n  /// must set this value to their drawable size to ensure that mouse interaction\n  /// functions correctly.\n  public var drawableSize = Vec2i(2000, 2000)\n  /// The scaling factor from true pixels to drawable pixels. Should be set by\n  /// renderers so that mouse coordinates can be correctly converted to drawable\n  /// coordinates by GUI code.\n  public var drawableScalingFactor: Float = 1\n\n  /// The cursor position in the message input. 0 is the end of the message,\n  /// and the maximum value is the beginning of the message.\n  public var messageInputCursor: Int = 0\n\n  /// Rendering statistics to display to the user.\n  public var renderStatistics = RenderStatistics()\n  /// The render statistics last returned by `debouncedRenderStatistics`.\n  private var debouncedRenderStatisticsStorage = RenderStatistics()\n  /// The last time that the debounced render statistics were updated at.\n  private var lastDebouncedRenderStatisticsUpdate: CFAbsoluteTime = 0\n\n  /// The currently active GUI window (e.g. crafting table, chest, etc). Inventory is treated\n  /// differently because it's always 'open' (in the eyes of the server, and in the sense that\n  /// the client always remembers what items are in it). If the inventory is open, this should\n  /// still be `nil`.\n  public var window: Window?\n\n  /// The item stack currently being moved by the mouse.\n  public var mouseItemStack: ItemStack?\n\n  /// Boss bars currently visible to the player.\n  public var bossBars: [BossBar] = []\n\n  /// The chat input field cursor as an index into ``messageInput``.\n  public var messageInputCursorIndex: String.Index {\n    if let messageInput = messageInput {\n      return messageInput.index(messageInput.endIndex, offsetBy: -messageInputCursor)\n    } else {\n      return \"\".endIndex\n    }\n  }\n\n  /// Whether chat is open or not.\n  public var showChat: Bool {\n    return messageInput != nil\n  }\n\n  /// Whether the player is allowed to move, as opposed to being in a\n  /// menu or having chat open.\n  public var movementAllowed: Bool {\n    return !showChat && !showInventory && window == nil\n  }\n\n  /// The render statistics but only updated every `Self.renderStatisticsUpdateInterval`.\n  public mutating func debouncedRenderStatistics() -> RenderStatistics {\n    let time = CFAbsoluteTimeGetCurrent()\n    if time > lastDebouncedRenderStatisticsUpdate + Self.renderStatisticsUpdateInterval {\n      debouncedRenderStatisticsStorage = renderStatistics\n      lastDebouncedRenderStatisticsUpdate = time\n    }\n    return debouncedRenderStatisticsStorage\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Game.swift",
    "content": "import FirebladeECS\nimport Foundation\n\n/// Stores all of the game data such as entities, chunks and chat messages.\npublic final class Game: @unchecked Sendable {\n  // MARK: Public properties\n\n  /// The scheduler that runs the game's systems every 20th of a second (by default).\n  public private(set) var tickScheduler: TickScheduler\n  /// The event bus for emitting events.\n  public private(set) var eventBus: EventBus\n  /// Maps Vanilla entity Ids to identifiers of entities in the ``Nexus``.\n  private var entityIdToEntityIdentifier: [Int: EntityIdentifier] = [:]\n\n  /// The world the player is currently connected to.\n  private(set) public var world: World\n  /// The list of all players in the game.\n  public var tabList = TabList()\n  /// The names of all worlds in this game\n  public var worldNames: [Identifier] = []\n  /// A structure holding information about all of the dimensions (sent by server).\n  public var dimensions = [Dimension.overworld]\n  /// Registry containing all recipes in this game (sent by server).\n  public var recipeRegistry = RecipeRegistry()\n\n  /// The render distance of the server.\n  public var maxViewDistance = 0\n  /// The maximum number of players that can join the server.\n  public var maxPlayers = 0\n\n  /// Whether the server sends reduced debug info or not.\n  public var debugInfoReduced = false\n  /// Whether the client should show the respawn screen on death.\n  public var respawnScreenEnabled = true\n\n  /// The difficulty of the server.\n  public var difficulty = Difficulty.normal\n  /// Whether the server is hardcode or not.\n  public var isHardcore = false\n  /// Whether the server's difficulty is locked for the player or not.\n  public var isDifficultyLocked = true\n\n  // MARK: Private properties\n\n  #if DEBUG_LOCKS\n    /// A locked for managing safe access of ``nexus``.\n    public let nexusLock = ReadWriteLock()\n  #else\n    /// A locked for managing safe access of ``nexus``.\n    private let nexusLock = ReadWriteLock()\n  #endif\n  /// The container for the game's entities. Strictly only contains what Minecraft counts as\n  /// entities. Doesn't include block entities.\n  private let nexus = Nexus()\n  /// The player.\n  private var player: Player\n  /// The current input state (keyboard and mouse).\n  private let inputState: InputState\n\n  /// A lock for managing safe access of ``gui``.\n  private let guiLock = ReadWriteLock()\n  /// The current GUI state (f3 screen, inventory, etc).\n  private let gui: InGameGUI\n  /// Storage for the current GUI state. Protected by ``nexusLock`` since it's stored in the\n  /// nexus.\n  private let _guiState: GUIStateStorage\n\n  // MARK: Init\n\n  /// Creates a game with default properties. Creates the player. Starts the tick loop.\n  public init(\n    eventBus: EventBus,\n    configuration: ClientConfiguration,\n    connection: ServerConnection? = nil,\n    font: Font,\n    locale: MinecraftLocale\n  ) {\n    self.eventBus = eventBus\n\n    world = World(eventBus: eventBus)\n\n    tickScheduler = TickScheduler(nexus, nexusLock: nexusLock, world)\n\n    inputState = nexus.single(InputState.self).component\n    gui = InGameGUI()\n    _guiState = nexus.single(GUIStateStorage.self).component\n\n    player = Player()\n    var player = player\n    player.add(to: self)\n    self.player = player\n\n    // The order of the systems may seem weird, but it has to be this way so that the physics\n    // behaves identically to vanilla\n    tickScheduler.addSystem(PlayerFrictionSystem())\n    tickScheduler.addSystem(PlayerClimbSystem())\n    tickScheduler.addSystem(PlayerGravitySystem())\n    tickScheduler.addSystem(PlayerSmoothingSystem())\n    tickScheduler.addSystem(PlayerBlockBreakingSystem(connection, self))\n    // TODO: Make sure that font gets updated when resource pack gets updated, will likely\n    //   require significant refactoring if we wanna do it right (as in not just hacking it\n    //   together for the specific case of PlayerInputSystem); proper resource pack propagation\n    //   will probably take quite a bit of work.\n    tickScheduler.addSystem(\n      PlayerInputSystem(connection, self, eventBus, configuration, font, locale)\n    )\n    tickScheduler.addSystem(PlayerFlightSystem())\n    tickScheduler.addSystem(PlayerAccelerationSystem())\n    tickScheduler.addSystem(PlayerJumpSystem())\n    tickScheduler.addSystem(PlayerVelocitySystem())\n    tickScheduler.addSystem(PlayerCollisionSystem())\n    tickScheduler.addSystem(PlayerPositionSystem())\n    tickScheduler.addSystem(PlayerFOVSystem())\n\n    tickScheduler.addSystem(EntitySmoothingSystem())\n    tickScheduler.addSystem(EntityPacketHandlingSystem())\n    tickScheduler.addSystem(EntityMovementSystem())\n\n    if let connection = connection {\n      tickScheduler.addSystem(PlayerPacketSystem(connection))\n    }\n\n    // Start tick loop\n    tickScheduler.startTickLoop()\n  }\n\n  // MARK: Input\n\n  /// Handles a key press.\n  /// - Parameters:\n  ///   - key: The pressed key if any.\n  ///   - input: The pressed input if any.\n  ///   - characters: The characters typed by the pressed key.\n  public func press(key: Key?, input: Input?, characters: [Character] = []) {  // swiftlint:disable:this cyclomatic_complexity\n    nexusLock.acquireWriteLock()\n    defer { nexusLock.unlock() }\n    inputState.press(key: key, input: input, characters: characters)\n  }\n\n  /// Handles a key release.\n  /// - Parameters:\n  ///   - key: The released key if any.\n  ///   - input: The released input if any.\n  public func release(key: Key?, input: Input?) {\n    nexusLock.acquireWriteLock()\n    defer { nexusLock.unlock() }\n    inputState.release(key: key, input: input)\n  }\n\n  /// Releases all inputs. That includes keys.\n  public func releaseAllInputs() {\n    nexusLock.acquireWriteLock()\n    defer { nexusLock.unlock() }\n    inputState.releaseAll()\n  }\n\n  /// Moves the mouse.\n  ///\n  /// See ``Client/moveMouse(x:y:deltaX:deltaY:)`` for the reasoning behind\n  /// having both absolute and relative parameters (it's currently necessary\n  /// but could be fixed by cleaning up the input handling architecture).\n  /// - Parameters:\n  ///   - x: The absolute mouse x (relative to the play area's top left corner).\n  ///   - y: The absolute mouse y (relative to the play area's top left corner).\n  ///   - deltaX: The change in mouse x.\n  ///   - deltaY: The change in mouse y.\n  public func moveMouse(x: Float, y: Float, deltaX: Float, deltaY: Float) {\n    nexusLock.acquireWriteLock()\n    defer { nexusLock.unlock() }\n    inputState.moveMouse(x: x, y: y, deltaX: deltaX, deltaY: deltaY)\n  }\n\n  /// Moves the left thumbstick.\n  /// - Parameters:\n  ///   - x: The x positon.\n  ///   - y: The y position.\n  public func moveLeftThumbstick(_ x: Float, _ y: Float) {\n    nexusLock.acquireWriteLock()\n    defer { nexusLock.unlock() }\n    inputState.moveLeftThumbstick(x, y)\n  }\n\n  /// Moves the right thumbstick.\n  /// - Parameters:\n  ///   - x: The x positon.\n  ///   - y: The y position.\n  public func moveRightThumbstick(_ x: Float, _ y: Float) {\n    nexusLock.acquireWriteLock()\n    defer { nexusLock.unlock() }\n    inputState.moveRightThumbstick(x, y)\n  }\n\n  public func accessInputState<R>(acquireLock: Bool = true, action: (InputState) -> R) -> R {\n    if acquireLock { nexusLock.acquireWriteLock() }\n    defer { if acquireLock { nexusLock.unlock() } }\n    return action(inputState)\n  }\n\n  /// Gets a copy of the current GUI state.\n  /// - Returns: A copy of the current GUI state.\n  public func guiState() -> GUIState {\n    nexusLock.acquireReadLock()\n    defer { nexusLock.unlock() }\n    return _guiState.inner\n  }\n\n  /// Handles a received chat message.\n  public func receiveChatMessage(acquireLock: Bool = true, _ message: ChatMessage) {\n    if acquireLock { nexusLock.acquireWriteLock() }\n    defer { if acquireLock { nexusLock.unlock() } }\n    _guiState.chat.add(message)\n  }\n\n  /// Mutates the GUI state with a given action.\n  public func mutateGUIState<R>(acquireLock: Bool = true, action: (inout GUIState) throws -> R)\n    rethrows -> R\n  {\n    if acquireLock { nexusLock.acquireWriteLock() }\n    defer { if acquireLock { nexusLock.unlock() } }\n    return try action(&_guiState.inner)\n  }\n\n  /// Updates the GUI's render statistics.\n  public func updateRenderStatistics(acquireLock: Bool = true, to statistics: RenderStatistics) {\n    mutateGUIState(acquireLock: false) { state in\n      state.renderStatistics = statistics\n    }\n  }\n\n  /// Compile the in-game GUI to a renderable.\n  /// - acquireGUILock: If `false`, a GUI lock will not be acquired. Use with caution.\n  /// - acquireNexusLock: If `false`, a GUI lock will not be acquired (otherwise a nexus lock will be\n  ///   acquired if guiState isn't supplied). Use with caution.\n  /// - connection: Used to notify the server of window interactions and related operations.\n  /// - font: Font to use when rendering, used to compute text sizing and wrapping.\n  /// - locale: Locale used to resolve chat message content.\n  /// - guiState: Avoids the need for this function to call out to the nexus redundantly if the caller already\n  ///   has a reference to the gui state.\n  public func compileGUI(\n    acquireGUILock: Bool = true,\n    acquireNexusLock: Bool = true,\n    withFont font: Font,\n    locale: MinecraftLocale,\n    connection: ServerConnection?,\n    guiState: GUIStateStorage? = nil\n  ) -> GUIElement.GUIRenderable {\n    // Acquire the nexus lock first as that's the one that threads can be sitting inside of with `Game.accessNexus`.\n    // If we get the GUI lock first then the renderer can be waiting for the nexus lock while PlayerInputSystem is\n    // sitting with a nexus lock and waiting for a gui lock.\n    // TODO: Formalize the idea of keeping a consistent 'topological' ordering for locks throughout the project.\n    //   I think that would prevent this class of deadlocks.\n    var state: GUIStateStorage\n    if let guiState = guiState {\n      state = guiState\n    } else {\n      if acquireNexusLock { nexusLock.acquireWriteLock() }\n      state = nexus.single(GUIStateStorage.self).component\n    }\n    if acquireGUILock { guiLock.acquireWriteLock() }\n    defer { if acquireGUILock { guiLock.unlock() } }\n    defer { if acquireNexusLock && guiState == nil { nexusLock.unlock() } }\n    return gui.content(game: self, connection: connection, state: state)\n      .resolveConstraints(availableSize: state.drawableSize, font: font, locale: locale)\n  }\n\n  // MARK: Entity\n\n  /// A method for creating entities in a thread-safe manor.\n  ///\n  /// The builder can handle up to 20 components. This should be enough in most cases but if not,\n  /// components can be added to the entity directly, this is just more convenient. The builder can\n  /// only work for up to 20 components because of a limitation regarding result builders.\n  /// - Parameters:\n  ///   - id: The id to create the entity with.\n  ///   - builder: The builder that creates the components for the entity.\n  ///   - action: An action to perform on the entity once it's created.\n  public func createEntity(\n    id: Int,\n    @ComponentsBuilder using builder: () -> [Component],\n    action: ((Entity) -> Void)? = nil\n  ) {\n    nexusLock.acquireWriteLock()\n    defer { nexusLock.unlock() }\n\n    let entity = nexus.createEntity(with: builder())\n    entityIdToEntityIdentifier[id] = entity.identifier\n    action?(entity)\n  }\n\n  /// Allows thread safe access to a given entity.\n  /// - Parameters:\n  ///   - id: The id of the entity to access.\n  ///   - action: The action to perform on the entity if it exists.\n  public func accessEntity<R>(\n    id: Int,\n    acquireLock: Bool = true,\n    action: (Entity) throws -> R\n  ) rethrows -> R? {\n    if acquireLock { nexusLock.acquireWriteLock() }\n    defer { if acquireLock { nexusLock.unlock() } }\n\n    if let identifier = entityIdToEntityIdentifier[id] {\n      return try action(nexus.entity(from: identifier))\n    } else {\n      return nil\n    }\n  }\n\n  /// Allows thread safe access to a given component.\n  /// - Parameters:\n  ///   - entityId: The id of the entity with the component.\n  ///   - componentType: The type of component to access.\n  ///   - acquireLock: If `false`, no lock is acquired. Only use if you know what you're doing.\n  ///   - action: The action to perform on the component if the entity exists and contains that component.\n  public func accessComponent<T: Component, R>(\n    entityId: Int,\n    _ componentType: T.Type,\n    acquireLock: Bool = true,\n    action: (T) throws -> R\n  ) rethrows -> R? {\n    if acquireLock { nexusLock.acquireWriteLock() }\n    defer { if acquireLock { nexusLock.unlock() } }\n\n    guard\n      let identifier = entityIdToEntityIdentifier[entityId],\n      let component = nexus.entity(from: identifier).get(component: T.self)\n    else {\n      return nil\n    }\n\n    return try action(component)\n  }\n\n  /// Removes the entity with the given vanilla id from the game if it exists.\n  /// - Parameter id: The id of the entity to remove.\n  public func removeEntity(acquireLock: Bool = true, id: Int) {\n    if acquireLock { nexusLock.acquireWriteLock() }\n    defer { if acquireLock { nexusLock.unlock() } }\n\n    if let identifier = entityIdToEntityIdentifier[id] {\n      nexus.destroy(entityId: identifier)\n    }\n  }\n\n  /// Updates an entity's id if it exists.\n  /// - Parameters:\n  ///   - id: The current id of the entity.\n  ///   - newId: The new id for the entity.\n  public func updateEntityId(_ id: Int, to newId: Int) {\n    nexusLock.acquireWriteLock()\n    defer { nexusLock.unlock() }\n\n    if let identifier = entityIdToEntityIdentifier.removeValue(forKey: id) {\n      entityIdToEntityIdentifier[newId] = identifier\n      if let component = nexus.entity(from: identifier).get(component: EntityId.self) {\n        component.id = newId\n      }\n    }\n  }\n\n  /// Allows thread safe access to the nexus.\n  /// - Parameter action: The action to perform on the nexus.\n  public func accessNexus(action: (Nexus) -> Void) {\n    nexusLock.acquireWriteLock()\n    defer { nexusLock.unlock() }\n\n    action(nexus)\n  }\n\n  /// Allows thread safe access to the player.\n  /// - Parameters:\n  ///   - acquireLock: If `false`, no lock is acquired. Only use if you know what you're doing.\n  ///   - action: The action to perform on the player.\n  public func accessPlayer<T>(acquireLock: Bool = true, action: (Player) throws -> T) rethrows -> T\n  {\n    if acquireLock { nexusLock.acquireWriteLock() }\n    defer { if acquireLock { nexusLock.unlock() } }\n\n    return try action(player)\n  }\n\n  /// Queues handling of an entity-related packet to occur during the next game tick.\n  /// - Parameters:\n  ///   - packet: The packet to queue.\n  ///   - client: The client to handle the packet for.\n  public func handleDuringTick(_ packet: ClientboundEntityPacket, client: Client) {\n    nexusLock.acquireWriteLock()\n    defer { nexusLock.unlock() }\n\n    let packetStore = nexus.single(ClientboundEntityPacketStore.self).component\n    packetStore.add(packet, client: client)\n  }\n\n  // MARK: Player\n\n  /// Gets the position of the block currently targeted by the player.\n  /// - Parameters:\n  ///   - acquireLock: If `false`, no locks are acquired. Only use if you know what you're doing.\n  public func targetedBlockIgnoringEntities(acquireLock: Bool = true) -> Targeted<BlockPosition>? {\n    if acquireLock {\n      nexusLock.acquireReadLock()\n    }\n\n    let ray = player.ray\n\n    if acquireLock {\n      nexusLock.unlock()\n    }\n\n    for position in VoxelRay(along: ray, count: 7) {\n      let block = world.getBlock(at: position, acquireLock: acquireLock)\n      let boundingBox = block.shape.outlineShape.offset(by: position.doubleVector)\n      if let (distance, face) = boundingBox.intersectionDistanceAndFace(with: ray) {\n        guard distance <= Player.buildingReach else {\n          break\n        }\n\n        let targetedPosition = ray.direction * distance + ray.origin\n        return Targeted<BlockPosition>(\n          target: position,\n          distance: distance,\n          face: face,\n          targetedPosition: targetedPosition\n        )\n      }\n    }\n\n    return nil\n  }\n\n  public func targetedBlock(acquireLock: Bool = true) -> Targeted<BlockPosition>? {\n    guard let targetedThing = targetedThing(acquireLock: acquireLock) else {\n      return nil\n    }\n\n    guard case let .block(position) = targetedThing.target else {\n      return nil\n    }\n\n    return targetedThing.map(constant(position))\n  }\n\n  // TODO: Make a value type for entity ids so that this doesn't return a targeted integer (just feels confusing).\n  /// - Returns: The id of the entity targeted by the player, if any.\n  public func targetedEntityIgnoringBlocks(acquireLock: Bool = true) -> Targeted<Int>? {\n    if acquireLock { nexusLock.acquireReadLock() }\n    defer { if acquireLock { nexusLock.unlock() } }\n\n    let playerPosition = player.position.vector\n    let playerRay = player.ray\n\n    let family = nexus.family(\n      requiresAll: EntityId.self,\n      EntityPosition.self,\n      EntityHitBox.self,\n      excludesAll: ClientPlayerEntity.self\n    )\n\n    var candidate: Targeted<Int>?\n    for (id, position, hitbox) in family {\n      // Should be big enough radius not to accidentally exclude big entities such as the Ender Dragon?\n      guard (playerPosition - position.vector).magnitude < 12 else {\n        continue\n      }\n\n      let aabb = hitbox.aabb(at: position.vector)\n      guard\n        let (distance, face) = aabb.intersectionDistanceAndFace(with: playerRay),\n        distance >= 0 || aabb.contains(Vec3d(playerRay.origin)),\n        distance <= Player.attackReach\n      else {\n        continue\n      }\n\n      let newCandidate = Targeted<Int>(\n        target: id.id,\n        distance: distance,\n        face: face,\n        targetedPosition: playerRay.direction * distance + playerRay.origin\n      )\n\n      if let currentCandidate = candidate {\n        if distance < currentCandidate.distance {\n          candidate = newCandidate\n        }\n      } else {\n        candidate = newCandidate\n      }\n    }\n\n    return candidate\n  }\n\n  public func targetedEntity(acquireLock: Bool = true) -> Targeted<Int>? {\n    guard let targetedThing = targetedThing(acquireLock: acquireLock) else {\n      return nil\n    }\n\n    guard case let .entity(id) = targetedThing.target else {\n      return nil\n    }\n\n    return targetedThing.map(constant(id))\n  }\n\n  /// - Returns: The closest thing targeted by the player.\n  public func targetedThing(acquireLock: Bool = true) -> Targeted<Thing>? {\n    let targetedBlock = targetedBlockIgnoringEntities(acquireLock: acquireLock)\n    let targetedEntity = targetedEntityIgnoringBlocks(acquireLock: acquireLock)\n    if let block = targetedBlock, let entity = targetedEntity {\n      if block.distance < entity.distance {\n        return block.map(Thing.block)\n      } else {\n        return entity.map(Thing.entity)\n      }\n    } else if let block = targetedBlock {\n      return block.map(Thing.block)\n    } else if let entity = targetedEntity {\n      return entity.map(Thing.entity)\n    } else {\n      return nil\n    }\n  }\n\n  /// Gets current gamemode of the player.\n  public func currentGamemode() -> Gamemode? {\n    var gamemode: Gamemode? = nil\n    accessPlayer { player in\n      gamemode = player.gamemode.gamemode\n    }\n\n    return gamemode\n  }\n\n  /// Calculates the current fov multiplier from various factors such as movement speed\n  /// and whether the player is flying. Emulates vanilla's behaviour.\n  public func fovMultiplier() -> Float {\n    return accessPlayer { player in\n      return player.fov.smoothMultiplier\n    }\n  }\n\n  // MARK: Lifecycle\n\n  /// Sets the game's event bus. This is a method in case the game ever needs to listen to the event\n  /// bus, this way means that the listener can be added again.\n  public func setEventBus(_ eventBus: EventBus) {\n    self.eventBus = eventBus\n    self.world.eventBus = eventBus\n  }\n\n  /// Changes to a new world.\n  /// - Parameter world: The new world.\n  public func changeWorld(to newWorld: World) {\n    // TODO: Make this threadsafe\n    self.world = newWorld\n    tickScheduler.setWorld(to: newWorld)\n\n    nexusLock.acquireWriteLock()\n    defer { nexusLock.unlock() }\n\n    entityIdToEntityIdentifier = entityIdToEntityIdentifier.filter { (id, identifier) in\n      let isClientPlayer = id == player.entityId.id\n      if !isClientPlayer {\n        nexus.destroy(entityId: identifier)\n      }\n      return isClientPlayer\n    }\n  }\n\n  /// Stops the tick scheduler.\n  public func stopTickScheduler() {\n    tickScheduler.cancel()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Input/Input.swift",
    "content": "import Foundation\n\n/// A player input. On a laptop or desktop, this represents a key press or mouse button press.\npublic enum Input: String, Codable, CaseIterable {\n  case place\n  case destroy\n\n  case moveForward\n  case moveBackward\n  case strafeLeft\n  case strafeRight\n  case jump\n  case fly\n  case sneak\n  case sprint\n  case toggleHUD\n  case toggleDebugHUD\n  case toggleInventory\n  case changePerspective\n  case performGPUFrameCapture\n  case dropItem\n  case slot1\n  case slot2\n  case slot3\n  case slot4\n  case slot5\n  case slot6\n  case slot7\n  case slot8\n  case slot9\n  case nextSlot\n  case previousSlot\n  case openChat\n\n  public var isBindable: Bool {\n    return self != .fly\n  }\n\n  public var humanReadableLabel: String {\n    switch self {\n      case .place:\n        return \"Use item/Place block\"\n      case .destroy:\n        return \"Attack/Destroy\"\n      case .moveForward:\n        return \"Move forward\"\n      case .moveBackward:\n        return \"Move backward\"\n      case .strafeLeft:\n        return \"Strafe left\"\n      case .strafeRight:\n        return \"Strafe right\"\n      case .jump:\n        return \"Jump\"\n      case .fly:\n        return \"Fly\"\n      case .sneak:\n        return \"Sneak\"\n      case .sprint:\n        return \"Sprint\"\n      case .toggleHUD:\n        return \"Toggle HUD\"\n      case .toggleDebugHUD:\n        return \"Toggle debug HUD\"\n      case .toggleInventory:\n        return \"Toggle Inventory\"\n      case .changePerspective:\n        return \"Change Perspective\"\n      case .performGPUFrameCapture:\n        return \"Perform GPU trace\"\n      case .dropItem:\n        return \"Drop Selected Item\"\n      case .slot1:\n        return \"Slot 1\"\n      case .slot2:\n        return \"Slot 2\"\n      case .slot3:\n        return \"Slot 3\"\n      case .slot4:\n        return \"Slot 4\"\n      case .slot5:\n        return \"Slot 5\"\n      case .slot6:\n        return \"Slot 6\"\n      case .slot7:\n        return \"Slot 7\"\n      case .slot8:\n        return \"Slot 8\"\n      case .slot9:\n        return \"Slot 9\"\n      case .nextSlot:\n        return \"Next slot\"\n      case .previousSlot:\n        return \"Previous slot\"\n      case .openChat:\n        return \"Open chat\"\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Input/Key.swift",
    "content": "import Foundation\n\npublic enum Key: CustomStringConvertible, Hashable {\n  case leftShift\n  case rightShift\n  case leftControl\n  case rightControl\n  case leftOption\n  case rightOption\n  case leftCommand\n  case rightCommand\n  case function\n\n  case a\n  case b\n  case c\n  case d\n  case e\n  case f\n  case g\n  case h\n  case i\n  case j\n  case k\n  case l\n  case m\n  case n\n  case o\n  case p\n  case q\n  case r\n  case s\n  case t\n  case u\n  case v\n  case w\n  case x\n  case y\n  case z\n\n  case zero\n  case one\n  case two\n  case three\n  case four\n  case five\n  case six\n  case seven\n  case eight\n  case nine\n\n  case numberPad0\n  case numberPad1\n  case numberPad2\n  case numberPad3\n  case numberPad4\n  case numberPad5\n  case numberPad6\n  case numberPad7\n  case numberPad8\n  case numberPad9\n\n  case numberPadDecimal\n  case numberPadPlus\n  case numberPadMinus\n  case numberPadEquals\n  case numberPadAsterisk\n  case numberPadForwardSlash\n  case numberPadClear\n  case numberPadEnter\n\n  case dash\n  case equals\n  case backSlash\n  case forwardSlash\n  case openSquareBracket\n  case closeSquareBracket\n\n  case comma\n  case period\n  case backTick\n  case semicolon\n  case singleQuote\n\n  case tab\n  case insert\n  case enter\n  case space\n  case delete\n  case escape\n\n  case f1\n  case f2\n  case f3\n  case f4\n  case f5\n  case f6\n  case f7\n  case f8\n  case f9\n  case f10\n  case f11\n  case f12\n  case f13\n  case f14\n  case f15\n  case f16\n  case f17\n  case f18\n  case f19\n  case f20\n\n  case home\n  case end\n  case pageUp\n  case pageDown\n  case forwardDelete\n\n  case upArrow\n  case downArrow\n  case leftArrow\n  case rightArrow\n\n  case leftMouseButton\n  case rightMouseButton\n  case scrollUp\n  case scrollDown\n\n  case otherMouseButton(Int)\n\n  /// Whether the key is a control key.\n  public var isControl: Bool {\n    self == .leftControl || self == .rightControl\n  }\n\n  /// Whether the key is a command key.\n  public var isCommand: Bool {\n    self == .leftCommand || self == .rightCommand\n  }\n\n  /// Whether the key is a shift key.\n  public var isShift: Bool {\n    self == .leftShift || self == .rightShift\n  }\n\n  /// Whether the key is an option key.\n  public var isOption: Bool {\n    self == .leftOption || self == .rightOption\n  }\n\n  /// The key's display name.\n  public var description: String {\n    name.display\n  }\n\n  /// The key's name including both the raw and display representations.\n  public var name: (raw: String, display: String) {\n    switch self {\n      case .leftShift: return (\"leftShift\", \"Left shift\")\n      case .rightShift: return (\"rightShift\", \"Right shift\")\n      case .leftControl: return (\"leftControl\", \"Left control\")\n      case .rightControl: return (\"rightControl\", \"Right control\")\n      case .leftOption: return (\"leftOption\", \"Left option\")\n      case .rightOption: return (\"rightOption\", \"Right option\")\n      case .leftCommand: return (\"leftCommand\", \"Left command\")\n      case .rightCommand: return (\"rightCommand\", \"Right command\")\n      case .function: return (\"function\", \"Function\")\n\n      case .a: return (\"a\", \"A\")\n      case .b: return (\"b\", \"B\")\n      case .c: return (\"c\", \"C\")\n      case .d: return (\"d\", \"D\")\n      case .e: return (\"e\", \"E\")\n      case .f: return (\"f\", \"F\")\n      case .g: return (\"g\", \"G\")\n      case .h: return (\"h\", \"H\")\n      case .i: return (\"i\", \"I\")\n      case .j: return (\"j\", \"J\")\n      case .k: return (\"k\", \"K\")\n      case .l: return (\"l\", \"L\")\n      case .m: return (\"m\", \"M\")\n      case .n: return (\"n\", \"N\")\n      case .o: return (\"o\", \"O\")\n      case .p: return (\"p\", \"P\")\n      case .q: return (\"q\", \"Q\")\n      case .r: return (\"r\", \"R\")\n      case .s: return (\"s\", \"S\")\n      case .t: return (\"t\", \"T\")\n      case .u: return (\"u\", \"U\")\n      case .v: return (\"v\", \"V\")\n      case .w: return (\"w\", \"W\")\n      case .x: return (\"x\", \"X\")\n      case .y: return (\"y\", \"Y\")\n      case .z: return (\"z\", \"Z\")\n\n      case .zero: return (\"zero\", \"0\")\n      case .one: return (\"one\", \"1\")\n      case .two: return (\"two\", \"2\")\n      case .three: return (\"three\", \"3\")\n      case .four: return (\"four\", \"4\")\n      case .five: return (\"five\", \"5\")\n      case .six: return (\"six\", \"6\")\n      case .seven: return (\"seven\", \"7\")\n      case .eight: return (\"eight\", \"8\")\n      case .nine: return (\"nine\", \"9\")\n\n      case .numberPad0: return (\"numberPad0\", \"Numpad 0\")\n      case .numberPad1: return (\"numberPad1\", \"Numpad 1\")\n      case .numberPad2: return (\"numberPad2\", \"Numpad 2\")\n      case .numberPad3: return (\"numberPad3\", \"Numpad 3\")\n      case .numberPad4: return (\"numberPad4\", \"Numpad 4\")\n      case .numberPad5: return (\"numberPad5\", \"Numpad 5\")\n      case .numberPad6: return (\"numberPad6\", \"Numpad 6\")\n      case .numberPad7: return (\"numberPad7\", \"Numpad 7\")\n      case .numberPad8: return (\"numberPad8\", \"Numpad 8\")\n      case .numberPad9: return (\"numberPad9\", \"Numpad 9\")\n\n      case .numberPadDecimal: return (\"numberPadDecimal\", \"Numpad .\")\n      case .numberPadPlus: return (\"numberPadPlus\", \"Numpad +\")\n      case .numberPadMinus: return (\"numberPadMinus\", \"Numpad -\")\n      case .numberPadEquals: return (\"numberPadEquals\", \"Numpad =\")\n      case .numberPadAsterisk: return (\"numberPadAsterisk\", \"Numpad *\")\n      case .numberPadForwardSlash: return (\"numberPadForwardSlash\", \"Numpad /\")\n      case .numberPadClear: return (\"numberPadClear\", \"Numpad clear\")\n      case .numberPadEnter: return (\"numberPadEnter\", \"Numpad enter\")\n\n      case .dash: return (\"dash\", \"-\")\n      case .equals: return (\"equals\", \"=\")\n      case .backSlash: return (\"backSlash\", \"\\\\\")\n      case .forwardSlash: return (\"forwardSlash\", \"/\")\n      case .openSquareBracket: return (\"openSquareBracket\", \"[\")\n      case .closeSquareBracket: return (\"closeSquareBracket\", \"]\")\n\n      case .comma: return (\"comma\", \",\")\n      case .period: return (\"period\", \".\")\n      case .backTick: return (\"backTick\", \"`\")\n      case .semicolon: return (\"semicolon\", \";\")\n      case .singleQuote: return (\"singleQuote\", \"'\")\n\n      case .tab: return (\"tab\", \"Tab\")\n      case .insert: return (\"insert\", \"Insert\")\n      case .enter: return (\"enter\", \"Enter\")\n      case .space: return (\"space\", \"Space\")\n      case .delete: return (\"delete\", \"Delete\")\n      case .escape: return (\"escape\", \"Escape\")\n\n      case .f1: return (\"f1\", \"F1\")\n      case .f2: return (\"f2\", \"F2\")\n      case .f3: return (\"f3\", \"F3\")\n      case .f4: return (\"f4\", \"F4\")\n      case .f5: return (\"f5\", \"F5\")\n      case .f6: return (\"f6\", \"F6\")\n      case .f7: return (\"f7\", \"F7\")\n      case .f8: return (\"f8\", \"F8\")\n      case .f9: return (\"f9\", \"F9\")\n      case .f10: return (\"f10\", \"F10\")\n      case .f11: return (\"f11\", \"F11\")\n      case .f12: return (\"f12\", \"F12\")\n      case .f13: return (\"f13\", \"F13\")\n      case .f14: return (\"f14\", \"F14\")\n      case .f15: return (\"f15\", \"F15\")\n      case .f16: return (\"f16\", \"F16\")\n      case .f17: return (\"f17\", \"F17\")\n      case .f18: return (\"f18\", \"F18\")\n      case .f19: return (\"f19\", \"F19\")\n      case .f20: return (\"f20\", \"F20\")\n      case .forwardDelete: return (\"forwardDelete\", \"Forward delete\")\n\n      case .home: return (\"home\", \"Home\")\n      case .end: return (\"end\", \"End\")\n      case .pageUp: return (\"pageUp\", \"Page up\")\n      case .pageDown: return (\"pageDown\", \"Page down\")\n\n      case .upArrow: return (\"upArrow\", \"Up arrow\")\n      case .downArrow: return (\"downArrow\", \"Down arrow\")\n      case .leftArrow: return (\"leftArrow\", \"Left arrow\")\n      case .rightArrow: return (\"rightArrow\", \"Right arrow\")\n\n      case .leftMouseButton: return (\"leftMouseButton\", \"Left click\")\n      case .rightMouseButton: return (\"rightMouseButton\", \"Right click\")\n      case .scrollUp: return (\"scrollUp\", \"Scroll up\")\n      case .scrollDown: return (\"scrollDown\", \"Scroll down\")\n\n      case .otherMouseButton(let number):\n        return (\"mouseButton\\(number)\", \"Mouse button \\(number)\")\n    }\n  }\n\n  public init?(keyCode: UInt16) {\n    if let key = Self.keyCodeToKey[keyCode] {\n      self = key\n    } else {\n      return nil\n    }\n  }\n\n  private static let keyCodeToKey: [UInt16: Key] = [\n    0x00: .a,\n    0x01: .s,\n    0x02: .d,\n    0x03: .f,\n    0x04: .h,\n    0x05: .g,\n    0x06: .z,\n    0x07: .x,\n    0x08: .c,\n    0x09: .v,\n    0x0B: .b,\n    0x0C: .q,\n    0x0D: .w,\n    0x0E: .e,\n    0x0F: .r,\n    0x10: .y,\n    0x11: .t,\n    0x12: .one,\n    0x13: .two,\n    0x14: .three,\n    0x15: .four,\n    0x16: .six,\n    0x17: .five,\n    0x18: .equals,\n    0x19: .nine,\n    0x1A: .seven,\n    0x1B: .dash,\n    0x1C: .eight,\n    0x1D: .zero,\n    0x1E: .closeSquareBracket,\n    0x1F: .o,\n    0x20: .u,\n    0x21: .openSquareBracket,\n    0x22: .i,\n    0x23: .p,\n    0x25: .l,\n    0x26: .j,\n    0x27: .singleQuote,\n    0x28: .k,\n    0x29: .semicolon,\n    0x2A: .backSlash,\n    0x2B: .comma,\n    0x2C: .forwardSlash,\n    0x2D: .m,\n    0x2E: .m,\n    0x2F: .period,\n    0x32: .backTick,\n    0x41: .numberPadDecimal,\n    0x43: .numberPadAsterisk,\n    0x45: .numberPadPlus,\n    0x47: .numberPadClear,\n    0x4B: .numberPadForwardSlash,\n    0x4C: .numberPadEnter,\n    0x4E: .numberPadMinus,\n    0x51: .numberPadEquals,\n    0x52: .numberPad0,\n    0x53: .numberPad1,\n    0x54: .numberPad2,\n    0x55: .numberPad3,\n    0x56: .numberPad4,\n    0x57: .numberPad5,\n    0x58: .numberPad6,\n    0x59: .numberPad7,\n    0x5B: .numberPad8,\n    0x5C: .numberPad9,\n    0x24: .enter,\n    0x30: .tab,\n    0x31: .space,\n    0x33: .delete,\n    0x35: .escape,\n    0x40: .f17,\n    0x4F: .f18,\n    0x50: .f19,\n    0x5A: .f20,\n    0x60: .f5,\n    0x61: .f6,\n    0x62: .f7,\n    0x63: .f3,\n    0x64: .f8,\n    0x65: .f9,\n    0x67: .f11,\n    0x69: .f13,\n    0x6A: .f16,\n    0x6B: .f14,\n    0x6D: .f10,\n    0x6F: .f12,\n    0x71: .f15,\n    0x72: .insert,\n    0x73: .home,\n    0x74: .pageUp,\n    0x75: .forwardDelete,\n    0x76: .f4,\n    0x77: .end,\n    0x78: .f2,\n    0x79: .pageDown,\n    0x7A: .f1,\n    0x7B: .leftArrow,\n    0x7C: .rightArrow,\n    0x7D: .downArrow,\n    0x7E: .upArrow,\n  ]\n}\n\nextension Key: RawRepresentable {\n  /// The key's name in-code. More suitable for config files.\n  public var rawValue: String {\n    switch self {\n      case let .otherMouseButton(number):\n        return \"mouseButton\\(number)\"\n      default:\n        return name.raw\n    }\n  }\n\n  public init?(rawValue: String) {\n    if rawValue.hasPrefix(\"mouseButton\") {\n      let number = rawValue.dropFirst(\"mouseButton\".count)\n      guard let number = Int(number) else {\n        return nil\n      }\n      self = .otherMouseButton(number)\n    } else {\n      guard let key = Self.rawValueToKey[rawValue] else {\n        return nil\n      }\n      self = key\n    }\n  }\n\n  /// Used to convert raw values to keys. Works for all keys except\n  /// ``Key/otherMouseButton`` which contains an associated value.\n  private static let rawValueToKey: [String: Key] = [\n    \"leftShift\": .leftShift,\n    \"rightShift\": .rightShift,\n    \"leftControl\": .leftControl,\n    \"rightControl\": .rightControl,\n    \"leftOption\": .leftOption,\n    \"rightOption\": .rightOption,\n    \"leftCommand\": .leftCommand,\n    \"rightCommand\": .rightCommand,\n    \"function\": .function,\n    \"a\": .a,\n    \"b\": .b,\n    \"c\": .c,\n    \"d\": .d,\n    \"e\": .e,\n    \"f\": .f,\n    \"g\": .g,\n    \"h\": .h,\n    \"i\": .i,\n    \"j\": .j,\n    \"k\": .k,\n    \"l\": .l,\n    \"m\": .m,\n    \"n\": .n,\n    \"o\": .o,\n    \"p\": .p,\n    \"q\": .q,\n    \"r\": .r,\n    \"s\": .s,\n    \"t\": .t,\n    \"u\": .u,\n    \"v\": .v,\n    \"w\": .w,\n    \"x\": .x,\n    \"y\": .y,\n    \"z\": .z,\n    \"zero\": .zero,\n    \"one\": .one,\n    \"two\": .two,\n    \"three\": .three,\n    \"four\": .four,\n    \"five\": .five,\n    \"six\": .six,\n    \"seven\": .seven,\n    \"eight\": .eight,\n    \"nine\": .nine,\n    \"numberPad0\": .numberPad0,\n    \"numberPad1\": .numberPad1,\n    \"numberPad2\": .numberPad2,\n    \"numberPad3\": .numberPad3,\n    \"numberPad4\": .numberPad4,\n    \"numberPad5\": .numberPad5,\n    \"numberPad6\": .numberPad6,\n    \"numberPad7\": .numberPad7,\n    \"numberPad8\": .numberPad8,\n    \"numberPad9\": .numberPad9,\n    \"numberPadDecimal\": .numberPadDecimal,\n    \"numberPadPlus\": .numberPadPlus,\n    \"numberPadMinus\": .numberPadMinus,\n    \"numberPadEquals\": .numberPadEquals,\n    \"numberPadAsterisk\": .numberPadAsterisk,\n    \"numberPadForwardSlash\": .numberPadForwardSlash,\n    \"numberPadClear\": .numberPadClear,\n    \"numberPadEnter\": .numberPadEnter,\n    \"dash\": .dash,\n    \"equals\": .equals,\n    \"backSlash\": .backSlash,\n    \"forwardSlash\": .forwardSlash,\n    \"openSquareBracket\": .openSquareBracket,\n    \"closeSquareBracket\": .closeSquareBracket,\n    \"comma\": .comma,\n    \"period\": .period,\n    \"backTick\": .backTick,\n    \"semicolon\": .semicolon,\n    \"singleQuote\": .singleQuote,\n    \"tab\": .tab,\n    \"insert\": .insert,\n    \"enter\": .enter,\n    \"space\": .space,\n    \"delete\": .delete,\n    \"escape\": .escape,\n    \"f1\": .f1,\n    \"f2\": .f2,\n    \"f3\": .f3,\n    \"f4\": .f4,\n    \"f5\": .f5,\n    \"f6\": .f6,\n    \"f7\": .f7,\n    \"f8\": .f8,\n    \"f9\": .f9,\n    \"f10\": .f10,\n    \"f11\": .f11,\n    \"f12\": .f12,\n    \"f13\": .f13,\n    \"f14\": .f14,\n    \"f15\": .f15,\n    \"f16\": .f16,\n    \"f17\": .f17,\n    \"f18\": .f18,\n    \"f19\": .f19,\n    \"f20\": .f20,\n    \"forwardDelete\": .forwardDelete,\n    \"home\": .home,\n    \"end\": .end,\n    \"pageUp\": .pageUp,\n    \"pageDown\": .pageDown,\n    \"upArrow\": .upArrow,\n    \"downArrow\": .downArrow,\n    \"leftArrow\": .leftArrow,\n    \"rightArrow\": .rightArrow,\n    \"leftMouseButton\": .leftMouseButton,\n    \"rightMouseButton\": .rightMouseButton,\n    \"scrollUp\": .scrollUp,\n    \"scrollDown\": .scrollDown,\n  ]\n}\n\nextension Key: Codable {\n  public init(from decoder: Decoder) throws {\n    let container = try decoder.singleValueContainer()\n    let rawValue = try container.decode(String.self)\n    guard let key = Key(rawValue: rawValue) else {\n      throw RichError(\"No such key '\\(rawValue)'\")\n    }\n    self = key\n  }\n\n  public func encode(to encoder: Encoder) throws {\n    var container = encoder.singleValueContainer()\n    try container.encode(rawValue)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Input/Keymap.swift",
    "content": "import Foundation\n\n/// A keymap stores the user's keybindings.\npublic struct Keymap {\n  /// The client's default key bindings.\n  public static var `default` = Keymap(bindings: [\n    .place: .rightMouseButton,\n    .destroy: .leftMouseButton,\n    .moveForward: .w,\n    .moveBackward: .s,\n    .strafeLeft: .a,\n    .strafeRight: .d,\n    .jump: .space,\n    .sneak: .leftShift,\n    .sprint: .leftControl,\n    .toggleHUD: .f1,\n    .toggleDebugHUD: .f3,\n    .toggleInventory: .e,\n    .changePerspective: .f5,\n    .dropItem: .q,\n    .slot1: .one,\n    .slot2: .two,\n    .slot3: .three,\n    .slot4: .four,\n    .slot5: .five,\n    .slot6: .six,\n    .slot7: .seven,\n    .slot8: .eight,\n    .slot9: .nine,\n    .nextSlot: .scrollUp,\n    .previousSlot: .scrollDown,\n    .openChat: .t\n  ])\n\n  /// The user's keybindings.\n  public var bindings: [Input: Key]\n\n  /// Creates a new keymap.\n  /// - Parameter bindings: Bindings for the new keymap.\n  init(bindings: [Input: Key]) {\n    self.bindings = bindings\n  }\n\n  /// - Returns: The input action for the given key if bound.\n  public func getInput(for key: Key) -> Input? {\n    for (input, inputKey) in bindings where key == inputKey {\n      return input\n    }\n    return nil\n  }\n}\n\n/// We implement `Codable` ourselves because the default codable works very strangely,\n/// with keys that aren't strings or integers.\n///\n/// Here's a sample of what the default implementations do (wtf),\n///\n/// ```json\n/// \"bindings\" : [\n///   {\n///     \"moveForward\" : {\n///\n///     }\n///   },\n///   {\n///     \"w\" : {\n///\n///     }\n///   },\n///   {\n///     \"previousSlot\" : {\n///\n///     }\n///   },\n///   {\n///     \"scrollDown\" : {\n///\n///     }\n///   },\n///   ...\n/// ]\n/// ```\n///\n/// Each key value pair is actually just two consecutive objects in an array, with empty\n/// payloads. Here's the format we implement ourselves,\n/// \n/// ```json\n/// \"bindings\": {\n///   \"moveForward\": \"w\",\n///   \"previousSlot\": \"scrollDown\"\n/// }\n/// ```\nextension Keymap: Codable {\n  public init(from decoder: Decoder) throws {\n    let container = try decoder.container(keyedBy: Input.self)\n\n    bindings = [:]\n    for input in container.allKeys {\n      bindings[input] = try container.decode(Key.self, forKey: input)\n    }\n  }\n\n  public func encode(to encoder: Encoder) throws {\n    var container = encoder.container(keyedBy: Input.self)\n\n    for (input, key) in bindings {\n      try container.encode(key, forKey: input)\n    }\n  }\n}\n\nextension Input: CodingKey {\n  public var stringValue: String {\n    rawValue\n  }\n\n  public var intValue: Int? {\n    nil\n  }\n\n  public init?(stringValue: String) {\n    self.init(rawValue: stringValue)\n  }\n\n  public init?(intValue: Int) {\n    return nil\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Input/ModifierKey.swift",
    "content": "import Foundation\n\npublic enum ModifierKey: Hashable, Codable {\n  case leftShift\n  case rightShift\n  case leftControl\n  case rightControl\n  case leftOption\n  case rightOption\n  case leftCommand\n  case rightCommand\n  case function\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Logger.swift",
    "content": "@_exported import DeltaLogger\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Cipher.swift",
    "content": "import CryptoSwift\n\n/// An AES-128-CFB8 stream cipher used for encryption and decryption in the network protocol.\npublic class Cipher {\n  /// The operation that a cipher is made to perform.\n  public enum Operation {\n    case encrypt\n    case decrypt\n  }\n\n  /// The cipher's mode of operation.\n  public var operation: Operation\n\n  /// The cipher's internal AES encryptor or decryptor.\n  private var cryptor: Cryptor & Updatable\n\n  /// Creates a new cipher.\n  /// - Parameters:\n  ///   - key: The key. Must be at least 16 bytes.\n  ///   - iv: The initial vector. Must be at least 16 bytes.\n  /// - Precondition: Key must be at least 16 bytes and iv must be at least 16 bytes.\n  public init(_ operation: Operation, key: [UInt8], iv: [UInt8]) throws {\n    precondition(key.count >= 16, \"Key must be at least 16 bytes, was \\(key.count)\")\n    precondition(iv.count >= 16, \"IV must be at least 16 bytes, was \\(iv.count)\")\n\n    self.operation = operation\n\n    let aes = try AES(\n      key: key,\n      blockMode: CFB(iv: iv, segmentSize: .cfb8),\n      padding: .noPadding\n    )\n\n    switch operation {\n      case .encrypt:\n        cryptor = try aes.makeEncryptor()\n      case .decrypt:\n        cryptor = try aes.makeDecryptor()\n    }\n  }\n\n  public func update(with bytes: [UInt8]) throws -> [UInt8] {\n    let new = try cryptor.update(withBytes: bytes)\n    return new\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/ConnectionState.swift",
    "content": "import Foundation\n\nextension ServerConnection {\n  public enum State {\n    case idle\n    case connecting\n    case handshaking\n    case status\n    case login\n    case play\n    case disconnected\n    \n    public var packetState: PacketState? {\n      switch self {\n        case .handshaking:\n          return .handshaking\n        case .status:\n          return .status\n        case .login:\n          return .login\n        case .play:\n          return .play\n        default:\n          return nil\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Endianness.swift",
    "content": "/// The endianness of some bytes.\n///\n/// See [Wikipedia's entry on endianness](https://en.wikipedia.org/wiki/Endianness) for more information.\npublic enum Endianness {\n  case little\n  case big\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/LANServerEnumerator.swift",
    "content": "import Foundation\nimport Parsing\n\n#if !canImport(Combine)\nimport OpenCombine\n#endif\n\n/// An error that occured during LAN server enumeration.\nenum LANServerEnumeratorError: LocalizedError {\n  /// Failed to create multicast receiving socket.\n  case failedToCreateMulticastSocket(Error)\n  /// Failed to read from multicast socket.\n  case failedToReadFromSocket(Error)\n\n  var errorDescription: String? {\n    switch self {\n      case .failedToCreateMulticastSocket(let error):\n        return \"\"\"\n        Failed to create multicast receiving socket for LAN server enumerator\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n      case .failedToReadFromSocket(let error):\n        return \"\"\"\n        Failed to read from multicast socket for LAN server enumerator\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n    }\n  }\n}\n\n/// Used to discover LAN servers (servers on the same network as the client).\n///\n/// LAN servers broadcast themselves through UDP multicast packets on the `224.0.2.60` multicast group\n/// and port `4445`. They send messages of the form `[MOTD]username - world name[/MOTD][AD]port[/AD]`.\n///\n/// As well as discovering servers, the enumerator also pings them for more information (see ``pingers``).\n///\n/// Make sure to call ``stop()`` before trying to create another enumerator.\npublic class LANServerEnumerator: ObservableObject {\n  // MARK: Static properties\n\n  /// Used to prevent DoS attacks. 50 LAN servers at a time is definitely enough, in most cases there will be at most 2 or 3.\n  public static let maximumServerCount = 50\n\n  // MARK: Public properties\n\n  /// Pingers for all currently identified LAN servers.\n  @Published public var pingers: [Pinger] = []\n  /// Whether the enumerator has errored or not.\n  @Published public var hasErrored = false\n  /// All currently identified servers.\n  public var servers: [ServerDescriptor] = []\n\n  // MARK: Private properties\n\n  /// Multicast socket for receiving packets.\n  private var socket: Socket?\n  /// Whether the enumerator is listening already or not.\n  private var isListening = false\n\n  /// Used to notify about errors.\n  private let eventBus: EventBus\n  /// Dispatch queue used for networking.\n  private let queue = DispatchQueue(label: \"LANServerEnumerator\")\n\n  // MARK: Init\n\n  /// Creates a new LAN server enumerator.\n  ///\n  /// To start enumeration call `start`.\n  /// - Parameter eventBus: Event bus to dispatch errors to.\n  public init(eventBus: EventBus) {\n    self.eventBus = eventBus\n  }\n\n  deinit {\n    try? socket?.close()\n  }\n\n  /// Creates a new multicast socket for this instance to use for receiving broadcasts from servers\n  /// on the local network..\n  public func createSocket() throws {\n    do {\n      let socket = try Socket(.ip4, .udp)\n      try socket.setValue(true, for: BoolSocketOption.localAddressReuse)\n      try socket.bind(to: Socket.Address.ip4(\"224.0.2.60\", 4445))\n\n      try socket.setValue(\n        try MembershipRequest(\n          groupAddress: \"224.0.2.60\",\n          localAddress: \"0.0.0.0\"\n        ),\n        for: MembershipRequestSocketOption.addMembership\n      )\n\n      self.socket = socket\n    } catch {\n      throw LANServerEnumeratorError.failedToCreateMulticastSocket(error)\n    }\n  }\n\n  /// An async read loop that receives and parses messages from servers on the local network.\n  ///\n  /// It is implemented recursively to prevent the method from creating a reference cycle\n  /// (by dropping self temporarily each iteration).\n  public func asyncSocketReadLoop() {\n    queue.async { [weak self] in\n      guard let self = self, let socket = self.socket else {\n        return\n      }\n\n      do {\n        let (content, sender) = try socket.recvFrom(atMost: 16384)\n        self.handlePacket(sender: sender, content: content)\n      } catch {\n        ThreadUtil.runInMain {\n          self.hasErrored = true\n        }\n        self.eventBus.dispatch(\n          ErrorEvent(\n            error: LANServerEnumeratorError.failedToReadFromSocket(error),\n            message: \"LAN server enumeration failed\"\n          )\n        )\n        return\n      }\n\n      self.asyncSocketReadLoop()\n    }\n  }\n\n  // MARK: Public methods\n\n  /// Starts listening for LAN servers announcing themselves.\n  public func start() throws {\n    if socket == nil {\n      try createSocket()\n      asyncSocketReadLoop()\n      isListening = true\n    } else {\n      log.warning(\"Attempted to start LANServerEnumerator twice\")\n    }\n  }\n\n  /// Stops scanning for new LAN servers and closes the multicast socket. Any pings that are in progress will still be completed.\n  public func stop() {\n    if isListening {\n      try? socket?.close()\n      socket = nil\n    } else {\n      log.warning(\"Attempted to stop LANServerEnumerator while it wasn't started\")\n    }\n  }\n\n  /// Clears all currently discovered servers.\n  public func clear() {\n    servers = []\n    ThreadUtil.runInMain {\n      pingers = []\n    }\n  }\n\n  // MARK: Private methods\n\n  /// Parses LAN server multicast messages.\n  ///\n  /// They are expected to be of the form: `[MOTD]message of the day[/MOTD][AD]port[/AD]`.\n  /// Apparently sometimes the entire address is included in the `AD` section so that is\n  /// handled too.\n  private func handlePacket(sender: Socket.Address, content: [UInt8]) {\n    // Cap the maximum number of LAN servers that can be discovered\n    guard servers.count < Self.maximumServerCount else {\n      return\n    }\n\n    // Extract motd and port\n    guard\n      let content = String(bytes: content, encoding: .utf8),\n      case let .ip4(host, _) = sender,\n      let (motd, port) = parseMessage(content)\n    else {\n      log.error(\"Failed to parse LAN server broadcast message\")\n      return\n    }\n\n    let server = ServerDescriptor(name: motd, host: host, port: port)\n    if servers.contains(server) {\n      return\n    }\n\n    // Ping the server\n    let pinger = Pinger(server)\n    Task {\n      try? await pinger.ping()\n    }\n    servers.append(server)\n\n    ThreadUtil.runInMain {\n      pingers.append(pinger)\n    }\n\n    log.trace(\"Received LAN server multicast packet: motd=`\\(motd)`, address=`\\(host):\\(port)`\")\n  }\n\n  /// Parses a message of the form `\"[MOTD]motd[/MOTD][AD]port[/AD]\"`.\n  /// - Parameter message: The message to parse.\n  /// - Returns: The motd and port.\n  private func parseMessage(_ message: String) -> (String, UInt16)? {\n    let portParser = OneOf<Substring, _, _> {\n      // Sometimes the port also includes a host (\"host:port\") so we must handle that case\n      Parse {\n        Prefix { $0 != \":\" }\n        \":\"\n        UInt16.parser()\n      }.map { $0.1 }\n\n      // Otherwise it's just a port\n      UInt16.parser()\n    }\n\n    let packetParser = Parse<Substring, _> {\n      \"[MOTD]\"\n      Prefix { $0 != \"[\"}\n      \"[/MOTD][AD]\"\n      portParser\n      \"[/AD]\"\n    }.map { tuple -> (String, UInt16) in\n      (String(tuple.0), tuple.1)\n    }\n\n    do {\n      return try packetParser.parse(message)\n    } catch {\n      log.warning(\"Invalid LAN server broadcast message received: \\\"\\(message)\\\", error: \\(error)\")\n      return nil\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Buffer.swift",
    "content": "import Foundation\n\n/// A byte buffer.\n///\n/// All methods read unsigned values unless otherwise specified in their name.\npublic struct Buffer {\n  /// The buffer's underlying byte array.\n  public private(set) var bytes: [UInt8]\n  /// The current index of the read/write head.\n  public var index = 0\n\n  /// The buffer's current length.\n  public var length: Int {\n    return self.bytes.count\n  }\n\n  /// The number of bytes remaining in the buffer.\n  public var remaining: Int {\n    return length - index\n  }\n\n  // MARK: Init\n\n  /// Creates an empty buffer.\n  public init() {\n    self.bytes = []\n  }\n\n  /// Creates a buffer with the given bytes.\n  /// - Parameter bytes: The buffer's initial bytes.\n  public init(_ bytes: [UInt8]) {\n    self.bytes = bytes\n  }\n\n  // MARK: Reading\n\n  /// Skips forward or backward over the specified number of bytes.\n  /// - Parameter count: Number of bytes to skip (a negative number goes backwards).\n  /// - Throws: ``BufferError/skippedOutOfBounds`` if the new index is out of bounds.\n  public mutating func skip(_ count: Int) throws {\n    index += count\n    if remaining < 0 || index < 0 {\n      throw BufferError.skippedOutOfBounds(length: length, index: index)\n    }\n  }\n\n  /// Reads an unsigned integer with the specified number of bytes and the given endianness.\n  /// - Parameters:\n  ///   - size: The size of the integer in bytes (must be at most 8).\n  ///   - endianness: The endianness of the integer.\n  /// - Returns: The integer stored as a 64 bit unsigned integer.\n  /// - Throws: ``BufferError/rangeOutOfBounds`` if the requested number of bytes can't be read.\n  public mutating func readInteger(size: Int, endianness: Endianness) throws -> UInt64 {\n    assert(size <= 8)\n    // TODO: throw error if out of bounds and turn assert into an error too\n\n    var bitPattern: UInt64 = 0\n    switch endianness {\n      case .little:\n        for index in 0..<size {\n          let byte = try readByte()\n          bitPattern |= UInt64(byte) << (index * 8)\n        }\n      case .big:\n        let sizeMinusOne = size - 1\n        for index in 0..<size {\n          let byte = try readByte()\n          bitPattern |= UInt64(byte) << ((sizeMinusOne - index) * 8)\n        }\n    }\n\n    return bitPattern\n  }\n\n  /// Reads the byte at ``index``.\n  /// - Returns: The byte.\n  /// - Throws: ``BufferError/outOfBounds`` if ``remaining`` is not positive.\n  public mutating func readByte() throws -> UInt8 {\n    guard remaining > 0 else {\n      throw BufferError.outOfBounds(length: length, index: index)\n    }\n\n    let byte = bytes[index]\n    index += 1\n    return byte\n  }\n\n  /// Reads the signed byte at ``index``.\n  /// - Returns: The signed byte.\n  /// - Throws: ``BufferError/outOfBounds`` if ``remaining`` is not positive.\n  public mutating func readSignedByte() throws -> Int8 {\n    let byte = Int8(bitPattern: try readByte())\n    return byte\n  }\n\n  /// Reads a specified number of bytes (starting from ``index``).\n  /// - Returns: The bytes.\n  /// - Throws: ``BufferError/rangeOutOfBounds`` if the requested number of bytes can't be read.\n  public mutating func readBytes(_ count: Int) throws -> [UInt8] {\n    guard remaining >= count else {\n      throw BufferError.rangeOutOfBounds(length: length, start: index, end: count + index)\n    }\n\n    let byteArray = Array(bytes[index..<(index + count)])\n    index += count\n    return byteArray\n  }\n\n  /// Reads a specified number of signed bytes (starting from ``index``).\n  /// - Returns: The bytes.\n  /// - Throws: ``BufferError/rangeOutOfBounds`` if the requested number of bytes can't be read.\n  public mutating func readSignedBytes(_ count: Int) throws -> [Int8] {\n    guard remaining >= count else {\n      throw BufferError.rangeOutOfBounds(length: length, start: index, end: count + index)\n    }\n\n    let unsignedBytes = try readBytes(count)\n    var signedBytes: [Int8] = []\n    for i in 0..<unsignedBytes.count {\n      signedBytes[i] = Int8(bitPattern: unsignedBytes[i])\n    }\n    return signedBytes\n  }\n\n  /// Reads all bytes remaining in the buffer.\n  /// - Returns: The remaining bytes (empty if ``index`` is out of bounds).\n  public mutating func readRemainingBytes() -> [UInt8] {\n    let remainingBytes = (try? readBytes(remaining)) ?? []\n    index = length\n    return remainingBytes\n  }\n\n  /// Reads an unsigned short (2 byte integer).\n  /// - Parameter endianness: The endianness of the integer.\n  /// - Returns: The unsigned short.\n  /// - Throws: ``BufferError/outOfBounds`` if ``index`` is out of bounds.\n  public mutating func readShort(endianness: Endianness) throws -> UInt16 {\n    return UInt16(try readInteger(size: MemoryLayout<UInt16>.stride, endianness: endianness))\n  }\n\n  /// Reads a signed short (2 byte integer).\n  /// - Parameter endianness: The endianness of the integer.\n  /// - Returns: The signed short.\n  /// - Throws: ``BufferError/outOfBounds`` if ``index`` is out of bounds.\n  public mutating func readSignedShort(endianness: Endianness) throws -> Int16 {\n    return Int16(bitPattern: try readShort(endianness: endianness))\n  }\n\n  /// Reads an unsigned integer (4 bytes).\n  /// - Parameter endianness: The endianness of the integer.\n  /// - Returns: The unsigned integer.\n  /// - Throws: ``BufferError/outOfBounds`` if ``index`` is out of bounds.\n  public mutating func readInteger(endianness: Endianness) throws -> UInt32 {\n    return UInt32(try readInteger(size: MemoryLayout<UInt32>.stride, endianness: endianness))\n  }\n\n  /// Reads a signed integer (4 bytes).\n  /// - Parameter endianness: The endianness of the integer.\n  /// - Returns: The signed integer.\n  /// - Throws: ``BufferError/outOfBounds`` if ``index`` is out of bounds.\n  public mutating func readSignedInteger(endianness: Endianness) throws -> Int32 {\n    return Int32(bitPattern: try readInteger(endianness: endianness))\n  }\n\n  /// Reads an unsigned long (8 bytes).\n  /// - Parameter endianness: The endianness of the integer.\n  /// - Returns: The unsigned long.\n  /// - Throws: ``BufferError/outOfBounds`` if ``index`` is out of bounds.\n  public mutating func readLong(endianness: Endianness) throws -> UInt64 {\n    return try readInteger(size: MemoryLayout<UInt64>.stride, endianness: endianness)\n  }\n\n  /// Reads a signed long (8 bytes).\n  /// - Parameter endianness: The endianness of the integer.\n  /// - Returns: The signed long.\n  /// - Throws: ``BufferError/outOfBounds`` if ``index`` is out of bounds.\n  public mutating func readSignedLong(endianness: Endianness) throws -> Int64 {\n    return Int64(bitPattern: try readLong(endianness: endianness))\n  }\n\n  /// Reads a float (4 bytes).\n  /// - Parameter endianness: The endianness of the float.\n  /// - Returns: The float.\n  /// - Throws: ``BufferError/outOfBounds`` if ``index`` is out of bounds.\n  public mutating func readFloat(endianness: Endianness) throws -> Float {\n    return Float(bitPattern: try readInteger(endianness: endianness))\n  }\n\n  /// Reads a double (4 bytes).\n  /// - Parameter endianness: The endianness of the double.\n  /// - Returns: The double.\n  /// - Throws: ``BufferError/outOfBounds`` if ``index`` is out of bounds.\n  public mutating func readDouble(endianness: Endianness) throws -> Double {\n    return Double(bitPattern: try readLong(endianness: endianness))\n  }\n\n  /// Reads a variable length integer.\n  /// - Parameter maximumSize: The maximum number of bytes after decoding (i.e. a maximum of 4 could\n  ///   require reading 5 bytes because only 7 bits are encoded per byte).\n  /// - Returns: The integer stored as a 64 bit integer.\n  /// - Throws: ``BufferError/outOfBounds`` if ``index`` is out of bounds. ``BufferError/variableIntegerTooLarge``\n  ///   if the integer is larger than `maximumSize`.\n  /// - Precondition: `maximumSize` is no more than 8 (the number of bytes in a `UInt64`).\n  public mutating func readVariableLengthInteger(maximumSize: Int) throws -> UInt64 {\n    precondition(maximumSize <= MemoryLayout<UInt64>.stride)\n\n    let maximumBits = UInt64(maximumSize * 8)\n\n    var bitCount: UInt64 = 0\n    var bitPattern: UInt64 = 0\n    while true {\n      guard bitCount < maximumBits else {\n        throw BufferError.variableIntegerTooLarge(maximum: maximumSize)\n      }\n\n      // Read byte and remove continuation bit\n      let byte = try readByte()\n      let newBits = UInt64(byte & 0x7f)\n\n      // Ensure that the new bits would not overflow the integer\n      let remainingBits: UInt64 = maximumBits - bitCount\n      if remainingBits < 8 {\n        // mask is 0b11...11 where the number of 1s is remainingBits\n        let mask: UInt64 = (1 << remainingBits) - 1\n        guard newBits & mask == newBits else {\n          throw BufferError.variableIntegerTooLarge(maximum: maximumSize)\n        }\n      }\n\n      // Prepend bits to the bit pattern\n      bitPattern += newBits << bitCount\n      bitCount += 7\n\n      // Check for continuation bit (most significant bit)\n      if byte & 0x80 != 0x80 {\n        break\n      }\n    }\n\n    return bitPattern\n  }\n\n  /// Reads a variable length integer (4 bytes, stored as up to 5 bytes).\n  /// - Returns: The integer stored as a 64 bit integer.\n  /// - Throws: ``BufferError/outOfBounds`` if ``index`` is out of bounds. ``BufferError/variableIntegerTooLarge``\n  ///   if the integer is encoded as more than 5 bytes.\n  public mutating func readVariableLengthInteger() throws -> Int32 {\n    let int = try readVariableLengthInteger(maximumSize: MemoryLayout<Int32>.stride)\n    let bitPattern = UInt32(int)\n    return Int32(bitPattern: bitPattern)\n  }\n\n  /// Reads a variable length long (8 bytes, stored as up to 10 bytes).\n  /// - Returns: The integer stored as a 64 bit integer.\n  /// - Throws: ``BufferError/outOfBounds`` if ``index`` is out of bounds. ``BufferError/variableIntegerTooLarge``\n  ///   if the long is encoded as more than 10 bytes.\n  public mutating func readVariableLengthLong() throws -> Int64 {\n    let bitPattern = try readVariableLengthInteger(maximumSize: MemoryLayout<Int64>.stride)\n    return Int64(bitPattern: bitPattern)\n  }\n\n  /// Reads a string.\n  /// - Parameter length: The length of the string in bytes.\n  /// - Returns: The string.\n  /// - Throws: ``BufferError/outOfBounds`` if ``index`` is out of bounds. ``BufferError/invalidByteInUTF8String``\n  ///   if the bytes cannot be converted to a strig.\n  public mutating func readString(length: Int) throws -> String {\n    let stringBytes = try readBytes(length)\n    guard let string = String(bytes: stringBytes, encoding: .utf8) else {\n      throw BufferError.invalidByteInUTF8String\n    }\n    return string\n  }\n\n  // MARK: Writing\n\n  public mutating func writeByte(_ byte: UInt8) {\n    bytes.append(byte)\n  }\n\n  public mutating func writeSignedByte(_ signedByte: Int8) {\n    writeByte(UInt8(bitPattern: signedByte))\n  }\n\n  public mutating func writeBytes(_ byteArray: [UInt8]) {\n    bytes.append(contentsOf: byteArray)\n  }\n\n  public mutating func writeSignedBytes(_ signedBytes: [Int8]) {\n    for signedByte in signedBytes {\n      bytes.append(UInt8(bitPattern: signedByte))\n    }\n  }\n\n  public mutating func writeBitPattern(_ bitPattern: UInt64, numBytes: Int, endianness: Endianness) {\n    switch endianness {\n      case .big:\n        for i in 1...numBytes {\n          let byte = UInt8((bitPattern >> ((numBytes - i) * 8)) & 0xff)\n          writeByte(byte)\n        }\n      case .little:\n        for i in 1...numBytes {\n          let byte = UInt8((bitPattern >> ((numBytes - (numBytes - i)) * 8)) & 0xff)\n          writeByte(byte)\n        }\n    }\n  }\n\n  public mutating func writeShort(_ short: UInt16, endianness: Endianness) {\n    writeBitPattern(UInt64(short), numBytes: 2, endianness: endianness)\n  }\n\n  public mutating func writeSignedShort(_ signedShort: Int16, endianness: Endianness) {\n    writeShort(UInt16(bitPattern: signedShort), endianness: endianness)\n  }\n\n  public mutating func writeInt(_ int: UInt32, endianness: Endianness) {\n    writeBitPattern(UInt64(int), numBytes: 4, endianness: endianness)\n  }\n\n  public mutating func writeSignedInt(_ signedInt: Int32, endianness: Endianness) {\n    writeInt(UInt32(bitPattern: signedInt), endianness: endianness)\n  }\n\n  public mutating func writeLong(_ long: UInt64, endianness: Endianness) {\n    writeBitPattern(long, numBytes: 8, endianness: endianness)\n  }\n\n  public mutating func writeSignedLong(_ signedLong: Int64, endianness: Endianness) {\n    writeLong(UInt64(bitPattern: signedLong), endianness: endianness)\n  }\n\n  public mutating func writeFloat(_ float: Float, endianness: Endianness) {\n    writeBitPattern(UInt64(float.bitPattern), numBytes: 4, endianness: endianness)\n  }\n\n  public mutating func writeDouble(_ double: Double, endianness: Endianness) {\n    writeBitPattern(double.bitPattern, numBytes: 8, endianness: endianness)\n  }\n\n  public mutating func writeVarBitPattern(_ varBitPattern: UInt64) {\n    var bitPattern = varBitPattern\n    repeat {\n      var toWrite = bitPattern & 0x7f\n      bitPattern >>= 7\n      if bitPattern != 0 {\n        toWrite |= 0x80\n      }\n      writeByte(UInt8(toWrite))\n    } while bitPattern != 0\n  }\n\n  public mutating func writeVarInt(_ varInt: Int32) {\n    let bitPattern = UInt32(bitPattern: varInt)\n    writeVarBitPattern(UInt64(bitPattern))\n  }\n\n  public mutating func writeVarLong(_ varLong: Int64) {\n    let bitPattern = UInt64(bitPattern: varLong)\n    writeVarBitPattern(bitPattern)\n  }\n\n  public mutating func writeString(_ string: String) {\n    let stringBytes = [UInt8](string.utf8)\n    writeBytes(stringBytes)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/BufferError.swift",
    "content": "import Foundation\n\n/// An error thrown by ``Buffer``.\npublic enum BufferError: Error {\n  case invalidByteInUTF8String\n  case skippedOutOfBounds(length: Int, index: Int)\n  case outOfBounds(length: Int, index: Int)\n  case rangeOutOfBounds(length: Int, start: Int, end: Int)\n  case variableIntegerTooLarge(maximum: Int)\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/PacketRegistry.swift",
    "content": "import Foundation\n\n/// Stores the clientbound packet types for a given protocol version and assigns them ids.\n///\n/// Packets are also grouped by connection state. For example, packet 0x01 in the handshaking\n/// state is usually differnet to packet 0x01 in the status state.\npublic struct PacketRegistry {\n  /// The client bound packets of this protocol.\n  public var clientboundPackets: [PacketState: [Int: ClientboundPacket.Type]] = [\n    .handshaking: [:],\n    .status: [:],\n    .login: [:],\n    .play: [:]\n  ]\n\n  /// Creates an empty packet registry.\n  public init() {}\n\n  // swiftlint:disable function_body_length\n  /// Creates the packet registry for the 1.16.1 protocol version.\n  public static func create_1_16_1() -> PacketRegistry {\n    var registry = PacketRegistry()\n\n    registry.addClientboundPackets([\n      StatusResponsePacket.self,\n      PongPacket.self\n    ], toState: .status)\n\n    registry.addClientboundPackets([\n      LoginDisconnectPacket.self,\n      EncryptionRequestPacket.self,\n      LoginSuccessPacket.self,\n      SetCompressionPacket.self,\n      LoginPluginRequestPacket.self,\n      PlayDisconnectPacket.self\n    ], toState: .login)\n\n    registry.addClientboundPackets([\n      SpawnEntityPacket.self,\n      SpawnExperienceOrbPacket.self,\n      SpawnLivingEntityPacket.self,\n      SpawnPaintingPacket.self,\n      SpawnPlayerPacket.self,\n      EntityAnimationPacket.self,\n      StatisticsPacket.self,\n      AcknowledgePlayerDiggingPacket.self,\n      BlockBreakAnimationPacket.self,\n      BlockEntityDataPacket.self,\n      BlockActionPacket.self,\n      BlockChangePacket.self,\n      BossBarPacket.self,\n      ServerDifficultyPacket.self,\n      ChatMessageClientboundPacket.self,\n      MultiBlockUpdatePacket.self,\n      TabCompleteClientboundPacket.self,\n      DeclareCommandsPacket.self,\n      WindowConfirmationClientboundPacket.self,\n      CloseWindowClientboundPacket.self,\n      WindowItemsPacket.self,\n      SetSlotPacket.self,\n      SetCooldownPacket.self,\n      PluginMessagePacket.self,\n      NamedSoundEffectPacket.self,\n      PlayDisconnectPacket.self,\n      EntityStatusPacket.self,\n      ExplosionPacket.self,\n      UnloadChunkPacket.self,\n      ChangeGameStatePacket.self,\n      OpenHorseWindowPacket.self,\n      KeepAliveClientboundPacket.self,\n      ChunkDataPacket.self,\n      EffectPacket.self,\n      ParticlePacket.self,\n      UpdateLightPacket.self,\n      JoinGamePacket.self,\n      MapDataPacket.self,\n      TradeListPacket.self,\n      EntityPositionPacket.self,\n      EntityPositionAndRotationPacket.self,\n      EntityRotationPacket.self,\n      EntityMovementPacket.self,\n      VehicleMoveClientboundPacket.self,\n      OpenBookPacket.self,\n      OpenWindowPacket.self,\n      OpenSignEditorPacket.self,\n      CraftRecipeResponsePacket.self,\n      PlayerAbilitiesPacket.self,\n      CombatEventPacket.self,\n      PlayerInfoPacket.self,\n      FacePlayerPacket.self,\n      PlayerPositionAndLookClientboundPacket.self,\n      UnlockRecipesPacket.self,\n      DestroyEntitiesPacket.self,\n      RemoveEntityEffectPacket.self,\n      ResourcePackSendPacket.self,\n      RespawnPacket.self,\n      EntityHeadLookPacket.self,\n      SelectAdvancementTabPacket.self,\n      WorldBorderPacket.self,\n      CameraPacket.self,\n      HeldItemChangePacket.self,\n      UpdateViewPositionPacket.self,\n      UpdateViewDistancePacket.self,\n      SpawnPositionPacket.self,\n      DisplayScoreboardPacket.self,\n      EntityMetadataPacket.self,\n      AttachEntityPacket.self,\n      EntityVelocityPacket.self,\n      EntityEquipmentPacket.self,\n      SetExperiencePacket.self,\n      UpdateHealthPacket.self,\n      ScoreboardObjectivePacket.self,\n      SetPassengersPacket.self,\n      TeamsPacket.self,\n      UpdateScorePacket.self,\n      TimeUpdatePacket.self,\n      TitlePacket.self,\n      EntitySoundEffectPacket.self,\n      SoundEffectPacket.self,\n      StopSoundPacket.self,\n      PlayerListHeaderAndFooterPacket.self,\n      NBTQueryResponsePacket.self,\n      CollectItemPacket.self,\n      EntityTeleportPacket.self,\n      AdvancementsPacket.self,\n      EntityAttributesPacket.self,\n      EntityEffectPacket.self,\n      DeclareRecipesPacket.self,\n      TagsPacket.self,\n      WindowPropertyPacket.self\n    ], toState: .play)\n    return registry\n  }\n  // swiftlint:enable function_body_length\n\n  /// Adds an array of clientbound packets to the given connection state. Each packet is assigned the id which is stored as a static property on the type.\n  public mutating func addClientboundPackets(_ packets: [ClientboundPacket.Type], toState state: PacketState) {\n    for packet in packets {\n      addClientboundPacket(packet, toState: state)\n    }\n  }\n\n  /// Adds a clientbound packet to the given connection state. The packet is assigned the id which is stored in the static property `id`.\n  public mutating func addClientboundPacket(_ packet: ClientboundPacket.Type, toState state: PacketState) {\n    let id = packet.id\n    if var packets = clientboundPackets[state] {\n      packets[id] = packet\n      clientboundPackets[state] = packets\n    } else {\n      clientboundPackets[state] = [id: packet]\n    }\n  }\n\n  /// Gets the packet type for the requested packet id and connection state.\n  public func getClientboundPacketType(withId id: Int, andState state: PacketState) -> ClientboundPacket.Type? {\n    return clientboundPackets[state]?[id]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/PacketState.swift",
    "content": "import Foundation\n\npublic enum PacketState {\n  case handshaking\n  case status\n  case login\n  case play\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/ClientboundEntityPacket.swift",
    "content": "/// A marker for packets that must be handled during the game tick.\n///\n/// > Warning: Packets conforming to this must not acquire a nexus lock because this will cause\n/// > deadlocks. The tick scheduler will already have a lock by the time the packets are handled.\npublic protocol ClientboundEntityPacket: ClientboundPacket {}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/ClientboundPacket.swift",
    "content": "import Foundation\n\npublic enum ClientboundPacketError: LocalizedError {\n  case invalidDifficulty\n  case invalidGamemode(rawValue: Int8)\n  case invalidServerId\n  case invalidJSONString\n  case invalidInventorySlotCount(Int)\n  case invalidInventorySlotIndex(Int, windowId: Int)\n  case invalidChangeGameStateReasonRawValue(ChangeGameStatePacket.Reason.RawValue)\n  case invalidDimension(Identifier)\n  case invalidBossBarActionId(Int)\n  case invalidBossBarColorId(Int)\n  case invalidBossBarStyleId(Int)\n  case duplicateBossBar(UUID)\n  case noSuchBossBar(UUID)\n  case invalidPoseId(Int)\n  case invalidEntityMetadataDatatypeId(Int)\n  case incorrectEntityMetadataDatatype(\n    property: String,\n    expectedType: String,\n    value: EntityMetadataPacket.Value\n  )\n\n  public var errorDescription: String? {\n    switch self {\n      case .invalidDifficulty:\n        return \"Invalid difficulty.\"\n      case let .invalidGamemode(rawValue):\n        return \"\"\"\n          Invalid gamemode.\n          Raw value: \\(rawValue)\n          \"\"\"\n      case .invalidServerId:\n        return \"Invalid server Id.\"\n      case .invalidJSONString:\n        return \"Invalid JSON string.\"\n      case let .invalidInventorySlotCount(slotCount):\n        return \"\"\"\n          Invalid inventory slot count.\n          Slot count: \\(slotCount)\n          \"\"\"\n      case let .invalidInventorySlotIndex(slotIndex, windowId):\n        return \"\"\"\n          Invalid inventory slot index.\n          Slot index: \\(slotIndex)\n          Window Id: \\(windowId)\n          \"\"\"\n      case let .invalidChangeGameStateReasonRawValue(rawValue):\n        return \"\"\"\n          Invalid change game state reason.\n          Raw value: \\(rawValue)\n          \"\"\"\n      case let .invalidDimension(identifier):\n        return \"\"\"\n          Invalid dimension.\n          Identifier: \\(identifier)\n          \"\"\"\n      case let .invalidBossBarActionId(actionId):\n        return \"\"\"\n          Invalid boss bar action id.\n          Id: \\(actionId)\n          \"\"\"\n      case let .invalidBossBarColorId(colorId):\n        return \"\"\"\n          Invalid boss bar color id.\n          Id: \\(colorId)\n          \"\"\"\n      case let .invalidBossBarStyleId(styleId):\n        return \"\"\"\n          Invalid boss bar style id.\n          Id: \\(styleId)\n          \"\"\"\n      case let .duplicateBossBar(uuid):\n        return \"\"\"\n          Received duplicate boss bar.\n          UUID: \\(uuid.uuidString)\n          \"\"\"\n      case let .noSuchBossBar(uuid):\n        return \"\"\"\n          Received update for non-existent boss bar.\n          UUID: \\(uuid)\n          \"\"\"\n      case let .invalidPoseId(poseId):\n        return \"\"\"\n          Received invalid pose id.\n          Id: \\(poseId)\n          \"\"\"\n      case let .invalidEntityMetadataDatatypeId(datatypeId):\n        return \"\"\"\n          Received invalid entity metadata datatype id.\n          Id: \\(datatypeId)\n          \"\"\"\n      case let .incorrectEntityMetadataDatatype(property, expectedType, value):\n        return \"\"\"\n          Received entity metadata property with invalid data type.\n          Property name: \\(property)\n          Expected type: \\(expectedType)\n          Value: \\(value)\n          \"\"\"\n    }\n  }\n}\n\npublic protocol ClientboundPacket {\n  static var id: Int { get }\n\n  init(from packetReader: inout PacketReader) throws\n  func handle(for client: Client) throws\n  func handle(for pinger: Pinger) throws\n}\n\nextension ClientboundPacket {\n  public func handle(for client: Client) {\n    return\n  }\n\n  public func handle(for pinger: Pinger) {\n    return\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Handshaking/Serverbound/HandshakePacket.swift",
    "content": "import Foundation\n\npublic struct HandshakePacket: ServerboundPacket {\n  public static let id: Int = 0x00\n  \n  public var protocolVersion: Int\n  public var serverAddr: String\n  public var serverPort: Int\n  public var nextState: NextState\n  \n  public enum NextState: Int {\n    case status = 1\n    case login = 2\n  }\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(Int32(protocolVersion))\n    writer.writeString(serverAddr)\n    writer.writeUnsignedShort(UInt16(serverPort))\n    writer.writeVarInt(Int32(nextState.rawValue))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Login/Clientbound/EncryptionRequestPacket.swift",
    "content": "import Foundation\n\npublic enum EncryptionRequestPacketError: LocalizedError {\n  case incorrectAccountType\n\n  public var errorDescription: String? {\n    switch self {\n      case .incorrectAccountType:\n        return \"Incorrect account type.\"\n    }\n  }\n}\n\npublic struct EncryptionRequestPacket: ClientboundPacket, Sendable {\n  public static let id: Int = 0x01\n\n  public let serverId: String\n  public let publicKey: [UInt8]\n  public let verifyToken: [UInt8]\n\n  public init(from packetReader: inout PacketReader) throws {\n    serverId = try packetReader.readString()\n\n    let publicKeyLength = try packetReader.readVarInt()\n    publicKey = try packetReader.readByteArray(length: publicKeyLength)\n\n    let verifyTokenLength = try packetReader.readVarInt()\n    verifyToken = try packetReader.readByteArray(length: verifyTokenLength)\n  }\n\n  public func handle(for client: Client) throws {\n    client.eventBus.dispatch(LoginStartEvent())\n    let sharedSecret = try CryptoUtil.generateRandomBytes(16)\n\n    guard let serverIdData = serverId.data(using: .ascii) else {\n      throw ClientboundPacketError.invalidServerId\n    }\n\n    // Calculate the server hash\n    let serverHash = try CryptoUtil.sha1MojangDigest([\n      serverIdData,\n      Data(sharedSecret),\n      Data(publicKey)\n    ])\n\n    // TODO: Clean up EncryptionRequestPacket.handle\n    // Request to join the server\n    if let account = client.account?.online {\n      let accessToken = account.accessToken\n      let selectedProfile = account.id\n\n      Task {\n        do {\n          try await MojangAPI.join(\n            accessToken: accessToken.token,\n            selectedProfile: selectedProfile,\n            serverHash: serverHash\n          )\n        } catch {\n          log.error(\"Join request for online server failed: \\(error)\")\n          client.eventBus.dispatch(PacketHandlingErrorEvent(packetId: Self.id, error: \"Join request for online server failed: \\(error)\"))\n          return\n        }\n\n        // block inbound thread until encryption is enabled\n        client.connection?.networkStack.inboundThread.sync {\n          do {\n            let publicKeyData = Data(publicKey)\n\n            // Send encryption response packet\n            let encryptedSharedSecret = try CryptoUtil.encryptRSA(\n              data: Data(sharedSecret),\n              publicKeyDERData: publicKeyData)\n            let encryptedVerifyToken = try CryptoUtil.encryptRSA(\n              data: Data(verifyToken),\n              publicKeyDERData: publicKeyData)\n            let encryptionResponse = EncryptionResponsePacket(\n              sharedSecret: [UInt8](encryptedSharedSecret),\n              verifyToken: [UInt8](encryptedVerifyToken))\n            try client.sendPacket(encryptionResponse)\n\n            // Wait for packet to send then enable encryption\n            try client.connection?.networkStack.outboundThread.sync {\n              try client.connection?.enableEncryption(sharedSecret: sharedSecret)\n            }\n          } catch {\n            log.error(\"Failed to enable encryption: \\(error)\")\n          }\n        }\n      }\n    } else {\n      log.error(\"Cannot join an online server with an offline account\")\n      throw EncryptionRequestPacketError.incorrectAccountType\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Login/Clientbound/LoginDisconnectPacket.swift",
    "content": "import Foundation\n\npublic struct LoginDisconnectPacket: ClientboundPacket {\n  public static let id: Int = 0x00\n  \n  public var reason: ChatComponent\n  \n  public init(from packetReader: inout PacketReader) throws {\n    reason = try packetReader.readChat()\n  }\n  \n  public func handle(for client: Client) {\n    let locale = client.resourcePack.getDefaultLocale()\n    let message = reason.toText(with: locale)\n\n    log.trace(\"Disconnected from server: \\(message)\")\n    client.eventBus.dispatch(LoginDisconnectEvent(reason: message))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Login/Clientbound/LoginPluginRequestPacket.swift",
    "content": "import Foundation\n\npublic struct LoginPluginRequestPacket: ClientboundPacket {\n  public static let id: Int = 0x04\n\n  public var messageId: Int\n  public var channel: Identifier\n  public var data: [UInt8]\n\n  public init(from packetReader: inout PacketReader) throws {\n    messageId = try packetReader.readVarInt()\n    channel = try packetReader.readIdentifier()\n    data = try packetReader.readByteArray(length: packetReader.remaining)\n  }\n\n  public func handle(for client: Client) throws {\n    try client.sendPacket(LoginPluginResponsePacket(\n      messageId: messageId,\n      wasSuccess: false,\n      data: []\n    ))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Login/Clientbound/LoginSuccessPacket.swift",
    "content": "import Foundation\n\npublic struct LoginSuccessPacket: ClientboundPacket {\n  public static let id: Int = 0x02\n  \n  public var uuid: UUID\n  public var username: String\n  \n  public init(from packetReader: inout PacketReader) throws {\n    uuid = try packetReader.readUUID()\n    username = try packetReader.readString()\n  }\n  \n  public func handle(for client: Client) throws {\n    client.connection?.setState(.play)\n    client.eventBus.dispatch(LoginSuccessEvent())\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Login/Clientbound/SetCompressionPacket.swift",
    "content": "import Foundation\n\npublic struct SetCompressionPacket: ClientboundPacket {\n  public static let id: Int = 0x03\n\n  public var threshold: Int\n  \n  public init(from packetReader: inout PacketReader) throws {\n    threshold = try packetReader.readVarInt()\n  }\n  \n  public func handle(for client: Client) throws {\n    client.connection?.setCompression(threshold: threshold)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Login/Serverbound/EncryptionResponsePacket.swift",
    "content": "import Foundation\n\npublic struct EncryptionResponsePacket: ServerboundPacket {\n  public static let id: Int = 0x01\n  \n  public var sharedSecret: [UInt8]\n  public var verifyToken: [UInt8]\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(Int32(sharedSecret.count))\n    writer.writeByteArray(sharedSecret)\n    writer.writeVarInt(Int32(verifyToken.count))\n    writer.writeByteArray(verifyToken)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Login/Serverbound/LoginPluginResponsePacket.swift",
    "content": "import Foundation\n\npublic struct LoginPluginResponsePacket: ServerboundPacket {\n  public static let id: Int = 0x02\n  \n  public var messageId: Int\n  public var wasSuccess: Bool\n  public var data: [UInt8]\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(Int32(messageId))\n    writer.writeBool(!data.isEmpty ? wasSuccess : false)\n    writer.writeByteArray(data)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Login/Serverbound/LoginStartPacket.swift",
    "content": "import Foundation\n\npublic struct LoginStartPacket: ServerboundPacket {\n  public static let id: Int = 0x00\n  \n  public var username: String\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeString(username)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/PacketReader.swift",
    "content": "import FirebladeMath\nimport Foundation\n\n/// A wrapper around ``Buffer`` that is specialized for reading Minecraft packets.\npublic struct PacketReader {\n  /// The packet's id.\n  public let packetId: Int\n  /// The packet's bytes as a buffer.\n  public var buffer: Buffer\n\n  /// The number of bytes remaining in the packet's buffer.\n  public var remaining: Int {\n    return buffer.remaining\n  }\n\n  // MARK: Init\n\n  /// Creates a new packet reader.\n  ///\n  /// Expects the start of the bytes to be a variable length integer encoding the packet's id.\n  /// - Parameter bytes: The packet's bytes.\n  /// - Throws: A ``BufferError`` if the packet's id cannot be read.\n  public init(bytes: [UInt8]) throws {\n    self.buffer = Buffer(bytes)\n    self.packetId = Int(try buffer.readVariableLengthInteger())\n  }\n\n  /// Creates a new packet reader.\n  ///\n  /// Expects the start of the buffer to be a variable length integer encoding the packet's id.\n  /// - Parameter bytes: The packet's bytes as a buffer. Reading starts from the buffer's current index.\n  /// - Throws: A ``BufferError`` if the packet's id cannot be read.\n  public init(buffer: Buffer) throws {\n    self.buffer = buffer\n    self.packetId = Int(try self.buffer.readVariableLengthInteger())\n  }\n\n  // MARK: Public methods\n\n  /// Reads a boolean (1 byte).\n  /// - Returns: A boolean.\n  /// - Throws: A ``BufferError`` if out of bounds.\n  public mutating func readBool() throws -> Bool {\n    let byte = try buffer.readByte()\n    let bool = byte == 1\n    return bool\n  }\n\n  /// Optionally reads a value (assuming that the value's presence is indicated by a boolean\n  /// field directly preceding it).\n  public mutating func readOptional<T>(_ inner: (inout Self) throws -> T) throws -> T? {\n    if try readBool() {\n      return try inner(&self)\n    } else {\n      return nil\n    }\n  }\n\n  /// Reads a direction (represented as a VarInt).\n  public mutating func readDirection() throws -> Direction {\n    let rawValue = try readVarInt()\n    guard let direction = Direction(rawValue: rawValue) else {\n      throw PacketReaderError.invalidDirection(rawValue)\n    }\n    return direction\n  }\n\n  /// Reads a signed byte.\n  /// - Returns: A signed byte.\n  /// - Throws: A ``BufferError`` if out of bounds.\n  public mutating func readByte() throws -> Int8 {\n    return try buffer.readSignedByte()\n  }\n\n  /// Reads an unsigned byte.\n  /// - Returns: An unsigned byte.\n  /// - Throws: A ``BufferError`` if out of bounds.\n  public mutating func readUnsignedByte() throws -> UInt8 {\n    return try buffer.readByte()\n  }\n\n  /// Reads a signed short (2 bytes).\n  /// - Returns: A signed short.\n  /// - Throws: A ``BufferError`` if out of bounds.\n  public mutating func readShort() throws -> Int16 {\n    return try buffer.readSignedShort(endianness: .big)\n  }\n\n  /// Reads an unsigned short (2 bytes).\n  /// - Returns: An unsigned short.\n  /// - Throws: A ``BufferError`` if out of bounds.\n  public mutating func readUnsignedShort() throws -> UInt16 {\n    return try buffer.readShort(endianness: .big)\n  }\n\n  /// Reads a signed integer (4 bytes).\n  /// - Returns: A signed integer.\n  /// - Throws: A ``BufferError`` if out of bounds.\n  public mutating func readInt() throws -> Int {\n    return Int(try buffer.readSignedInteger(endianness: .big))\n  }\n\n  /// Reads a signed long (8 bytes).\n  /// - Returns: A signed long.\n  /// - Throws: A ``BufferError`` if out of bounds.\n  public mutating func readLong() throws -> Int {\n    return Int(try buffer.readSignedLong(endianness: .big))\n  }\n\n  /// Reads a float (4 bytes).\n  /// - Returns: A float.\n  /// - Throws: A ``BufferError`` if out of bounds.\n  public mutating func readFloat() throws -> Float {\n    return try buffer.readFloat(endianness: .big)\n  }\n\n  /// Reads a double (8 bytes).\n  /// - Returns: A double.\n  /// - Throws: A ``BufferError`` if out of bounds.\n  public mutating func readDouble() throws -> Double {\n    return try buffer.readDouble(endianness: .big)\n  }\n\n  /// Reads a length prefixed string (length must be encoded as a variable length integer).\n  /// - Returns: A string.\n  /// - Throws: A ``BufferError`` if any reads go out of bounds. ``PacketReaderError/stringTooLong`` if the string is longer than 32767 (Minecraft's maximum string length).\n  public mutating func readString() throws -> String {\n    let length = Int(try buffer.readVariableLengthInteger())\n\n    guard length <= 32767 else {\n      throw PacketReaderError.stringTooLong(length: length)\n    }\n\n    let string = try buffer.readString(length: length)\n    return string\n  }\n\n  /// Reads a variable length integer (4 bytes, encoded as up to 5 bytes).\n  /// - Returns: A signed integer.\n  /// - Throws: A ``BufferError`` if any reads go out of bounds or the integer is encoded as more than 5 bytes.\n  public mutating func readVarInt() throws -> Int {\n    return Int(try buffer.readVariableLengthInteger())\n  }\n\n  /// Reads a variable length long (8 bytes, encoded as up to 10 bytes).\n  /// - Returns: A signed long.\n  /// - Throws: A ``BufferError`` if any reads go out of bounds or the long is encoded as more than 10 bytes.\n  public mutating func readVarLong() throws -> Int {\n    return Int(try buffer.readVariableLengthLong())\n  }\n\n  /// Reads and parses a JSON-encoded chat component.\n  /// - Returns: A chat component.\n  /// - Throws: A ``BufferError`` if any reads go out of bounds. A ``ChatComponentError`` if the component is invalid.\n  public mutating func readChat() throws -> ChatComponent {\n    let string = try readString()\n    do {\n      let data = Data(string.utf8)\n      let chat = try JSONDecoder().decode(ChatComponent.self, from: data)\n      return chat\n    } catch {\n      log.warning(\"Failed to decode chat message: '\\(string)' with error '\\(error)'\")\n\n      // TODO: Remove fallback once all chat components are handled\n      return ChatComponent(style: .init(), content: .string(\"<invalid chat message>\"), children: [])\n    }\n  }\n\n  /// Reads and parses an identifier (e.g. `minecraft:block/dirt`).\n  /// - Returns: An identifier.\n  /// - Throws: A ``BufferError`` if any reads go out of bounds. ``PacketReaderError/invalidIdentifier`` if the identifier is invalid.\n  public mutating func readIdentifier() throws -> Identifier {\n    let string = try readString()\n    do {\n      let identifier = try Identifier(string)\n      return identifier\n    } catch {\n      throw PacketReaderError.invalidIdentifier(string)\n    }\n  }\n\n  /// Reads an item stack.\n  /// - Returns: An item stack, or `nil` if the item stack is not present (in-game).\n  /// - Throws: A ``BufferError`` if any reads go out of bounds. ``PacketReaderError/invalidNBT`` if the slot has invalid NBT data.\n  public mutating func readSlot() throws -> Slot {\n    let isPresent = try readBool()\n    if isPresent {\n      let itemId = try readVarInt()\n      let itemCount = Int(try readByte())\n      let nbt = try readNBTCompound()\n      return Slot(ItemStack(itemId: itemId, itemCount: itemCount, nbt: nbt))\n    } else {\n      return Slot()\n    }\n  }\n\n  /// Reads an NBT compound.\n  /// - Returns: An NBT compound.\n  /// - Throws: A ``BufferError`` if any reads go out of bounds. ``PacketReaderError/invalidNBT`` if the NBT is invalid.\n  public mutating func readNBTCompound() throws -> NBT.Compound {\n    do {\n      let compound = try NBT.Compound(fromBuffer: buffer)\n      try buffer.skip(compound.numBytes)\n      return compound\n    } catch {\n      throw PacketReaderError.invalidNBT(error)\n    }\n  }\n\n  /// Reads an angle (1 byte) and returns it as radians.\n  /// - Returns: An angle in radians.\n  /// - Throws: A ``BufferError`` if any reads go out of bounds.\n  public mutating func readAngle() throws -> Float {\n    let angle = try readUnsignedByte()\n    return Float(angle) / 128 * .pi\n  }\n\n  /// Reads a UUID (16 bytes).\n  /// - Returns: A UUID.\n  /// - Throws: A ``BufferError`` if any reads go out of bounds.\n  public mutating func readUUID() throws -> UUID {\n    var bytes = try buffer.readBytes(16)\n    let uuid = Data(bytes: &bytes, count: bytes.count).withUnsafeBytes { pointer in\n      pointer.load(as: UUID.self)\n    }\n    return uuid\n  }\n\n  /// Reads a byte array.\n  /// - Parameter length: The length of byte array to read.\n  /// - Returns: A byte array.\n  /// - Throws: A ``BufferError`` if any reads go out of bounds.\n  public mutating func readByteArray(length: Int) throws -> [UInt8] {\n    return try buffer.readBytes(length)\n  }\n\n  /// Reads a packed block position (8 bytes).\n  ///\n  /// The x and z coordinates take up 26 bits each and the y coordinate takes up 12 bits.\n  /// - Returns: A block position.\n  /// - Throws: A ``BufferError`` if any reads go out of bounds.\n  public mutating func readBlockPosition() throws -> BlockPosition {\n    let val = try buffer.readLong(endianness: .big)\n\n    // Extract the bit patterns (in the order x, then z, then y)\n    var x = UInt32(val >> 38)  // x is 26 bit\n    var z = UInt32((val << 26) >> 38)  // z is 26 bit\n    var y = UInt32(val & 0xfff)  // y is 12 bit\n\n    // x and z are 26-bit signed integers, y is a 12-bit signed integer\n    let xSignBit = (x & (1 << 25)) >> 25\n    let ySignBit = (y & (1 << 11)) >> 11\n    let zSignBit = (z & (1 << 25)) >> 25\n\n    // Convert to 32 bit signed bit patterns\n    if xSignBit == 1 {\n      x |= 0b111111 << 26\n    }\n    if ySignBit == 1 {\n      y |= 0b1111_11111111_11111111 << 12\n    }\n    if zSignBit == 1 {\n      z |= 0b111111 << 26\n    }\n\n    return BlockPosition(\n      x: Int(Int32(bitPattern: x)),\n      y: Int(Int32(bitPattern: y)),\n      z: Int(Int32(bitPattern: z))\n    )\n  }\n\n  /// Reads an entity rotation (2 bytes) and returns it in radians.\n  ///\n  /// Expects yaw to be before pitch unless `pitchFirst` is `true`. Every packet except one has yaw first, thanks Mojang.\n  /// - Parameter pitchFirst: If `true`, pitch is read before yaw.\n  /// - Returns: An entity rotation in radians.\n  /// - Throws: A ``BufferError`` if any reads go out of bounds.\n  public mutating func readEntityRotation(pitchFirst: Bool = false) throws -> (\n    pitch: Float, yaw: Float\n  ) {\n    var pitch: Float = 0\n    if pitchFirst {\n      pitch = try readAngle()\n    }\n    let yaw = try readAngle()\n    if !pitchFirst {\n      pitch = try readAngle()\n    }\n    return (pitch: pitch, yaw: yaw)\n  }\n\n  /// Reads an entity position (24 bytes).\n  /// - Returns: An entity position.\n  /// - Throws: A ``BufferError`` if any reads go out of bounds.\n  public mutating func readEntityPosition() throws -> Vec3d {\n    let x = try readDouble()\n    let y = try readDouble()\n    let z = try readDouble()\n    return Vec3d(x, y, z)\n  }\n\n  /// Reads an entity velocity (6 bytes).\n  /// - Returns: An entity velocity.\n  /// - Throws: A ``BufferError`` if any reads go out of bounds.\n  public mutating func readEntityVelocity() throws -> Vec3d {\n    let x = try Double(readShort()) / 8000\n    let y = try Double(readShort()) / 8000\n    let z = try Double(readShort()) / 8000\n    return Vec3d(x, y, z)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/PacketReaderError.swift",
    "content": "import Foundation\n\n/// An error thrown by ``PacketReader``.\npublic enum PacketReaderError: Error {\n  case stringTooLong(length: Int)\n  case invalidNBT(Error)\n  case invalidIdentifier(String)\n  case invalidDirection(Int)\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/PacketWriter.swift",
    "content": "import Foundation\nimport FirebladeMath\n\n// TODO: document packet writer\npublic struct PacketWriter {\n  public var buffer: Buffer\n\n  public init() {\n    buffer = Buffer([])\n  }\n\n  // MARK: Basic datatypes\n\n  public mutating func writeBool(_ bool: Bool) {\n    let byte: UInt8 = bool ? 1 : 0\n    writeUnsignedByte(byte)\n  }\n\n  public mutating func writeByte(_ byte: Int8) {\n    buffer.writeSignedByte(byte)\n  }\n\n  public  mutating func writeUnsignedByte(_ unsignedByte: UInt8) {\n    buffer.writeByte(unsignedByte)\n  }\n\n  public mutating func writeByteArray(_ byteArray: [UInt8]) {\n    buffer.writeBytes(byteArray)\n  }\n\n  public mutating func writeShort(_ short: Int16) {\n    buffer.writeSignedShort(short, endianness: .big)\n  }\n\n  public mutating func writeUnsignedShort(_ unsignedShort: UInt16) {\n    buffer.writeShort(unsignedShort, endianness: .big)\n  }\n\n  public mutating func writeInt(_ int: Int32) {\n    buffer.writeSignedInt(int, endianness: .big)\n  }\n\n  public mutating func writeLong(_ long: Int64) {\n    buffer.writeSignedLong(long, endianness: .big)\n  }\n\n  public mutating func writeFloat(_ float: Float) {\n    buffer.writeFloat(float, endianness: .big)\n  }\n\n  public mutating func writeDouble(_ double: Double) {\n    buffer.writeDouble(double, endianness: .big)\n  }\n\n  public mutating func writeString(_ string: String) {\n    let length = string.utf8.count\n    precondition(length < 32767, \"string too long to write\")\n\n    writeVarInt(Int32(length))\n    buffer.writeString(string)\n  }\n\n  public mutating func writeVarInt(_ varInt: Int32) {\n    buffer.writeVarInt(varInt)\n  }\n\n  public mutating func writeVarLong(_ varLong: Int64) {\n    buffer.writeVarLong(varLong)\n  }\n\n  public mutating func writeIdentifier(_ identifier: Identifier) {\n    writeString(identifier.description)\n  }\n\n  // MARK: Complex datatypes\n  // IMPLEMENT: Entity Metadata, Angle\n\n  public mutating func writeSlot(_ slot: Slot) {\n    if let itemStack = slot.stack {\n      writeBool(true)\n      writeVarInt(Int32(itemStack.itemId))\n      writeByte(Int8(itemStack.count))\n      writeNBT(itemStack.nbt)\n    } else {\n      writeBool(false)\n    }\n  }\n\n  public mutating func writeNBT(_ nbtCompound: NBT.Compound) {\n    var compound = nbtCompound\n    buffer.writeBytes(compound.pack())\n  }\n\n  public mutating func writeUUID(_ uuid: UUID) {\n    let bytes = uuid.toBytes()\n    buffer.writeBytes(bytes)\n  }\n\n  public mutating func writePosition(_ position: BlockPosition) {\n    var val: UInt64 = (UInt64(bitPattern: Int64(position.x)) & 0x3FFFFFF) << 38\n    val |= (UInt64(bitPattern: Int64(position.z)) & 0x3FFFFFF) << 12\n    val |= UInt64(bitPattern: Int64(position.y)) & 0xFFF\n    buffer.writeLong(val, endianness: .big)\n  }\n\n  public mutating func writeEntityPosition(_ position: Vec3d) {\n    writeDouble(position.x)\n    writeDouble(position.y)\n    writeDouble(position.z)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/AcknowledgePlayerDiggingPacket.swift",
    "content": "import Foundation\n\npublic struct AcknowledgePlayerDiggingPacket: ClientboundPacket {\n  public static let id: Int = 0x07\n  \n  public var location: BlockPosition\n  public var block: Int\n  public var status: Int\n  public var successful: Bool\n  \n  public init(from packetReader: inout PacketReader) throws {\n    location = try packetReader.readBlockPosition()\n    block = try packetReader.readVarInt()\n    status = try packetReader.readVarInt()\n    successful = try packetReader.readBool()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/AdvancementsPacket.swift",
    "content": "import Foundation\n\npublic struct AdvancementsPacket: ClientboundPacket {\n  public static let id: Int = 0x57\n\n  public var shouldReset: Bool\n  public var advancements: [Identifier: Advancement]\n  public var advancementsToRemove: [Identifier]\n  public var advancementProgresses: [Identifier: AdvancementProgress]\n\n  public struct Advancement {\n    public var hasParent: Bool\n    public var parentId: Identifier?\n    public var hasDisplay: Bool\n    public var displayData: AdvancementDisplay?\n    public var criteria: [Identifier]\n    public var requirements: [[String]]\n  }\n\n  public struct AdvancementDisplay {\n    public var title: ChatComponent\n    public var description: ChatComponent\n    public var icon: Slot\n    public var frameType: Int\n    public var flags: Int\n    public var backgroundTexture: Identifier?\n    public var xCoord: Float\n    public var yCoord: Float\n  }\n\n  public struct AdvancementProgress {\n    public var criteria: [Identifier: CriterionProgress]\n  }\n\n  public struct CriterionProgress {\n    public var achieved: Bool\n    public var dateOfAchieving: Int?\n  }\n\n  public init(from packetReader: inout PacketReader) throws {\n    shouldReset = try packetReader.readBool()\n    advancements = try Self.readAdvancements(from: &packetReader)\n    advancementsToRemove = try Self.readAdvancementsToRemove(from: &packetReader)\n    advancementProgresses = try Self.readAdvancementProgresses(from: &packetReader)\n  }\n\n  private static func readAdvancements(from packetReader: inout PacketReader) throws -> [Identifier: Advancement] {\n    let mappingSize = try packetReader.readVarInt()\n    var advancements: [Identifier: Advancement] = [:]\n    for _ in 0..<mappingSize {\n      let key = try packetReader.readIdentifier()\n\n      // read advancement\n      let hasParent = try packetReader.readBool()\n      let parentId = hasParent ? try packetReader.readIdentifier() : nil\n      let hasDisplay = try packetReader.readBool()\n      var displayData: AdvancementDisplay?\n      if hasDisplay {\n        let title = try packetReader.readChat()\n        let description = try packetReader.readChat()\n        let icon = try packetReader.readSlot()\n        let frameType = try packetReader.readVarInt()\n        let flags = try packetReader.readInt() // 0x1: has background texture, 0x2: show toast, 0x4: hidden\n        let backgroundTexture = flags & 0x1 == 0x1 ? try packetReader.readIdentifier() : nil\n        let xCoord = try packetReader.readFloat()\n        let yCoord = try packetReader.readFloat()\n        displayData = AdvancementDisplay(\n          title: title, description: description, icon: icon, frameType: frameType,\n          flags: flags, backgroundTexture: backgroundTexture, xCoord: xCoord, yCoord: yCoord\n        )\n      }\n\n      let numCriteria = try packetReader.readVarInt()\n      var criteria: [Identifier] = []\n      for _ in 0..<numCriteria {\n        let criterion = try packetReader.readIdentifier()\n        criteria.append(criterion)\n      }\n\n      let arrayLength = try packetReader.readVarInt()\n      var requirements: [[String]] = []\n      for _ in 0..<arrayLength {\n        let arrayLength2 = try packetReader.readVarInt()\n        var requirement: [String] = []\n        for _ in 0..<arrayLength2 {\n          let criterion = try packetReader.readString()\n          requirement.append(criterion)\n        }\n        requirements.append(requirement)\n      }\n\n      let value = Advancement(\n        hasParent: hasParent, parentId: parentId, hasDisplay: hasDisplay,\n        displayData: displayData, criteria: criteria, requirements: requirements\n      )\n      advancements[key] = value\n    }\n    return advancements\n  }\n\n  private static func readAdvancementsToRemove(from packetReader: inout PacketReader) throws -> [Identifier] {\n    let listSize = try packetReader.readVarInt()\n    var advancementsToRemove: [Identifier] = []\n    for _ in 0..<listSize {\n      let identifier = try packetReader.readIdentifier()\n      advancementsToRemove.append(identifier)\n    }\n    return advancementsToRemove\n  }\n\n  private static func readAdvancementProgresses(from packetReader: inout PacketReader) throws -> [Identifier: AdvancementProgress] {\n    let progressSize = try packetReader.readVarInt()\n    var progressMapping: [Identifier: AdvancementProgress] = [:]\n    for _ in 0..<progressSize {\n      let key = try packetReader.readIdentifier()\n\n      // read advancement progress\n      let size = try packetReader.readVarInt()\n      var criteria: [Identifier: CriterionProgress] = [:]\n      for _ in 0..<size {\n        let identifier = try packetReader.readIdentifier()\n\n        // read criterion progress\n        let achieved = try packetReader.readBool()\n        let dateOfAchieving = try achieved ? packetReader.readLong() : nil\n\n        let progress = CriterionProgress(achieved: achieved, dateOfAchieving: dateOfAchieving)\n        criteria[identifier] = progress\n      }\n\n      let advancementProgress = AdvancementProgress(criteria: criteria)\n      progressMapping[key] = advancementProgress\n    }\n    return progressMapping\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/AttachEntityPacket.swift",
    "content": "import Foundation\n\npublic struct AttachEntityPacket: ClientboundPacket {\n  public static let id: Int = 0x45\n  \n  public var attachedEntityId: Int\n  public var holdingEntityId: Int\n\n  public init(from packetReader: inout PacketReader) throws {\n    attachedEntityId = try packetReader.readInt()\n    holdingEntityId = try packetReader.readInt()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/BlockActionPacket.swift",
    "content": "import Foundation\n\npublic struct BlockActionPacket: ClientboundPacket {\n  public static let id: Int = 0x0a\n  \n  public var location: BlockPosition\n  public var actionId: UInt8\n  public var actionParam: UInt8\n  public var blockType: Int // this is the block id not the block state\n  \n  public init(from packetReader: inout PacketReader) throws {\n    location = try packetReader.readBlockPosition()\n    actionId = try packetReader.readUnsignedByte()\n    actionParam = try packetReader.readUnsignedByte()\n    blockType = try packetReader.readVarInt()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/BlockBreakAnimationPacket.swift",
    "content": "import Foundation\n\npublic struct BlockBreakAnimationPacket: ClientboundPacket {\n  public static let id: Int = 0x08\n\n  public var entityId: Int\n  public var location: BlockPosition\n  public var destroyStage: Int8\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    location = try packetReader.readBlockPosition()\n    destroyStage = try packetReader.readByte()\n  }\n\n  public func handle(for client: Client) throws {\n    client.game.world.setBlockBreakingStage(at: location, to: Int(destroyStage), for: entityId)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/BlockChangePacket.swift",
    "content": "import Foundation\n\npublic struct BlockChangePacket: ClientboundPacket {\n  public static let id: Int = 0x0b\n  \n  public var location: BlockPosition\n  public var blockId: Int\n  \n  public init(from packetReader: inout PacketReader) throws {\n    location = try packetReader.readBlockPosition()\n    blockId = try packetReader.readVarInt()\n  }\n  \n  public func handle(for client: Client) throws {\n    client.game.world.setBlockId(at: location, to: blockId)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/BlockEntityDataPacket.swift",
    "content": "import Foundation\n\npublic struct BlockEntityDataPacket: ClientboundPacket {\n  public static let id: Int = 0x09\n\n  public var position: BlockPosition\n  public var action: UInt8\n  public var nbtData: NBT.Compound\n\n  public init(from packetReader: inout PacketReader) throws {\n    position = try packetReader.readBlockPosition()\n    action = try packetReader.readUnsignedByte()\n    nbtData = try packetReader.readNBTCompound()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/BossBarPacket.swift",
    "content": "import Foundation\n\npublic struct BossBarPacket: ClientboundPacket {\n  public static let id: Int = 0x0c\n  \n  public var uuid: UUID\n  public var action: BossBarAction\n\n  \n  public enum BossBarAction {\n    case add(title: ChatComponent, health: Float, color: BossBar.Color, style: BossBar.Style, flags: BossBar.Flags)\n    case remove\n    case updateHealth(health: Float)\n    case updateTitle(title: ChatComponent)\n    case updateStyle(color: BossBar.Color, style:BossBar.Style)\n    case updateFlags(flags: BossBar.Flags)\n  }\n  \n  public init(from packetReader: inout PacketReader) throws {\n    uuid = try packetReader.readUUID()\n    let actionId = try packetReader.readVarInt()\n    switch actionId {\n      case 0:\n        let title = try packetReader.readChat()\n        let health = try packetReader.readFloat()\n        let color = try Self.readColor(from: &packetReader)\n        let style = try Self.readStyle(from: &packetReader)\n        let flags = try Self.readFlags(from: &packetReader)\n        action = .add(title: title, health: health, color: color, style: style, flags: flags)\n      case 1:\n        action = .remove\n      case 2:\n        let health = try packetReader.readFloat()\n        action = .updateHealth(health: health)\n      case 3:\n        let title = try packetReader.readChat()\n        action = .updateTitle(title: title)\n      case 4:\n        let color = try Self.readColor(from: &packetReader)\n        let style = try Self.readStyle(from: &packetReader)\n        action = .updateStyle(color: color, style: style)\n      case 5:\n        let flags = try Self.readFlags(from: &packetReader)\n        action = .updateFlags(flags: flags)\n      default:\n        throw ClientboundPacketError.invalidBossBarActionId(actionId)\n    }\n  }\n\n  public static func readColor(from packetReader: inout PacketReader) throws -> BossBar.Color {\n    let colorId = try packetReader.readVarInt()\n    guard let color = BossBar.Color(rawValue: colorId) else {\n      throw ClientboundPacketError.invalidBossBarColorId(colorId)\n    }\n    return color\n  }\n\n  public static func readStyle(from packetReader: inout PacketReader) throws -> BossBar.Style {\n    let styleId = try packetReader.readVarInt()\n    guard let style = BossBar.Style(rawValue: styleId) else {\n      throw ClientboundPacketError.invalidBossBarStyleId(styleId)\n    }\n    return style\n  }\n\n  public static func readFlags(from packetReader: inout PacketReader) throws -> BossBar.Flags {\n    let bitField = try packetReader.readUnsignedByte()\n    return BossBar.Flags(\n      darkenSky: bitField & 1 == 1,\n      createFog: bitField & 4 == 4,\n      isEnderDragonHealthBar: bitField & 2 == 2\n    )\n  }\n\n  public func handle(for client: Client) throws {\n    try client.game.mutateGUIState { guiState in\n      for (i, var bar) in guiState.bossBars.enumerated() where bar.id == uuid {\n        switch action {\n          case .remove:\n            guiState.bossBars.remove(at: i)\n            return\n          case let .updateHealth(health):\n            bar.health = health\n          case let .updateTitle(title):\n            bar.title = title\n          case let .updateStyle(color, style):\n            bar.color = color\n            bar.style = style\n          case let .updateFlags(flags):\n            bar.flags = flags\n          case .add:\n            throw ClientboundPacketError.duplicateBossBar(uuid)\n        }\n        guiState.bossBars[i] = bar\n        return\n      }\n\n      switch action {\n        case let .add(title, health, color, style, flags):\n          guiState.bossBars.append(\n            BossBar(\n              id: uuid,\n              title: title,\n              health: health,\n              color: color,\n              style: style,\n              flags: flags\n            )\n          )\n        default:\n          throw ClientboundPacketError.noSuchBossBar(uuid)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/CameraPacket.swift",
    "content": "import Foundation\n\npublic struct CameraPacket: ClientboundPacket {\n  public static let id: Int = 0x3e\n  \n  public var cameraEntityId: Int\n\n  public init(from packetReader: inout PacketReader) throws {\n    cameraEntityId = try packetReader.readVarInt()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/ChangeGameStatePacket.swift",
    "content": "import Foundation\n\npublic struct ChangeGameStatePacket: ClientboundPacket {\n  public static let id: Int = 0x1e\n\n  public var reason: Reason\n  public var value: Float\n\n  public enum Reason: UInt8 {\n    case noRespawnBlockAvailable\n    case endRaining\n    case beginRaining\n    case changeGamemode\n    case winGame\n    case demoEvent\n    case arrowHitPlayer\n    case rainLevelChange\n    case thunderLevelChange\n    case playPufferfishStingSound\n    case playElderGuardianMobAppearance\n    case enableRespawnScreen\n  }\n\n  public init(from packetReader: inout PacketReader) throws {\n    let byte = try packetReader.readUnsignedByte()\n    guard let reason = Reason(rawValue: byte) else {\n      throw ClientboundPacketError.invalidChangeGameStateReasonRawValue(byte)\n    }\n    self.reason = reason\n    value = try packetReader.readFloat()\n  }\n\n  public func handle(for client: Client) throws {\n    switch reason {\n      case .changeGamemode:\n        let rawValue = Int8(value)\n        guard let gamemode = Gamemode(rawValue: rawValue) else {\n          throw ClientboundPacketError.invalidGamemode(rawValue: rawValue)\n        }\n\n        client.game.accessPlayer { player in\n          player.gamemode.gamemode = gamemode\n        }\n      default:\n        // TODO: Implement rest of ChangeGameStatePacket handling\n        break\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/ChatMessageClientboundPacket.swift",
    "content": "import Foundation\n\npublic struct ChatMessageClientboundPacket: ClientboundEntityPacket {\n  public static let id: Int = 0x0e\n\n  public var content: ChatComponent\n  public var position: Int8\n  public var sender: UUID\n\n  public init(from packetReader: inout PacketReader) throws {\n    content = try packetReader.readChat()\n    position = try packetReader.readByte()\n    sender = try packetReader.readUUID()\n  }\n\n  public func handle(for client: Client) throws {\n    let locale = client.resourcePack.getDefaultLocale()\n\n    let message = ChatMessage(content: content, sender: sender)\n    client.game.receiveChatMessage(acquireLock: false, message)\n\n    client.eventBus.dispatch(ChatMessageReceivedEvent(message: message))\n\n    let text = content.toText(with: locale)\n    log.info(text)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/ChunkDataPacket.swift",
    "content": "import Foundation\n\npublic struct ChunkDataPacket: ClientboundPacket {\n  public static let id: Int = 0x21\n\n  public var position: ChunkPosition\n  public var fullChunk: Bool\n  public var primaryBitMask: Int\n  public var heightMap: HeightMap\n  public var ignoreOldData: Bool\n  public var biomeIds: [UInt8]\n  public var sections: [Chunk.Section]\n  public var blockEntities: [BlockEntity]\n\n  public var presentSections: [Int] {\n    return BinaryUtil.setBits(of: primaryBitMask, n: Chunk.numSections)\n  }\n\n  public init(from packetReader: inout PacketReader) throws {\n    let chunkX = Int(try packetReader.readInt())\n    let chunkZ = Int(try packetReader.readInt())\n    position = ChunkPosition(chunkX: chunkX, chunkZ: chunkZ)\n\n    fullChunk = try packetReader.readBool()\n    ignoreOldData = try packetReader.readBool()\n    primaryBitMask = try packetReader.readVarInt()\n\n    let heightMaps = try packetReader.readNBTCompound()\n    let heightMapCompact: [Int] = try heightMaps.get(\"MOTION_BLOCKING\")\n    heightMap = try Self.unpackHeightMap(heightMapCompact.map { UInt64($0) })\n\n    biomeIds = []\n    if fullChunk {\n      biomeIds.reserveCapacity(1024)\n\n      // Biomes are stored as big endian ints but biome ids are never bigger than a UInt8, so it's easy to\n      let packedBiomes = try packetReader.readByteArray(length: 1024 * 4)\n      for i in 0..<1024 {\n        biomeIds.append(packedBiomes[i * 4 + 3])\n      }\n    }\n\n    _ = try packetReader.readVarInt() // Data length (not used)\n\n    sections = try Self.readChunkSections(&packetReader, primaryBitMask: primaryBitMask)\n\n    // Read block entities\n    let numBlockEntities = try packetReader.readVarInt()\n    blockEntities = []\n    blockEntities.reserveCapacity(numBlockEntities)\n    for _ in 0..<numBlockEntities {\n      do {\n        let blockEntityNBT = try packetReader.readNBTCompound()\n        let x: Int32 = try blockEntityNBT.get(\"x\")\n        let y: Int32 = try blockEntityNBT.get(\"y\")\n        let z: Int32 = try blockEntityNBT.get(\"z\")\n        let position = BlockPosition(x: Int(x), y: Int(y), z: Int(z))\n\n        let identifierString: String = try blockEntityNBT.get(\"id\")\n        let identifier = try Identifier(identifierString)\n\n        let blockEntity = BlockEntity(position: position, identifier: identifier, nbt: blockEntityNBT)\n\n        blockEntities.append(blockEntity)\n      } catch {\n        log.warning(\"Error decoding block entity: \\(error)\")\n      }\n    }\n  }\n\n  public func handle(for client: Client) throws {\n    if let existingChunk = client.game.world.chunk(at: position) {\n      existingChunk.update(with: self)\n      client.eventBus.dispatch(World.Event.UpdateChunk(position: position, updatedSections: presentSections))\n    } else {\n      let chunk = Chunk(self)\n      client.game.world.addChunk(chunk, at: position)\n    }\n  }\n\n  /// Unpacks a heightmap in the format at https://wiki.vg/Chunk_Format. There are 256 values that are each 9 bits, compacted into longs.\n  private static func unpackHeightMap(_ compact: [UInt64]) throws -> HeightMap {\n    let values: [Int] = unpackLongArray(\n      bitsPerValue: 9,\n      longArray: compact,\n      count: Chunk.blocksPerLayer)\n    return HeightMap(heightMap: values)\n  }\n\n  /// Reads the chunk section data from the given packet. The bitmask contains which chunk sections are present.\n  ///\n  /// Some C code is used to quickly unpack the compacted long arrays which contain the block data. This\n  /// is because Swift was too slow for the tight loop and c was so many times faster.\n  private static func readChunkSections(_ packetReader: inout PacketReader, primaryBitMask: Int) throws -> [Chunk.Section] {\n    var sections: [Chunk.Section] = []\n    let presentSections = BinaryUtil.setBits(of: primaryBitMask, n: Chunk.numSections)\n    sections.reserveCapacity(presentSections.count)\n\n    for sectionIndex in 0..<Chunk.numSections {\n      if presentSections.contains(sectionIndex) {\n        let blockCount = try packetReader.readShort()\n        let bitsPerBlock = try packetReader.readUnsignedByte()\n\n        // Read palette if present\n        var palette: [UInt16] = []\n        if bitsPerBlock <= 8 {\n          let paletteLength = try packetReader.readVarInt()\n          palette.reserveCapacity(paletteLength)\n          for _ in 0..<paletteLength {\n            palette.append(UInt16(try packetReader.readVarInt()))\n          }\n        }\n\n        // Read block states\n        let dataArrayLength = try packetReader.readVarInt()\n        var dataArray: [UInt64] = []\n        dataArray.reserveCapacity(dataArrayLength)\n        for _ in 0..<dataArrayLength {\n          dataArray.append(UInt64(try packetReader.buffer.readLong(endianness: .big)))\n        }\n\n        let blocks: [UInt16] = unpackLongArray(bitsPerValue: Int(bitsPerBlock), longArray: dataArray, count: Chunk.Section.numBlocks)\n\n        let section = Chunk.Section(blockIds: blocks, palette: palette, blockCount: Int(blockCount))\n        sections.append(section)\n      } else {\n        // TODO: don't initialise empty sections until they are needed\n        // Section is empty\n        let section = Chunk.Section()\n        sections.append(section)\n      }\n    }\n    return sections\n  }\n\n  private static func unpackLongArray<T: FixedWidthInteger>(bitsPerValue: Int, longArray: [UInt64], count: Int) -> [T] {\n    let mask = T((1 << bitsPerValue) - 1)\n    let valuesPerLong = 64 / bitsPerValue\n\n    if count / valuesPerLong >= longArray.count {\n      // TODO: throw an error\n    }\n\n    let values = [T](unsafeUninitializedCapacity: Chunk.Section.numBlocks) { buffer, initializedCount in\n      longArray.withUnsafeBufferPointer { longPointer in\n        for i in 0..<count {\n          let index = i / valuesPerLong\n          let offset = (i % valuesPerLong) &* bitsPerValue\n\n          let value = T(truncatingIfNeeded: longPointer[index] &>> offset) & mask\n          buffer[i] = value\n        }\n      }\n\n      initializedCount = count\n    }\n\n    return values\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/CloseWindowClientboundPacket.swift",
    "content": "import Foundation\n\npublic struct CloseWindowClientboundPacket: ClientboundPacket {\n  public static let id: Int = 0x13\n  \n  public var windowId: UInt8\n  \n  public init(from packetReader: inout PacketReader) throws {\n    windowId = try packetReader.readUnsignedByte()\n  }\n\n  public func handle(for client: Client) throws {\n    try client.game.mutateGUIState { guiState in\n      guard let window = guiState.window else {\n        log.warning(\"Received CloseWindowClientboundPacket with no open window (window id: \\(windowId))\")\n        return\n      }\n\n      guard window.id == windowId else {\n        log.warning(\"Received CloseWindowClientboundPacket for non-existent window with id '\\(windowId)'\")\n        return\n      }\n\n      // Connection is set to nil since we don't need to send a packet (we just received one)\n      try window.close(mouseStack: &guiState.mouseItemStack, eventBus: client.eventBus, connection: nil)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/CollectItemPacket.swift",
    "content": "import Foundation\n\npublic struct CollectItemPacket: ClientboundPacket {\n  public static let id: Int = 0x55\n  \n  public var collectedEntityId: Int\n  public var collectorEntityId: Int\n  public var pickupItemCount: Int\n\n  public init(from packetReader: inout PacketReader) throws {\n    collectedEntityId = try packetReader.readVarInt()\n    collectorEntityId = try packetReader.readVarInt()\n    pickupItemCount = try packetReader.readVarInt()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/CombatEventPacket.swift",
    "content": "import Foundation\n\npublic struct CombatEventPacket: ClientboundPacket {\n  public static let id: Int = 0x32\n  \n  public var event: CombatEvent?\n  \n  public enum CombatEvent {\n    case enterCombat\n    case endCombat(duration: Int, entityId: Int)\n    case entityDead(playerId: Int, entityId: Int, message: ChatComponent)\n  }\n  \n  public init(from packetReader: inout PacketReader) throws {\n    let eventId = try packetReader.readVarInt()\n    switch eventId {\n      case 0: // enter combat\n        event = .enterCombat\n      case 1: // end combat\n        let duration = try packetReader.readVarInt()\n        let entityId = try packetReader.readInt()\n        event = .endCombat(duration: duration, entityId: entityId)\n      case 2: // entity dead\n        let playerId = try packetReader.readVarInt()\n        let entityId = try packetReader.readInt()\n        let message = try packetReader.readChat()\n        event = .entityDead(playerId: playerId, entityId: entityId, message: message)\n      default:\n        event = nil\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/CraftRecipeResponsePacket.swift",
    "content": "import Foundation\n\npublic struct CraftRecipeResponsePacket: ClientboundPacket {\n  public static let id: Int = 0x30\n  \n  public var windowId: Int8\n  public var recipe: Identifier\n  \n  public init(from packetReader: inout PacketReader) throws {\n    windowId = try packetReader.readByte()\n    recipe = try packetReader.readIdentifier()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/DeclareCommandsPacket.swift",
    "content": "import Foundation\n\npublic struct DeclareCommandsPacket: ClientboundPacket {\n  public static let id: Int = 0x11\n  \n  public init(from packetReader: inout PacketReader) throws {\n    // IMPLEMENT: declare commands packet\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/DeclareRecipesPacket.swift",
    "content": "import Foundation\n\npublic enum DeclareRecipesPacketError: LocalizedError {\n  case unknownRecipeType(_ typeDescription: String)\n  case unknownHeatRecipeType(_ typeDescription: String)\n  case unknownSpecialRecipeType(_ typeDescription: String)\n  \n  public var errorDescription: String? {\n    switch self {\n      case .unknownRecipeType(let typeDescription):\n        return \"\"\"\n        Unknown recipe type.\n        Type Description: \\(typeDescription)\n        \"\"\"\n      case .unknownHeatRecipeType(let typeDescription):\n        return \"\"\"\n        Unknown heat recipe type.\n        Type Description: \\(typeDescription)\n        \"\"\"\n      case .unknownSpecialRecipeType(let typeDescription):\n        return \"\"\"\n        Unknown special recipe type.\n        Type Description: \\(typeDescription)\n        \"\"\"\n    }\n  }\n}\n\npublic struct DeclareRecipesPacket: ClientboundPacket {\n  public static let id: Int = 0x5a\n\n  public var recipeRegistry: RecipeRegistry\n\n  public init(from packetReader: inout PacketReader) throws {\n    recipeRegistry = RecipeRegistry()\n    let numRecipes = try packetReader.readVarInt()\n    for _ in 0..<numRecipes {\n      let type = try packetReader.readIdentifier()\n      let recipeId = try packetReader.readString()\n\n      if type.namespace == \"minecraft\" {\n        switch type.name {\n          case \"crafting_shapeless\":\n            let recipe = try Self.readShapelessCraftingRecipe(from: &packetReader)\n            recipeRegistry.craftingRecipes[recipeId] = recipe\n          case \"crafting_shaped\":\n            let recipe = try Self.readShapedCraftingRecipe(from: &packetReader)\n            recipeRegistry.craftingRecipes[recipeId] = recipe\n          case \"smelting\", \"blasting\", \"smoking\", \"campfire_cooking\":\n            let recipe = try Self.readHeatRecipe(from: &packetReader, type: type)\n            recipeRegistry.heatRecipes[recipeId] = recipe\n          case \"stonecutting\":\n            let recipe = try Self.readStonecuttingRecipe(from: &packetReader)\n            recipeRegistry.stonecuttingRecipes[recipeId] = recipe\n          case \"smithing\":\n            let recipe = try Self.readSmithingRecipe(from: &packetReader)\n            recipeRegistry.smithingRecipes[recipeId] = recipe\n          case let recipeType where recipeType.starts(with: \"crafting_special_\"):\n            let recipe = try Self.specialRecipe(for: recipeType)\n            recipeRegistry.specialRecipes[recipeId] = recipe\n          default:\n            throw DeclareRecipesPacketError.unknownRecipeType(type.description)\n        }\n      }\n    }\n  }\n\n  public func handle(for client: Client) throws {\n    client.game.recipeRegistry = recipeRegistry\n  }\n\n  private static func readShapelessCraftingRecipe(from packetReader: inout PacketReader) throws -> CraftingShapeless {\n    let group = try packetReader.readString()\n\n    var ingredients: [Ingredient] = []\n    let ingredientCount = try packetReader.readVarInt()\n    for _ in 0..<ingredientCount {\n      let count = try packetReader.readVarInt()\n      var itemStacks: [Slot] = []\n      for _ in 0..<count {\n        let itemStack = try packetReader.readSlot()\n        itemStacks.append(itemStack)\n      }\n      let ingredient = Ingredient(ingredients: itemStacks)\n      ingredients.append(ingredient)\n    }\n\n    let result = try packetReader.readSlot()\n\n    return CraftingShapeless(group: group, ingredients: ingredients, result: result)\n  }\n\n  private static func readShapedCraftingRecipe(from packetReader: inout PacketReader) throws -> CraftingShaped {\n    let width = Int(try packetReader.readVarInt())\n    let height = Int(try packetReader.readVarInt())\n    let group = try packetReader.readString()\n\n    var ingredients: [Ingredient] = []\n    let ingredientCount = width * height\n    for _ in 0..<ingredientCount {\n      let count = try packetReader.readVarInt()\n      var itemStacks: [Slot] = []\n      for _ in 0..<count {\n        let itemStack = try packetReader.readSlot()\n        itemStacks.append(itemStack)\n      }\n      let ingredient = Ingredient(ingredients: itemStacks)\n      ingredients.append(ingredient)\n    }\n\n    let result = try packetReader.readSlot()\n\n    return CraftingShaped(group: group, width: width, height: height, ingredients: ingredients, result: result)\n  }\n\n  private static func readHeatRecipe(from packetReader: inout PacketReader, type: Identifier) throws -> HeatRecipe {\n    let group = try packetReader.readString()\n\n    var itemStacks: [Slot] = []\n    let count = try packetReader.readVarInt()\n    for _ in 0..<count {\n      let itemStack = try packetReader.readSlot()\n      itemStacks.append(itemStack)\n    }\n    let ingredient = Ingredient(ingredients: itemStacks)\n\n    let result = try packetReader.readSlot()\n    let experience = try packetReader.readFloat()\n    let cookingTime = Int(try packetReader.readVarInt())\n\n    var recipe: HeatRecipe\n    switch type.name {\n      case \"smelting\":\n        recipe = SmeltingRecipe(group: group, ingredient: ingredient, result: result, experience: experience, cookingTime: cookingTime)\n      case \"blasting\":\n        recipe = BlastingRecipe(group: group, ingredient: ingredient, result: result, experience: experience, cookingTime: cookingTime)\n      case \"smoking\":\n        recipe = SmokingRecipe(group: group, ingredient: ingredient, result: result, experience: experience, cookingTime: cookingTime)\n      case \"campfire_cooking\":\n        recipe = CampfireCookingRecipe(group: group, ingredient: ingredient, result: result, experience: experience, cookingTime: cookingTime)\n      default:\n        throw DeclareRecipesPacketError.unknownHeatRecipeType(type.description)\n    }\n\n    return recipe\n  }\n\n  private static func readStonecuttingRecipe(from packetReader: inout PacketReader) throws -> StonecuttingRecipe {\n    let group = try packetReader.readString()\n\n    var itemStacks: [Slot] = []\n    let count = try packetReader.readVarInt()\n    for _ in 0..<count {\n      let itemStack = try packetReader.readSlot()\n      itemStacks.append(itemStack)\n    }\n    let ingredient = Ingredient(ingredients: itemStacks)\n\n    let result = try packetReader.readSlot()\n\n    return StonecuttingRecipe(group: group, ingredient: ingredient, result: result)\n  }\n\n  private static func readSmithingRecipe(from packetReader: inout PacketReader) throws -> SmithingRecipe {\n    var baseSlots: [Slot] = []\n    let baseCount = try packetReader.readVarInt()\n    for _ in 0..<baseCount {\n      let itemStack = try packetReader.readSlot()\n      baseSlots.append(itemStack)\n    }\n    let baseIngredient = Ingredient(ingredients: baseSlots)\n\n    var additionSlots: [Slot] = []\n    let additionCount = try packetReader.readVarInt()\n    for _ in 0..<additionCount {\n      let itemStack = try packetReader.readSlot()\n      additionSlots.append(itemStack)\n    }\n    let additionIngredient = Ingredient(ingredients: additionSlots)\n\n    let result = try packetReader.readSlot()\n\n    return SmithingRecipe(base: baseIngredient, addition: additionIngredient, result: result)\n  }\n\n  // swiftlint:disable:next cyclomatic_complexity\n  private static func specialRecipe(for recipeType: String) throws -> SpecialRecipe {\n    switch recipeType {\n      case \"crafting_special_armordye\":\n        return ArmorDyeRecipe()\n      case \"crafting_special_bookcloning\":\n        return BookCloningRecipe()\n      case \"crafting_special_mapcloning\":\n        return MapCloningRecipe()\n      case \"crafting_special_mapextending\":\n        return MapExtendingRecipe()\n      case \"crafting_special_firework_rocket\":\n        return FireworkRocketRecipe()\n      case \"crafting_special_firework_star\":\n        return FireworkStarRecipe()\n      case \"crafting_special_firework_star_fade\":\n        return FireworkStarFadeRecipe()\n      case \"crafting_special_repairitem\":\n        return RepairItemRecipe()\n      case \"crafting_special_tippedarrow\":\n        return TippedArrowRecipe()\n      case \"crafting_special_bannerduplicate\":\n        return BannerDuplicateRecipe()\n      case \"crafting_special_banneraddpattern\":\n        return BannerAddPatternRecipe()\n      case \"crafting_special_shielddecoration\":\n        return ShieldDecorationRecipe()\n      case \"crafting_special_shulkerboxcoloring\":\n        return ShulkerBoxColouringRecipe()\n      case \"crafting_special_suspiciousstew\":\n        return SuspiciousStewRecipe()\n      default:\n        throw DeclareRecipesPacketError.unknownSpecialRecipeType(recipeType)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/DestroyEntitiesPacket.swift",
    "content": "/// Removes entities from the game (either dead or disconnected or outside render distance).\npublic struct DestroyEntitiesPacket: ClientboundEntityPacket {\n  public static let id: Int = 0x37\n\n  public var entityIds: [Int]\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityIds = []\n    let count = try packetReader.readVarInt()\n    for _ in 0..<count {\n      let entityId = try packetReader.readVarInt()\n      entityIds.append(entityId)\n    }\n  }\n\n  /// Should only be called if a nexus lock has already been acquired.\n  public func handle(for client: Client) throws {\n    for entityId in entityIds {\n      client.game.removeEntity(acquireLock: false, id: entityId)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/DisplayScoreboardPacket.swift",
    "content": "import Foundation\n\npublic struct DisplayScoreboardPacket: ClientboundPacket {\n  public static let id: Int = 0x43\n  \n  public var position: Int8\n  public var scoreName: String\n\n  public init(from packetReader: inout PacketReader) throws {\n    position = try packetReader.readByte()\n    scoreName = try packetReader.readString()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EffectPacket.swift",
    "content": "import Foundation\n\npublic struct EffectPacket: ClientboundPacket {\n  public static let id: Int = 0x22\n  \n  public var effectId: Int\n  public var location: BlockPosition\n  public var data: Int\n  public var disableRelativeVolume: Bool\n  \n  public init(from packetReader: inout PacketReader) throws {\n    effectId = try packetReader.readInt()\n    location = try packetReader.readBlockPosition()\n    data = try packetReader.readInt()\n    disableRelativeVolume = try packetReader.readBool()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EntityAnimationPacket.swift",
    "content": "import Foundation\n\npublic struct EntityAnimationPacket: ClientboundPacket {\n  public static let id: Int = 0x05\n  \n  public var entityId: Int\n  public var animationId: UInt8\n  \n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    animationId = try packetReader.readUnsignedByte()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EntityAttributesPacket.swift",
    "content": "import Foundation\n\npublic enum EntityAttributesPacketError: LocalizedError {\n  case invalidModifierOperationRawValue(EntityAttributeModifier.Operation.RawValue)\n  case invalidAttributeKey(String)\n  \n  public var errorDescription: String? {\n    switch self {\n      case .invalidModifierOperationRawValue(let rawValue):\n        return \"\"\"\n        Invalid modifier operation.\n        Raw value: \\(rawValue)\n        \"\"\"\n      case .invalidAttributeKey(let attributeKey):\n        return \"Invalid attribute key: \\(attributeKey)\"\n    }\n  }\n}\n\npublic struct EntityAttributesPacket: ClientboundPacket {\n  public static let id: Int = 0x58\n\n  public var entityId: Int\n  public var attributes: [EntityAttributeKey: EntityAttributeValue]\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n\n    attributes = [:]\n    let numProperties = try packetReader.readInt()\n    for _ in 0..<numProperties {\n      let key = try packetReader.readIdentifier()\n      guard let attributeKey = EntityAttributeKey(rawValue: key.description) else {\n        throw EntityAttributesPacketError.invalidAttributeKey(key.description)\n      }\n\n      let value = try packetReader.readDouble()\n\n      var modifiers: [EntityAttributeModifier] = []\n      let numModifiers = try packetReader.readVarInt()\n      for _ in 0..<numModifiers {\n        let uuid = try packetReader.readUUID()\n        let amount = try packetReader.readDouble()\n        let rawOperation = try packetReader.readUnsignedByte()\n        guard let operation = EntityAttributeModifier.Operation(rawValue: rawOperation) else {\n          throw EntityAttributesPacketError.invalidModifierOperationRawValue(rawOperation)\n        }\n        let modifier = EntityAttributeModifier(uuid: uuid, amount: amount, operation: operation)\n        modifiers.append(modifier)\n      }\n\n      let attributeValue = EntityAttributeValue(baseValue: value, modifiers: modifiers)\n      attributes[attributeKey] = attributeValue\n    }\n  }\n\n  public func handle(for client: Client) throws {\n    client.game.accessEntity(id: entityId) { entity in\n      guard let attributesComponent: EntityAttributes = entity.get() else {\n        log.warning(\"Entity attributes for entity with no attributes component: eid=\\(entityId)\")\n        return\n      }\n\n      for (key, value) in attributes {\n        attributesComponent[key] = value\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EntityEffectPacket.swift",
    "content": "import Foundation\n\npublic struct EntityEffectPacket: ClientboundPacket {\n  public static let id: Int = 0x59\n  \n  public var entityId: Int\n  public var effectId: Int8\n  public var amplifier: Int8\n  public var duration: Int\n  public var flags: Int8\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    effectId = try packetReader.readByte()\n    amplifier = try packetReader.readByte()\n    duration = try packetReader.readVarInt()\n    flags = try packetReader.readByte()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EntityEquipmentPacket.swift",
    "content": "import Foundation\n\npublic struct EntityEquipmentPacket: ClientboundPacket {\n  public static let id: Int = 0x47\n  \n  public var entityId: Int\n  public var equipments: [Equipment]\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    equipments = []\n    var isLastEquipment = false\n    while !isLastEquipment {\n      var slot = try packetReader.readUnsignedByte()\n      if slot & 0x80 != 0x80 {\n        isLastEquipment = true\n      }\n      slot = slot & 0x7f\n      let item = try packetReader.readSlot()\n      let equipment = Equipment(slot: slot, item: item)\n      equipments.append(equipment)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EntityHeadLookPacket.swift",
    "content": "import Foundation\n\npublic struct EntityHeadLookPacket: ClientboundEntityPacket {\n  public static let id: Int = 0x3b\n\n  public var entityId: Int\n  public var headYaw: Float\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    headYaw = try packetReader.readAngle()\n  }\n\n  /// Should only be called if a nexus write lock is already acquired.\n  public func handle(for client: Client) throws {\n    // TODO: Lerp entity head rotation (with a lerp duration of 3 ticks)\n    //   Would be best to implement by modifying EntityLerpState\n    client.game.accessComponent(entityId: entityId, EntityHeadYaw.self, acquireLock: false) { component in\n      component.yaw = headYaw\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EntityMetadataPacket.swift",
    "content": "import Foundation\n\n// TODO: Update this when adding a new protocol version (the format changes each version).\npublic struct EntityMetadataPacket: ClientboundPacket {\n  public static let id: Int = 0x44\n\n  public var entityId: Int\n\n  public var metadata: [MetadataEntry]\n\n  public struct MetadataEntry {\n    public var index: Int\n    public var value: Value\n  }\n\n  public enum Value {\n    case byte(Int8)\n    case varInt(Int)\n    case float(Float)\n    case string(String)\n    case chat(ChatComponent)\n    case optionalChat(ChatComponent?)\n    case slot(Slot)\n    case bool(Bool)\n    case rotation(Vec3f)\n    case position(BlockPosition)\n    case optionalPosition(BlockPosition?)\n    case direction(Direction)\n    case optionalUUID(UUID?)\n    case optionalBlockStateId(Int?)\n    case nbt(NBT.Compound)\n    case particle(Particle)\n    case villagerData(type: Int, profession: Int, level: Int)\n    case entityId(Int?)\n    case pose(Pose)\n\n    public enum Pose: Int {\n      case standing = 0\n      case fallFlying = 1\n      case sleeping = 2\n      case swimming = 3\n      case spinAttack = 4\n      case sneaking = 5\n      case longJumping = 6\n      case dying = 7\n      case croaking = 8\n      case usingTongue = 9\n      case sitting = 10\n      case roaring = 11\n      case sniffing = 12\n      case emerging = 13\n      case digging = 14\n    }\n\n    public struct Particle {\n      // TODO: These will need updating when adding support for new protocol versions. Ideally\n      //   they should be loaded dynamically from either pixlyzer (I don't think it has the right\n      //   data), or from our own data files of some sort.\n      public static let blockParticleId = 3\n      public static let dustParticleId = 14\n      public static let fallingDustParticleId = 23\n      public static let itemParticleId = 32\n\n      public var id: Int\n      public var data: Data?\n\n      public enum Data {\n        case block(blockStateId: Int)\n        case dust(red: Float, green: Float, blue: Float, scale: Float)\n        case fallingDust(blockStateId: Int)\n        case item(Slot)\n      }\n    }\n  }\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n\n    metadata = []\n    while true {\n      let index = try packetReader.readUnsignedByte()\n      if index == 0xff {\n        break\n      }\n\n      let type = try packetReader.readVarInt()\n      let value: Value\n      switch type {\n        case 0:\n          value = .byte(try packetReader.readByte())\n        case 1:\n          value = .varInt(try packetReader.readVarInt())\n        case 2:\n          value = .float(try packetReader.readFloat())\n        case 3:\n          value = .string(try packetReader.readString())\n        case 4:\n          value = .chat(try packetReader.readChat())\n        case 5:\n          value = .optionalChat(\n            try packetReader.readOptional { reader in\n              try reader.readChat()\n            }\n          )\n        case 6:\n          value = .slot(try packetReader.readSlot())\n        case 7:\n          value = .bool(try packetReader.readBool())\n        case 8:\n          value = .rotation(\n            Vec3f(\n              try packetReader.readFloat(),\n              try packetReader.readFloat(),\n              try packetReader.readFloat()\n            )\n          )\n        case 9:\n          value = .position(try packetReader.readBlockPosition())\n        case 10:\n          value = .optionalPosition(\n            try packetReader.readOptional { reader in\n              try reader.readBlockPosition()\n            }\n          )\n        case 11:\n          value = .direction(try packetReader.readDirection())\n        case 12:\n          value = .optionalUUID(\n            try packetReader.readOptional { reader in\n              try reader.readUUID()\n            }\n          )\n        case 13:\n          let rawValue = try packetReader.readVarInt()\n          if rawValue == 0 {\n            value = .optionalBlockStateId(nil)\n          } else {\n            value = .optionalBlockStateId(rawValue - 1)\n          }\n        case 14:\n          value = .nbt(try packetReader.readNBTCompound())\n        case 15:\n          let particleId = try packetReader.readVarInt()\n          let data: Value.Particle.Data?\n          switch particleId {\n            case Value.Particle.blockParticleId:\n              data = .block(blockStateId: try packetReader.readVarInt())\n            case Value.Particle.dustParticleId:\n              data = .dust(\n                red: try packetReader.readFloat(),\n                green: try packetReader.readFloat(),\n                blue: try packetReader.readFloat(),\n                scale: try packetReader.readFloat()\n              )\n            case Value.Particle.fallingDustParticleId:\n              data = .fallingDust(blockStateId: try packetReader.readVarInt())\n            case Value.Particle.itemParticleId:\n              data = .item(try packetReader.readSlot())\n            default:\n              data = nil\n          }\n          value = .particle(Value.Particle(id: particleId, data: data))\n        case 16:\n          value = .villagerData(\n            type: try packetReader.readVarInt(),\n            profession: try packetReader.readVarInt(),\n            level: try packetReader.readVarInt()\n          )\n        case 17:\n          // Value is an optional varint, but 0 represents `nil` and any other value\n          // represents `1 + value`\n          let rawValue = try packetReader.readVarInt()\n          if rawValue == 0 {\n            value = .entityId(nil)\n          } else {\n            value = .entityId(rawValue - 1)\n          }\n        case 18:\n          let rawValue = try packetReader.readVarInt()\n          guard let pose = Value.Pose(rawValue: rawValue) else {\n            throw ClientboundPacketError.invalidPoseId(rawValue)\n          }\n          value = .pose(pose)\n        default:\n          throw ClientboundPacketError.invalidEntityMetadataDatatypeId(type)\n      }\n\n      metadata.append(MetadataEntry(index: Int(index), value: value))\n    }\n  }\n\n  public func handle(for client: Client) throws {\n    try client.game.accessEntity(id: entityId) { entity in\n      guard let metadataComponent = entity.get(component: EntityMetadata.self) else {\n        log.warning(\"Entity '\\(entityId)' is missing components required to handle \\(Self.self)\")\n        return\n      }\n\n      for entry in metadata {\n        switch metadataComponent.specializedMetadata {\n          case var .mob(mobMetadata):\n            if entry.index == 14 {\n              guard case let .byte(flags) = entry.value else {\n                throw ClientboundPacketError.incorrectEntityMetadataDatatype(\n                  property: \"Mob.noAI\",\n                  expectedType: \"byte\",\n                  value: entry.value\n                )\n              }\n\n              mobMetadata.noAI = flags & 0x01 == 0x01\n              metadataComponent.specializedMetadata = .mob(mobMetadata)\n            }\n          case var .item(itemMetadata):\n            if entry.index == 7 {\n              guard case let .slot(slot) = entry.value else {\n                throw ClientboundPacketError.incorrectEntityMetadataDatatype(\n                  property: \"Item.slot\",\n                  expectedType: \"slot\",\n                  value: entry.value\n                )\n              }\n\n              itemMetadata.slot = slot\n              metadataComponent.specializedMetadata = .item(itemMetadata)\n            }\n          case nil:\n            break\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EntityMovementPacket.swift",
    "content": "import Foundation\nimport FirebladeMath\n\npublic struct EntityMovementPacket: ClientboundEntityPacket {\n  public static let id: Int = 0x2b\n\n  public var entityId: Int\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n  }\n\n  /// Should only be called if a nexus write lock is already acquired.\n  public func handle(for client: Client) throws {\n    client.game.accessComponent(entityId: entityId, EntityVelocity.self, acquireLock: false) { velocity in\n      velocity.vector = Vec3d.zero\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EntityPositionAndRotationPacket.swift",
    "content": "import FirebladeMath\nimport Foundation\n\npublic struct EntityPositionAndRotationPacket: ClientboundEntityPacket {\n  public static let id: Int = 0x29\n\n  /// The entity's id.\n  public var entityId: Int\n  /// Change in x coordinate measured in 1/4096ths of a block.\n  public var deltaX: Int16\n  /// Change in y coordinate measured in 1/4096ths of a block.\n  public var deltaY: Int16\n  /// Change in z coordinate measured in 1/4096ths of a block.\n  public var deltaZ: Int16\n  /// The entity's new pitch.\n  public var pitch: Float\n  /// The entity's new yaw.\n  public var yaw: Float\n  /// Whether the entity is on the ground or not. See ``EntityOnGround``.\n  public var onGround: Bool\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    deltaX = try packetReader.readShort()\n    deltaY = try packetReader.readShort()\n    deltaZ = try packetReader.readShort()\n    (pitch, yaw) = try packetReader.readEntityRotation()\n    onGround = try packetReader.readBool()\n  }\n\n  /// Should only be called if a nexus write lock is already acquired.\n  public func handle(for client: Client) throws {\n    let x = Double(deltaX) / 4096\n    let y = Double(deltaY) / 4096\n    let z = Double(deltaZ) / 4096\n    let relativePosition = Vec3d(x, y, z)\n\n    client.game.accessEntity(id: entityId, acquireLock: false) { entity in\n      guard\n        let position = entity.get(component: EntityPosition.self),\n        let lerpState = entity.get(component: EntityLerpState.self),\n        let velocity = entity.get(component: EntityVelocity.self),\n        let kind = entity.get(component: EntityKindId.self)?.entityKind,\n        let onGroundComponent = entity.get(component: EntityOnGround.self)\n      else {\n        log.warning(\n          \"Entity '\\(entityId)' is missing required components to handle \\(Self.self)\"\n        )\n        return\n      }\n\n      velocity.vector = .zero\n\n      let currentTargetPosition = lerpState.currentLerp?.targetPosition ?? position.vector\n      onGroundComponent.onGround = onGround\n      lerpState.lerp(\n        to: currentTargetPosition + relativePosition,\n        pitch: pitch,\n        yaw: yaw,\n        duration: kind.defaultLerpDuration\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EntityPositionPacket.swift",
    "content": "import FirebladeMath\nimport Foundation\n\npublic struct EntityPositionPacket: ClientboundEntityPacket {\n  public static let id: Int = 0x28\n\n  /// The entity's id.\n  public var entityId: Int\n  /// Change in x coordinate measured in 1/4096ths of a block.\n  public var deltaX: Int16\n  /// Change in y coordinate measured in 1/4096ths of a block.\n  public var deltaY: Int16\n  /// Change in z coordinate measured in 1/4096ths of a block.\n  public var deltaZ: Int16\n  /// Whether the entity is on the ground or not. See ``EntityOnGround``.\n  public var onGround: Bool\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    deltaX = try packetReader.readShort()\n    deltaY = try packetReader.readShort()\n    deltaZ = try packetReader.readShort()\n    onGround = try packetReader.readBool()\n  }\n\n  /// Should only be called if a nexus write lock is already acquired.\n  public func handle(for client: Client) throws {\n    let x = Double(deltaX) / 4096\n    let y = Double(deltaY) / 4096\n    let z = Double(deltaZ) / 4096\n    let relativePosition = Vec3d(x, y, z)\n\n    client.game.accessEntity(id: entityId, acquireLock: false) { entity in\n      guard\n        let position = entity.get(component: EntityPosition.self),\n        let rotation = entity.get(component: EntityRotation.self),\n        let lerpState = entity.get(component: EntityLerpState.self),\n        let velocity = entity.get(component: EntityVelocity.self),\n        let kind = entity.get(component: EntityKindId.self)?.entityKind,\n        let onGroundComponent = entity.get(component: EntityOnGround.self)\n      else {\n        log.warning(\n          \"Entity '\\(entityId)' is missing required components to handle \\(Self.self)\"\n        )\n        return\n      }\n\n      velocity.vector = .zero\n\n      // TODO: When lerping for a minecart, the velocity should get set to the relative\n      //   position too.\n      let currentTargetPosition = lerpState.currentLerp?.targetPosition ?? position.vector\n      onGroundComponent.onGround = onGround\n      lerpState.lerp(\n        to: currentTargetPosition + relativePosition,\n        pitch: lerpState.currentLerp?.targetPitch ?? rotation.pitch,\n        yaw: lerpState.currentLerp?.targetYaw ?? rotation.yaw,\n        duration: kind.defaultLerpDuration\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EntityRotationPacket.swift",
    "content": "import Foundation\n\npublic struct EntityRotationPacket: ClientboundEntityPacket {\n  public static let id: Int = 0x2a\n\n  /// The entity's id.\n  public var entityId: Int\n  /// The entity's new pitch.\n  public var pitch: Float\n  /// The entity's new yaw.\n  public var yaw: Float\n  /// Whether the entity is on the ground or not. See ``EntityOnGround``.\n  public var onGround: Bool\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    (pitch, yaw) = try packetReader.readEntityRotation()\n    onGround = try packetReader.readBool()\n  }\n\n  /// Should only be called if a nexus write lock is already acquired.\n  public func handle(for client: Client) throws {\n    client.game.accessEntity(id: entityId, acquireLock: false) { entity in\n      guard\n        let position = entity.get(component: EntityPosition.self),\n        let lerpState = entity.get(component: EntityLerpState.self),\n        let kind = entity.get(component: EntityKindId.self)?.entityKind,\n        let onGroundComponent = entity.get(component: EntityOnGround.self)\n      else {\n        log.warning(\n          \"Entity '\\(entityId)' is missing required components to handle \\(Self.self)\"\n        )\n        return\n      }\n\n      let currentTargetPosition = lerpState.currentLerp?.targetPosition ?? position.vector\n      onGroundComponent.onGround = onGround\n      lerpState.lerp(\n        to: currentTargetPosition,\n        pitch: pitch,\n        yaw: yaw,\n        duration: kind.defaultLerpDuration\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EntitySoundEffectPacket.swift",
    "content": "import Foundation\n\npublic struct EntitySoundEffectPacket: ClientboundPacket {\n  public static let id: Int = 0x50\n  \n  public var soundId: Int\n  public var soundCategory: Int\n  public var entityId: Int\n  public var volume: Float\n  public var pitch: Float\n\n  public init(from packetReader: inout PacketReader) throws {\n    soundId = try packetReader.readVarInt()\n    soundCategory = try packetReader.readVarInt()\n    entityId = try packetReader.readVarInt()\n    volume = try packetReader.readFloat()\n    pitch = try packetReader.readFloat()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EntityStatusPacket.swift",
    "content": "import Foundation\n\npublic struct EntityStatusPacket: ClientboundEntityPacket {\n  public static let id: Int = 0x1b\n\n  public var entityId: Int\n  public var status: Status?\n\n  // TODO: Add other statuses\n  public enum Status: Int8 {\n    case death = 3\n  }\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readInt()\n    status = Status(rawValue: try packetReader.readByte())\n  }\n\n  /// Should only be called if a nexus lock has already been acquired.\n  public func handle(for client: Client) throws {\n    if status == .death {\n      if entityId != client.game.accessPlayer(acquireLock: false, action: \\.entityId.id) {\n        // TODO: Play a death animation instead of instantly removing entities on death\n        client.game.removeEntity(acquireLock: false, id: entityId)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EntityTeleportPacket.swift",
    "content": "import Foundation\nimport FirebladeMath\n\npublic struct EntityTeleportPacket: ClientboundEntityPacket {\n  public static let id: Int = 0x56\n\n  /// The entity's id.\n  public var entityId: Int\n  /// The entity's new position.\n  public var position: Vec3d\n  /// The entity's new pitch.\n  public var pitch: Float\n  /// The entity's new yaw.\n  public var yaw: Float\n  /// Whether the entity is on the ground or not. See ``EntityOnGround``.\n  public var onGround: Bool\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    position = try packetReader.readEntityPosition()\n    (pitch, yaw) = try packetReader.readEntityRotation()\n    onGround = try packetReader.readBool()\n  }\n\n  /// Should only be called if a nexus write lock is already acquired.\n  public func handle(for client: Client) throws {\n    client.game.accessEntity(id: entityId, acquireLock: false) { entity in\n      guard\n        let lerpState = entity.get(component: EntityLerpState.self),\n        let velocity = entity.get(component: EntityVelocity.self),\n        let kind = entity.get(component: EntityKindId.self)?.entityKind,\n        let onGroundComponent = entity.get(component: EntityOnGround.self)\n      else {\n        return\n      }\n\n      velocity.vector = .zero\n\n      onGroundComponent.onGround = onGround\n      lerpState.lerp(\n        to: position,\n        pitch: pitch,\n        yaw: yaw,\n        duration: kind.defaultLerpDuration\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/EntityVelocityPacket.swift",
    "content": "import FirebladeMath\nimport Foundation\n\npublic struct EntityVelocityPacket: ClientboundEntityPacket {\n  public static let id: Int = 0x46\n\n  /// The entity's id.\n  public var entityId: Int\n  /// The entity's new velocity.\n  public var velocity: Vec3d\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    velocity = try packetReader.readEntityVelocity()\n  }\n\n  /// Should only be called if a nexus write lock is already acquired.\n  public func handle(for client: Client) throws {\n    client.game.accessComponent(\n      entityId: entityId,\n      EntityVelocity.self,\n      acquireLock: false\n    ) { velocityComponent in\n      // I think this packet is the cause of most of our weird entity behaviour\n      // TODO: Figure out why handling velocity is causing entities to drift (observe spiders for a while\n      //   to reproduce issue). Works best if spider is trying to climb a wall but it stuck under a roof.\n      velocityComponent.vector = velocity\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/ExplosionPacket.swift",
    "content": "import Foundation\n\npublic struct ExplosionPacket: ClientboundPacket {\n  public static let id: Int = 0x1c\n  \n  public var x: Float\n  public var y: Float\n  public var z: Float\n  public var strength: Float\n  public var records: [(Int8, Int8, Int8)] // swiftlint:disable:this large_tuple\n  public var playerMotionX: Float\n  public var playerMotionY: Float\n  public var playerMotionZ: Float\n  \n  public init(from packetReader: inout PacketReader) throws {\n    x = try packetReader.readFloat()\n    y = try packetReader.readFloat()\n    z = try packetReader.readFloat()\n    strength = try packetReader.readFloat()\n    \n    records = []\n    let recordCount = try packetReader.readInt()\n    for _ in 0..<recordCount {\n      let record = (\n        try packetReader.readByte(),\n        try packetReader.readByte(),\n        try packetReader.readByte()\n      )\n      records.append(record)\n    }\n    \n    playerMotionX = try packetReader.readFloat()\n    playerMotionY = try packetReader.readFloat()\n    playerMotionZ = try packetReader.readFloat()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/FacePlayerPacket.swift",
    "content": "import Foundation\nimport FirebladeMath\n\npublic struct FacePlayerPacket: ClientboundPacket {\n  public static let id: Int = 0x34\n\n  public var feetOrEyes: Int\n  public var targetPosition: Vec3d\n  public var isEntity: Bool\n  public var entityId: Int?\n  public var entityFeetOrEyes: Int?\n\n  public init(from packetReader: inout PacketReader) throws {\n    feetOrEyes = try packetReader.readVarInt()\n    targetPosition = try packetReader.readEntityPosition()\n    isEntity = try packetReader.readBool()\n    if isEntity {\n      entityId = try packetReader.readVarInt()\n      entityFeetOrEyes = try packetReader.readVarInt()\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/HeldItemChangePacket.swift",
    "content": "import Foundation\n\npublic struct HeldItemChangePacket: ClientboundPacket {\n  public static let id: Int = 0x3f\n  \n  public var slot: Int8\n  \n  public init(from packetReader: inout PacketReader) throws {\n    slot = try packetReader.readByte()\n  }\n  \n  public func handle(for client: Client) throws {\n    client.game.accessPlayer { player in\n      player.inventory.selectedHotbarSlot = Int(slot)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/JoinGamePacket.swift",
    "content": "import Foundation\n\n// TODO: Rename to JoinWorldPacket? That would make more sense to me\npublic struct JoinGamePacket: ClientboundPacket {\n  public static let id: Int = 0x25\n\n  public var playerEntityId: Int\n  public var isHardcore: Bool\n  public var gamemode: Gamemode\n  public var previousGamemode: Gamemode?\n  public var worldCount: Int\n  public var worldNames: [Identifier]\n  public var dimensions: [Dimension]\n  public var currentDimensionIdentifier: Identifier\n  public var worldName: Identifier\n  public var hashedSeed: Int\n  public var maxPlayers: UInt8\n  public var viewDistance: Int\n  public var reducedDebugInfo: Bool\n  public var enableRespawnScreen: Bool\n  public var isDebug: Bool\n  public var isFlat: Bool\n\n  public init(from packetReader: inout PacketReader) throws {\n    playerEntityId = try packetReader.readInt()\n    let gamemodeInt = Int8(try packetReader.readUnsignedByte())\n    isHardcore = gamemodeInt & 0x8 == 0x8\n    guard let gamemode = Gamemode(rawValue: gamemodeInt) else {\n      throw ClientboundPacketError.invalidGamemode(rawValue: gamemodeInt)\n    }\n    self.gamemode = gamemode\n    let previousGamemodeInt = try packetReader.readByte()\n    previousGamemode = Gamemode(rawValue: previousGamemodeInt)\n    worldCount = try packetReader.readVarInt()\n    worldNames = []\n    for _ in 0..<worldCount {\n      worldNames.append(try packetReader.readIdentifier())\n    }\n\n    dimensions = []\n    let dimensionCodec = try packetReader.readNBTCompound()\n    let dimensionList: [NBT.Compound] = try dimensionCodec.getList(\"dimension\")\n    for compound in dimensionList {\n      dimensions.append(try Dimension(from: compound))\n    }\n\n    currentDimensionIdentifier = try packetReader.readIdentifier()\n    worldName = try packetReader.readIdentifier()\n    hashedSeed = try packetReader.readLong()\n    maxPlayers = try packetReader.readUnsignedByte()\n    viewDistance = try packetReader.readVarInt()\n    reducedDebugInfo = try packetReader.readBool()\n    enableRespawnScreen = try packetReader.readBool()\n    isDebug = try packetReader.readBool()\n    isFlat = try packetReader.readBool()\n  }\n\n  public func handle(for client: Client) throws {\n    guard let currentDimension = dimensions.first(where: { dimension in\n      return dimension.identifier == currentDimensionIdentifier\n    }) else {\n      throw ClientboundPacketError.invalidDimension(currentDimensionIdentifier)\n    }\n\n    let world = World(\n      name: worldName,\n      dimension: currentDimension,\n      hashedSeed: hashedSeed,\n      isFlat: isFlat,\n      isDebug: isDebug,\n      eventBus: client.eventBus\n    )\n\n    client.game.maxPlayers = Int(maxPlayers)\n    client.game.maxViewDistance = viewDistance\n    client.game.debugInfoReduced = reducedDebugInfo\n    client.game.respawnScreenEnabled = enableRespawnScreen\n    client.game.isHardcore = isHardcore\n    client.game.dimensions = dimensions\n\n    var oldPlayerEntityId: Int?\n    client.game.accessPlayer { player in\n      player.playerAttributes.previousGamemode = previousGamemode\n      player.gamemode.gamemode = gamemode\n      player.playerAttributes.isHardcore = isHardcore\n      oldPlayerEntityId = player.entityId.id\n    }\n\n    if let oldPlayerEntityId = oldPlayerEntityId {\n      client.game.updateEntityId(oldPlayerEntityId, to: playerEntityId)\n    }\n\n    client.game.changeWorld(to: world)\n\n    // TODO: the event below should be dispatched from game instead of here. Event dispatching should\n    //       be done in a way that makes it clear what will and what won't emit an event.\n    client.eventBus.dispatch(JoinWorldEvent())\n    client.hasFinishedDownloadingTerrain = false\n\n    try client.connection?.sendPacket(ClientSettingsPacket(client.configuration))\n\n    client.connection?.hasJoined = true\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/KeepAliveClientboundPacket.swift",
    "content": "import Foundation\n\npublic struct KeepAliveClientboundPacket: ClientboundPacket {\n  public static let id: Int = 0x20\n  \n  public var keepAliveId: Int\n  \n  public init(from packetReader: inout PacketReader) throws {\n    keepAliveId = try packetReader.readLong()\n  }\n  \n  public func handle(for client: Client) throws {\n    let keepAlive = KeepAliveServerBoundPacket(keepAliveId: keepAliveId)\n    try client.sendPacket(keepAlive)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/MapDataPacket.swift",
    "content": "import Foundation\n\npublic struct MapDataPacket: ClientboundPacket {\n  public static let id: Int = 0x26\n  \n  public var mapId: Int\n  public var scale: Int8\n  public var trackingPosition: Bool\n  public var locked: Bool\n  public var icons: [MapIcon]\n  public var columns: UInt8\n  \n  public struct MapIcon {\n    var type: Int\n    var x: Int8\n    var z: Int8\n    var direction: Int8\n    var displayName: ChatComponent?\n  }\n  \n  public init(from packetReader: inout PacketReader) throws {\n    mapId = try packetReader.readVarInt()\n    scale = try packetReader.readByte()\n    trackingPosition = try packetReader.readBool()\n    locked = try packetReader.readBool()\n    \n    icons = []\n    let iconCount = try packetReader.readVarInt()\n    for _ in 0..<iconCount {\n      let type = try packetReader.readVarInt()\n      let x = try packetReader.readByte()\n      let z = try packetReader.readByte()\n      let direction = try packetReader.readByte()\n      let hasDisplayName = try packetReader.readBool()\n      var displayName: ChatComponent?\n      if hasDisplayName {\n        displayName = try packetReader.readChat()\n      }\n      let icon = MapIcon(type: type, x: x, z: z, direction: direction, displayName: displayName)\n      icons.append(icon)\n    }\n    \n    columns = try packetReader.readUnsignedByte()\n    if columns > 0 {\n      _ = try packetReader.readByte() // rows\n      _ = try packetReader.readByte() // x\n      _ = try packetReader.readByte() // z\n      let length = try packetReader.readVarInt()\n      var data: [UInt8] = []\n      for _ in 0..<length {\n        data.append(try packetReader.readUnsignedByte())\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/MultiBlockUpdatePacket.swift",
    "content": "import Foundation\n\npublic struct MultiBlockUpdatePacket: ClientboundPacket {\n  public static let id: Int = 0x0f\n\n  /// A block change in a multi-block change.\n  public struct BlockUpdateRecord {\n    /// X coordinate of the block change relative to the chunk.\n    public var x: UInt8\n    /// Y coordinate of the block change relative to the chunk.\n    public var y: UInt8\n    /// Z coordinate of the block change relative to the chunk.\n    public var z: UInt8\n\n    /// The new block state for the block.\n    public var blockId: Int\n  }\n\n  /// The position of the chunk the multi-block change occured in.\n  public var chunkPosition: ChunkPosition\n  /// The block changes.\n  public var records: [BlockUpdateRecord]\n\n  public init(from packetReader: inout PacketReader) throws {\n    let chunkX = try packetReader.readInt()\n    let chunkZ = try packetReader.readInt()\n    chunkPosition = ChunkPosition(chunkX: chunkX, chunkZ: chunkZ)\n\n    records = []\n\n    let recordCount = try packetReader.readVarInt()\n    for _ in 0..<recordCount {\n      let value = try packetReader.readUnsignedByte()\n      let x = value >> 4 & 0x0f\n      let z = value & 0x0f\n      let y = try packetReader.readUnsignedByte()\n      let blockId = try packetReader.readVarInt()\n      let record = BlockUpdateRecord(x: x, y: y, z: z, blockId: blockId)\n      records.append(record)\n    }\n  }\n\n  public func handle(for client: Client) throws {\n    let updates = records.map { record in\n      return World.Event.SingleBlockUpdate(\n        position: BlockPosition(\n          x: Int(record.x) + chunkPosition.chunkX * Chunk.width,\n          y: Int(record.y),\n          z: Int(record.z) + chunkPosition.chunkZ * Chunk.depth\n        ),\n        newState: record.blockId\n      )\n    }\n\n    client.game.world.processMultiBlockUpdate(updates, inChunkAt: chunkPosition)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/NBTQueryResponsePacket.swift",
    "content": "import Foundation\n\npublic struct NBTQueryResponsePacket: ClientboundPacket {\n  public static let id: Int = 0x54\n  \n  public var transactionId: Int\n  public var nbt: NBT.Compound\n\n  public init(from packetReader: inout PacketReader) throws {\n    transactionId = try packetReader.readVarInt()\n    nbt = try packetReader.readNBTCompound()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/NamedSoundEffectPacket.swift",
    "content": "import Foundation\n\npublic struct NamedSoundEffectPacket: ClientboundPacket {\n  public static let id: Int = 0x19\n  \n  public var soundName: Identifier\n  public var soundCategory: Int\n  public var effectPositionX: Int\n  public var effectPositionY: Int\n  public var effectPositionZ: Int\n  public var volume: Float\n  public var pitch: Float\n  \n  public init(from packetReader: inout PacketReader) throws {\n    soundName = try packetReader.readIdentifier()\n    soundCategory = try packetReader.readVarInt()\n    effectPositionX = try packetReader.readInt()\n    effectPositionY = try packetReader.readInt()\n    effectPositionZ = try packetReader.readInt()\n    volume = try packetReader.readFloat()\n    pitch = try packetReader.readFloat()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/OpenBookPacket.swift",
    "content": "import Foundation\n\npublic struct OpenBookPacket: ClientboundPacket {\n  public static let id: Int = 0x2d\n  \n  public var hand: Int\n  \n  public init(from packetReader: inout PacketReader) throws {\n    hand = try packetReader.readVarInt()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/OpenHorseWindowPacket.swift",
    "content": "import Foundation\n\npublic struct OpenHorseWindowPacket: ClientboundPacket {\n  public static let id: Int = 0x1f\n  \n  public var windowId: Int8\n  public var numberOfSlots: Int\n  public var entityId: Int\n  \n  public init(from packetReader: inout PacketReader) throws {\n    windowId = try packetReader.readByte()\n    numberOfSlots = try packetReader.readVarInt()\n    entityId = try packetReader.readInt()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/OpenSignEditorPacket.swift",
    "content": "import Foundation\n\npublic struct OpenSignEditorPacket: ClientboundPacket {\n  public static let id: Int = 0x2f\n  \n  public var location: BlockPosition\n  \n  public init(from packetReader: inout PacketReader) throws {\n    location = try packetReader.readBlockPosition()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/OpenWindowPacket.swift",
    "content": "import Foundation\n\npublic struct OpenWindowPacket: ClientboundPacket {\n  public static let id: Int = 0x2e\n  \n  public var windowId: Int\n  public var windowTypeId: Int\n  public var windowTitle: ChatComponent\n  \n  public init(from packetReader: inout PacketReader) throws {\n    windowId = try packetReader.readVarInt()\n    windowTypeId = try packetReader.readVarInt()\n    windowTitle = try packetReader.readChat()\n  }\n\n  public func handle(for client: Client) throws {\n    guard let windowType = WindowType.types[.vanilla(windowTypeId)] else {\n      log.warning(\"Unknown window type '\\(windowTypeId)' received in OpenWindowPacket\")\n      // Immediately close window to avoid accidentally getting banned for starting to\n      // move with the window still open.\n      try client.connection?.sendPacket(CloseWindowServerboundPacket(\n        windowId: UInt8(windowId)\n      ))\n      return\n    }\n\n    client.game.mutateGUIState { guiState in\n      guiState.window = Window(\n        id: windowId,\n        type: windowType\n      )\n    }\n\n    client.game.accessInputState { inputState in\n      inputState.releaseAll()\n    }\n    client.eventBus.dispatch(ReleaseCursorEvent())\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/ParticlePacket.swift",
    "content": "import Foundation\nimport FirebladeMath\n\npublic struct ParticlePacket: ClientboundPacket {\n  public static let id: Int = 0x23\n\n  public var particleId: Int\n  public var isLongDistance: Bool\n  public var position: Vec3d\n  public var offsetX: Float\n  public var offsetY: Float\n  public var offsetZ: Float\n  public var particleData: Float\n  public var particleCount: Int\n\n  public init(from packetReader: inout PacketReader) throws {\n    particleId = try packetReader.readInt()\n    isLongDistance = try packetReader.readBool()\n    position = try packetReader.readEntityPosition()\n    offsetX = try packetReader.readFloat()\n    offsetY = try packetReader.readFloat()\n    offsetZ = try packetReader.readFloat()\n    particleData = try packetReader.readFloat()\n    particleCount = try packetReader.readInt()\n\n    // TODO: there is also a data field but i really don't feel like decoding it rn\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/PlayDisconnectPacket.swift",
    "content": "import Foundation\n\npublic struct PlayDisconnectPacket: ClientboundPacket {\n  public static let id: Int = 0x1a\n  \n  public var reason: ChatComponent\n  \n  public init(from packetReader: inout PacketReader) throws {\n    reason = try packetReader.readChat()\n  }\n  \n  public func handle(for client: Client) {\n    let locale = client.resourcePack.getDefaultLocale()\n    let message = reason.toText(with: locale)\n\n    log.info(\"Disconnected from server: \\(message)\")\n    client.eventBus.dispatch(PlayDisconnectEvent(reason: message))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/PlayerAbilitiesPacket.swift",
    "content": "import Foundation\n\npublic struct PlayerAbilitiesPacket: ClientboundPacket {\n  public static let id: Int = 0x31\n  \n  public var flags: PlayerFlags\n  public var flyingSpeed: Float\n  public var fovModifier: Float\n  \n  public init(from packetReader: inout PacketReader) throws {\n    flags = PlayerFlags(rawValue: try packetReader.readUnsignedByte())\n    flyingSpeed = try packetReader.readFloat()\n    fovModifier = try packetReader.readFloat()\n  }\n  \n  public func handle(for client: Client) throws {\n    client.game.accessPlayer { player in\n      let attributes = player.playerAttributes\n      attributes.flyingSpeed = flyingSpeed\n      attributes.fovModifier = fovModifier\n      attributes.isInvulnerable = flags.contains(.invulnerable)\n      attributes.canFly = flags.contains(.canFly)\n      attributes.canInstantBreak = flags.contains(.instantBreak)\n      \n      player.flying.isFlying = flags.contains(.flying)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/PlayerInfoPacket.swift",
    "content": "import Foundation\n\npublic struct PlayerInfoPacket: ClientboundPacket {\n  public static let id: Int = 0x33\n\n  public var playerActions: [(uuid: UUID, action: PlayerInfoAction)]\n\n  public enum PlayerInfoAction {\n    case addPlayer(playerInfo: PlayerInfo)\n    case updateGamemode(gamemode: Gamemode?)\n    case updateLatency(ping: Int)\n    case updateDisplayName(displayName: ChatComponent?)\n    case removePlayer\n  }\n\n  public init(from packetReader: inout PacketReader) throws {\n    let actionId = try packetReader.readVarInt()\n    let numPlayers = try packetReader.readVarInt()\n    playerActions = []\n    for _ in 0..<numPlayers {\n      let uuid = try packetReader.readUUID()\n      let playerAction: PlayerInfoAction\n      switch actionId {\n        case 0: // add player\n          let playerInfo = try Self.readPlayerInfo(from: &packetReader, uuid: uuid)\n          playerAction = .addPlayer(playerInfo: playerInfo)\n        case 1: // update gamemode\n          let gamemode = try Self.readGamemode(from: &packetReader)\n          playerAction = .updateGamemode(gamemode: gamemode)\n        case 2: // update latency\n          let ping = try packetReader.readVarInt()\n          playerAction = .updateLatency(ping: ping)\n        case 3: // update display name\n          let displayName = try Self.readDisplayName(from: &packetReader)\n          playerAction = .updateDisplayName(displayName: displayName)\n        case 4: // remove player\n          playerAction = .removePlayer\n        default:\n          log.warning(\"invalid player info action\")\n          continue\n      }\n      playerActions.append((uuid: uuid, action: playerAction))\n    }\n  }\n\n  public func handle(for client: Client) throws {\n    for playerAction in playerActions {\n      let uuid = playerAction.uuid\n      let action = playerAction.action\n\n      switch action {\n        case let .addPlayer(playerInfo: playerInfo):\n          client.game.tabList.addPlayer(playerInfo)\n        case let .updateGamemode(gamemode: gamemode):\n          client.game.tabList.updateGamemode(gamemode, uuid: uuid)\n        case let .updateLatency(ping: ping):\n          client.game.tabList.updateLatency(ping, uuid: uuid)\n        case let .updateDisplayName(displayName: displayName):\n          client.game.tabList.updateDisplayName(displayName, uuid: uuid)\n        case .removePlayer:\n          client.game.tabList.removePlayer(uuid: uuid)\n      }\n    }\n  }\n\n  private static func readGamemode(from packetReader: inout PacketReader) throws -> Gamemode? {\n    let rawValue = Int8(try packetReader.readVarInt())\n    guard rawValue != -1 else {\n      return nil\n    }\n\n    guard let gamemode = Gamemode(rawValue: rawValue) else {\n      throw ClientboundPacketError.invalidGamemode(rawValue: rawValue)\n    }\n\n    return gamemode\n  }\n\n  private static func readDisplayName(from packetReader: inout PacketReader) throws -> ChatComponent? {\n    var displayName: ChatComponent?\n    if try packetReader.readBool() {\n      displayName = try packetReader.readChat()\n    }\n    return displayName\n  }\n\n  private static func readPlayerInfo(from packetReader: inout PacketReader, uuid: UUID) throws -> PlayerInfo {\n    let playerName = try packetReader.readString()\n\n    let numProperties = try packetReader.readVarInt()\n    var properties: [PlayerProperty] = []\n    for _ in 0..<numProperties {\n      let propertyName = try packetReader.readString()\n      let value = try packetReader.readString()\n      var signature: String?\n      if try packetReader.readBool() {\n        signature = try packetReader.readString()\n      }\n      let property = PlayerProperty(name: propertyName, value: value, signature: signature)\n      properties.append(property)\n    }\n\n    let gamemode = try Self.readGamemode(from: &packetReader)\n    let ping = try packetReader.readVarInt()\n    let displayName = try Self.readDisplayName(from: &packetReader)\n\n    return PlayerInfo(\n      uuid: uuid,\n      name: playerName,\n      properties: properties,\n      gamemode: gamemode,\n      ping: ping,\n      displayName: displayName\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/PlayerListHeaderAndFooterPacket.swift",
    "content": "import Foundation\n\npublic struct PlayerListHeaderAndFooterPacket: ClientboundPacket {\n  public static let id: Int = 0x53\n  \n  public var header: ChatComponent\n  public var footer: ChatComponent\n\n  public init(from packetReader: inout PacketReader) throws {\n    header = try packetReader.readChat()\n    footer = try packetReader.readChat()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/PlayerPositionAndLookClientboundPacket.swift",
    "content": "import Foundation\nimport FirebladeMath\n\npublic struct PlayerPositionAndLookClientboundPacket: ClientboundPacket {\n  public static let id: Int = 0x35\n\n  public var position: Vec3d\n  public var yaw: Float\n  public var pitch: Float\n  public var flags: PositionAndLookFlags\n  public var teleportId: Int\n\n  public struct PositionAndLookFlags: OptionSet {\n    public let rawValue: UInt8\n\n    public init(rawValue: UInt8) {\n      self.rawValue = rawValue\n    }\n\n    public static let x = PositionAndLookFlags(rawValue: 0x01)\n    public static let y = PositionAndLookFlags(rawValue: 0x02)\n    public static let z = PositionAndLookFlags(rawValue: 0x04)\n    public static let yRot = PositionAndLookFlags(rawValue: 0x08)\n    public static let xRot = PositionAndLookFlags(rawValue: 0x10)\n  }\n\n  public init(from packetReader: inout PacketReader) throws {\n    position = try packetReader.readEntityPosition()\n    yaw = MathUtil.radians(from: try packetReader.readFloat())\n    pitch = MathUtil.radians(from: try packetReader.readFloat())\n    flags = try PositionAndLookFlags(rawValue: packetReader.readUnsignedByte())\n    teleportId = try packetReader.readVarInt()\n  }\n\n  public func handle(for client: Client) throws {\n    let teleportConfirm = TeleportConfirmPacket(teleportId: teleportId)\n    try client.sendPacket(teleportConfirm)\n\n    if !client.hasFinishedDownloadingTerrain {\n      client.hasFinishedDownloadingTerrain = true\n      log.info(\"Finished downloading terrain\")\n      client.eventBus.dispatch(TerrainDownloadCompletionEvent())\n    }\n\n    client.game.accessPlayer { player in\n      let position = player.position\n      let rotation = player.rotation\n\n      if flags.contains(.x) {\n        position.x += self.position.x\n      } else {\n        position.x = self.position.x\n      }\n\n      if flags.contains(.y) {\n        position.y += self.position.y\n      } else {\n        position.y = self.position.y\n      }\n\n      if flags.contains(.z) {\n        position.z += self.position.z\n      } else {\n        position.z = self.position.z\n      }\n\n      if flags.contains(.yRot) {\n        rotation.yaw += yaw\n      } else {\n        rotation.yaw = yaw\n      }\n\n      if flags.contains(.xRot) {\n        rotation.pitch += pitch\n      } else {\n        rotation.pitch = pitch\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/PluginMessagePacket.swift",
    "content": "import Foundation\n\npublic struct PluginMessagePacket: ClientboundPacket {\n  public static var id: Int = 0x18\n  \n  public var pluginMessage: PluginMessage\n  \n  // TODO: Give this its own file and probably specify that it's a mojang plugin not a delta client one.\n  public struct PluginMessage {\n    public var channel: Identifier\n    public var data: Buffer\n  }\n  \n  public init(from packetReader: inout PacketReader) throws {\n    let channel = try packetReader.readIdentifier()\n    let data = packetReader.buffer\n    pluginMessage = PluginMessage(channel: channel, data: data)\n  }\n  \n  public func handle(for client: Client) throws {\n    log.debug(\"plugin message received with channel: \\(pluginMessage.channel)\")\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/RemoveEntityEffectPacket.swift",
    "content": "import Foundation\n\npublic struct RemoveEntityEffectPacket: ClientboundPacket {\n  public static let id: Int = 0x38\n  \n  public var entityId: Int\n  public var effectId: Int8\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    effectId = try packetReader.readByte()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/ResourcePackSendPacket.swift",
    "content": "import Foundation\n\npublic struct ResourcePackSendPacket: ClientboundPacket {\n  public static let id: Int = 0x39\n  \n  public var url: String\n  public var hash: String\n\n  public init(from packetReader: inout PacketReader) throws {\n    url = try packetReader.readString()\n    hash = try packetReader.readString()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/RespawnPacket.swift",
    "content": "import Foundation\n\npublic enum RespawnPacketError: LocalizedError {\n  case invalidPreviousGamemodeRawValue(Gamemode.RawValue)\n\n  public var errorDescription: String? {\n    switch self {\n      case .invalidPreviousGamemodeRawValue(let rawValue):\n        return \"\"\"\n          Invalid previous gamemode raw value.\n          Raw value: \\(rawValue)\n          \"\"\"\n    }\n  }\n}\n\npublic struct RespawnPacket: ClientboundPacket {\n  public static let id: Int = 0x3a\n\n  public var currentDimensionIdentifier: Identifier\n  public var worldName: Identifier\n  public var hashedSeed: Int\n  public var gamemode: Gamemode\n  public var previousGamemode: Gamemode?\n  public var isDebug: Bool\n  public var isFlat: Bool\n  public var copyMetadata: Bool\n\n  public init(from packetReader: inout PacketReader) throws {\n    currentDimensionIdentifier = try packetReader.readIdentifier()\n    worldName = try packetReader.readIdentifier()\n    hashedSeed = try packetReader.readLong()\n\n    let rawGamemode = try packetReader.readByte()\n    let rawPreviousGamemode = try packetReader.readByte()\n\n    guard let gamemode = Gamemode(rawValue: rawGamemode) else {\n      throw ClientboundPacketError.invalidGamemode(rawValue: rawGamemode)\n    }\n\n    self.gamemode = gamemode\n\n    if rawPreviousGamemode == -1 {\n      previousGamemode = nil\n    } else {\n      guard let previousGamemode = Gamemode(rawValue: rawPreviousGamemode) else {\n        throw RespawnPacketError.invalidPreviousGamemodeRawValue(rawPreviousGamemode)\n      }\n\n      self.previousGamemode = previousGamemode\n    }\n\n    isDebug = try packetReader.readBool()\n    isFlat = try packetReader.readBool()\n    copyMetadata = try packetReader.readBool()  // TODO: not used yet\n  }\n\n  public func handle(for client: Client) throws {\n    guard\n      let currentDimension = client.game.dimensions.first(where: { dimension in\n        return dimension.identifier == currentDimensionIdentifier\n      })\n    else {\n      throw ClientboundPacketError.invalidDimension(currentDimensionIdentifier)\n    }\n\n    let world = World(\n      name: worldName,\n      dimension: currentDimension,\n      hashedSeed: hashedSeed,\n      isFlat: isFlat,\n      isDebug: isDebug,\n      eventBus: client.eventBus\n    )\n\n    // TODO: implement copyMetadata\n\n    // TODO: check if the discussion at https://wiki.vg/Protocol#Respawn about respawning to the same dimension applies or if it's just a java edition bug\n    client.game.changeWorld(to: world)\n    client.hasFinishedDownloadingTerrain = false\n    client.game.eventBus.dispatch(JoinWorldEvent())\n\n    client.game.accessPlayer { player in\n      player.gamemode.gamemode = gamemode\n      player.playerAttributes.previousGamemode = previousGamemode\n\n      // Reset player physics\n      player.acceleration.vector = .zero\n      player.velocity.vector = .zero\n    }\n\n    // TODO: get auto respawn working\n    let clientStatus = ClientStatusPacket(action: .performRespawn)\n    try client.sendPacket(clientStatus)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/ScoreboardObjectivePacket.swift",
    "content": "import Foundation\n\npublic struct ScoreboardObjectivePacket: ClientboundPacket {\n  public static let id: Int = 0x4a\n  \n  public var objectiveName: String\n  public var mode: UInt8\n  public var objectiveValue: ChatComponent?\n  public var type: Int?\n\n  public init(from packetReader: inout PacketReader) throws {\n    objectiveName = try packetReader.readString()\n    mode = try packetReader.readUnsignedByte()\n    if mode == 0 || mode == 2 {\n      objectiveValue = try packetReader.readChat()\n      type = try packetReader.readVarInt()\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/SelectAdvancementTabPacket.swift",
    "content": "import Foundation\n\npublic struct SelectAdvancementTabPacket: ClientboundPacket {\n  public static let id: Int = 0x3c\n  \n  public var identifier: Identifier?\n\n  public init(from packetReader: inout PacketReader) throws {\n    if try packetReader.readBool() {\n      identifier = try packetReader.readIdentifier()\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/ServerDifficultyPacket.swift",
    "content": "import Foundation\n\npublic struct ServerDifficultyPacket: ClientboundPacket {\n  public static let id: Int = 0x0d\n  \n  public var difficulty: Difficulty\n  public var isLocked: Bool\n  \n  public init(from packetReader: inout PacketReader) throws {\n    guard let difficulty = Difficulty(rawValue: try packetReader.readUnsignedByte()) else {\n      throw ClientboundPacketError.invalidDifficulty\n    }\n    self.difficulty = difficulty\n    isLocked = try packetReader.readBool()\n  }\n  \n  public func handle(for client: Client) throws {\n    client.game.difficulty = difficulty\n    client.game.isDifficultyLocked = isLocked\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/SetCooldownPacket.swift",
    "content": "import Foundation\n\npublic struct SetCooldownPacket: ClientboundPacket {\n  public static let id: Int = 0x17\n  \n  public var itemId: Int\n  public var cooldownTicks: Int\n  \n  public init(from packetReader: inout PacketReader) throws {\n    itemId = try packetReader.readVarInt()\n    cooldownTicks = try packetReader.readVarInt()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/SetExperiencePacket.swift",
    "content": "import Foundation\n\npublic struct SetExperiencePacket: ClientboundPacket {\n  public static let id: Int = 0x48\n  \n  public var experienceBar: Float\n  public var level: Int\n  public var totalExperience: Int\n\n  public init(from packetReader: inout PacketReader) throws {\n    experienceBar = try packetReader.readFloat()\n    level = try packetReader.readVarInt()\n    totalExperience = try packetReader.readVarInt()\n  }\n  \n  public func handle(for client: Client) throws {\n    client.game.accessPlayer { player in\n      let experience = player.experience\n      experience.experienceBarProgress = experienceBar\n      experience.experienceLevel = level\n      experience.experience = totalExperience\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/SetPassengersPacket.swift",
    "content": "import Foundation\n\npublic struct SetPassengersPacket: ClientboundPacket {\n  public static let id: Int = 0x4b\n  \n  public var entityId: Int\n  public var passengers: [Int]\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    \n    passengers = []\n    let count = try packetReader.readVarInt()\n    for _ in 0..<count {\n      let passenger = try packetReader.readVarInt()\n      passengers.append(passenger)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/SetSlotPacket.swift",
    "content": "import Foundation\n\npublic struct SetSlotPacket: ClientboundPacket {\n  public static let id: Int = 0x16\n\n  public static let mouseSlot = -1\n  public static let mouseWindowId = -1\n  public static let inventoryNoAnimationWindowId = -2\n\n  public var windowId: Int8\n  public var slot: Int16\n  public var slotData: Slot\n\n  public init(from packetReader: inout PacketReader) throws {\n    windowId = try packetReader.readByte()\n    slot = try packetReader.readShort()\n    slotData = try packetReader.readSlot()\n  }\n\n  public func handle(for client: Client) throws {\n    let slot = Int(slot)\n    let windowId = Int(windowId)\n\n    if slot == Self.mouseSlot && windowId == Self.mouseWindowId {\n      client.game.mutateGUIState { guiState in\n        guiState.mouseItemStack = slotData.stack\n      }\n      return\n    }\n\n    // Only player inventory is handled at the moment\n    guard\n      windowId == PlayerInventory.windowId\n      || windowId == Self.inventoryNoAnimationWindowId\n    else {\n      return\n    }\n\n    // Check for out-of-bounds\n    guard slot >= 0 && slot < PlayerInventory.slotCount else {\n      throw ClientboundPacketError.invalidInventorySlotIndex(slot, windowId: windowId)\n    }\n\n    // TODO: Figure out why this fails when joining servers like Hypixel\n    // If window id is 0, only hotbar slots can be sent (and should be animated)\n    // if windowId == 0 {\n    //   guard slot >= PlayerInventory.hotbarSlotStartIndex && slot <= PlayerInventory.hotbarSlotEndIndex else {\n    //     throw ClientboundPacketError.invalidInventorySlotIndex(slot, window: windowId)\n    //   }\n    // }\n\n    client.game.accessPlayer { player in\n      if Int(windowId) == player.inventory.window.id {\n        player.inventory.window.slots[slot] = slotData\n      }\n    }\n\n    client.game.mutateGUIState { guiState in\n      if let window = guiState.window, Int(windowId) == window.id {\n        window.slots[slot] = slotData\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/SoundEffectPacket.swift",
    "content": "import Foundation\n\npublic struct SoundEffectPacket: ClientboundPacket {\n  public static let id: Int = 0x51\n  \n  public var soundId: Int\n  public var soundCategory: Int\n  public var effectPositionX: Int\n  public var effectPositionY: Int\n  public var effectPositionZ: Int\n  public var volume: Float\n  public var pitch: Float\n\n  public init(from packetReader: inout PacketReader) throws {\n    soundId = try packetReader.readVarInt()\n    soundCategory = try packetReader.readVarInt()\n    effectPositionX = try packetReader.readInt()\n    effectPositionY = try packetReader.readInt()\n    effectPositionZ = try packetReader.readInt()\n    volume = try packetReader.readFloat()\n    pitch = try packetReader.readFloat()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/SpawnEntityPacket.swift",
    "content": "import FirebladeECS\nimport FirebladeMath\nimport Foundation\n\npublic struct SpawnEntityPacket: ClientboundPacket {\n  public static let id: Int = 0x00\n\n  public var entityId: Int\n  public var objectUUID: UUID\n  public var type: Int\n  public var position: Vec3d\n  public var pitch: Float\n  public var yaw: Float\n  public var data: Int\n  public var velocity: Vec3d?\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    objectUUID = try packetReader.readUUID()\n    type = try packetReader.readVarInt()\n    position = try packetReader.readEntityPosition()\n    // Seems a lil sus that this is the only packet that has pitch and yaw in the other order\n    (pitch, yaw) = try packetReader.readEntityRotation(pitchFirst: true)\n    data = try packetReader.readInt()\n\n    if data > 0 {\n      velocity = try packetReader.readEntityVelocity()\n    }\n  }\n\n  public func handle(for client: Client) throws {\n    guard let entityKind = RegistryStore.shared.entityRegistry.entity(withId: type) else {\n      log.warning(\"Ignored entity received with unknown type: \\(type)\")\n      return\n    }\n\n    client.game.createEntity(id: entityId) {\n      NonLivingEntity()\n      EntityKindId(type)\n      EntityId(entityId)\n      ObjectUUID(objectUUID)\n      ObjectData(data)\n      EntityHitBox(width: entityKind.width, height: entityKind.height)\n      EntityOnGround(false)\n      EntityPosition(position)\n      EntityVelocity(velocity ?? .zero)\n      EntityRotation(pitch: pitch, yaw: yaw)\n      EntityLerpState()\n      EntityAttributes()\n      EntityMetadata(inheritanceChain: entityKind.inheritanceChain)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/SpawnExperienceOrbPacket.swift",
    "content": "import Foundation\nimport FirebladeMath\n\npublic struct SpawnExperienceOrbPacket: ClientboundPacket {\n  public static let id: Int = 0x01\n\n  public var entityId: Int\n  public var position: Vec3d\n  public var count: Int16\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    position = try packetReader.readEntityPosition()\n    count = try packetReader.readShort()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/SpawnLivingEntityPacket.swift",
    "content": "import FirebladeMath\nimport Foundation\n\npublic struct SpawnLivingEntityPacket: ClientboundPacket {\n  public static let id: Int = 0x02\n\n  public var entityId: Int\n  public var entityUUID: UUID\n  public var type: Int\n  public var position: Vec3d\n  public var pitch: Float\n  public var yaw: Float\n  public var headYaw: Float\n  public var velocity: Vec3d\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    entityUUID = try packetReader.readUUID()\n    type = try packetReader.readVarInt()\n    position = try packetReader.readEntityPosition()\n    (pitch, yaw) = try packetReader.readEntityRotation()\n    headYaw = try packetReader.readAngle()\n    velocity = try packetReader.readEntityVelocity()\n  }\n\n  public func handle(for client: Client) {\n    guard let entityKind = RegistryStore.shared.entityRegistry.entity(withId: type) else {\n      log.warning(\"Entity spawned with invalid type id: \\(type)\")\n      return\n    }\n\n    if entityKind.isEnderDragon {\n      client.game.createEntity(id: entityId) {\n        LivingEntity()  // Mark it as a living entity\n        EntityKindId(type)\n        EntityId(entityId)\n        EntityUUID(entityUUID)\n        EntityOnGround(true)\n        EntityPosition(position)\n        EntityVelocity(velocity)\n        EntityHitBox(width: entityKind.width, height: entityKind.height)\n        EntityRotation(pitch: pitch, yaw: yaw)\n        EntityHeadYaw(headYaw)\n        EntityLerpState()\n        EntityAttributes()\n        EntityMetadata(inheritanceChain: entityKind.inheritanceChain)\n        EnderDragonParts()\n      }\n    } else {\n      client.game.createEntity(id: entityId) {\n        LivingEntity()  // Mark it as a living entity\n        EntityKindId(type)\n        EntityId(entityId)\n        EntityUUID(entityUUID)\n        EntityOnGround(true)\n        EntityPosition(position)\n        EntityVelocity(velocity)\n        EntityHitBox(width: entityKind.width, height: entityKind.height)\n        EntityRotation(pitch: pitch, yaw: yaw)\n        EntityHeadYaw(headYaw)\n        EntityLerpState()\n        EntityAttributes()\n        EntityMetadata(inheritanceChain: entityKind.inheritanceChain)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/SpawnPaintingPacket.swift",
    "content": "import Foundation\n\npublic struct SpawnPaintingPacket: ClientboundPacket {\n  public static let id: Int = 0x03\n  \n  public var entityId: Int\n  public var entityUUID: UUID\n  public var motive: Int\n  public var location: BlockPosition\n  public var direction: UInt8 // TODO_LATER\n  \n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    entityUUID = try packetReader.readUUID()\n    motive = try packetReader.readVarInt()\n    location = try packetReader.readBlockPosition()\n    direction = try packetReader.readUnsignedByte()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/SpawnPlayerPacket.swift",
    "content": "import FirebladeMath\nimport Foundation\n\npublic struct SpawnPlayerPacket: ClientboundPacket {\n  public static let id: Int = 0x04\n\n  /// The player's entity id.\n  public var entityId: Int\n  /// The player's UUID.\n  public var playerUUID: UUID\n  /// The player's position.\n  public var position: Vec3d\n  /// The player's pitch.\n  public var pitch: Float\n  /// The player's yaw.\n  public var yaw: Float\n\n  public init(from packetReader: inout PacketReader) throws {\n    entityId = try packetReader.readVarInt()\n    playerUUID = try packetReader.readUUID()\n    position = try packetReader.readEntityPosition()\n    (pitch, yaw) = try packetReader.readEntityRotation()\n  }\n\n  public func handle(for client: Client) throws {\n    let playerEntity = RegistryStore.shared.entityRegistry.playerEntityKind\n    client.game.createEntity(id: entityId) {\n      LivingEntity()\n      PlayerEntity()\n      EntityKindId(playerEntity.id)\n      EntityId(entityId)\n      EntityUUID(playerUUID)\n      EntityHitBox(width: playerEntity.width, height: playerEntity.height)\n      EntityOnGround(true)\n      EntityPosition(position)\n      EntityVelocity(0, 0, 0)\n      EntityRotation(pitch: pitch, yaw: yaw)\n      EntityLerpState()\n      EntityAttributes()\n      EntityMetadata(inheritanceChain: playerEntity.inheritanceChain)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/SpawnPositionPacket.swift",
    "content": "import Foundation\n\npublic struct SpawnPositionPacket: ClientboundPacket {\n  public static let id: Int = 0x42\n\n  public var location: BlockPosition\n\n  public init(from packetReader: inout PacketReader) throws {\n    location = try packetReader.readBlockPosition()\n  }\n\n  public func handle(for client: Client) throws {\n    client.game.accessPlayer { player in\n      player.playerAttributes.spawnPosition = location\n    }\n\n    // notify server that we are ready to finish login\n    let clientStatus = ClientStatusPacket(action: .performRespawn)\n    try client.sendPacket(clientStatus)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/StatisticsPacket.swift",
    "content": "import Foundation\n\npublic struct StatisticsPacket: ClientboundPacket {\n  public static let id: Int = 0x06\n  \n  public var statistics: [Statistic]\n  \n  public init(from packetReader: inout PacketReader) throws {\n    statistics = []\n    \n    let count = try packetReader.readVarInt()\n    for _ in 0..<count {\n      let categoryId = try packetReader.readVarInt()\n      let statisticId = try packetReader.readVarInt()\n      let value = try packetReader.readVarInt()\n      let statistic = Statistic(categoryId: categoryId, statisticId: statisticId, value: value)\n      statistics.append(statistic)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/StopSoundPacket.swift",
    "content": "import Foundation\n\npublic struct StopSoundPacket: ClientboundPacket {\n  public static let id: Int = 0x52\n  \n  public var flags: Int8\n  public var source: Int?\n  public var sound: Identifier?\n\n  public init(from packetReader: inout PacketReader) throws {\n    flags = try packetReader.readByte()\n    if flags & 0x1 == 0x1 {\n      source = try packetReader.readVarInt()\n    }\n    if flags & 0x2 == 0x2 {\n      sound = try packetReader.readIdentifier()\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/TabCompleteClientboundPacket.swift",
    "content": "import Foundation\n\npublic struct TabCompleteClientboundPacket: ClientboundPacket {\n  public static let id: Int = 0x10\n  \n  public struct TabCompleteMatch {\n    public let match: String\n    public let hasTooltip: Bool\n    public let tooltip: ChatComponent?\n  }\n  \n  public var id: Int\n  public var start: Int\n  public var length: Int\n  public var matches: [TabCompleteMatch]\n  \n  public init(from packetReader: inout PacketReader) throws {\n    id = try packetReader.readVarInt()\n    start = try packetReader.readVarInt()\n    length = try packetReader.readVarInt()\n    \n    matches = []\n    let count = try packetReader.readVarInt()\n    for _ in 0..<count {\n      let match = try packetReader.readString()\n      let hasTooltip = try packetReader.readBool()\n      var tooltip: ChatComponent?\n      if hasTooltip {\n        tooltip = try packetReader.readChat()\n      }\n      matches.append(TabCompleteMatch(match: match, hasTooltip: hasTooltip, tooltip: tooltip))\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/TagsPacket.swift",
    "content": "import Foundation\n\n// TODO_LATER: fill this out more as needed\npublic struct TagsPacket: ClientboundPacket {\n  public static let id: Int = 0x5b\n  \n  public init(from packetReader: inout PacketReader) throws {\n    for _ in 0..<4 {\n      let length = try packetReader.readVarInt()\n      for _ in 0..<length {\n        let tagName = try packetReader.readString()\n        _ = tagName\n        let count = try packetReader.readVarInt()\n        for _ in 0..<count {\n          let entry = try packetReader.readVarInt()\n          _ = entry\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/TeamsPacket.swift",
    "content": "import Foundation\n\npublic struct TeamsPacket: ClientboundPacket {\n  public static let id: Int = 0x4c\n  \n  public var teamName: String\n  public var action: TeamAction\n  \n  public enum TeamAction {\n    case create(action: Create)\n    case remove\n    case updateInfo(action: UpdateInfo)\n    case addPlayers(entities: [String])\n    case removePlayers(entities: [String])\n    \n    public struct Create {\n      public var teamDisplayName: ChatComponent\n      public var friendlyFlags: Int8\n      public var nameTagVisibility: String\n      public var collisionRule: String\n      public var teamColor: Int\n      public var teamPrefix: ChatComponent\n      public var teamSuffix: ChatComponent\n      public var entities: [String]\n    }\n    \n    public struct UpdateInfo {\n      public var teamDisplayName: ChatComponent\n      public var friendlyFlags: Int8\n      public var nameTagVisibility: String\n      public var collisionRule: String\n      public var teamColor: Int\n      public var teamPrefix: ChatComponent\n      public var teamSuffix: ChatComponent\n    }\n  }\n\n  public init(from packetReader: inout PacketReader) throws {\n    teamName = try packetReader.readString()\n    let mode = try packetReader.readByte()\n    switch mode {\n      case 0: // create\n        let createAction = try Self.readCreateAction(from: &packetReader)\n        action = .create(action: createAction)\n      case 1: // remove\n        action = .remove\n      case 2: // update info\n        let updateInfoAction = try Self.readUpdateInfoAction(from: &packetReader)\n        action = .updateInfo(action: updateInfoAction)\n      case 3: // add players\n        let entities = try Self.readEntities(from: &packetReader)\n        action = .addPlayers(entities: entities)\n      case 4: // remove players\n        let entities = try Self.readEntities(from: &packetReader)\n        action = .removePlayers(entities: entities)\n      default:\n        log.debug(\"invalid team action\")\n        action = .remove\n    }\n  }\n\n  private static func readEntities(from packetReader: inout PacketReader) throws -> [String] {\n    let entityCount = try packetReader.readVarInt()\n    var entities: [String] = []\n    for _ in 0..<entityCount {\n      let entity = try packetReader.readString()\n      entities.append(entity)\n    }\n    return entities\n  }\n\n  private static func readCreateAction(from packetReader: inout PacketReader) throws -> TeamAction.Create {\n    let teamDisplayName = try packetReader.readChat()\n    let friendlyFlags = try packetReader.readByte()\n    let nameTagVisibility = try packetReader.readString()\n    let collisionRule = try packetReader.readString()\n    let teamColor = try packetReader.readVarInt()\n    let teamPrefix = try packetReader.readChat()\n    let teamSuffix = try packetReader.readChat()\n    let entities = try Self.readEntities(from: &packetReader)\n    \n    return TeamAction.Create(\n      teamDisplayName: teamDisplayName,\n      friendlyFlags: friendlyFlags,\n      nameTagVisibility: nameTagVisibility,\n      collisionRule: collisionRule,\n      teamColor: teamColor,\n      teamPrefix: teamPrefix,\n      teamSuffix: teamSuffix,\n      entities: entities\n    )\n  }\n\n  private static func readUpdateInfoAction(from packetReader: inout PacketReader) throws -> TeamAction.UpdateInfo {\n    let teamDisplayName = try packetReader.readChat()\n    let friendlyFlags = try packetReader.readByte()\n    let nameTagVisibility = try packetReader.readString()\n    let collisionRule = try packetReader.readString()\n    let teamColor = try packetReader.readVarInt()\n    let teamPrefix = try packetReader.readChat()\n    let teamSuffix = try packetReader.readChat()\n\n    return TeamAction.UpdateInfo(\n      teamDisplayName: teamDisplayName,\n      friendlyFlags: friendlyFlags,\n      nameTagVisibility: nameTagVisibility,\n      collisionRule: collisionRule,\n      teamColor: teamColor,\n      teamPrefix: teamPrefix,\n      teamSuffix: teamSuffix\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/TimeUpdatePacket.swift",
    "content": "import Foundation\n\npublic struct TimeUpdatePacket: ClientboundPacket {\n  public static let id: Int = 0x4e\n\n  public var worldAge: Int\n  public var timeOfDay: Int\n\n  public init(from packetReader: inout PacketReader) throws {\n    worldAge = try packetReader.readLong()\n    timeOfDay = try packetReader.readLong()\n  }\n\n  public func handle(for client: Client) throws {\n    client.game.world.setAge(worldAge)\n    client.game.world.setTimeOfDay(timeOfDay)\n    client.eventBus.dispatch(World.Event.TimeUpdate(\n      worldAge: worldAge,\n      timeOfDay: timeOfDay\n    ))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/TitlePacket.swift",
    "content": "import Foundation\n\npublic struct TitlePacket: ClientboundPacket {\n  public static let id: Int = 0x4f\n  \n  public var action: TitleAction\n  \n  public enum TitleAction {\n    case setTitle(text: ChatComponent)\n    case setSubtitle(text: ChatComponent)\n    case setActionBar(text: ChatComponent)\n    case setTimesAndDisplay(fadeIn: Int, stay: Int, fadeOut: Int)\n    case hide\n    case reset\n  }\n  \n  public init(from packetReader: inout PacketReader) throws {\n    let actionId = try packetReader.readVarInt()\n    switch actionId {\n      case 0: // set title\n        let text = try packetReader.readChat()\n        action = .setTitle(text: text)\n      case 1: // set subtitle\n        let text = try packetReader.readChat()\n        action = .setSubtitle(text: text)\n      case 2: // set action bar\n        let text = try packetReader.readChat()\n        action = .setActionBar(text: text)\n      case 3: // set times and display\n        let fadeIn = try packetReader.readInt()\n        let stay = try packetReader.readInt()\n        let fadeOut = try packetReader.readInt()\n        action = .setTimesAndDisplay(fadeIn: fadeIn, stay: stay, fadeOut: fadeOut)\n      case 4: // hide\n        action = .hide\n      case 5: // reset\n        action = .reset\n      default:\n        log.debug(\"invalid title action received\")\n        action = .reset\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/TradeListPacket.swift",
    "content": "import Foundation\n\npublic struct TradeListPacket: ClientboundPacket {\n  public static let id: Int = 0x27\n\n  public var windowId: Int\n  public var trades: [Trade]\n  public var villagerLevel: Int\n  public var experience: Int\n  public var isRegularVillager: Bool\n  public var canRestock: Bool\n\n  public struct Trade {\n    public var firstInputItem: Slot\n    public var outputItem: Slot\n    public var secondInputItem: Slot?\n    public var tradeDisabled: Bool\n    public var numUses: Int // number of uses so far\n    public var maxUses: Int // maximum number of uses before disabled\n    public var xp: Int\n    public var specialPrice: Int\n    public var priceMultiplier: Float\n    public var demand: Int\n  }\n\n  public init(from packetReader: inout PacketReader) throws {\n    windowId = try packetReader.readVarInt()\n\n    trades = []\n    let count = try packetReader.readUnsignedByte()\n    for _ in 0..<count {\n      let firstInputItem = try packetReader.readSlot()\n      let outputItem = try packetReader.readSlot()\n      let hasSecondInputItem = try packetReader.readBool()\n      var secondInputItem: Slot?\n      if hasSecondInputItem {\n        secondInputItem = try packetReader.readSlot()\n      }\n      let tradeDisabled = try packetReader.readBool()\n      let numUses = try packetReader.readInt()\n      let maxUses = try packetReader.readInt()\n      let xp = try packetReader.readInt()\n      let specialPrice = try packetReader.readInt()\n      let priceMultiplier = try packetReader.readFloat()\n      let demand = try packetReader.readInt()\n      let trade = Trade(\n        firstInputItem: firstInputItem,\n        outputItem: outputItem,\n        secondInputItem: secondInputItem,\n        tradeDisabled: tradeDisabled,\n        numUses: numUses,\n        maxUses: maxUses,\n        xp: xp,\n        specialPrice: specialPrice,\n        priceMultiplier: priceMultiplier,\n        demand: demand\n      )\n      trades.append(trade)\n    }\n\n    villagerLevel = try packetReader.readVarInt()\n    experience = try packetReader.readVarInt()\n    isRegularVillager = try packetReader.readBool()\n    canRestock = try packetReader.readBool()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/UnloadChunkPacket.swift",
    "content": "import Foundation\n\npublic struct UnloadChunkPacket: ClientboundPacket {\n  public static let id: Int = 0x1d\n  \n  public var chunkPosition: ChunkPosition\n  \n  public init(from packetReader: inout PacketReader) throws {\n    let chunkX = Int(try packetReader.readInt())\n    let chunkZ = Int(try packetReader.readInt())\n    chunkPosition = ChunkPosition(chunkX: chunkX, chunkZ: chunkZ)\n  }\n  \n  public func handle(for client: Client) {\n    client.game.world.removeChunk(at: chunkPosition)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/UnlockRecipesPacket.swift",
    "content": "import Foundation\n\npublic struct UnlockRecipesPacket: ClientboundPacket {\n  public static let id: Int = 0x36\n  \n  public var action: Int\n  public var craftingRecipeBookOpen: Bool\n  public var craftingRecipeBookFilterActive: Bool\n  public var smeltingRecipeBookOpen: Bool\n  public var smeltingRecipeBookFilterActive: Bool\n  public var recipeIds: [Identifier]\n  public var initRecipeIds: [Identifier]?\n\n  public init(from packetReader: inout PacketReader) throws {\n    action = try packetReader.readVarInt()\n    craftingRecipeBookOpen = try packetReader.readBool()\n    craftingRecipeBookFilterActive = try packetReader.readBool()\n    smeltingRecipeBookOpen = try packetReader.readBool()\n    smeltingRecipeBookFilterActive = try packetReader.readBool()\n    \n    recipeIds = []\n    var count = try packetReader.readVarInt()\n    for _ in 0..<count {\n      let identifier = try packetReader.readIdentifier()\n      recipeIds.append(identifier)\n    }\n    \n    if action == 0 { // init\n      var initRecipeIds = [Identifier]()\n      count = try packetReader.readVarInt()\n      for _ in 0..<count {\n        let identifier = try packetReader.readIdentifier()\n        initRecipeIds.append(identifier)\n      }\n      self.initRecipeIds = initRecipeIds\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/UpdateHealthPacket.swift",
    "content": "import Foundation\n\npublic struct UpdateHealthPacket: ClientboundPacket {\n  public static let id: Int = 0x49\n\n  public var health: Float\n  public var food: Int\n  public var foodSaturation: Float\n\n  public init(from packetReader: inout PacketReader) throws {\n    health = try packetReader.readFloat()\n    food = try packetReader.readVarInt()\n    foodSaturation = try packetReader.readFloat()\n  }\n\n  public func handle(for client: Client) throws {\n    client.game.accessPlayer { player in\n      player.health.health = health\n      player.nutrition.food = food\n      player.nutrition.saturation = foodSaturation\n    }\n\n    if health <= 0 {\n      // TODO: Implement respawn screen (shown when world has respawnScreenEnabled), for now\n      //   we just instantly respawn.\n      try client.sendPacket(ClientStatusPacket(action: .performRespawn))\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/UpdateLightPacket.swift",
    "content": "import Foundation\n\npublic struct UpdateLightPacket: ClientboundPacket {\n  public static let id: Int = 0x24\n  \n  public var chunkPosition: ChunkPosition\n  public var trustEdges: Bool\n  public var skyLightMask: Int\n  public var blockLightMask: Int\n  public var emptySkyLightMask: Int\n  public var emptyBlockLightMask: Int\n  public var skyLightArrays: [[UInt8]]\n  public var blockLightArrays: [[UInt8]]\n  \n  public init(from packetReader: inout PacketReader) throws {\n    let chunkX = try packetReader.readVarInt()\n    let chunkZ = try packetReader.readVarInt()\n    chunkPosition = ChunkPosition(chunkX: chunkX, chunkZ: chunkZ)\n    trustEdges = try packetReader.readBool()\n    skyLightMask = try packetReader.readVarInt()\n    blockLightMask = try packetReader.readVarInt()\n    emptySkyLightMask = try packetReader.readVarInt()\n    emptyBlockLightMask = try packetReader.readVarInt()\n    \n    skyLightArrays = []\n    var numArrays = BinaryUtil.setBits(of: skyLightMask, n: Chunk.numSections + 2).count\n    for _ in 0..<numArrays {\n      let length = try packetReader.readVarInt()\n      let bytes = try packetReader.readByteArray(length: length)\n      skyLightArrays.append(bytes)\n    }\n    \n    blockLightArrays = []\n    numArrays = BinaryUtil.setBits(of: blockLightMask, n: Chunk.numSections + 2).count\n    for _ in 0..<numArrays {\n      let length = try packetReader.readVarInt()\n      let bytes = try packetReader.readByteArray(length: length)\n      blockLightArrays.append(bytes)\n    }\n  }\n  \n  private static func unpackLightingArray(_ packed: [UInt8]) -> [UInt8] {\n    var unpacked: [UInt8] = []\n    for packedLight in packed {\n      unpacked.append(packedLight & 0x0f)\n      unpacked.append(packedLight >> 4)\n    }\n    return unpacked\n  }\n  \n  /// Gets a list of sections present based on a bitmask.\n  private static func sectionsPresent(in bitmask: Int) -> [Int] {\n    var present = BinaryUtil.setBits(of: bitmask, n: Chunk.numSections + 2)\n    present = present.map { $0 - 1 }\n    return present\n  }\n  \n  public func handle(for client: Client) throws {\n    let skyLightIndices = Self.sectionsPresent(in: skyLightMask)\n    let blockLightIndices = Self.sectionsPresent(in: blockLightMask)\n    \n    var unpackedSkyLightArrays: [Int: [UInt8]] = [:]\n    for (index, array) in zip(skyLightIndices, skyLightArrays) {\n      unpackedSkyLightArrays[index] = Self.unpackLightingArray(array)\n    }\n    \n    var unpackedBlockLightArrays: [Int: [UInt8]] = [:]\n    for (index, array) in zip(blockLightIndices, blockLightArrays) {\n      unpackedBlockLightArrays[index] = Self.unpackLightingArray(array)\n    }\n    \n    let data = ChunkLightingUpdateData(\n      trustEdges: trustEdges,\n      emptySkyLightSections: Self.sectionsPresent(in: emptySkyLightMask),\n      emptyBlockLightSections: Self.sectionsPresent(in: emptyBlockLightMask),\n      skyLightArrays: unpackedSkyLightArrays,\n      blockLightArrays: unpackedBlockLightArrays)\n    client.game.world.updateChunkLighting(at: chunkPosition, with: data)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/UpdateScorePacket.swift",
    "content": "import Foundation\n\npublic struct UpdateScorePacket: ClientboundPacket {\n  public static let id: Int = 0x4d\n  \n  public var entityName: String\n  public var action: Int8\n  public var objectiveName: String\n  public var value: Int?\n  \n  public init(from packetReader: inout PacketReader) throws {\n    // TODO: implement strings with max length in packetreader\n    entityName = try packetReader.readString()\n    action = try packetReader.readByte()\n    objectiveName = try packetReader.readString()\n    if action != 1 {\n      value = try packetReader.readVarInt()\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/UpdateViewDistancePacket.swift",
    "content": "import Foundation\n\npublic struct UpdateViewDistancePacket: ClientboundPacket {\n  public static let id: Int = 0x41\n  \n  public var viewDistance: Int\n\n  public init(from packetReader: inout PacketReader) throws {\n    viewDistance = try packetReader.readVarInt()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/UpdateViewPositionPacket.swift",
    "content": "import Foundation\n\npublic struct UpdateViewPositionPacket: ClientboundPacket {\n  public static let id: Int = 0x40\n  \n  public var chunkPosition: ChunkPosition\n  \n  public init(from packetReader: inout PacketReader) throws {\n    let chunkX = Int(try packetReader.readVarInt())\n    let chunkZ = Int(try packetReader.readVarInt())\n    chunkPosition = ChunkPosition(chunkX: chunkX, chunkZ: chunkZ)\n  }\n  \n  public func handle(for client: Client) throws {\n    // TODO: trigger world to recalculate which chunks should be rendered (if a circle is decided on for chunk rendering)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/VehicleMoveClientboundPacket.swift",
    "content": "import Foundation\nimport FirebladeMath\n\npublic struct VehicleMoveClientboundPacket: ClientboundPacket {\n  public static let id: Int = 0x2c\n\n  public var position: Vec3d\n  public var yaw: Float\n  public var pitch: Float\n\n  public init(from packetReader: inout PacketReader) throws {\n    position = try packetReader.readEntityPosition()\n    yaw = try packetReader.readFloat()\n    pitch = try packetReader.readFloat()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/WindowConfirmationClientboundPacket.swift",
    "content": "import Foundation\n\npublic struct WindowConfirmationClientboundPacket: ClientboundPacket {\n  public static let id: Int = 0x12\n  \n  public var windowId: Int8\n  public var actionNumber: Int16\n  public var accepted: Bool\n  \n  public init(from packetReader: inout PacketReader) throws {\n    windowId = try packetReader.readByte()\n    actionNumber = try packetReader.readShort()\n    accepted = try packetReader.readBool()\n  }\n\n  public func handle(for client: Client) throws {\n    // TODO: Should this return false when the `accepted` (from the server) is false?\n    try client.sendPacket(WindowConfirmationServerboundPacket(\n      windowId: windowId,\n      actionNumber: actionNumber,\n      accepted: true\n    ))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/WindowItemsPacket.swift",
    "content": "import Foundation\n\npublic struct WindowItemsPacket: ClientboundPacket {\n  public static let id: Int = 0x14\n\n  public var windowId: UInt8\n  public var slots: [Slot]\n\n  public init(from packetReader: inout PacketReader) throws {\n    windowId = try packetReader.readUnsignedByte()\n\n    slots = []\n    let count = try packetReader.readShort()\n    for _ in 0..<count {\n      let slot = try packetReader.readSlot()\n      slots.append(slot)\n    }\n  }\n\n  public func handle(for client: Client) throws {\n    client.game.accessPlayer { player in\n      guard windowId == player.inventory.window.id else {\n        return\n      }\n\n      guard slots.count == player.inventory.window.type.slotCount else {\n        log.warning(\"Invalid player inventory slot count: \\(slots.count)\")\n        // Silently ignore for now because Hypixel sends packets that violate this\n        // TODO: Don't ignore invalid slot counts\n        return\n      }\n\n      player.inventory.window.slots = slots\n    }\n\n    client.game.mutateGUIState { guiState in\n      guard let window = guiState.window, windowId == window.id else {\n        return\n      }\n\n      guard slots.count == window.type.slotCount else {\n        log.warning(\"Invalid window slot count: \\(slots.count)\")\n        return\n      }\n\n      window.slots = slots\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/WindowPropertyPacket.swift",
    "content": "import Foundation\n\npublic struct WindowPropertyPacket: ClientboundPacket {\n  public static let id: Int = 0x15\n  \n  public var windowId: UInt8\n  public var property: Int16\n  public var value: Int16\n  \n  public init(from packetReader: inout PacketReader) throws {\n    windowId = try packetReader.readUnsignedByte()\n    property = try packetReader.readShort()\n    value = try packetReader.readShort()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Clientbound/WorldBorderPacket.swift",
    "content": "import Foundation\n\npublic enum WorldBorderPacketError: LocalizedError {\n  case invalidActionId(Int)\n  \n  public var errorDescription: String? {\n    switch self {\n      case .invalidActionId(let id):\n        return \"Invalid action Id: \\(id)\"\n    }\n  }\n}\n\npublic struct WorldBorderPacket: ClientboundPacket {\n  public static let id: Int = 0x3d\n  \n  public var action: WorldBorderAction\n  \n  public enum WorldBorderAction {\n    case setSize(diameter: Double)\n    case lerpSize(oldDiameter: Double, newDiameter: Double, speed: Int)\n    case setCenter(x: Double, z: Double)\n    case initialise(action: InitialiseAction)\n    case setWarningTime(warningTime: Int)\n    case setWarningBlocks(warningBlocks: Int)\n    \n    public struct InitialiseAction {\n      public var x: Double\n      public var z: Double\n      public var oldDiameter: Double\n      public var newDiameter: Double\n      public var speed: Int\n      public var portalTeleportBoundary: Int\n      public var warningTime: Int\n      public var warningBlocks: Int\n    }\n  }\n  \n  public init(from packetReader: inout PacketReader) throws {\n    let actionId = try packetReader.readVarInt()\n    switch actionId {\n      case 0: // set size\n        let diameter = try packetReader.readDouble()\n        action = .setSize(diameter: diameter)\n      case 1: // lerp size\n        let oldDiameter = try packetReader.readDouble()\n        let newDiameter = try packetReader.readDouble()\n        let speed = try packetReader.readVarLong()\n        action = .lerpSize(oldDiameter: oldDiameter, newDiameter: newDiameter, speed: speed)\n      case 2: // set center\n        let x = try packetReader.readDouble()\n        let z = try packetReader.readDouble()\n        action = .setCenter(x: x, z: z)\n      case 3: // initialise\n        let x = try packetReader.readDouble()\n        let z = try packetReader.readDouble()\n        let oldDiameter = try packetReader.readDouble()\n        let newDiameter = try packetReader.readDouble()\n        let speed = try packetReader.readVarLong()\n        let portalTeleportBoundary = try packetReader.readVarInt()\n        let warningTime = try packetReader.readVarInt()\n        let warningBlocks = try packetReader.readVarInt()\n        let initAction = WorldBorderAction.InitialiseAction(\n          x: x,\n          z: z,\n          oldDiameter: oldDiameter,\n          newDiameter: newDiameter,\n          speed: speed,\n          portalTeleportBoundary: portalTeleportBoundary,\n          warningTime: warningTime,\n          warningBlocks: warningBlocks\n        )\n        action = .initialise(action: initAction)\n      case 4: // set warning time\n        let warningTime = try packetReader.readVarInt()\n        action = .setWarningTime(warningTime: warningTime)\n      case 5: // set warning blocks\n        let warningBlocks = try packetReader.readVarInt()\n        action = .setWarningBlocks(warningBlocks: warningBlocks)\n      default:\n        throw WorldBorderPacketError.invalidActionId(actionId)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/AdvancementTabPacket.swift",
    "content": "import Foundation\n\npublic struct AdvancementTabPacket: ServerboundPacket {\n  public static let id: Int = 0x21\n  \n  public var action: AdvancementTabAction\n  \n  public enum AdvancementTabAction {\n    case openedTab(tabId: Identifier)\n    case closedScreen\n  }\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    switch action {\n      case let .openedTab(tabId: tabId):\n        writer.writeVarInt(0) // opened tab\n        writer.writeIdentifier(tabId)\n      case .closedScreen:\n        writer.writeVarInt(1) // closed screen\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/AnimationServerboundPacket.swift",
    "content": "import Foundation\n\npublic struct AnimationServerboundPacket: ServerboundPacket {\n  public static let id: Int = 0x2b\n  \n  public var hand: Hand\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(hand.rawValue)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/ChatMessageServerboundPacket.swift",
    "content": "import Foundation\n\npublic struct ChatMessageServerboundPacket: ServerboundPacket {\n  public static let id: Int = 0x03\n\n  public var message: String\n\n  public init(_ message: String) {\n    self.message = message\n  }\n\n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeString(message)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/ClickWindowButtonPacket.swift",
    "content": "import Foundation\n\npublic struct ClickWindowButtonPacket: ServerboundPacket {\n  public static let id: Int = 0x08\n  \n  public var windowId: Int8\n  public var buttonId: Int8\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeByte(windowId)\n    writer.writeByte(buttonId)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/ClickWindowPacket.swift",
    "content": "import Foundation\n\npublic struct ClickWindowPacket: ServerboundPacket {\n  public static let id: Int = 0x09\n\n  public var windowId: UInt8\n  /// A unique id for the action, used by the server when sending confirmation packets.\n  /// Vanilla computes this from per-window counters.\n  public var actionId: Int16\n  public var action: Action\n  public var clickedItem: Slot\n\n  public enum Action {\n    case leftClick(slot: Int16?)\n    case rightClick(slot: Int16?)\n    case shiftLeftClick(slot: Int16)\n    case shiftRightClick(slot: Int16)\n    case numberKey(slot: Int16, number: Int8)\n    case middleClick(slot: Int16)\n    case dropOne(slot: Int16)\n    case dropStack(slot: Int16)\n    case leftClickOutsideInventory\n    case rightClickOutsideInventory\n    case startLeftDrag\n    case startRightDrag\n    case startMiddleDrag\n    case addLeftDragSlot(slot: Int16)\n    case addRightDragSlot(slot: Int16)\n    case addMiddleDragSlot(slot: Int16)\n    case endLeftDrag\n    case endRightDrag\n    case endMiddleDrag\n    case doubleClick(slot: Int16)\n\n    var rawValue: (mode: Int32, button: Int8, slot: Int16?) {\n      switch self {\n        case let .leftClick(slot):\n          return (0, 0, slot ?? -999)\n        case let .rightClick(slot):\n          return (0, 1, slot ?? -999)\n        case let .shiftLeftClick(slot):\n          return (1, 0, slot)\n        case let .shiftRightClick(slot):\n          return (1, 1, slot)\n        case let .numberKey(slot, number):\n          return (2, number, slot)\n        case let .middleClick(slot):\n          return (3, 2, slot)\n        case let .dropOne(slot):\n          return (4, 0, slot)\n        case let .dropStack(slot):\n          return (4, 1, slot)\n        case .leftClickOutsideInventory:\n          return (4, 0, nil)\n        case .rightClickOutsideInventory:\n          return (4, 1, nil)\n        case .startLeftDrag:\n          return (5, 0, nil)\n        case .startRightDrag:\n          return (5, 4, nil)\n        case .startMiddleDrag:\n          return (5, 8, nil)\n        case let .addLeftDragSlot(slot):\n          return (5, 1, slot)\n        case let .addRightDragSlot(slot):\n          return (5, 5, slot)\n        case let .addMiddleDragSlot(slot):\n          return (5, 9, slot)\n        case .endLeftDrag:\n          return (5, 2, nil)\n        case .endRightDrag:\n          return (5, 6, nil)\n        case .endMiddleDrag:\n          return (5, 10, nil)\n        case let .doubleClick(slot):\n          return (6, 0, slot)\n      }\n    }\n  }\n\n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeUnsignedByte(windowId)\n    let (mode, button, slot) = action.rawValue\n    writer.writeShort(slot ?? -999)\n    writer.writeByte(button)\n    writer.writeShort(actionId)\n    writer.writeVarInt(mode)\n    writer.writeSlot(clickedItem)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/ClientSettingsPacket.swift",
    "content": "import Foundation\n\npublic struct ClientSettingsPacket: ServerboundPacket {\n  public static let id: Int = 0x05\n\n  public var locale: String\n  public var viewDistance: Int8\n  public var chatMode: ChatMode\n  public var chatColors: Bool\n  public var displayedSkinParts: SkinParts\n  public var mainHand: DominantHand\n\n  public init(\n    locale: String,\n    viewDistance: Int8,\n    chatMode: ChatMode,\n    chatColors: Bool,\n    displayedSkinParts: SkinParts,\n    mainHand: DominantHand\n  ) {\n    self.locale = locale\n    self.viewDistance = viewDistance\n    self.chatMode = chatMode\n    self.chatColors = chatColors\n    self.displayedSkinParts = displayedSkinParts\n    self.mainHand = mainHand\n  }\n\n  public init(\n    _ configuration: ClientConfiguration\n  ) {\n    self.locale = Constants.locale\n    self.viewDistance = Int8(configuration.render.renderDistance)\n    self.chatMode = .enabled\n    self.chatColors = true\n    self.displayedSkinParts = [.cape, .hat, .jacket, .leftSleeve, .rightSleeve, .leftPantsLeg, .rightPantsLeg]\n    self.mainHand = .right\n  }\n\n  public enum ChatMode: Int32 {\n    case enabled = 0\n    case commandsOnly = 1\n    case hidden = 2\n  }\n\n  public struct SkinParts: OptionSet {\n    public let rawValue: UInt8\n\n    public init(rawValue: UInt8) {\n      self.rawValue = rawValue\n    }\n\n    public static let cape = SkinParts(rawValue: 0x01)\n    public static let jacket = SkinParts(rawValue: 0x02)\n    public static let leftSleeve = SkinParts(rawValue: 0x04)\n    public static let rightSleeve = SkinParts(rawValue: 0x08)\n    public static let leftPantsLeg = SkinParts(rawValue: 0x10)\n    public static let rightPantsLeg = SkinParts(rawValue: 0x20)\n    public static let hat = SkinParts(rawValue: 0x40)\n  }\n\n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeString(locale)\n    writer.writeByte(viewDistance)\n    writer.writeVarInt(chatMode.rawValue)\n    writer.writeBool(chatColors)\n    writer.writeUnsignedByte(displayedSkinParts.rawValue)\n    writer.writeVarInt(mainHand.rawValue)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/ClientStatusPacket.swift",
    "content": "import Foundation\n\npublic struct ClientStatusPacket: ServerboundPacket {\n  public static let id: Int = 0x04\n  \n  public var action: ClientStatusAction\n  \n  public enum ClientStatusAction: Int32 {\n    case performRespawn = 0\n    case requestStats = 1\n  }\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(action.rawValue)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/CloseWindowServerboundPacket.swift",
    "content": "import Foundation\n\npublic struct CloseWindowServerboundPacket: ServerboundPacket {\n  public static let id: Int = 0x0a\n  \n  public var windowId: UInt8\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeUnsignedByte(windowId)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/CraftRecipeRequestPacket.swift",
    "content": "import Foundation\n\npublic struct CraftRecipeRequestPacket: ServerboundPacket {\n  public static let id: Int = 0x19\n  \n  public var windowId: Int8\n  public var recipe: Identifier\n  public var makeAll: Bool\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeByte(windowId)\n    writer.writeIdentifier(recipe)\n    writer.writeBool(makeAll)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/CreativeInventoryActionPacket.swift",
    "content": "import Foundation\n\npublic struct CreativeInventoryActionPacket: ServerboundPacket {\n  public static let id: Int = 0x27\n\n  public var slot: Int16\n  public var clickedItem: Slot\n\n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeShort(slot)\n    writer.writeSlot(clickedItem)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/EditBookPacket.swift",
    "content": "import Foundation\n\npublic struct EditBookPacket: ServerboundPacket {\n  public static let id: Int = 0x0c\n  \n  public var newBook: Slot\n  public var isSigning: Bool\n  public var hand: Hand\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeSlot(newBook)\n    writer.writeBool(isSigning)\n    writer.writeVarInt(hand.rawValue)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/EntityActionPacket.swift",
    "content": "import Foundation\n\npublic struct EntityActionPacket: ServerboundPacket {\n  public static let id: Int = 0x1c\n  \n  public var entityId: Int32\n  public var action: PlayerEntityAction\n  public var jumpBoost: Int32 = 0\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(entityId)\n    writer.writeVarInt(action.rawValue)\n    writer.writeVarInt(jumpBoost)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/GenerateStructurePacket.swift",
    "content": "import Foundation\n\npublic struct GenerateStructurePacket: ServerboundPacket {\n  public static let id: Int = 0x0f\n  \n  public var location: BlockPosition\n  public var levels: Int32\n  public var keepJigsaws: Bool\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writePosition(location)\n    writer.writeVarInt(levels)\n    writer.writeBool(keepJigsaws)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/HeldItemChangeServerboundPacket.swift",
    "content": "import Foundation\n\npublic struct HeldItemChangeServerboundPacket: ServerboundPacket {\n  public static let id: Int = 0x24\n  \n  public var slot: Int16\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeShort(slot)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/InteractEntityPacket.swift",
    "content": "import Foundation\n\npublic struct InteractEntityPacket: ServerboundPacket {\n  public static let id: Int = 0x0e\n  \n  public var entityId: Int32\n  public var interaction: EntityInteraction\n  \n  public enum EntityInteraction {\n    case interact(hand: Hand, isSneaking: Bool)\n    case attack(isSneaking: Bool)\n    case interactAt(targetX: Float, targetY: Float, targetZ: Float, hand: Hand, isSneaking: Bool)\n  }\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(entityId)\n    switch interaction {\n      case let .interact(hand: hand, isSneaking: isSneaking):\n        writer.writeVarInt(0) // interact\n        writer.writeVarInt(hand.rawValue)\n        writer.writeBool(isSneaking)\n      case let .attack(isSneaking: isSneaking):\n        writer.writeVarInt(1) // attack\n        writer.writeBool(isSneaking)\n      case let .interactAt(targetX: targetX, targetY: targetY, targetZ: targetZ, hand: hand, isSneaking: isSneaking):\n        writer.writeVarInt(2) // interact at\n        writer.writeFloat(targetX)\n        writer.writeFloat(targetY)\n        writer.writeFloat(targetZ)\n        writer.writeVarInt(hand.rawValue)\n        writer.writeBool(isSneaking)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/KeepAliveServerboundPacket.swift",
    "content": "import Foundation\n\npublic struct KeepAliveServerBoundPacket: ServerboundPacket {\n  public static let id: Int = 0x10\n  \n  public var keepAliveId: Int\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeLong(Int64(keepAliveId))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/LockDifficultyPacket.swift",
    "content": "import Foundation\n\npublic struct LockDifficultyPacket: ServerboundPacket {\n  public static let id: Int = 0x11\n  \n  public var locked: Bool\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeBool(locked)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/NameItemPacket.swift",
    "content": "import Foundation\n\npublic struct NameItemPacket: ServerboundPacket {\n  public static let id: Int = 0x1f\n  \n  public var itemName: String\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeString(itemName)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/PickItemPacket.swift",
    "content": "import Foundation\n\npublic struct PickItemPacket: ServerboundPacket {\n  public static let id: Int = 0x18\n  \n  public var slotToUse: Int32\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(slotToUse)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/PlayerAbilitiesServerboundPacket.swift",
    "content": "import Foundation\n\npublic struct PlayerAbilitiesServerboundPacket: ServerboundPacket {\n  public static let id: Int = 0x1a\n  \n  public var flags: PlayerFlags\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeUnsignedByte(flags.rawValue)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/PlayerBlockPlacementPacket.swift",
    "content": "import Foundation\n\npublic struct PlayerBlockPlacementPacket: ServerboundPacket {\n  public static let id: Int = 0x2d\n  \n  public var hand: Hand\n  public var location: BlockPosition\n  public var face: Direction\n  public var cursorPositionX: Float\n  public var cursorPositionY: Float\n  public var cursorPositionZ: Float\n  public var insideBlock: Bool\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(hand.rawValue)\n    writer.writePosition(location)\n    writer.writeVarInt(Int32(face.rawValue)) // wth mojang, why is it a varInt here and a byte somewhere else. it's literally the same enum! >:(\n    writer.writeFloat(cursorPositionX)\n    writer.writeFloat(cursorPositionY)\n    writer.writeFloat(cursorPositionZ)\n    writer.writeBool(insideBlock)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/PlayerDiggingPacket.swift",
    "content": "import Foundation\n\npublic struct PlayerDiggingPacket: ServerboundPacket {\n  public static let id: Int = 0x1b\n\n  public var status: DiggingStatus\n  public var location: BlockPosition\n  public var face: Direction\n\n  public enum DiggingStatus: Int32 {\n    case startedDigging = 0\n    case cancelledDigging = 1\n    case finishedDigging = 2\n    case dropItemStack = 3\n    case dropItem = 4\n    case shootArrowOrFinishEating = 5\n    case swapItemInHand = 6\n  }\n\n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(status.rawValue)\n    writer.writePosition(location)\n    writer.writeByte(Int8(face.rawValue))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/PlayerMovementPacket.swift",
    "content": "import Foundation\n\npublic struct PlayerMovementPacket: ServerboundPacket {\n  public static let id: Int = 0x15\n  \n  public var onGround: Bool\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeBool(onGround)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/PlayerPositionAndRotationServerboundPacket.swift",
    "content": "import Foundation\nimport FirebladeMath\n\npublic struct PlayerPositionAndRotationServerboundPacket: ServerboundPacket {\n  public static let id: Int = 0x13\n\n  public var position: Vec3d // y is feet position\n  public var yaw: Float\n  public var pitch: Float\n  public var onGround: Bool\n\n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeEntityPosition(position)\n    writer.writeFloat(yaw)\n    writer.writeFloat(pitch)\n    writer.writeBool(onGround)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/PlayerPositionPacket.swift",
    "content": "import Foundation\nimport FirebladeMath\n\npublic struct PlayerPositionPacket: ServerboundPacket {\n  public static let id: Int = 0x12\n\n  public var position: Vec3d // Position of feet\n  public var onGround: Bool\n\n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeEntityPosition(position)\n    writer.writeBool(onGround)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/PlayerRotationPacket.swift",
    "content": "import Foundation\n\npublic struct PlayerRotationPacket: ServerboundPacket {\n  public static let id: Int = 0x14\n  \n  public var yaw: Float\n  public var pitch: Float\n  public var onGround: Bool\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeFloat(yaw)\n    writer.writeFloat(pitch)\n    writer.writeBool(onGround)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/PluginMessageServerboundPacket.swift",
    "content": "import Foundation\n\npublic struct PluginMessageServerboundPacket: ServerboundPacket {\n  public static let id: Int = 0x0b\n  \n  public var channel: Identifier\n  public var data: [UInt8]\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeIdentifier(channel)\n    writer.writeByteArray(data)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/QueryBlockNBTPacket.swift",
    "content": "import Foundation\n\npublic struct QueryBlockNBTPacket: ServerboundPacket {\n  public static let id: Int = 0x01\n  \n  public var transactionId: Int32\n  public var location: BlockPosition\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(transactionId)\n    writer.writePosition(location)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/QueryEntityNBTPacket.swift",
    "content": "import Foundation\n\npublic struct QueryEntityNBTPacket: ServerboundPacket {\n  public static let id: Int = 0x0d\n  \n  public var transactionId: Int32\n  public var entityId: Int32\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(transactionId)\n    writer.writeVarInt(entityId)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/RecipeBookDataPacket.swift",
    "content": "import Foundation\n\npublic struct RecipeBookDataPacket: ServerboundPacket {\n  public static let id: Int = 0x1e\n  \n  public var data: RecipeBookData\n  \n  public enum RecipeBookData {\n    case displayedRecipe(recipeId: Identifier)\n    case recipeBookStates(state: RecipeBookState)\n  }\n  \n  public struct RecipeBookState {\n    public var craftingRecipeBookOpen: Bool\n    public var craftingRecipeFilterActive: Bool\n    public var smeltingRecipeBookOpen: Bool\n    public var smeltingRecipeFilterActive: Bool\n    public var blastingRecipeBookOpen: Bool\n    public var blastingRecipeFilterActive: Bool\n    public var smokingRecipeBookOpen: Bool\n    public var smokingRecipeFilterActive: Bool\n  }\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    switch data {\n      case let .displayedRecipe(recipeId: recipeId):\n        writer.writeVarInt(0) // displayed recipes\n        writer.writeIdentifier(recipeId)\n      case let .recipeBookStates(state: state):\n        writer.writeVarInt(1) // recipe book states\n        writer.writeBool(state.craftingRecipeBookOpen)\n        writer.writeBool(state.craftingRecipeFilterActive)\n        writer.writeBool(state.smeltingRecipeBookOpen)\n        writer.writeBool(state.smeltingRecipeFilterActive)\n        writer.writeBool(state.blastingRecipeBookOpen)\n        writer.writeBool(state.blastingRecipeFilterActive)\n        writer.writeBool(state.smokingRecipeBookOpen)\n        writer.writeBool(state.smokingRecipeFilterActive)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/ResourcePackStatusPacket.swift",
    "content": "import Foundation\n\npublic struct ResourcePackStatusPacket: ServerboundPacket {\n  public static let id: Int = 0x20\n  \n  public var result: ResourcePackStatus\n  \n  public enum ResourcePackStatus: Int32 {\n    case successfullyLoaded = 0\n    case declined = 1\n    case failedDownload = 2\n    case accepted = 3\n  }\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(result.rawValue)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/SelectTradePacket.swift",
    "content": "import Foundation\n\npublic struct SelectTradePacket: ServerboundPacket {\n  public static let id: Int = 0x22\n  \n  public var selectedSlot: Int32\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(selectedSlot)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/SetBeaconEffectPacket.swift",
    "content": "import Foundation\n\npublic struct SetBeaconEffectPacket: ServerboundPacket {\n  public static let id: Int = 0x23\n  \n  public var primaryEffect: Int32\n  public var secondaryEffect: Int32\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(primaryEffect)\n    writer.writeVarInt(secondaryEffect)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/SetDifficultyPacket.swift",
    "content": "import Foundation\n\npublic struct SetDifficultyPacket: ServerboundPacket {\n  public static let id: Int = 0x02\n  \n  public var newDifficulty: Difficulty\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeUnsignedByte(newDifficulty.rawValue)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/SpectatePacket.swift",
    "content": "import Foundation\n\npublic struct SpectatePacket: ServerboundPacket {\n  public static let id: Int = 0x2c\n  \n  public var targetPlayer: UUID\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeUUID(targetPlayer)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/SteerBoatPacket.swift",
    "content": "import Foundation\n\npublic struct SteerBoatPacket: ServerboundPacket {\n  public static let id: Int = 0x17\n  \n  public var isLeftPaddleTurning: Bool\n  public var isRightPaddleTurning: Bool\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeBool(isLeftPaddleTurning)\n    writer.writeBool(isRightPaddleTurning)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/SteerVehiclePacket.swift",
    "content": "import Foundation\n\npublic struct SteerVehiclePacket: ServerboundPacket {\n  public static let id: Int = 0x1d\n  \n  public var sideways: Float\n  public var forward: Float\n  public var flags: UInt8\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeFloat(sideways)\n    writer.writeFloat(forward)\n    writer.writeUnsignedByte(flags)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/TabCompleteServerboundPacket.swift",
    "content": "import Foundation\n\npublic struct TabCompleteServerboundPacket: ServerboundPacket {\n  public static let id: Int = 0x06\n  \n  public var transactionId: Int32\n  public var text: String\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(transactionId)\n    writer.writeString(text)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/TeleportConfirmPacket.swift",
    "content": "import Foundation\n\npublic struct TeleportConfirmPacket: ServerboundPacket {\n  public static let id: Int = 0x00\n  \n  public var teleportId: Int\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(Int32(teleportId))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/UpdateCommandBlockMinecartPacket.swift",
    "content": "import Foundation\n\npublic struct UpdateCommandBlockMinecartPacket: ServerboundPacket {\n  public static let id: Int = 0x26\n  \n  public var entityId: Int32\n  public var command: String\n  public var trackOutput: Bool\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(entityId)\n    writer.writeString(command)\n    writer.writeBool(trackOutput)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/UpdateCommandBlockPacket.swift",
    "content": "import Foundation\n\npublic struct UpdateCommandBlockPacket: ServerboundPacket {\n  public static let id: Int = 0x25\n  \n  public var location: BlockPosition\n  public var command: String\n  public var mode: CommandBlockMode\n  public var flags: CommandBlockFlags\n  \n  public enum CommandBlockMode: Int32 {\n    case sequence = 0\n    case auto = 1\n    case redstone = 2\n  }\n  \n  public struct CommandBlockFlags: OptionSet {\n    public let rawValue: UInt8\n    \n    public init(rawValue: UInt8) {\n      self.rawValue = rawValue\n    }\n    \n    public static let trackOutput = CommandBlockFlags(rawValue: 0x01)\n    public static let isConditional = CommandBlockFlags(rawValue: 0x02)\n    public static let automatic = CommandBlockFlags(rawValue: 0x04)\n  }\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writePosition(location)\n    writer.writeString(command)\n    writer.writeVarInt(mode.rawValue)\n    writer.writeUnsignedByte(flags.rawValue)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/UpdateJigsawBlockPacket.swift",
    "content": "import Foundation\n\npublic struct UpdateJigsawBlockPacket: ServerboundPacket {\n  public static let id: Int = 0x28\n  \n  public var location: BlockPosition\n  public var name: Identifier\n  public var target: Identifier\n  public var pool: Identifier\n  public var finalState: String\n  public var jointType: String\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writePosition(location)\n    writer.writeIdentifier(name)\n    writer.writeIdentifier(target)\n    writer.writeIdentifier(pool)\n    writer.writeString(finalState)\n    writer.writeString(jointType)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/UpdateSignPacket.swift",
    "content": "import Foundation\n\npublic struct UpdateSignPacket: ServerboundPacket {\n  public static let id: Int = 0x2a\n  \n  public var location: BlockPosition\n  public var line1: String\n  public var line2: String\n  public var line3: String\n  public var line4: String\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writePosition(location)\n    writer.writeString(line1)\n    writer.writeString(line2)\n    writer.writeString(line3)\n    writer.writeString(line4)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/UpdateStructureBlockPacket.swift",
    "content": "import Foundation\n\npublic struct UpdateStructureBlockPacket: ServerboundPacket {\n  public static let id: Int = 0x29\n  \n  public var location: BlockPosition\n  public var action: StructureBlockAction\n  public var mode: StructureBlockMode\n  public var name: String\n  public var offsetX: Int8\n  public var offsetY: Int8\n  public var offsetZ: Int8\n  public var sizeX: Int8\n  public var sizeY: Int8\n  public var sizeZ: Int8\n  public var mirror: StructureBlockMirror\n  public var rotation: StructureBlockRotation\n  public var metadata: String\n  public var integrity: Float\n  public var seed: Int64\n  public var flags: Int8\n  \n  public enum StructureBlockAction: Int32 {\n    case updateData = 0\n    case saveStructure = 1\n    case loadStructure = 2\n    case detectSize = 3\n  }\n  \n  public enum StructureBlockMode: Int32 {\n    case save = 0\n    case load = 1\n    case corner = 2\n    case data = 3\n  }\n  \n  public enum StructureBlockMirror: Int32 {\n    case none = 0\n    case leftRight = 1\n    case frontBack = 2\n  }\n  \n  public enum StructureBlockRotation: Int32 {\n    case none = 0\n    case clockwise90 = 1\n    case clockwise180 = 2\n    case counterClockwise90 = 3\n  }\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writePosition(location)\n    writer.writeVarInt(action.rawValue)\n    writer.writeVarInt(mode.rawValue)\n    writer.writeString(name)\n    writer.writeByte(offsetX)\n    writer.writeByte(offsetY)\n    writer.writeByte(offsetZ)\n    writer.writeByte(sizeX)\n    writer.writeByte(sizeY)\n    writer.writeByte(sizeZ)\n    writer.writeVarInt(mirror.rawValue)\n    writer.writeVarInt(rotation.rawValue)\n    writer.writeString(metadata)\n    writer.writeFloat(integrity)\n    writer.writeVarLong(seed)\n    writer.writeByte(flags)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/UseItemPacket.swift",
    "content": "import Foundation\n\npublic struct UseItemPacket: ServerboundPacket {\n  public static let id: Int = 0x2e\n  \n  public var hand: Hand\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeVarInt(hand.rawValue)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/VehicleMoveServerboundPacket.swift",
    "content": "import Foundation\nimport FirebladeMath\n\npublic struct VehicleMoveServerboundPacket: ServerboundPacket {\n  public static let id: Int = 0x16\n\n  public var position: Vec3d\n  public var yaw: Float\n  public var pitch: Float\n\n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeEntityPosition(position)\n    writer.writeFloat(yaw)\n    writer.writeFloat(pitch)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Play/Serverbound/WindowConfirmationServerboundPacket.swift",
    "content": "import Foundation\n\npublic struct WindowConfirmationServerboundPacket: ServerboundPacket {\n  public static let id: Int = 0x07\n  \n  public var windowId: Int8\n  public var actionNumber: Int16\n  public var accepted: Bool\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeByte(windowId)\n    writer.writeShort(actionNumber)\n    writer.writeBool(accepted)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/ServerboundPacket.swift",
    "content": "import Foundation\n\npublic protocol ServerboundPacket {\n  static var id: Int { get }\n  \n  // writes payload to packetwriter (everything after packet id)\n  func writePayload(to writer: inout PacketWriter)\n}\n\nextension ServerboundPacket {\n  public func toBuffer() -> Buffer {\n    var writer = PacketWriter()\n    writer.writeVarInt(Int32(Self.id))\n    writePayload(to: &writer)\n    return writer.buffer\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Status/Clientbound/PongPacket.swift",
    "content": "import Foundation\n\npublic struct PongPacket: ClientboundPacket {\n  public static let id: Int = 0x01\n  \n  public var payload: Int\n  \n  public init(from packetReader: inout PacketReader) throws {\n    payload = try packetReader.readLong()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Status/Clientbound/StatusResponsePacket.swift",
    "content": "import Foundation\n\npublic struct StatusResponsePacket: ClientboundPacket {\n  public static let id: Int = 0x00\n\n  public var response: StatusResponse\n\n  public init(from packetReader: inout PacketReader) throws {\n    let json = try packetReader.readString()\n\n    guard let data = json.data(using: .utf8) else {\n      throw ClientboundPacketError.invalidJSONString\n    }\n\n    response = try JSONDecoder().decode(StatusResponse.self, from: data)\n  }\n\n  public func handle(for pinger: Pinger) throws {\n    ThreadUtil.runInMain {\n      log.debug(\"Received ping response from \\(pinger.descriptor.description)\")\n      pinger.response = Result.success(response)\n      pinger.closeConnection()\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Status/Serverbound/PingPacket.swift",
    "content": "import Foundation\n\npublic struct PingPacket: ServerboundPacket {\n  public static let id: Int = 0x01\n  \n  public var payload: Int\n  \n  public func writePayload(to writer: inout PacketWriter) {\n    writer.writeLong(Int64(payload))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/Packets/Status/Serverbound/StatusRequestPacket.swift",
    "content": "import Foundation\n\npublic struct StatusRequestPacket: ServerboundPacket {\n  public static let id: Int = 0x00\n  \n  public func writePayload(to writer: inout PacketWriter) { }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Protocol/ProtocolVersion.swift",
    "content": "import Foundation\n\n// Protocol version numbers can be found at https://wiki.vg/Protocol_version_numbers\n\n/// The enum containing all supported protocol versions.\npublic enum ProtocolVersion: Int {\n  // The v in the version names is required because in swift, 1_16_1 is interpreted as the number 1161\n  // Don't include the v for snapshot versions because it's not necessary and it's less obvious that\n  // it's not part of the version's name because snapshot names include letters.\n  case v1_16_1 = 736\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/ServerConnection.swift",
    "content": "import Foundation\nimport AsyncDNSResolver\n\npublic enum ServerConnectionError: LocalizedError {\n  case invalidPacketId(Int)\n  case failedToResolveHostname(hostname: String, Error?)\n  case failedToCreateDNSResolver(Error?)\n\n  public var errorDescription: String? {\n    switch self {\n      case .invalidPacketId(let id):\n        return \"Invalid packet id 0x\\(String(id, radix: 16)).\"\n      case .failedToResolveHostname(let hostname, let error):\n        return \"\"\"\n        Failed to resolve hostname.\n        Hostname: \\(hostname)\n        Reason: \\(error?.localizedDescription ?? \"unknown\")\n        \"\"\"\n      case .failedToCreateDNSResolver(let error):\n        return \"Failed to create DNS Resolver: \\(error?.localizedDescription ?? \"unknown\")\"\n    }\n  }\n}\n\npublic class ServerConnection {\n  public private(set) var host: String\n  public private(set) var ipAddress: String\n  public private(set) var port: UInt16\n\n  public var networkStack: NetworkStack\n\n  public var packetRegistry: PacketRegistry\n  public private(set) var state: State = .idle\n\n  public private(set) var eventBus: EventBus\n\n  public var version = ProtocolVersion.v1_16_1\n\n  /// `true` if the ``JoinGamePacket`` has been received.\n  public var hasJoined = false\n\n  /// The string representation of the socket this connection is connecting to.\n  public var socketAddress: String {\n    return \"\\(host):\\(port)\"\n  }\n\n  // MARK: Init\n\n  /// Create a new connection to the specified server.\n  public init(descriptor: ServerDescriptor, eventBus: EventBus? = nil) async throws {\n    let (ipAddress, port) = try await ServerConnection.resolve(descriptor)\n\n    host = descriptor.host\n    self.ipAddress = host\n    self.port = port\n\n    self.eventBus = eventBus ?? EventBus()\n    packetRegistry = PacketRegistry.create_1_16_1()\n    networkStack = NetworkStack(ipAddress, port, eventBus: self.eventBus)\n  }\n\n  // MARK: Lifecycle\n\n  /// Closes the connection.\n  public func close() {\n    networkStack.disconnect()\n    state = .disconnected\n  }\n\n  /// Restarts the connection to the server. Initiates the connection if not currently connected.\n  private func restart() throws {\n    state = .connecting\n    try networkStack.reconnect()\n  }\n\n  /// Sets the state of the server connection.\n  public func setState(_ newState: State) {\n    state = newState\n  }\n\n  // MARK: NetworkStack configuration\n\n  /// Sets the threshold required to compress a packet. Be careful, this isn't threadsafe.\n  public func setCompression(threshold: Int) {\n    networkStack.compressionLayer.compressionThreshold = threshold\n  }\n\n  /// Enables the packet encryption layer.\n  public func enableEncryption(sharedSecret: [UInt8]) throws {\n    try networkStack.encryptionLayer.enableEncryption(sharedSecret: sharedSecret)\n  }\n\n  // MARK: Packet\n\n  /// Sets the handler to use for received packets.\n  public func setPacketHandler(_ handler: @escaping (ClientboundPacket) -> Void) {\n    networkStack.packetHandler = { [weak self] packetReader in\n      guard let self = self else { return }\n      do {\n        if let packetState = self.state.packetState {\n          // Create a mutable packet reader\n          var reader = packetReader\n\n          // Get the correct type of packet\n          guard let packetType = self.packetRegistry.getClientboundPacketType(\n            withId: reader.packetId,\n            andState: packetState\n          ) else {\n            self.eventBus.dispatch(PacketDecodingErrorEvent(\n              packetId: packetReader.packetId,\n              error: \"Invalid packet id 0x\\(String(reader.packetId, radix: 16))\"\n            ))\n            log.warning(\"Non-existent packet received with id 0x\\(String(reader.packetId, radix: 16))\")\n            self.close()\n            return\n          }\n\n          // Read the packet and then run its handler\n          let packet = try packetType.init(from: &reader)\n          handler(packet)\n        }\n      } catch {\n        self.close()\n        self.eventBus.dispatch(PacketDecodingErrorEvent(packetId: packetReader.packetId, error: \"\\(error)\"))\n        log.warning(\"Failed to decode packet with id \\(String(packetReader.packetId, radix: 16)): \\(error)\")\n      }\n    }\n  }\n\n  /// Resolves a server descriptor into an IP and a port.\n  public static func resolve(_ server: ServerDescriptor) async throws -> (String, UInt16) {\n    do {\n      // We only care about IPv4\n      var isIp: Bool\n      let parts = server.host.split(separator: \".\")\n      if parts.count != 4 {\n        isIp = false\n      } else {\n        isIp = true\n        for part in parts {\n          if UInt(part) == nil || part.count > 3 {\n            isIp = false\n            break\n          }\n        }\n      }\n\n      guard !isIp else {\n        return (server.host, server.port ?? 25565)\n      }\n\n      // If `host` is an ip already, no need to perform DNS lookups\n      let resolver: AsyncDNSResolver\n\n      do {\n        resolver = AsyncDNSResolver(try CAresDNSResolver())\n      } catch {\n        throw ServerConnectionError.failedToCreateDNSResolver(error)\n      }\n\n      // Check for SRV records if no port is specified\n      if server.port == nil {\n        let records = try? await resolver.querySRV(name: \"_minecraft._tcp.\\(server.host)\")\n        if let record = records?.first {\n          return (record.host, record.port)\n        }\n      }\n\n      // Check for regular records\n      let records = try await resolver.queryA(name: server.host)\n      if let record = records.first {\n        return (record.address.address, server.port ?? 25565)\n      }\n\n      throw ServerConnectionError.failedToResolveHostname(hostname: server.host, nil)\n    } catch {\n      throw ServerConnectionError.failedToResolveHostname(hostname: server.host, error)\n    }\n  }\n\n  /// Sends a packet to the server.\n  /// - Parameter packet: The packet to send.\n  public func sendPacket(_ packet: ServerboundPacket) throws {\n    try networkStack.sendPacket(packet)\n  }\n\n  // MARK: Handshake\n\n  /// Sends a login request to the server. Throws if the packet fails to send.\n  /// - Parameter username: The username to login with.\n  public func login(username: String) throws {\n    try restart()\n\n    try handshake(nextState: .login)\n    let loginStart = LoginStartPacket(username: username)\n    try sendPacket(loginStart)\n  }\n\n  /// Sends a status request or 'ping'.\n  public func ping() throws {\n    try restart()\n\n    try handshake(nextState: .status)\n    let statusRequest = StatusRequestPacket()\n    try sendPacket(statusRequest)\n  }\n\n  /// Sends a handshake with the goal of transitioning to the given state (either status or login).\n  public func handshake(nextState: HandshakePacket.NextState) throws {\n    let handshake = HandshakePacket(\n      protocolVersion: Constants.protocolVersion,\n      serverAddr: host,\n      serverPort: Int(port),\n      nextState: nextState\n    )\n    try sendPacket(handshake)\n    state = (nextState == .login) ? .login : .status\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Socket+Darwin.swift",
    "content": "#if canImport(Darwin)\nimport Darwin\n\npublic extension Socket {\n  typealias FileDescriptorType = Int32\n}\n\nextension Socket.FileDescriptor {\n  static let invalid = Socket.FileDescriptor(rawValue: -1)\n}\n\nextension Socket.SocketType {\n  public var rawValue: Int32 {\n    switch self {\n      case .tcp: return Int32(SOCK_STREAM)\n      case .udp: return Int32(SOCK_DGRAM)\n    }\n  }\n  public var rawType: Int32 {\n    return Int32(AF_INET)\n  }\n}\n\nextension Socket.AddressFamily {\n  public var rawValue: Int32 {\n    switch self {\n      case .ip4: return Int32(AF_INET)\n      case .ip6: return Int32(AF_INET6)\n      case .unix: return Int32(AF_UNIX)\n    }\n  }\n}\n\nextension Socket {\n  static let in_addr_any = Darwin.in_addr(s_addr: Darwin.in_addr_t(0))\n\n  static func makeAddressINET(port: UInt16) -> Darwin.sockaddr_in {\n    Darwin.sockaddr_in(\n      sin_len: UInt8(MemoryLayout<sockaddr_in>.stride),\n      sin_family: sa_family_t(AF_INET),\n      sin_port: port.bigEndian,\n      sin_addr: in_addr_any,\n      sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)\n    )\n  }\n\n  static func makeAddressINET6(port: UInt16) -> Darwin.sockaddr_in6 {\n    Darwin.sockaddr_in6(\n      sin6_len: UInt8(MemoryLayout<sockaddr_in6>.stride),\n      sin6_family: sa_family_t(AF_INET6),\n      sin6_port: port.bigEndian,\n      sin6_flowinfo: 0,\n      sin6_addr: in6addr_any,\n      sin6_scope_id: 0\n    )\n  }\n\n  static func makeAddressUnix(path: String) -> Darwin.sockaddr_un {\n    var addr = Darwin.sockaddr_un()\n    addr.sun_family = sa_family_t(AF_UNIX)\n    let pathCount = min(path.utf8.count, 104)\n    let len = UInt8(MemoryLayout<UInt8>.size + MemoryLayout<sa_family_t>.size + pathCount + 1)\n    _ = withUnsafeMutablePointer(to: &addr.sun_path.0) { ptr in\n      path.withCString {\n        strncpy(ptr, $0, Int(len))\n      }\n    }\n    addr.sun_len = len\n    return addr\n  }\n\n  static func socket(_ domain: Int32, _ type: Int32, _ protocol: Int32) -> FileDescriptorType {\n    Darwin.socket(domain, type, `protocol`)\n  }\n\n  static func fcntl(_ fd: FileDescriptorType, _ cmd: Int32) -> Int32 {\n    Darwin.fcntl(fd, cmd)\n  }\n\n  static func fcntl(_ fd: FileDescriptorType, _ cmd: Int32, _ value: Int32) -> Int32 {\n    Darwin.fcntl(fd, cmd, value)\n  }\n\n  static func setsockopt(_ fd: FileDescriptorType, _ level: Int32, _ name: Int32,\n               _ value: UnsafeRawPointer!, _ len: socklen_t) -> Int32 {\n    Darwin.setsockopt(fd, level, name, value, len)\n  }\n\n  static func getsockopt(_ fd: FileDescriptorType, _ level: Int32, _ name: Int32,\n               _ value: UnsafeMutableRawPointer!, _ len: UnsafeMutablePointer<socklen_t>!) -> Int32 {\n    Darwin.getsockopt(fd, level, name, value, len)\n  }\n\n  static func getpeername(_ fd: FileDescriptorType, _ addr: UnsafeMutablePointer<sockaddr>!, _ len: UnsafeMutablePointer<socklen_t>!) -> Int32 {\n    Darwin.getpeername(fd, addr, len)\n  }\n\n  static func getsockname(_ fd: FileDescriptorType, _ addr: UnsafeMutablePointer<sockaddr>!, _ len: UnsafeMutablePointer<socklen_t>!) -> Int32 {\n    Darwin.getsockname(fd, addr, len)\n  }\n\n  static func inet_ntop(_ domain: Int32, _ addr: UnsafeRawPointer!,\n              _ buffer: UnsafeMutablePointer<CChar>!, _ addrLen: socklen_t) throws {\n    if Darwin.inet_ntop(domain, addr, buffer, addrLen) == nil {\n      throw SocketError.actionFailed(\"Convert ip address to string (ntop)\")\n    }\n  }\n\n  static func inet_pton(_ domain: Int32, _ buffer: UnsafePointer<CChar>!, _ addr: UnsafeMutableRawPointer!) -> Int32 {\n    Darwin.inet_pton(domain, buffer, addr)\n  }\n\n  static func bind(_ fd: FileDescriptorType, _ addr: UnsafePointer<sockaddr>!, _ len: socklen_t) -> Int32 {\n    Darwin.bind(fd, addr, len)\n  }\n\n  static func listen(_ fd: FileDescriptorType, _ backlog: Int32) -> Int32 {\n    Darwin.listen(fd, backlog)\n  }\n\n  static func accept(_ fd: FileDescriptorType, _ addr: UnsafeMutablePointer<sockaddr>!, _ len: UnsafeMutablePointer<socklen_t>!) -> Int32 {\n    Darwin.accept(fd, addr, len)\n  }\n\n  static func connect(_ fd: FileDescriptorType, _ addr: UnsafePointer<sockaddr>!, _ len: socklen_t) -> Int32 {\n    Darwin.connect(fd, addr, len)\n  }\n\n  static func read(_ fd: FileDescriptorType, _ buffer: UnsafeMutableRawPointer!, _ nbyte: Int) -> Int {\n    Darwin.read(fd, buffer, nbyte)\n  }\n\n  static func write(_ fd: FileDescriptorType, _ buffer: UnsafeRawPointer!, _ nbyte: Int) -> Int {\n    Darwin.write(fd, buffer, nbyte)\n  }\n\n  static func close(_ fd: FileDescriptorType) -> Int32 {\n    Darwin.close(fd)\n  }\n\n  static func unlink(_ addr: UnsafePointer<CChar>!) -> Int32 {\n    Darwin.unlink(addr)\n  }\n\n  static func poll(_ fds: UnsafeMutablePointer<pollfd>!, _ nfds: UInt32, _ tmo_p: Int32) -> Int32 {\n    Darwin.poll(fds, nfds, tmo_p)\n  }\n\n  static func pollfd(fd: FileDescriptorType, events: Int16, revents: Int16) -> Darwin.pollfd {\n    Darwin.pollfd(fd: fd, events: events, revents: revents)\n  }\n}\n#endif\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Socket+Glibc.swift",
    "content": "#if canImport(Glibc)\nimport Glibc\n\npublic extension Socket {\n  typealias FileDescriptorType = Int32\n}\n\nextension Socket.FileDescriptor {\n  static let invalid = Socket.FileDescriptor(rawValue: -1)\n}\n\nextension Socket.SocketType {\n  public var rawValue: Int32 {\n    switch self {\n      case .tcp: return Int32(SOCK_STREAM.rawValue)\n      case .udp: return Int32(SOCK_DGRAM.rawValue)\n    }\n  }\n}\n\nextension Socket.AddressFamily {\n  public var rawValue: Int32 {\n    switch self {\n      case .ip4: return Int32(AF_INET)\n      case .ip6: return Int32(AF_INET6)\n      case .unix: return Int32(AF_UNIX)\n    }\n  }\n}\n\nextension Socket {\n  static let tcp = Int32(SOCK_STREAM.rawValue)\n  static let udp = Int32(SOCK_DGRAM.rawValue)\n\n  static let in_addr_any = Glibc.in_addr(s_addr: Glibc.in_addr_t(0))\n\n  static func makeAddressINET(port: UInt16) -> Glibc.sockaddr_in {\n    Glibc.sockaddr_in(\n      sin_family: sa_family_t(AF_INET),\n      sin_port: port.bigEndian,\n      sin_addr: in_addr_any,\n      sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)\n    )\n  }\n\n  static func makeAddressINET6(port: UInt16) -> Glibc.sockaddr_in6 {\n    Glibc.sockaddr_in6(\n      sin6_family: sa_family_t(AF_INET6),\n      sin6_port: port.bigEndian,\n      sin6_flowinfo: 0,\n      sin6_addr: in6addr_any,\n      sin6_scope_id: 0\n    )\n  }\n\n  static func makeAddressLoopback(port: UInt16) -> Glibc.sockaddr_in6 {\n    Glibc.sockaddr_in6(\n      sin6_family: sa_family_t(AF_INET6),\n      sin6_port: port.bigEndian,\n      sin6_flowinfo: 0,\n      sin6_addr: in6addr_loopback,\n      sin6_scope_id: 0\n    )\n  }\n\n  static func makeAddressUnix(path: String) -> Glibc.sockaddr_un {\n    var addr = Glibc.sockaddr_un()\n    addr.sun_family = sa_family_t(AF_UNIX)\n    let pathCount = min(path.utf8.count, 104)\n    let len = UInt8(MemoryLayout<UInt8>.size + MemoryLayout<sa_family_t>.size + pathCount + 1)\n    _ = withUnsafeMutablePointer(to: &addr.sun_path.0) { ptr in\n      path.withCString {\n        strncpy(ptr, $0, Int(len))\n      }\n    }\n    return addr\n  }\n\n  static func socket(_ domain: Int32, _ type: Int32, _ protocol: Int32) -> Int32 {\n    Glibc.socket(domain, type, `protocol`)\n  }\n\n  static func fcntl(_ fd: Int32, _ cmd: Int32) -> Int32 {\n    Glibc.fcntl(fd, cmd)\n  }\n\n  static func fcntl(_ fd: Int32, _ cmd: Int32, _ value: Int32) -> Int32 {\n    Glibc.fcntl(fd, cmd, value)\n  }\n\n  static func setsockopt(_ fd: Int32, _ level: Int32, _ name: Int32,\n               _ value: UnsafeRawPointer!, _ len: socklen_t) -> Int32 {\n    Glibc.setsockopt(fd, level, name, value, len)\n  }\n\n  static func getsockopt(_ fd: Int32, _ level: Int32, _ name: Int32,\n               _ value: UnsafeMutableRawPointer!, _ len: UnsafeMutablePointer<socklen_t>!) -> Int32 {\n    Glibc.getsockopt(fd, level, name, value, len)\n  }\n\n  static func getpeername(_ fd: Int32, _ addr: UnsafeMutablePointer<sockaddr>!, _ len: UnsafeMutablePointer<socklen_t>!) -> Int32 {\n    Glibc.getpeername(fd, addr, len)\n  }\n\n  static func getsockname(_ fd: Int32, _ addr: UnsafeMutablePointer<sockaddr>!, _ len: UnsafeMutablePointer<socklen_t>!) -> Int32 {\n    Glibc.getsockname(fd, addr, len)\n  }\n\n  static func inet_ntop(_ domain: Int32, _ addr: UnsafeRawPointer!,\n              _ buffer: UnsafeMutablePointer<CChar>!, _ addrLen: socklen_t) throws {\n    if Glibc.inet_ntop(domain, addr, buffer, addrLen) == nil {\n      throw SocketError.actionFailed(\"Convert ip address to string (ntop)\")\n    }\n  }\n\n  static func inet_pton(_ domain: Int32, _ buffer: UnsafePointer<CChar>!, _ addr: UnsafeMutableRawPointer!) -> Int32 {\n    Glibc.inet_pton(domain, buffer, addr)\n  }\n\n  static func bind(_ fd: Int32, _ addr: UnsafePointer<sockaddr>!, _ len: socklen_t) -> Int32 {\n    Glibc.bind(fd, addr, len)\n  }\n\n  static func listen(_ fd: Int32, _ backlog: Int32) -> Int32 {\n    Glibc.listen(fd, backlog)\n  }\n\n  static func accept(_ fd: Int32, _ addr: UnsafeMutablePointer<sockaddr>!, _ len: UnsafeMutablePointer<socklen_t>!) -> Int32 {\n    Glibc.accept(fd, addr, len)\n  }\n\n  static func connect(_ fd: Int32, _ addr: UnsafePointer<sockaddr>!, _ len: socklen_t) -> Int32 {\n    Glibc.connect(fd, addr, len)\n  }\n\n  static func read(_ fd: Int32, _ buffer: UnsafeMutableRawPointer!, _ nbyte: Int) -> Int {\n    Glibc.read(fd, buffer, nbyte)\n  }\n\n  static func write(_ fd: Int32, _ buffer: UnsafeRawPointer!, _ nbyte: Int) -> Int {\n    Glibc.send(fd, buffer, nbyte, Int32(MSG_NOSIGNAL))\n  }\n\n  static func close(_ fd: Int32) -> Int32 {\n    Glibc.close(fd)\n  }\n\n  static func unlink(_ addr: UnsafePointer<CChar>!) -> Int32 {\n    Glibc.unlink(addr)\n  }\n\n  static func poll(_ fds: UnsafeMutablePointer<pollfd>!, _ nfds: UInt32, _ tmo_p: Int32) -> Int32 {\n    Glibc.poll(fds, UInt(nfds), tmo_p)\n  }\n\n  static func pollfd(fd: FileDescriptorType, events: Int16, revents: Int16) -> Glibc.pollfd {\n    Glibc.pollfd(fd: fd, events: events, revents: revents)\n  }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Socket.swift",
    "content": "import Foundation\n\n#if canImport(WinSDK)\n  import WinSDK.WinSock2\n#endif\n\n/// A socket that can connect to internet and Unix sockets.\n///\n/// It's basically just a low-overhead wrapper around the traditional C APIs for working with\n/// sockets. The aim of the wrapper is to be type-safe and cross-platform.\npublic struct Socket: Sendable, Hashable {\n  /// A type of socket that can be created.\n  public enum SocketType {\n    case tcp\n    case udp\n  }\n\n  /// An address family that can be connected to.\n  public enum AddressFamily {\n    case ip4\n    case ip6\n    case unix\n  }\n\n  /// An ipv4, ipv6 or unix address.\n  public enum Address: Hashable {\n    case ip4(String, UInt16)\n    case ip6(String, UInt16)\n    case unix(String)\n\n    /// An internal API for creating address from the C ``sockaddr_storage`` type.\n    /// - Throws: An error is thrown if an invalid IP address is encountered or the address family\n    ///   is unsupported.\n    init(from addr: sockaddr_storage) throws {\n      switch Int32(addr.ss_family) {\n        case AF_INET:\n          var addr_in: sockaddr_in = Socket.unsafeCast(addr)\n          let maxLength = socklen_t(INET_ADDRSTRLEN)\n          let buffer = UnsafeMutablePointer<CChar>.allocate(capacity: Int(maxLength))\n          try Socket.inet_ntop(AF_INET, &addr_in.sin_addr, buffer, maxLength)\n          self = .ip4(String(cString: buffer), UInt16(addr_in.sin_port).byteSwapped)\n        case AF_INET6:\n          var addr_in6: sockaddr_in6 = Socket.unsafeCast(addr)\n          let maxLength = socklen_t(INET6_ADDRSTRLEN)\n          let buffer = UnsafeMutablePointer<CChar>.allocate(capacity: Int(maxLength))\n          try Socket.inet_ntop(AF_INET6, &addr_in6.sin6_addr, buffer, maxLength)\n          self = .ip6(String(cString: buffer), UInt16(addr_in6.sin6_port).byteSwapped)\n        case AF_UNIX:\n          var addr_un: sockaddr_un = Socket.unsafeCast(addr)\n          self = withUnsafePointer(to: &addr_un.sun_path.0) {\n            return .unix(String(cString: $0))\n          }\n        default:\n          throw SocketError.unsupportedAddressFamily(rawValue: Int(addr.ss_family))\n      }\n    }\n\n    /// An internal API for converting addresses to their native storage type.\n    /// - Throws: An error is thrown if an invalid IP address is encountered.\n    func toNative() throws -> sockaddr {\n      switch self {\n        case .ip4(let host, let port):\n          var address = Socket.makeAddressINET(port: port)\n          address.sin_addr = try Socket.makeInAddr(fromIP4: host)\n          return Socket.unsafeCast(address)\n        case .ip6(let host, let port):\n          var address = Socket.makeAddressINET6(port: port)\n          address.sin6_addr = try Socket.makeInAddr(fromIP6: host)\n          return Socket.unsafeCast(address)\n        case .unix(let path):\n          return Socket.unsafeCast(makeAddressUnix(path: path))\n      }\n    }\n\n    /// The size of the native type that this address is represented by.\n    var nativeSize: Int {\n      switch self {\n        case .ip4: return MemoryLayout<sockaddr_in>.size\n        case .ip6: return MemoryLayout<sockaddr_in6>.size\n        case .unix: return MemoryLayout<sockaddr_un>.size\n      }\n    }\n  }\n\n  /// A set of socket flags.\n  public struct Flags: OptionSet {\n    public var rawValue: Int32\n\n    public init(rawValue: Int32) {\n      self.rawValue = rawValue\n    }\n\n    public static let nonBlocking = Flags(rawValue: O_NONBLOCK)\n  }\n\n  /// A file descriptor used as a reference to a particular socket.\n  public struct FileDescriptor: RawRepresentable, Sendable, Hashable {\n    public let rawValue: Socket.FileDescriptorType\n\n    public init(rawValue: Socket.FileDescriptorType) {\n      self.rawValue = rawValue\n    }\n  }\n\n  // MARK: Properties\n\n  /// The file descriptor of the socket.\n  private let file: FileDescriptor\n\n  // MARK: Init\n\n  /// Creates a new socket.\n  /// - Parameters:\n  ///   - addressFamily: The address family that will be used.\n  ///   - type: The type of socket to create.\n  /// - Throws: An error is thrown if the socket could not be created.\n  public init(_ addressFamily: AddressFamily, _ type: SocketType) throws {\n    let descriptor = FileDescriptor(\n      rawValue: Socket.socket(addressFamily.rawValue, type.rawValue, 0))\n    guard descriptor != .invalid else {\n      throw SocketError.actionFailed(\"create\")\n    }\n    self.file = descriptor\n  }\n\n  // MARK: Flags\n\n  /// Gets the socket's flags.\n  /// - Throws: An error is thrown if ``fcntl`` fails.\n  public func getFlags() throws -> Flags {\n    let flags = Socket.fcntl(file.rawValue, F_GETFL)\n    if flags == -1 {\n      throw SocketError.actionFailed(\"get flags\")\n    }\n    return Flags(rawValue: flags)\n  }\n\n  /// Sets the socket's flags.\n  /// - Throws: An error is thrown if ``fcntl`` fails.\n  public func setFlags(_ flags: Flags) throws {\n    if Socket.fcntl(file.rawValue, F_SETFL, flags.rawValue) == -1 {\n      throw SocketError.actionFailed(\"set flags\")\n    }\n  }\n\n  // MARK: Options\n\n  /// Sets a socket option's value.\n  public func setValue<O: SettableSocketOption>(_ value: O.Value, for option: O) throws {\n    var value = option.makeSocketValue(from: value)\n    let length = socklen_t(MemoryLayout<O.SocketValue>.size)\n    try withUnsafePointer(to: &value) { valuePointer in\n      guard\n        Socket.setsockopt(\n          file.rawValue,\n          option.level,\n          option.name,\n          UnsafeRawPointer(valuePointer),\n          length\n        ) >= 0\n      else {\n        throw SocketError.actionFailed(\"set option\")\n      }\n    }\n  }\n\n  /// Gets a socket option's value.\n  public func getValue<O: GettableSocketOption>(for option: O) throws -> O.Value {\n    let valuePtr = UnsafeMutablePointer<O.SocketValue>.allocate(capacity: 1)\n    var length = socklen_t(MemoryLayout<O.SocketValue>.size)\n    guard Socket.getsockopt(file.rawValue, option.level, option.name, valuePtr, &length) >= 0 else {\n      throw SocketError.actionFailed(\"get option\")\n    }\n    return option.makeValue(from: valuePtr.pointee)\n  }\n\n  // MARK: Listening\n\n  /// Binds the socket to a specific address.\n  public func bind(to address: Address) throws {\n    var addr = try address.toNative()\n    let result = Socket.bind(file.rawValue, &addr, socklen_t(address.nativeSize))\n    guard result >= 0 else {\n      throw SocketError.actionFailed(\"bind\")\n    }\n  }\n\n  /// Attempts to start listening on the socket.\n  public func listen(maxPendingConnection: Int32 = SOMAXCONN) throws {\n    if Socket.listen(file.rawValue, maxPendingConnection) == -1 {\n      let error = SocketError.actionFailed(\"listen\")\n      try close()\n      throw error\n    }\n  }\n\n  /// Attempts to accept a connection.\n  public func accept() throws -> (file: FileDescriptor, address: Address) {\n    var address = sockaddr_storage()\n    var len = socklen_t(MemoryLayout<sockaddr_storage>.size)\n\n    let newFile = withUnsafeMutablePointer(to: &address) {\n      $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {\n        FileDescriptor(rawValue: Socket.accept(file.rawValue, $0, &len))\n      }\n    }\n\n    guard newFile != .invalid else {\n      if errno == EWOULDBLOCK {\n        throw SocketError.blocked\n      } else {\n        throw SocketError.actionFailed(\"accept\")\n      }\n    }\n\n    return (newFile, try Address(from: address))\n  }\n\n  // MARK: Socket names\n\n  /// Gets the address of the address that the socket is connected to.\n  public func remotePeer() throws -> Address {\n    var addr = sockaddr_storage()\n    var len = socklen_t(MemoryLayout<sockaddr_storage>.size)\n\n    let result = withUnsafeMutablePointer(to: &addr) {\n      $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {\n        Socket.getpeername(file.rawValue, $0, &len)\n      }\n    }\n    if result != 0 {\n      throw SocketError.actionFailed(\"get peer name\")\n    }\n    return try Address(from: addr)\n  }\n\n  /// Gets the address of the socket.\n  public func sockname() throws -> Address {\n    var addr = sockaddr_storage()\n    var len = socklen_t(MemoryLayout<sockaddr_storage>.size)\n\n    let result = withUnsafeMutablePointer(to: &addr) {\n      $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {\n        Socket.getsockname(file.rawValue, $0, &len)\n      }\n    }\n    if result != 0 {\n      throw SocketError.actionFailed(\"get socket name\")\n    }\n    return try Address(from: addr)\n  }\n\n  /// An internal method for creating ``in_addr`` from an IP address string.\n  static func makeInAddr(fromIP4 address: String) throws -> in_addr {\n    var addr = in_addr()\n    guard address.withCString({ Socket.inet_pton(AF_INET, $0, &addr) }) == 1 else {\n      throw SocketError.actionFailed(\n        \"convert ipv4 address to in_addr (pton, address: '\\(address)')\")\n    }\n    return addr\n  }\n\n  /// An internal method for creating ``in6_addr`` from an IP address string.\n  static func makeInAddr(fromIP6 address: String) throws -> in6_addr {\n    var addr = in6_addr()\n    guard address.withCString({ Socket.inet_pton(AF_INET6, $0, &addr) }) == 1 else {\n      throw SocketError.actionFailed(\"convert ipv6 address to in6_addr (pton)\")\n    }\n    return addr\n  }\n\n  // MARK: Connecting\n\n  /// Attempts to connect to a given address.\n  public func connect(to address: Address) throws {\n    var addr = try address.toNative()\n    let result = Socket.connect(file.rawValue, &addr, socklen_t(address.nativeSize))\n    guard result >= 0 || errno == EISCONN else {\n      if errno == EINPROGRESS {\n        throw SocketError.blocked\n      } else {\n        throw SocketError.actionFailed(\"connect\")\n      }\n    }\n  }\n\n  // MARK: IO\n\n  /// Attempts to receive data from the socket and returns both the data and the address of the\n  /// peer that sent the data.\n  public func recvFrom(atMost length: Int) throws -> (data: [UInt8], address: Address) {\n    var sender = sockaddr_storage()\n    var sockaddrLength = UInt32(MemoryLayout<sockaddr_storage>.stride)\n    let bytes = try [UInt8](unsafeUninitializedCapacity: length) { buffer, count in\n      withUnsafeMutablePointer(to: &sender) { pointer in\n        pointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { pointer in\n          count = recvfrom(file.rawValue, buffer.baseAddress, length, 0, pointer, &sockaddrLength)\n        }\n      }\n\n      guard count > 0 else {\n        if errno == EWOULDBLOCK {\n          throw SocketError.blocked\n        } else if errno == EBADF || count == 0 {\n          throw SocketError.disconnected\n        } else {\n          throw SocketError.actionFailed(\"receive\")\n        }\n      }\n    }\n    return (bytes, try Address(from: sender))\n  }\n\n  /// Attempts to read at most the given number of bytes from the socket.\n  public func read(atMost length: Int) throws -> [UInt8] {\n    try [UInt8](unsafeUninitializedCapacity: length) { buffer, count in\n      count = Socket.read(file.rawValue, buffer.baseAddress, length)\n      guard count > 0 else {\n        if errno == EWOULDBLOCK {\n          throw SocketError.blocked\n        } else if errno == EBADF || count == 0 {\n          throw SocketError.disconnected\n        } else {\n          throw SocketError.actionFailed(\"read\")\n        }\n      }\n    }\n  }\n\n  /// Attempts to write the given data to the socket.\n  /// - Returns: The number of bytes sent.\n  public func write(_ data: Data) throws -> Data.Index {\n    return try data.withUnsafeBytes { buffer in\n      let sent = Socket.write(file.rawValue, buffer.baseAddress! - data.startIndex, data.endIndex)\n      guard sent > 0 else {\n        if errno == EWOULDBLOCK {\n          throw SocketError.blocked\n        } else if errno == EBADF {\n          throw SocketError.disconnected\n        } else {\n          throw SocketError.actionFailed(\"write\")\n        }\n      }\n      return sent\n    }\n  }\n\n  // MARK: Closing\n\n  /// Attempts to close the socket gracefully.\n  public func close() throws {\n    if Socket.close(file.rawValue) == -1 {\n      throw SocketError.actionFailed(\"close\")\n    }\n  }\n\n  /// An internal helper to clean up some of the ugly pointer code for operating with C types. Use\n  /// with care.\n  static func unsafeCast<A, B>(_ value: A) -> B {\n    var value = value\n    return withUnsafePointer(to: &value) { pointer in\n      return pointer.withMemoryRebound(to: B.self, capacity: 1) { pointer in\n        return pointer.pointee\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/SocketError.swift",
    "content": "import Foundation\n\n/// An error thrown by ``Socket`` or a related type.\npublic enum SocketError: LocalizedError {\n  case actionFailed(String)\n  case blocked\n  case disconnected\n  case unsupportedAddressFamily(rawValue: Int)\n\n  public var errorDescription: String? {\n    switch self {\n      case .actionFailed(let action):\n        return \"\"\"\n        Socket action failed.\n        Action: \\(action)\n        \"\"\"\n      case .blocked:\n        return \"A socket action failed because it blocked and the socket was non-blocking.\"\n      case .disconnected:\n        return \"A socket action failed because the socket was disconnected.\"\n      case .unsupportedAddressFamily(let rawValue):\n        return \"\"\"\n        Unsupported address family.\n        Raw value: \\(rawValue)\n        \"\"\"\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/SocketOption.swift",
    "content": "#if canImport(WinSDK)\nimport WinSDK.WinSock2\n#endif\nimport Foundation\n\n/// A socket option that is settable.\npublic protocol SettableSocketOption {\n  /// The Swift type of the value.\n  associatedtype Value\n  /// The C type of the value.\n  associatedtype SocketValue\n\n  /// The 'name' of the option (in C).\n  var name: Int32 { get }\n  /// The 'level' of the option (in C).\n  var level: Int32 { get }\n\n  /// Converts a Swift value to a C value.\n  func makeSocketValue(from value: Value) -> SocketValue\n}\n\n/// A socket option that is gettable.\npublic protocol GettableSocketOption {\n  /// The Swift type of the value.\n  associatedtype Value\n  /// The C type of the value.\n  associatedtype SocketValue\n\n  /// The 'name' of the option (in C).\n  var name: Int32 { get }\n  /// The 'level' of the option (in C).\n  var level: Int32 { get }\n\n  /// Converts a C value to a Swift value.\n  func makeValue(from socketValue: SocketValue) -> Value\n}\n\n/// A socket option that is both settable and gettable.\npublic protocol SocketOption: SettableSocketOption, GettableSocketOption {\n  /// The Swift type of the value.\n  associatedtype Value\n  /// The C type of the value.\n  associatedtype SocketValue\n\n  /// The 'name' of the option (in C).\n  var name: Int32 { get }\n  /// The 'level' of the option (in C).\n  var level: Int32 { get }\n\n  /// Converts a Swift value to a C value.\n  func makeValue(from socketValue: SocketValue) -> Value\n  /// Converts a C value to a Swift value.\n  func makeSocketValue(from value: Value) -> SocketValue\n}\n\nextension SettableSocketOption {\n  public var level: Int32 {\n    return SOL_SOCKET\n  }\n}\n\nextension GettableSocketOption {\n  public var level: Int32 {\n    return SOL_SOCKET\n  }\n}\n\nextension SocketOption {\n  public var level: Int32 {\n    return SOL_SOCKET\n  }\n}\n\n// MARK: Value types\n\n/// The value of a membership request socket option.\npublic struct MembershipRequest {\n  var groupAddress: in_addr\n  var localAddress: in_addr\n\n  /// Creates a new membership request.\n  public init(groupAddress: String, localAddress: String) throws {\n    self.groupAddress = try Socket.makeInAddr(fromIP4: groupAddress)\n    self.localAddress = try Socket.makeInAddr(fromIP4: localAddress)\n  }\n}\n\n/// The value of a time socket option (e.g. timeouts).\npublic struct TimeValue {\n  /// The number of seconds.\n  public var seconds: Int\n  /// The number of micro seconds (used to add precision).\n  public var microSeconds: Int\n\n  /// Creates a new time value.\n  public init(seconds: Int = 0, microSeconds: Int = 0) {\n    self.seconds = seconds\n    self.microSeconds = microSeconds\n  }\n}\n\n// MARK: Option types\n\n/// A boolean socket option (e.g. allow local address reuse).\npublic struct BoolSocketOption: SocketOption {\n  public var name: Int32\n\n  public init(name: Int32) {\n    self.name = name\n  }\n\n  public func makeValue(from socketValue: Int32) -> Bool {\n    socketValue > 0\n  }\n\n  public func makeSocketValue(from value: Bool) -> Int32 {\n    value ? 1 : 0\n  }\n}\n\n/// A membership request socket option.\npublic struct MembershipRequestSocketOption: SettableSocketOption {\n  public var name: Int32\n\n  public init(name: Int32) {\n    self.name = name\n  }\n\n  public var level: Int32 {\n    Int32(IPPROTO_IP)\n  }\n\n  public func makeSocketValue(from value: MembershipRequest) -> MembershipRequest {\n    value\n  }\n}\n\n/// A generic type for creating simple socket options where the Swift and C types of the value are the same.\npublic struct SimpleSocketOption<T>: SocketOption {\n  public var name: Int32\n\n  public init(name: Int32) {\n    self.name = name\n  }\n\n  public func makeValue(from socketValue: T) -> T {\n    socketValue\n  }\n\n  public func makeSocketValue(from value: T) -> T {\n    value\n  }\n}\n\n/// An integer socket option.\npublic typealias Int32SocketOption = SimpleSocketOption<Int32>\n/// A time socket option (e.g. a timeout).\npublic typealias TimeSocketOption = SimpleSocketOption<TimeValue>\n\n// MARK: Common options\n\npublic extension BoolSocketOption {\n  /// Whether local address reuse is allowed or not.\n  static var localAddressReuse: Self {\n    BoolSocketOption(name: SO_REUSEADDR)\n  }\n}\n\npublic extension MembershipRequestSocketOption {\n  /// A request to add the socket to a multicast group.\n  static var addMembership: Self {\n    MembershipRequestSocketOption(name: IP_ADD_MEMBERSHIP)\n  }\n\n  /// A request to remove the socket from a multicast group.\n  static var dropMembership: Self {\n    MembershipRequestSocketOption(name: IP_DROP_MEMBERSHIP)\n  }\n}\n\npublic extension Int32SocketOption {\n  /// The size of the send buffer.\n  static var sendBufferSize: Self {\n    Int32SocketOption(name: SO_SNDBUF)\n  }\n\n  /// The size of the receive buffer.\n  static var receiveBufferSize: Self {\n    Int32SocketOption(name: SO_RCVBUF)\n  }\n}\n\npublic extension TimeSocketOption {\n  /// The timeout for receiving data.\n  static var receiveTimeout: Self {\n    TimeSocketOption(name: SO_RCVTIMEO)\n  }\n\n  /// The timeout for sending data.\n  static var sendTimeout: Self {\n    TimeSocketOption(name: SO_SNDTIMEO)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Stack/Layers/CompressionLayer.swift",
    "content": "import Foundation\n\n/// An error with packet compression.\npublic enum CompressionLayerError: LocalizedError {\n  /// A compressed packet was under the required length for compressed packets.\n  case compressedPacketIsUnderThreshold(length: Int, threshold: Int)\n\n  public var errorDescription: String? {\n    switch self {\n      case .compressedPacketIsUnderThreshold(let length, let threshold):\n        return \"\"\"\n        A compressed packet was under the required length for compressed packets.\n        Length: \\(length)\n        Threshold: \\(threshold)\n        \"\"\"\n    }\n  }\n}\n\n/// Handles the compression and decompression of packets (outbound and inbound respectively).\npublic struct CompressionLayer {\n  // MARK: Public properties\n\n  /// Packets greater than or equal to this threshold (in length) will get compressed.\n  public var compressionThreshold = -1\n\n  // MARK: Init\n\n  /// Creates a new compression layer.\n  public init() {}\n\n  // MARK: Public properties\n\n  public func processInbound(_ buffer: Buffer) throws -> Buffer {\n    if compressionThreshold > 0 {\n      var buffer = buffer\n      let dataLength = Int(try buffer.readVariableLengthInteger())\n      if dataLength == 0 { // Packet isn't compressed\n        return buffer\n      } else { // Packet is compressed\n        if dataLength < compressionThreshold {\n          throw CompressionLayerError.compressedPacketIsUnderThreshold(length: dataLength, threshold: compressionThreshold)\n        } else {\n          let compressedBytes = buffer.readRemainingBytes()\n          let decompressed = try CompressionUtil.decompress(compressedBytes, decompressedLength: dataLength)\n          return Buffer(decompressed)\n        }\n      }\n    } else {\n      return buffer\n    }\n  }\n\n  public func processOutbound(_ buffer: Buffer) throws -> Buffer {\n    if compressionThreshold > 0 { // Compression is enabled\n      var buffer = buffer\n      var outBuffer = Buffer()\n      if buffer.length >= compressionThreshold {\n        let bytes = buffer.readRemainingBytes()\n        let compressed = try CompressionUtil.compress(bytes)\n        outBuffer.writeVarInt(Int32(compressed.count))\n        outBuffer.writeBytes(compressed)\n        return outBuffer\n      } else {\n        outBuffer.writeVarInt(0)\n        outBuffer.writeBytes(buffer.readRemainingBytes())\n        return outBuffer\n      }\n    } else { // Compression is disabled\n      return buffer\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Stack/Layers/EncryptionLayer.swift",
    "content": "import Foundation\n\n/// An error related to packet encryption and decryption.\npublic enum EncryptionLayerError: LocalizedError {\n  /// Failed to decrypt a packet.\n  case failedToDecryptPacket(Error)\n  /// Failed to encrypt a packet.\n  case failedToEncryptPacket(Error)\n\n  public var errorDescription: String? {\n    switch self {\n      case .failedToDecryptPacket(let error):\n        return \"\"\"\n        Failed to decrypt a packet.\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n      case .failedToEncryptPacket(let error):\n        return \"\"\"\n        Failed to encrypt a packet.\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n    }\n  }\n}\n\n/// Handles the encryption and decryption of packets (outbound and inbound respectively).\npublic struct EncryptionLayer {\n  // MARK: Private properties\n\n  /// The cipher used for decrypting inbound packets.\n  private var inputCipher: Cipher?\n  /// The cipher used for encryption outbound packets.\n  private var outputCipher: Cipher?\n\n  // MARK: Init\n\n  /// Creates a new encryption layer.\n  public init() {}\n\n  // MARK: Public methods\n\n  /// Enables the encryption layer.\n  /// - Parameter sharedSecret: The shared secret to initialize the stream ciphers with.\n  public mutating func enableEncryption(sharedSecret: [UInt8]) throws {\n    inputCipher = try Cipher(.decrypt, key: sharedSecret, iv: sharedSecret)\n    outputCipher = try Cipher(.encrypt, key: sharedSecret, iv: sharedSecret)\n  }\n\n  /// Decrypts a packet.\n  public func processInbound(_ buffer: Buffer) throws -> Buffer {\n    if let cipher = inputCipher {\n      do {\n        let decrypted = try cipher.update(with: buffer.bytes)\n        return Buffer(decrypted)\n      } catch {\n        throw EncryptionLayerError.failedToDecryptPacket(error)\n      }\n    } else {\n      return buffer\n    }\n  }\n\n  /// Encrypts a packet.\n  public func processOutbound(_ buffer: Buffer) throws -> Buffer {\n    if let cipher = outputCipher {\n      do {\n        let encrypted = try cipher.update(with: buffer.bytes)\n        return Buffer(encrypted)\n      } catch {\n        throw EncryptionLayerError.failedToEncryptPacket(error)\n      }\n    } else {\n      return buffer\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Stack/Layers/PacketLayer.swift",
    "content": "import Foundation\n\n/// Splits a stream of bytes into Minecraft packets.\npublic struct PacketLayer {\n  // MARK: Private properties\n\n  /// Keeps track of the state of the receiver across calls to ``processInbound(_:)``.\n  private var receiveState = ReceiveState()\n\n  /// The state of the receiver.\n  private struct ReceiveState {\n    var lengthBytes: [UInt8] = []\n    var length: Int?\n    var packet: [UInt8] = []\n  }\n\n  // MARK: Init\n\n  /// Creates a new packet layer.\n  public init() {}\n\n  // MARK: Public methods\n\n  /// Processes an inbound buffer.\n  ///\n  /// For each packet contained within the buffer it calls ``packetHandler``. It also works if\n  /// packets are split across multiple packets.\n  /// - Parameter buffer: The buffer of data received from the server.\n  /// - Returns: An array of buffers, each representing a Minecraft packet.\n  public mutating func processInbound(_ buffer: Buffer) throws -> [Buffer] {\n    var packets: [Buffer] = []\n\n    var buffer = buffer // mutable copy\n    while true {\n      if buffer.remaining == 0 {\n        break\n      }\n\n      guard let length = receiveState.length else {\n        receiveState.length = try readLength(from: &buffer)\n        continue\n      }\n\n      if length == 0 {\n        log.trace(\"Received empty packet\")\n        receiveState = ReceiveState()\n      } else if buffer.remaining != 0 {\n        let byteCount = min(buffer.remaining, length - receiveState.packet.count)\n        let bytes = try buffer.readBytes(byteCount)\n        receiveState.packet.append(contentsOf: bytes)\n\n        if receiveState.packet.count == length {\n          packets.append(Buffer(receiveState.packet))\n          receiveState = ReceiveState()\n        }\n      }\n    }\n\n    return packets\n  }\n\n  /// Wraps an outbound packet into a regularly formatted Minecraft packet (without compression and encryption).\n  ///\n  /// It prefixes the buffer with its length as a var int.\n  public func processOutbound(_ buffer: Buffer) -> Buffer {\n    var packed = Buffer()\n    packed.writeVarInt(Int32(buffer.length))\n    packed.writeBytes(buffer.bytes)\n    return packed\n  }\n\n  // MARK: Private methods\n\n  private mutating func readLength(from buffer: inout Buffer) throws -> Int? {\n    while buffer.remaining != 0 {\n      let byte = try buffer.readByte()\n      receiveState.lengthBytes.append(byte)\n      if byte & 0x80 == 0x00 {\n        break\n      }\n    }\n\n    if let lastLengthByte = receiveState.lengthBytes.last {\n      if lastLengthByte & 0x80 == 0x00 {\n        // Using standalone implementation of varint decoding to hopefully reduce networking overheads slightly?\n        var length = 0\n        for i in 0..<receiveState.lengthBytes.count {\n          let byte = receiveState.lengthBytes[i]\n          length += Int(byte & 0x7f) << (i * 7)\n        }\n        return length\n      }\n    }\n\n    return nil\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Stack/Layers/SocketLayer.swift",
    "content": "import Foundation\n\n/// An error thrown by ``SocketLayer``.\npublic enum SocketLayerError: LocalizedError {\n  case failedToCreateSocket(Error)\n  case alreadyConnected\n  case noValidDNSRecords(String)\n\n  public var errorDescription: String? {\n    switch self {\n      case .failedToCreateSocket(let error):\n        return \"\"\"\n        Failed to create socket.\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n      case .alreadyConnected:\n        return \"An attempt to connect was made while already connected.\"\n      case .noValidDNSRecords(let hostname):\n        return \"No valid DNS records for hostname '\\(hostname)'.\"\n    }\n  }\n}\n\n/// The network layer that handles a socket connection to a server.\n///\n/// Call ``connect()`` to start the socket connection. After connecting,\n/// ``receive()`` can be called to start the receive loop. ``packetHandler``\n/// will be called for each received packet.\npublic final class SocketLayer {\n  /// Called for each packet received.\n  public var packetHandler: ((Buffer) -> Void)?\n\n  /// The queue for receiving packets on.\n  private var ioQueue: DispatchQueue\n  /// The event bus to dispatch network errors on.\n  private var eventBus: EventBus\n\n  /// The IP address being connected to.\n  private var ipAddress: String\n  /// The port being connected to.\n  private var port: UInt16\n\n  /// A queue of packets waiting to be sent.\n  private var packetQueue: [Buffer] = []\n\n  /// The socket connection to the server.\n  private var socket: Socket?\n  /// The connection state of the socket.\n  private var state: State = .idle\n\n  /// A socket connection state.\n  private enum State {\n    case idle\n    case connecting\n    case connected\n    case disconnected\n  }\n\n  /// Creates a new socket layer for connecting to the given server.\n  /// - Parameters:\n  ///   - ipAddress: The ip address to connect to.\n  ///   - port: The port to connect to.\n  ///   - eventBus: The event bus to dispatch errors to.\n  public init(\n    _ ipAddress: String,\n    _ port: UInt16,\n    eventBus: EventBus\n  ) {\n    self.ipAddress = ipAddress\n    self.port = port\n    self.eventBus = eventBus\n\n    ioQueue = DispatchQueue(label: \"NetworkStack.ioThread\")\n  }\n\n  deinit {\n    disconnect()\n  }\n\n  /// Creates the layer's socket connection synchronously.\n  ///\n  /// Once connected it sends all packets that were waiting to be sent until connected (stored in\n  /// ``packetQueue``).\n  private func createSocket() throws {\n    do {\n      let socket = try Socket(.ip4, .tcp)\n\n      let timeout = TimeValue(seconds: 10)\n      try socket.setValue(timeout, for: TimeSocketOption.sendTimeout)\n      try socket.setValue(timeout, for: TimeSocketOption.receiveTimeout)\n\n      try socket.connect(to: Socket.Address.ip4(\n        ipAddress,\n        port\n      ))\n\n      self.socket = socket\n      state = .connected\n\n      // Send any packets that were waiting for a connection\n      for packet in packetQueue {\n        try send(packet)\n      }\n      packetQueue = []\n    } catch {\n      throw SocketLayerError.failedToCreateSocket(error)\n    }\n  }\n\n  /// Starts an asynchronous loop that receives and handles packets until disconnected or an error\n  /// occurs. Implemented recursively to avoid creating reference cycles.\n  private func asyncSocketReadLoop() {\n    ioQueue.async { [weak self] in\n      guard let self = self, let socket = self.socket else {\n        return\n      }\n\n      do {\n        let bytes = try socket.read(atMost: 4096)\n        let buffer = Buffer(bytes)\n        self.packetHandler?(buffer)\n\n        if self.state != .disconnected {\n          self.asyncSocketReadLoop()\n        }\n      } catch {\n        if self.state == .disconnected {\n          return\n        }\n\n        self.disconnect()\n        self.eventBus.dispatch(ConnectionFailedEvent(networkError: error))\n      }\n    }\n  }\n\n  /// Connects to the server.\n  public func connect() throws {\n    packetQueue = []\n    state = .connecting\n\n    if socket == nil {\n      ioQueue.async {\n        do {\n          try self.createSocket()\n          self.asyncSocketReadLoop()\n        } catch {\n          self.disconnect()\n          self.eventBus.dispatch(ConnectionFailedEvent(networkError: error))\n        }\n      }\n    } else {\n      throw SocketLayerError.alreadyConnected\n    }\n  }\n\n  /// Disconnect from the server.\n  public func disconnect() {\n    state = .disconnected\n    do {\n      try socket?.close()\n    } catch {\n      log.warning(\"Failed to close socket gracefully\")\n    }\n    socket = nil\n  }\n\n  /// Sends the given buffer to the connected server.\n  ///\n  /// If the connection is in a disconnected state, the packet is ignored. If the connection is\n  /// idle or connecting, the packet is stored to be sent once a connection is established.\n  /// - Parameter buffer: The buffer to send.\n  public func send(_ buffer: Buffer) throws {\n    guard state == .connected, let socket = socket else {\n      packetQueue.append(buffer)\n      return\n    }\n\n    let bytes = buffer.bytes\n    _ = try socket.write(Data(bytes))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Network/Stack/NetworkStack.swift",
    "content": "import Foundation\n\n/// The network stack that handles receiving and sending Minecraft packets.\n///\n/// See https://wiki.vg/Protocol#Packet_format for more information about the packet format.\n/// See https://wiki.vg/Protocol_Encryption for more in-depth information about packet encryption and decryption.\npublic final class NetworkStack {\n  // MARK: Public properties\n\n  /// The handler for packets received from the server.\n  public var packetHandler: ((PacketReader) -> Void)?\n\n  /// Handles the connection to the server (the raw network IO).\n  public var socketLayer: SocketLayer\n  /// Encrypts and decrypts packets.\n  public var encryptionLayer: EncryptionLayer\n  /// Compresses and decompresses packets.\n  public var compressionLayer: CompressionLayer\n  /// Splits the stream of bytes into individual Minecraft packets.\n  public var packetLayer: PacketLayer\n\n  /// The serial queue that outbound packets are handled on.\n  public var outboundThread: DispatchQueue\n  /// The serial queue that inbound packets are handled on.\n  public var inboundThread: DispatchQueue\n\n  // MARK: Private properties\n\n  /// The IP address being connected to.\n  private var ipAddress: String\n  /// The port being connected to.\n  private var port: UInt16\n  /// The event bus that network errors are dispatched to.\n  private var eventBus: EventBus\n\n  // MARK: Init\n\n  /// Creates a new network stack for connecting to the given server.\n  public init(_ ipAddress: String, _ port: UInt16, eventBus: EventBus) {\n    self.ipAddress = ipAddress\n    self.port = port\n    self.eventBus = eventBus\n\n    // Create threads\n    inboundThread = DispatchQueue(label: \"NetworkStack.inboundThread\")\n    outboundThread = DispatchQueue(label: \"NetworkStack.outboundThread\")\n\n    // Create layers\n    socketLayer = SocketLayer(ipAddress, port, eventBus: eventBus)\n    encryptionLayer = EncryptionLayer()\n    compressionLayer = CompressionLayer()\n    packetLayer = PacketLayer()\n\n    // Setup handler for packets received from the server\n    socketLayer.packetHandler = { [weak self] buffer in\n      guard let self = self else { return }\n      self.inboundThread.async {\n        var buffer = buffer\n        do {\n          buffer = try self.encryptionLayer.processInbound(buffer)\n          let buffers = try self.packetLayer.processInbound(buffer)\n\n          for buffer in buffers {\n            let buffer = try self.compressionLayer.processInbound(buffer)\n            let packetReader = try PacketReader(buffer: buffer)\n            log.trace(\"Packet received, id=0x\\(String(format: \"%02x\", packetReader.packetId))\")\n            self.packetHandler?(packetReader)\n          }\n        } catch {\n          log.warning(\"Failed to decode a packet received from the server: \\(error)\")\n          self.socketLayer.disconnect()\n          self.eventBus.dispatch(ErrorEvent(error: error, message: \"Failed to decode a packet received from the server\"))\n        }\n      }\n    }\n  }\n\n  // MARK: Public methods\n\n  /// Sends a packet. Throws if the packet failed to be encrypted.\n  public func sendPacket(_ packet: ServerboundPacket) throws {\n    var buffer = packet.toBuffer()\n    buffer = try compressionLayer.processOutbound(buffer)\n    buffer = packetLayer.processOutbound(buffer)\n    try outboundThread.sync {\n      buffer = try encryptionLayer.processOutbound(buffer)\n      try socketLayer.send(buffer)\n      log.trace(\"Packet sent, id=0x\\(String(format: \"%02x\", type(of: packet).id))\")\n    }\n  }\n\n  /// Connect to the server.\n  public func connect() throws {\n    let handler = socketLayer.packetHandler\n    socketLayer = SocketLayer(ipAddress, port, eventBus: eventBus)\n    socketLayer.packetHandler = handler\n\n    compressionLayer = CompressionLayer()\n    encryptionLayer = EncryptionLayer()\n    packetLayer = PacketLayer()\n\n    try socketLayer.connect()\n  }\n\n  /// Disconnect from the server.\n  public func disconnect() {\n    socketLayer.disconnect()\n  }\n\n  /// Reconnect to the server.\n  public func reconnect() throws {\n    disconnect()\n    try connect()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Physics/AxisAlignedBoundingBox.swift",
    "content": "import FirebladeMath\nimport Foundation\n\n/// An axis aligned bounding box used for efficient collisions and visibility checks.\npublic struct AxisAlignedBoundingBox: Codable {\n  // MARK: Public properties\n\n  /// The position of the minimum vertex of the bounding box.\n  public var position: Vec3d\n  /// The size of the bounding box.\n  public var size: Vec3d\n\n  /// The minimum vertex of the bounding box.\n  public var minimum: Vec3d {\n    position\n  }\n\n  /// The maximum vertex of the bounding box.\n  public var maximum: Vec3d {\n    position + size\n  }\n\n  /// All of the block positions that this AABB overlaps with.\n  public var blockPositions: [BlockPosition] {\n    let minX = Int(minimum.x.rounded(.down))\n    let maxX = Int(maximum.x.rounded(.down))\n    let minY = Int(minimum.y.rounded(.down))\n    let maxY = Int(maximum.y.rounded(.down))\n    let minZ = Int(minimum.z.rounded(.down))\n    let maxZ = Int(maximum.z.rounded(.down))\n\n    var positions: [BlockPosition] = []\n    positions.reserveCapacity((maxX - minX + 1) * (maxY - minY + 1) * (maxZ - minZ + 1))\n\n    for x in minX...maxX {\n      for y in minY...maxY {\n        for z in minZ...maxZ {\n          positions.append(BlockPosition(x: x, y: y, z: z))\n        }\n      }\n    }\n\n    return positions\n  }\n\n  // MARK: Init\n\n  /// Create a new axis aligned bounding box at a position with a given size.\n  /// - Parameters:\n  ///   - position: The position of the bounding box.\n  ///   - size: The size of the bounding box. Must be positive. The absolute value is taken just in case.\n  public init(position: Vec3d, size: Vec3d) {\n    self.position = position\n    self.size = MathUtil.abs(size)\n  }\n\n  /// Create a new axis aligned bounding box with given minimum and maximum vertices.\n  /// - Parameters:\n  ///   - minimum: The minimum vertex.\n  ///   - maximum: The maximum vertex.\n  public init(minimum: Vec3d, maximum: Vec3d) {\n    position = minimum\n    size = MathUtil.abs(maximum - minimum)\n  }\n\n  // MARK: Public methods\n\n  /// Get an array containing all 8 of this bounding box's vertices.\n  /// - Returns: This bounding box's vertices.\n  public func getVertices() -> [Vec3d] {\n    let minimum = minimum\n    let maximum = maximum\n\n    let bfl = minimum\n    let bfr = Vec3d(maximum.x, minimum.y, minimum.z)\n    let tfl = Vec3d(minimum.x, maximum.y, minimum.z)\n    let tfr = Vec3d(maximum.x, maximum.y, minimum.z)\n\n    let bbl = Vec3d(minimum.x, minimum.y, maximum.z)\n    let bbr = Vec3d(maximum.x, minimum.y, maximum.z)\n    let tbl = Vec3d(minimum.x, maximum.y, maximum.z)\n    let tbr = maximum\n\n    return [bfl, bfr, tfl, tfr, bbl, bbr, tbl, tbr]\n  }\n\n  /// Get an array containing all 8 of this bounding box's vertices in homogenous form.\n  /// - Returns: This bounding box's vertices in homogenous form.\n  public func getHomogenousVertices() -> [Vec4d] {\n    let minimum = minimum\n    let maximum = maximum\n\n    let bfl = Vec4d(minimum, 1)\n    let bfr = Vec4d(maximum.x, minimum.y, minimum.z, 1)\n    let tfl = Vec4d(minimum.x, maximum.y, minimum.z, 1)\n    let tfr = Vec4d(maximum.x, maximum.y, minimum.z, 1)\n\n    let bbl = Vec4d(minimum.x, minimum.y, maximum.z, 1)\n    let bbr = Vec4d(maximum.x, minimum.y, maximum.z, 1)\n    let tbl = Vec4d(minimum.x, maximum.y, maximum.z, 1)\n    let tbr = Vec4d(maximum, 1)\n\n    return [bfl, bfr, tfl, tfr, bbl, bbr, tbl, tbr]\n  }\n\n  /// Moves the bounding box by the given amount.\n  public func offset(by vector: Vec3d) -> AxisAlignedBoundingBox {\n    var aabb = self\n    aabb.position += vector\n    return aabb\n  }\n\n  /// Moves the bounding box by the given amount along a given axis.\n  /// - Parameters:\n  ///   - amount: The amount to move the bounding box by.\n  ///   - axis: The axis to move the bounding box along.\n  /// - Returns: The offset bounding box.\n  public func offset(by amount: Double, along axis: Axis) -> AxisAlignedBoundingBox {\n    return offset(by: axis.positiveDirection.doubleVector * amount)\n  }\n\n  /// Extends the bounding box by the given amount in the given direction.\n  public func extend(_ direction: Direction, amount: Double) -> AxisAlignedBoundingBox {\n    var aabb = self\n    aabb.size += MathUtil.abs(direction.doubleVector * amount)\n    if !direction.isPositive {\n      aabb.position += direction.doubleVector * amount\n    }\n    return aabb\n  }\n\n  /// Extends the bounding box by the given vector. Unlike `grow`, it doesn't expand in every direction.\n  /// The expansion direction along each axis depends on the sign of the corresponding component of the\n  /// vector.\n  public func extend(by vector: Vec3d) -> AxisAlignedBoundingBox {\n    var aabb = self\n    let xDirection = vector.x >= 0 ? Axis.x.positiveDirection : Axis.x.negativeDirection\n    let yDirection = vector.y >= 0 ? Axis.y.positiveDirection : Axis.y.negativeDirection\n    let zDirection = vector.z >= 0 ? Axis.z.positiveDirection : Axis.z.negativeDirection\n    aabb = aabb.extend(xDirection, amount: abs(vector.x))\n    aabb = aabb.extend(yDirection, amount: abs(vector.y))\n    aabb = aabb.extend(zDirection, amount: abs(vector.z))\n    return aabb\n  }\n\n  /// Grows by the given amount in each direction.\n  /// - Parameter amount: The amount to grow by.\n  /// - Returns: The new bounding box.\n  public func grow(by amount: Double) -> AxisAlignedBoundingBox {\n    return grow(by: Vec3d(repeating: amount))\n  }\n\n  /// Grows by the given amount in each direction.\n  /// - Parameter vector: The amount to grow by on each axis. The bounding box will grow by the given amount in both directions along the axis.\n  /// - Returns: The new bounding box.\n  public func grow(by vector: Vec3d) -> AxisAlignedBoundingBox {\n    var aabb = self\n    aabb.position -= vector\n    aabb.size += 2 * vector\n    return aabb\n  }\n\n  /// Shrinks by the given amount in each direction.\n  /// - Parameter amount: The amount to shrink by.\n  /// - Returns: The new bounding box.\n  public func shrink(by amount: Double) -> AxisAlignedBoundingBox {\n    return grow(by: -amount)\n  }\n\n  /// Shrinks by the given amount in each direction.\n  /// - Parameter vector: The amount to shrink by on each axis. The bounding box will shrink by the given amount in both directions along the axis.\n  /// - Returns: The new bounding box.\n  public func shrink(by vector: Vec3d) -> AxisAlignedBoundingBox {\n    return grow(by: -vector)\n  }\n\n  /// Checks whether the AABB overlaps with another given AABB.\n  /// - Parameter other: The AABB to check for intersection with.\n  /// - Returns: `true` if the AABBs intersect.\n  public func intersects(with other: AxisAlignedBoundingBox) -> Bool {\n    let minimum = minimum\n    let maximum = maximum\n\n    let otherMinimum = other.minimum\n    let otherMaximum = other.maximum\n\n    return\n      (minimum.x <= otherMaximum.x && maximum.x >= otherMinimum.x && minimum.y <= otherMaximum.y\n      && maximum.y >= otherMinimum.y && minimum.z <= otherMaximum.z && maximum.z >= otherMinimum.z)\n  }\n\n  /// Checks whether the AABB contains a given point.\n  public func contains(_ point: Vec3d) -> Bool {\n    return minimum.x <= point.x && minimum.y <= point.y && minimum.z <= point.z\n      && point.x <= maximum.x && point.y <= maximum.y && point.z <= maximum.z\n  }\n\n  /// Checks whether the AABB intersects with the given AABB.\n  /// - Parameter ray: The ray to check for intersection with.\n  /// - Returns: `true` if the ray intersects with the AABB.\n  public func intersects(with ray: Ray) -> Bool {\n    return intersectionDistanceAndFace(with: ray) != nil\n  }\n\n  /// Checks whether the AABB overlaps with a given ray.\n  /// - Parameter ray: The ray to check for intersection with.\n  /// - Returns: `true` if the ray intersects the AABB.\n  public func intersectionDistanceAndFace(with ray: Ray) -> (distance: Float, face: Direction)? {\n    // Algorithm explanation: https://tavianator.com/2011/ray_box.html\n    // As outlined in that post, this algorithm can be optimized if required\n\n    var entryAxis: Axis? = nil\n\n    let inverseDirection = 1 / ray.direction\n    let minimum = Vec3f(minimum)\n    let maximum = Vec3f(maximum)\n\n    let tx1 = (minimum.x - ray.origin.x) * inverseDirection.x\n    let tx2 = (maximum.x - ray.origin.x) * inverseDirection.x\n\n    var tmin = min(tx1, tx2)\n    var tmax = max(tx1, tx2)\n\n    if !tmin.isNaN {\n      entryAxis = .x\n    }\n\n    var prevtmin = tmin\n    let ty1 = (minimum.y - ray.origin.y) * inverseDirection.y\n    let ty2 = (maximum.y - ray.origin.y) * inverseDirection.y\n\n    tmin = max(tmin, min(ty1, ty2))\n    tmax = min(tmax, max(ty1, ty2))\n\n    if tmin != prevtmin {\n      entryAxis = .y\n    }\n\n    prevtmin = tmin\n    let tz1 = (minimum.z - ray.origin.z) * inverseDirection.z\n    let tz2 = (maximum.z - ray.origin.z) * inverseDirection.z\n\n    tmin = max(tmin, min(tz1, tz2))\n    tmax = min(tmax, max(tz1, tz2))\n\n    if tmin != prevtmin {\n      entryAxis = .z\n    }\n\n    guard let axis = entryAxis, tmax >= tmin else {\n      return nil\n    }\n\n    // The entry face is opposite to the direction the player is looking along the entry axis\n    let face: Direction\n    if ray.direction.component(along: axis) > 0 {\n      face = axis.negativeDirection\n    } else {\n      face = axis.positiveDirection\n    }\n\n    return (tmin, face)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Physics/CompoundBoundingBox.swift",
    "content": "import FirebladeMath\n\n/// A collision shape made up of any number of axis aligned bounding boxes.\npublic struct CompoundBoundingBox: Codable {\n  /// The shape's AABBs.\n  public var aabbs: [AxisAlignedBoundingBox] = []\n\n  /// Creates a new collision shape from an array of component AABBs.\n  /// - Parameter aabbs: The AABBs that make up the shape.\n  public init(_ aabbs: [AxisAlignedBoundingBox] = []) {\n    self.aabbs = aabbs\n  }\n\n  /// Adds an AABB to the shape.\n  /// - Parameter aabb: The AABB to add.\n  public mutating func addAABB(_ aabb: AxisAlignedBoundingBox) {\n    aabbs.append(aabb)\n  }\n\n  /// Adds an array of AABBs to the shape.\n  /// - Parameter aabbs: The AABBs to add.\n  public mutating func addAABBs(_ aabbs: [AxisAlignedBoundingBox]) {\n    self.aabbs.append(contentsOf: aabbs)\n  }\n\n  /// Checks whether this shape intersects with the given AABB.\n  /// - Parameter aabb: The AABB to check for intersection with.\n  /// - Returns: `true` if the shape intersects with the AABB.\n  public func intersects(with aabb: AxisAlignedBoundingBox) -> Bool {\n    for shapeAABB in aabbs {\n      if shapeAABB.intersects(with: aabb) {\n        return true\n      }\n    }\n    return false\n  }\n\n  /// Checks whether this shape intersects with the given ray.\n  /// - Parameter ray: The ray to check for intersection with.\n  /// - Returns: `true` if the shape intersects with the ray.\n  public func intersects(with ray: Ray) -> Bool {\n    for aabb in aabbs {\n      if aabb.intersects(with: ray) {\n        return true\n      }\n    }\n    return false\n  }\n\n  /// Gets the distance at which a given ray intersects with this shape from the ray's origin.\n  /// - Parameter ray: The ray to get the intersection distance of.\n  /// - Returns: The intersection distance.\n  public func intersectionDistanceAndFace(with ray: Ray) -> (distance: Float, face: Direction)? {\n    let intersections = aabbs.compactMap { aabb in\n      return aabb.intersectionDistanceAndFace(with: ray)\n    }\n    return intersections.min { a, b in\n      return a.distance < b.distance\n    }\n  }\n\n  /// Offsets the shape by a specified amount.\n  /// - Parameter vector: The amount to offset the shape by.\n  /// - Returns: The offset shape.\n  public func offset(by vector: Vec3d) -> CompoundBoundingBox {\n    var aabbs = aabbs\n    for (i, aabb) in aabbs.enumerated() {\n      aabbs[i] = aabb.offset(by: vector)\n    }\n    return CompoundBoundingBox(aabbs)\n  }\n\n  /// Adds another shape's AABBs to this shape.\n  /// - Parameter other: The shape to combine with.\n  public mutating func formUnion(_ other: CompoundBoundingBox) {\n    aabbs.append(contentsOf: other.aabbs)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Physics/PhysicsConstants.swift",
    "content": "public enum PhysicsConstants {\n  public static let frictionMultiplier = 0.91\n  public static let airResistanceMultiplier = 0.98\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Physics/Ray.swift",
    "content": "import Foundation\nimport FirebladeMath\n\npublic struct Ray {\n  public var origin: Vec3f\n  public var direction: Vec3f\n\n  public init(origin: Vec3f, direction: Vec3f) {\n    self.origin = origin\n    self.direction = direction\n  }\n\n  public init(from origin: Vec3f, pitch: Float, yaw: Float) {\n    self.origin = origin\n    direction = Vec3f(\n      -Foundation.sin(yaw) * Foundation.cos(pitch),\n      -Foundation.sin(pitch),\n      Foundation.cos(yaw) * Foundation.cos(pitch)\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Physics/VoxelRay.swift",
    "content": "import FirebladeMath\n\n/// A sequence of voxels along a ray.\n///\n/// Algorithm explanation: https://github.com/sketchpunk/FunWithWebGL2/blob/master/lesson_074_voxel_ray_intersection/test.html\npublic struct VoxelRay: Sequence, IteratorProtocol {\n  public var count: Int\n\n  private let step: Vec3i\n  private let initialVoxel: Vec3i\n  private let boundaryDistanceStep: Vec3f\n\n  private var isFirst = true\n  private var nextBoundaryDistance: Vec3f\n  private var previousVoxel: Vec3i\n\n  public init(along ray: Ray, count: Int? = nil) {\n    self.init(from: ray.origin, direction: ray.direction, count: count)\n  }\n\n  public init(from position: Vec3f, direction: Vec3f, count: Int? = nil) {\n    step = Vec3i(MathUtil.sign(direction))\n\n    initialVoxel = Vec3i(\n      Int(position.x.rounded(.down)),\n      Int(position.y.rounded(.down)),\n      Int(position.z.rounded(.down))\n    )\n\n    nextBoundaryDistance = Vec3f(\n      (position.x.rounded(.down) + (step.x == -1 ? 0 : 1) - position.x) / direction.x,\n      (position.y.rounded(.down) + (step.y == -1 ? 0 : 1) - position.y) / direction.y,\n      (position.z.rounded(.down) + (step.z == -1 ? 0 : 1) - position.z) / direction.z\n    )\n\n    boundaryDistanceStep = Vec3f(\n      Float(step.x) / direction.x,\n      Float(step.y) / direction.y,\n      Float(step.z) / direction.z\n    )\n\n    previousVoxel = initialVoxel\n    self.count = count ?? Int.max\n  }\n\n  public mutating func next() -> BlockPosition? {\n    if count == 0 {\n      return nil\n    }\n\n    count -= 1\n\n    var voxel: Vec3i\n    if isFirst {\n      isFirst = false\n      voxel = initialVoxel\n    } else {\n      voxel = previousVoxel\n\n      let minBoundaryDistance: Float = MathUtil.abs(nextBoundaryDistance).min()\n      if abs(nextBoundaryDistance.x) == minBoundaryDistance {\n        nextBoundaryDistance.x += boundaryDistanceStep.x\n        voxel.x += step.x\n      } else if abs(nextBoundaryDistance.y) == minBoundaryDistance {\n        nextBoundaryDistance.y += boundaryDistanceStep.y\n        voxel.y += step.y\n      } else {\n        nextBoundaryDistance.z += boundaryDistanceStep.z\n        voxel.z += step.z\n      }\n    }\n\n    previousVoxel = voxel\n    return BlockPosition(x: voxel.x, y: voxel.y, z: voxel.z)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Player/Player.swift",
    "content": "import FirebladeECS\nimport FirebladeMath\nimport Foundation\n\n/// Allows easy access to the player's components.\n///\n/// Please note that all components are classes.\npublic struct Player {\n  public static let attackReach: Float = 3\n  public static let buildingReach: Float = 4.5\n\n  /// The component storing the player's entity id.\n  public private(set) var entityId: EntityId\n  /// The component storing whether the player is on the ground/swimming or not.\n  public private(set) var onGround: EntityOnGround\n  /// The component storing the player's current experience level.\n  public private(set) var experience: EntityExperience\n  /// The component storing whether the player is flying.\n  public private(set) var flying: EntityFlying\n  /// The component storing whether the player is sprinting.\n  public private(set) var sprinting: EntitySprinting\n  /// The component storing whether the player is sneaking\n  public private(set) var sneaking: EntitySneaking\n  /// The component storing the player's health.\n  public private(set) var health: EntityHealth\n  /// The component storing the player's hunger and saturation.\n  public private(set) var nutrition: EntityNutrition\n  /// The component storing the player's position.\n  public private(set) var position: EntityPosition\n  /// The component storing the player's velocity.\n  public private(set) var velocity: EntityVelocity\n  /// The component storing the player's acceleration.\n  public private(set) var acceleration: EntityAcceleration\n  /// The component storing the plyaer's hit box.\n  public private(set) var hitBox: EntityHitBox\n  /// The component storing the player's rotation.\n  public private(set) var rotation: EntityRotation\n  /// The component storing the player's camera properties (does not include any settings such as fov that affect all entities).\n  public private(set) var camera: EntityCamera\n  /// The component storing the player's player-specific attributes.\n  public private(set) var playerAttributes: PlayerAttributes\n  /// The component storing the player's entity attributes.\n  public private(set) var entityAttributes: EntityAttributes\n  /// The component storing the player's entity metadata\n  public private(set) var entityMetadata: EntityMetadata\n  /// The component storing the player's gamemode related information.\n  public private(set) var gamemode: PlayerGamemode\n  /// The component storing the player's inventory.\n  public private(set) var inventory: PlayerInventory\n  /// The component storing the state of collisions from the latest tick.\n  public private(set) var collisionState: PlayerCollisionState\n  /// The component storing the player's fov multiplier.\n  public private(set) var fov: PlayerFOV\n\n  /// A ray starting from the player's eyes and travelling in the direction they are looking.\n  public var ray: Ray {\n    let eyePosition = Vec3f(position.smoothVector + [0, 1.625, 0])\n    return Ray(from: eyePosition, pitch: rotation.smoothPitch, yaw: rotation.smoothYaw)\n  }\n\n  /// Creates a player.\n  public init() {\n    let playerEntity = RegistryStore.shared.entityRegistry.playerEntityKind\n    entityId = EntityId(-1)  // Temporary value until the actual id is received from the server.\n    onGround = EntityOnGround(true)\n    // Having smoothing set to slightly more than a tick smooths out any hick ups caused by late ticks\n    position = EntityPosition(0, 0, 0, smoothingAmount: 1 / 18)\n    rotation = EntityRotation(pitch: 0.0, yaw: 0.0, smoothingAmount: 1 / 18)\n    velocity = EntityVelocity(0, 0, 0)\n    acceleration = EntityAcceleration(0, 0, 0)\n    hitBox = EntityHitBox(width: playerEntity.width, height: playerEntity.height)\n    experience = EntityExperience()\n    flying = EntityFlying()\n    sprinting = EntitySprinting()\n    sneaking = EntitySneaking()\n    health = EntityHealth()\n    nutrition = EntityNutrition()\n    playerAttributes = PlayerAttributes()\n    entityAttributes = EntityAttributes()\n    entityMetadata = EntityMetadata(inheritanceChain: playerEntity.inheritanceChain)\n    camera = EntityCamera()\n    gamemode = PlayerGamemode()\n    inventory = PlayerInventory()\n    collisionState = PlayerCollisionState()\n    fov = PlayerFOV()\n  }\n\n  /// Adds the player to a game.\n  /// - Parameter nexus: The game to create the player's entity in.\n  public mutating func add(to game: Game) {\n    game.createEntity(id: -1) {\n      LivingEntity()  // Mark it as a living entity\n      PlayerEntity()  // Mark it as a player\n      ClientPlayerEntity()  // Mark it as the current player\n      EntityKindId(RegistryStore.shared.entityRegistry.playerEntityKindId)  // Give it the entity kind id for player\n      entityId\n      onGround\n      position\n      rotation\n      velocity\n      acceleration\n      hitBox\n      experience\n      flying\n      sprinting\n      sneaking\n      health\n      nutrition\n      playerAttributes\n      entityAttributes\n      entityMetadata\n      camera\n      gamemode\n      inventory\n      collisionState\n      fov\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Player/PlayerInfo.swift",
    "content": "import Foundation\n\npublic struct PlayerInfo {\n  public var uuid: UUID\n  public var name: String\n  public var properties: [PlayerProperty]\n  public var gamemode: Gamemode?\n  /// The player's ping measured in milliseconds\n  public var ping: Int\n  public var displayName: ChatComponent?\n\n  /// The player's connection strength measured in bars (as displayed in the tab list).\n  public var connectionStrength: ConnectionStrength {\n    if ping < 0 {\n      return .noBars\n    } else if ping < 150 {\n      return .fiveBars\n    } else if ping < 300 {\n      return .fourBars\n    } else if ping < 600 {\n      return .threeBars\n    } else if ping < 1000 {\n      return .fourBars\n    } else {\n      return .fiveBars\n    }\n  }\n\n  public enum ConnectionStrength: Int {\n    case noBars\n    case oneBar\n    case twoBars\n    case threeBars\n    case fourBars\n    case fiveBars\n\n    /// The number of bars of connection in the range `0...5`.\n    var bars: Int {\n      rawValue\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Player/PlayerProperty.swift",
    "content": "import Foundation\n\npublic struct PlayerProperty {\n  public var name: String\n  public var value: String\n  public var signature: String?\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/CaptureCursorEvent.swift",
    "content": "public struct CaptureCursorEvent: Event {}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/ChatMessageReceivedEvent.swift",
    "content": "public struct ChatMessageReceivedEvent: Event {\n  public var message: ChatMessage\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/Connection/ConnectionFailedEvent.swift",
    "content": "import Foundation\n\npublic struct ConnectionFailedEvent: Event {\n  public var networkError: Error\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/Connection/ConnectionReadyEvent.swift",
    "content": "import Foundation\n\npublic struct ConnectionReadyEvent: Event {}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/Connection/LoginDisconnectEvent.swift",
    "content": "import Foundation\n\npublic struct LoginDisconnectEvent: Event {\n  public var reason: String\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/Connection/LoginStartEvent.swift",
    "content": "import Foundation\n\npublic struct LoginStartEvent: Event {}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/Connection/LoginSuccessEvent.swift",
    "content": "import Foundation\n\npublic struct LoginSuccessEvent: Event {}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/Connection/PacketDecodingErrorEvent.swift",
    "content": "import Foundation\n\npublic struct PacketDecodingErrorEvent: Event {\n  public var packetId: Int\n  public var error: String\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/Connection/PacketHandlingErrorEvent.swift",
    "content": "import Foundation\n\npublic struct PacketHandlingErrorEvent: Event {\n  public var packetId: Int\n  public var error: String\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/Connection/PlayDisconnectEvent.swift",
    "content": "import Foundation\n\npublic struct PlayDisconnectEvent: Event {\n  public var reason: String\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/ErrorEvent.swift",
    "content": "import Foundation\n\npublic struct ErrorEvent: Event {\n  public var error: Error\n  public var message: String?\n  \n  public init(error: Error, message: String? = nil) {\n    self.error = error\n    self.message = message\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/Event.swift",
    "content": "import Foundation\n\npublic protocol Event { }\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/EventBus.swift",
    "content": "import Foundation\n\n/// A simple yet flexible thread-safe subscriber-based event system. Events are dispatched\n/// concurrently.\npublic class EventBus {\n  /// The maximum number of handlers that are allowed to run concurrently.\n  public static let maximumConcurrentHandlers = 20\n\n  /// The array of registered event handlers.\n  private var handlers: [(Event) -> Void] = []\n  /// A lock for managing thread safe read and write of ``handlers``.\n  private var handlersLock = ReadWriteLock()\n\n  /// The concurrent dispatch queue for dispatching events.\n  private var dispatchQueue = DispatchQueue(label: \"events\", attributes: .concurrent)\n  /// The serial dispatch queue for managing event dispatches without blocking callers.\n  private var managementQueue = DispatchQueue(label: \"EventBus.managementQueue\")\n  /// Used to limit the number of event handlers run at once.\n  private var semaphore = DispatchSemaphore(value: maximumConcurrentHandlers)\n\n  /// Creates a new event bus.\n  public init() {}\n\n  /// Registers a handler to receive updates.\n  public func registerHandler(_ handler: @escaping (Event) -> Void) {\n    handlersLock.acquireWriteLock()\n    defer { handlersLock.unlock() }\n    handlers.append(handler)\n  }\n\n  /// Concurrently sends an event to all registered handlers. Doesn't block the calling thread.\n  public func dispatch(_ event: Event) {\n    managementQueue.async {\n      // Copy handlers to avoid calling handlers while inside handlersLock.\n      // Some handlers may themselves want to acquire the handlersLock (e.g.\n      // to register a handler)\n      self.handlersLock.acquireReadLock()\n      let handlers = self.handlers\n      self.handlersLock.unlock()\n\n      for handler in handlers {\n        self.semaphore.wait()\n        self.dispatchQueue.async {\n          handler(event)\n          self.semaphore.signal()\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/Input/KeyPressEvent.swift",
    "content": "public struct KeyPressEvent: Event {\n  public var key: Key?\n  public var input: Input?\n  public var characters: [Character]\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/Input/KeyReleaseEvent.swift",
    "content": "public struct KeyReleaseEvent: Event {\n  public var key: Key?\n  public var input: Input?\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/Input/OpenInGameMenuEvent.swift",
    "content": "public struct OpenInGameMenuEvent: Event {}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/Input/ReleaseCursorEvent.swift",
    "content": "public struct ReleaseCursorEvent: Event {}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/Render/FinishFrameCaptureEvent.swift",
    "content": "import Foundation\n\n/// The event emitted when a GPU frame capture is finished.\npublic struct FinishFrameCaptureEvent: Event {\n  /// The file the capture was outputted to.\n  public var file: URL\n\n  public init(file: URL) {\n    self.file = file\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/World/AddChunk.swift",
    "content": "extension World.Event {\n  public struct AddChunk: Event {\n    public let position: ChunkPosition\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/World/JoinWorldEvent.swift",
    "content": "import Foundation\n\npublic struct JoinWorldEvent: Event {}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/World/MultiBlockUpdate.swift",
    "content": "import Foundation\n\nextension World.Event {\n  /// An event triggered when multiple blocks are updated at once.\n  public struct MultiBlockUpdate: Event {\n    public let updates: [SingleBlockUpdate]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/World/RemoveChunk.swift",
    "content": "import Foundation\n\nextension World.Event {\n  public struct RemoveChunk: Event {\n    public let position: ChunkPosition\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/World/SingleBlockUpdate.swift",
    "content": "import Foundation\n\nextension World.Event {\n  /// An event triggered when a single block is updated within a chunk. Use in conjunction with\n  /// MultiBlockUpdate to receive all block updates.\n  public struct SingleBlockUpdate: Event {\n    public let position: BlockPosition\n    public let newState: Int\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/World/TerrainDownloadCompletionEvent.swift",
    "content": "import Foundation\n\npublic struct TerrainDownloadCompletionEvent: Event {}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/World/TimeUpdate.swift",
    "content": "extension World.Event {\n  public struct TimeUpdate: Event {\n    public let worldAge: Int\n    public let timeOfDay: Int\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/World/UpdateChunk.swift",
    "content": "import Foundation\n\nextension World.Event {\n  /// An event that is dispatched when the client receives a `ChunkDataPacket` for an existing chunk.\n  public struct UpdateChunk: Event {\n    /// The position of the updated chunk.\n    public let position: ChunkPosition\n    /// The sections that were updated by the chunk update.\n    public let updatedSections: [Int]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/World/UpdateChunkLighting.swift",
    "content": "import Foundation\n\nextension World.Event {\n  public struct UpdateChunkLighting: Event {\n    public let position: ChunkPosition\n    public let data: ChunkLightingUpdateData\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Event/World/WorldEvent.swift",
    "content": "import Foundation\n\nextension World {\n  public enum Event {}\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/Plugin.swift",
    "content": "/// A plugin that modifies the behaviour of the client.\npublic protocol Plugin {\n  // MARK: Init\n  \n  /// Creates a new instance of the plugin.\n  init()\n  \n  // MARK: Lifecycle\n  \n  /// Called when the plugin has been loaded into the plugin environment.\n  func finishLoading()\n  \n  /// Called just before the plugin gets unloaded.\n  ///\n  /// Not called when the client gets closed, only when the plugin is unloaded through the UI.\n  func willUnload()\n  \n  // MARK: Event handling\n  \n  /// Called when the client is about to join a server.\n  /// - Parameters:\n  ///   - server: The server that the client will connect to.\n  ///   - client: The client that is going to connect to the server.\n  func willJoinServer(_ server: ServerDescriptor, client: Client)\n  \n  /// Called whenever an event is emitted by the client or another plugin.\n  /// - Parameter event: The event that was emitted.\n  func handle(_ event: Event)\n}\n\npublic extension Plugin {\n  func finishLoading() {}\n  func willUnload() {}\n  func willJoinServer(_ server: ServerDescriptor, client: Client) {}\n  func handle(_ event: Event) {}\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/PluginBuilder.swift",
    "content": "import Foundation\n\n/// Used to transport a plugin from its dylib to the client.\npublic class PluginBuilder {\n  /// The type of plugin to build.\n  public var pluginType: Plugin.Type\n  \n  /// A retained opaque pointer that points to this builder.\n  public var retainedOpaquePointer: UnsafeMutableRawPointer {\n    return Unmanaged.passRetained(self).toOpaque()\n  }\n  \n  /// Creates a builder for the specified plugin.\n  public init(_ pluginType: Plugin.Type) {\n    self.pluginType = pluginType\n  }\n  \n  /// Creates an instance of the plugin this builder was created for.\n  public func build() -> Plugin {\n    return pluginType.init()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/PluginEnvironment.swift",
    "content": "import Foundation\nimport OrderedCollections\n\n#if !canImport(Combine)\nimport OpenCombine\n#endif\n\n/// Storage and manager for all currently loaded plugins.\npublic class PluginEnvironment: ObservableObject {\n  /// Used to typecast the builder function in plugin dylibs.\n  private typealias BuilderFunction = @convention(c) () -> UnsafeMutableRawPointer\n\n  /// The plugins that are currently loaded (keyed by plugin identifier).\n  @Published public var plugins: OrderedDictionary<String, LoadedPlugin> = [:]\n  /// Plugins that are unloaded.\n  @Published public var unloadedPlugins: OrderedDictionary<String, UnloadedPlugin> = [:]\n  /// Errors encountered while loading plugins. `bundle` is the filename of the plugin's bundle.\n  @Published public var errors: [PluginError] = []\n\n  /// A plugin which has been loaded.\n  public struct LoadedPlugin {\n    /// The plugin which was loaded.\n    public var plugin: Plugin\n    /// The location of the plugin.\n    public var bundle: URL\n    /// Information about the plugin loaded from its manifest file.\n    public var manifest: PluginManifest\n\n    /// The unloaded version of this plugin.\n    public var unloaded: UnloadedPlugin {\n      UnloadedPlugin(bundle: bundle, manifest: manifest)\n    }\n  }\n\n  /// A plugin which has been unloaded.\n  public struct UnloadedPlugin {\n    /// The location of the plugin.\n    public var bundle: URL\n    /// Information about the plugin loaded from its manifest file.\n    public var manifest: PluginManifest\n  }\n\n  /// An error related to a particular plugin.\n  public struct PluginError: LocalizedError {\n    /// The bundle of the plugin that the error occurred for.\n    public let bundle: String\n    /// The underlying error that caused this error to be thrown if any.\n    public let underlyingError: Error\n\n    public var errorDescription: String? {\n      \"\"\"\n      \\(String(describing: Self.self)).\n      Reason: \\(underlyingError.localizedDescription)\n      Bundle: \\(bundle)\n      \"\"\"\n    }\n  }\n\n  /// Creates an empty plugin environment.\n  public init() {}\n\n  /// Returns the specified plugin if it's loaded.\n  public func plugin(_ identifier: String) -> Plugin? {\n    return plugins[identifier]?.plugin\n  }\n\n  /// Loads all plugins contained within the specified directory.\n  ///\n  /// Plugins must be in the top level of the directory and must have the `.deltaplugin` file extension.\n  ///\n  /// Throws if it fails to enumerate the contents of `directory`. Any errors from plugin loading are added to ``errors``.\n  /// - Parameter directory: Directory to load plugins from.\n  /// - Parameter excludedIdentifiers: Identifier's of plugins to keep as unloaded (they will still be registered though).\n  public func loadPlugins(from directory: URL, excluding excludedIdentifiers: [String] = []) throws {\n    guard FileSystem.directoryExists(directory) else {\n      return\n    }\n\n    let contents = try FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil, options: [])\n    for file in contents where file.pathExtension == \"deltaplugin\" {\n      if plugins.values.contains(where: { $0.bundle == file }) {\n        continue\n      }\n      do {\n        let manifest = try loadPluginManifest(file)\n        if excludedIdentifiers.contains(manifest.identifier) || unloadedPlugins.keys.contains(manifest.identifier) {\n          log.info(\"Skipping plugin '\\(manifest.identifier)' (\\(file.lastPathComponent))\")\n          ThreadUtil.runInMain {\n            unloadedPlugins[manifest.identifier] = UnloadedPlugin(bundle: file, manifest: manifest)\n          }\n          continue\n        }\n        try loadPlugin(from: file, manifest)\n      } catch {\n        ThreadUtil.runInMain {\n          let bundle = file.lastPathComponent\n          log.error(\"Error occured when loading plugin '\\(bundle)': \\(error)\")\n          errors.append(PluginError(bundle: bundle, underlyingError: error))\n        }\n      }\n    }\n  }\n\n  /// Loads a plugin from its bundle.\n  /// - Parameter pluginBundle: The plugin's bundle directory.\n  /// - Parameter manifest: The plugin's manifest. If not provided, the manifest is loaded from the bundle.\n  public func loadPlugin(from pluginBundle: URL, _ manifest: PluginManifest? = nil) throws {\n    let manifest = try manifest ?? loadPluginManifest(pluginBundle)\n\n    log.info(\"Loading plugin '\\(manifest.identifier)' ('\\(pluginBundle.lastPathComponent)')\")\n\n    // Check that the plugin isn't already loaded\n    guard plugins[manifest.identifier] == nil else {\n      throw PluginLoadingError.alreadyLoaded\n    }\n\n    // Open the plugin's dylib\n    guard let pluginLibrary = dlopen(pluginBundle.appendingPathComponent(\"libPlugin.dylib\").path, RTLD_NOW|RTLD_LOCAL) else {\n      if let error = dlerror() {\n        throw PluginLoadingError.failedToOpenDylib(String(format: \"%s\", error))\n      } else {\n        throw PluginLoadingError.failedToOpenDylib(nil)\n      }\n    }\n\n    // Make sure the dylib gets closed when it's not required anymore\n    defer {\n      dlclose(pluginLibrary)\n    }\n\n    // Get the plugin's builder function\n    guard let pluginBuilderSymbol = dlsym(pluginLibrary, \"buildPlugin\") else {\n      throw PluginLoadingError.missingBuilderFunction\n    }\n\n    let buildBuilder: BuilderFunction = unsafeBitCast(pluginBuilderSymbol, to: BuilderFunction.self)\n    let builder = Unmanaged<PluginBuilder>.fromOpaque(buildBuilder()).takeRetainedValue()\n    let plugin = builder.build()\n\n    ThreadUtil.runInMain {\n      plugins[manifest.identifier] = LoadedPlugin(plugin: plugin, bundle: pluginBundle, manifest: manifest)\n      unloadedPlugins.removeValue(forKey: manifest.identifier)\n    }\n    plugin.finishLoading()\n  }\n\n  /// Loads a plugin's manifest from its bundle.\n  /// - Parameter pluginBundle: The plugin's bundle directory.\n  public func loadPluginManifest(_ pluginBundle: URL) throws -> PluginManifest {\n    do {\n      let contents = try Data(contentsOf: pluginBundle.appendingPathComponent(\"manifest.json\"))\n      return try CustomJSONDecoder().decode(PluginManifest.self, from: contents)\n    } catch {\n      throw PluginLoadingError.invalidManifest(error)\n    }\n  }\n\n  /// Reloads all currently loaded plugins.\n  /// - Parameter directory: A directory to check for new plugins in (any plugins that are currently unloaded will be skipped).\n  public func reloadAll(_ directory: URL? = nil) {\n    ThreadUtil.runInMain {\n      errors = []\n    }\n\n    let plugins = self.plugins\n    unloadAll(keepRegistered: false)\n\n    for (_, plugin) in plugins {\n      do {\n        try loadPlugin(from: plugin.bundle)\n      } catch {\n        errors.append(PluginError(bundle: plugin.bundle.lastPathComponent, underlyingError: error))\n      }\n    }\n\n    if let directory = directory {\n      try? loadPlugins(from: directory)\n    }\n  }\n\n  /// Unloads all loaded plugins.\n  /// - Parameter keepRegistered: If `true`, the client will remember the plugins and keep them in ``unloadedPlugins``.\n  ///                             This keeps the plugins unloaded across sessions.\n  public func unloadAll(keepRegistered: Bool = true) {\n    log.debug(\"Unloading all plugins\")\n    for identifier in plugins.keys {\n      unloadPlugin(identifier, keepRegistered: keepRegistered)\n    }\n  }\n\n  /// Unloads the specified plugin if it's loaded. Does nothing if the plugin does not exist.\n  /// - Parameter identifier: The identifier of the plugin to unload.\n  /// - Parameter keepRegistered: If `true`, the client will remember the plugin and keep it in ``unloadedPlugins``.\n  ///                             This keeps the plugin unloaded across sessions.\n  public func unloadPlugin(_ identifier: String, keepRegistered: Bool = true) {\n    log.debug(\"Unloading plugin '\\(identifier)'\")\n    if let plugin = plugins[identifier] {\n      plugin.plugin.willUnload()\n      ThreadUtil.runInMain {\n        plugins.removeValue(forKey: identifier)\n        if keepRegistered {\n          unloadedPlugins[identifier] = plugin.unloaded\n        }\n      }\n    }\n  }\n\n  /// Called when the client is about to join a server.\n  /// - Parameters:\n  ///   - server: The server that the client will connect to.\n  ///   - client: The client that is going to connect to the server.\n  public func handleWillJoinServer(server: ServerDescriptor, client: Client) {\n    for (_, plugin) in plugins {\n      plugin.plugin.willJoinServer(server, client: client)\n    }\n  }\n\n  /// Sets up the environment to relay all events from an event bus to all loaded plugins.\n  /// - Parameter eventBus: The event bus to listen to.\n  public func addEventBus(_ eventBus: EventBus) {\n    eventBus.registerHandler { [weak self] event in\n      guard let self = self else { return }\n      self.handle(event: event)\n    }\n  }\n\n  /// Notifies all loaded plugins of an event.\n  /// - Parameter event: The event to notify all loaded plugins of.\n  public func handle(event: Event) {\n    for (_, plugin) in plugins {\n      plugin.plugin.handle(event)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/PluginLoadingError.swift",
    "content": "import Foundation\n\n/// An error related to plugin loading.\npublic enum PluginLoadingError: LocalizedError {\n  /// Failed to open the plugin's dylib (the part containing the plugin's functionality).\n  case failedToOpenDylib(String?)\n  /// The plugin does not contain a `@_cdecl`'d function called `buildPlugin`.\n  case missingBuilderFunction\n  /// A plugin with the same identifier is already loaded.\n  case alreadyLoaded\n  /// The plugin's manifest file is invalid.\n  case invalidManifest(Error)\n  \n  public var errorDescription: String? {\n    switch self {\n      case .failedToOpenDylib(let reason):\n        return \"Failed to open the plugin's dynamic library: \\(reason ?? \"(no reason provided)\")\"\n      case .missingBuilderFunction:\n        return \"Builder function not found (the plugin may be incorrectly built or corrupted)\"\n      case .alreadyLoaded:\n        return \"A plugin with the same identifier is already loaded\"\n      case .invalidManifest(let error):\n        return \"\"\"\n        The plugin's manifest file is invalid.\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Plugin/PluginManifest.swift",
    "content": "import Foundation\n\n/// Basic information about a plugin.\npublic struct PluginManifest: Codable {\n  /// A unique identifier for the plugin. Should not be the same as any other plugins. A common format is `com.example.plugin`.\n  public var identifier: String\n  /// The display name for the plugin to display to users.\n  public var name: String\n  /// A brief description of the plugin.\n  public var description: String\n  /// The plugin's version (not the Delta Client version).\n  public var version: String\n  /// The name of your plugin's SwiftPM target.\n  public var target: String\n  \n  public init(identifier: String, name: String, description: String, version: String, target: String) {\n    self.identifier = identifier\n    self.name = name\n    self.description = description\n    self.version = version\n    self.target = target\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/BlastingRecipe.swift",
    "content": "import Foundation\n\nstruct BlastingRecipe: HeatRecipe {\n  var group: String\n  var ingredient: Ingredient\n  var result: Slot\n  \n  var experience: Float\n  var cookingTime: Int\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/CampfireCookingRecipe.swift",
    "content": "import Foundation\n\nstruct CampfireCookingRecipe: HeatRecipe {\n  var group: String\n  var ingredient: Ingredient\n  var result: Slot\n  \n  var experience: Float\n  var cookingTime: Int\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/CraftingRecipe.swift",
    "content": "import Foundation\n\npublic protocol CraftingRecipe {\n  var group: String { get }\n  var ingredients: [Ingredient] { get }\n  var result: Slot { get }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/CraftingShaped.swift",
    "content": "import Foundation\n\nstruct CraftingShaped: CraftingRecipe {\n  var group: String\n  \n  var width: Int\n  var height: Int\n  var ingredients: [Ingredient]\n  \n  var result: Slot\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/CraftingShapeless.swift",
    "content": "import Foundation\n\nstruct CraftingShapeless: CraftingRecipe {\n  var group: String\n  var ingredients: [Ingredient]\n  var result: Slot\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/HeatRecipe.swift",
    "content": "import Foundation\n\n// encompasses smelting, blasting, smoking and campfire cooking\npublic protocol HeatRecipe {\n  var group: String { get }\n  var ingredient: Ingredient { get }\n  var result: Slot { get }\n\n  var experience: Float { get }\n  var cookingTime: Int { get }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/Ingredient.swift",
    "content": "import Foundation\n\npublic struct Ingredient {\n  public var ingredients: [Slot]\n  public var count: Int {\n    return ingredients.count\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/RecipeItem.swift",
    "content": "import Foundation\n\npublic struct RecipeItem {\n  public var id: Int\n  public var nbt: NBT.Compound?\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/RecipeRegistry.swift",
    "content": "import Foundation\n\npublic struct RecipeRegistry {\n  public var craftingRecipes: [String: CraftingRecipe] = [:]\n  public var heatRecipes: [String: HeatRecipe] = [:]\n  public var specialRecipes: [String: SpecialRecipe] = [:]\n  public var stonecuttingRecipes: [String: StonecuttingRecipe] = [:]\n  public var smithingRecipes: [String: SmithingRecipe] = [:]\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SmeltingRecipe.swift",
    "content": "import Foundation\n\nstruct SmeltingRecipe: HeatRecipe {\n  var group: String\n  var ingredient: Ingredient\n  var result: Slot\n\n  var experience: Float\n  var cookingTime: Int\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SmithingRecipe.swift",
    "content": "import Foundation\n\npublic struct SmithingRecipe {\n  public var base: Ingredient\n  public var addition: Ingredient\n  public var result: Slot\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SmokingRecipe.swift",
    "content": "import Foundation\n\nstruct SmokingRecipe: HeatRecipe {\n  var group: String\n  var ingredient: Ingredient\n  var result: Slot\n  \n  var experience: Float\n  var cookingTime: Int\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/ArmorDyeRecipe.swift",
    "content": "import Foundation\n\nstruct ArmorDyeRecipe: SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/BannerAddPatternRecipe.swift",
    "content": "import Foundation\n\nstruct BannerAddPatternRecipe: SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/BannerDuplicateRecipe.swift",
    "content": "import Foundation\n\nstruct BannerDuplicateRecipe: SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/BookCloningRecipe.swift",
    "content": "import Foundation\n\nstruct BookCloningRecipe: SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/FireworkRocketRecipe.swift",
    "content": "import Foundation\n\nstruct FireworkRocketRecipe: SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/FireworkStarFadeRecipe.swift",
    "content": "import Foundation\n\nstruct FireworkStarFadeRecipe: SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/FireworkStarRecipe.swift",
    "content": "import Foundation\n\nstruct FireworkStarRecipe: SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/MapCloningRecipe.swift",
    "content": "import Foundation\n\nstruct MapCloningRecipe: SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/MapExtendingRecipe.swift",
    "content": "import Foundation\n\nstruct MapExtendingRecipe: SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/RepairItemRecipe.swift",
    "content": "import Foundation\n\nstruct RepairItemRecipe: SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/ShieldDecorationRecipe.swift",
    "content": "import Foundation\n\nstruct ShieldDecorationRecipe: SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/ShulkerBoxColouringRecipe.swift",
    "content": "import Foundation\n\nstruct ShulkerBoxColouringRecipe: SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/SpecialRecipe.swift",
    "content": "import Foundation\n\npublic protocol SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/SuspiciousStewRecipe.swift",
    "content": "import Foundation\n\nstruct SuspiciousStewRecipe: SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/SpecialCrafting/TippedArrowRecipe.swift",
    "content": "import Foundation\n\nstruct TippedArrowRecipe: SpecialRecipe {\n  \n}\n"
  },
  {
    "path": "Sources/Core/Sources/Recipe/StonecuttingRecipe.swift",
    "content": "import Foundation\n\npublic struct StonecuttingRecipe {\n  public var group: String\n  public var ingredient: Ingredient\n  public var result: Slot\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Biome/Biome.swift",
    "content": "import Foundation\n\n/// Information about a biome.\n///\n/// This only contains data that cannot be changed by resourcepacks. See ``BiomeColors`` if you\n/// are looking for foliage and grass colors.\npublic struct Biome: Codable {\n  /// The biome's unique id.\n  public var id: Int\n  /// The identifier used to refer to the biome in a user friendly way.\n  public var identifier: Identifier\n  \n  /// Don't know what this does yet.\n  public var depth: Float = 0\n  /// Don't know what this does yet.\n  public var scale: Float = 0\n  /// The biome's default temperature value. Used to find biome colors.\n  public var temperature: Float = 0\n  /// The biome's default rainfall value. Used to find biome colors.\n  public var rainfall: Float = 0\n  \n  /// The color of render distance fog.\n  public var fogColor = RGBColor.white\n  /// The color of the sky.\n  public var skyColor = RGBColor.white\n  /// The color of the water block.\n  public var waterColor = RGBColor.white\n  /// The color of the haze seen underwater.\n  public var waterFogColor = RGBColor.white\n  \n  /// The group the biome is part of.\n  public var category = Category.none\n  /// The type of precipitation that occurs in this biome.\n  public var precipitationType = PrecipitationType.none\n  \n  // MARK: Init\n  \n  /// Creates a biome with the given parameters.\n  /// - Parameters:\n  ///   - id: The biome's unique id.\n  ///   - identifier: The identifier used to refer to the biome in a user friendly way.\n  ///   - depth: See ``depth``. Defaults to 0.\n  ///   - scale: See ``scale``. Defaults to 0.\n  ///   - temperature: See ``temperature``. Defaults to 0.\n  ///   - rainfall: See ``rainfall``. Defaults to 0.\n  ///   - fogColor: See ``fogColor``. Defaults to ``RGBColor/white``.\n  ///   - skyColor: See ``skyColor``. Defaults to ``RGBColor/white``.\n  ///   - waterColor: See ``waterColor``. Defaults to ``RGBColor/white``.\n  ///   - waterFogColor: See ``waterFogColor``. Defaults to ``RGBColor/white``.\n  ///   - category: See ``category``. Defaults to ``Category/none``.\n  ///   - precipitationType: See ``precipitationType``. Defaults to ``PrecipitationType/none``.\n  public init(\n    id: Int,\n    identifier: Identifier,\n    depth: Float = 0,\n    scale: Float = 0,\n    temperature: Float = 0,\n    rainfall: Float = 0,\n    fogColor: RGBColor = RGBColor.white,\n    skyColor: RGBColor = RGBColor.white,\n    waterColor: RGBColor = RGBColor.white,\n    waterFogColor: RGBColor = RGBColor.white,\n    category: Biome.Category = Category.none,\n    precipitationType: Biome.PrecipitationType = PrecipitationType.none\n  ) {\n    self.id = id\n    self.identifier = identifier\n    self.depth = depth\n    self.scale = scale\n    self.temperature = temperature\n    self.rainfall = rainfall\n    self.fogColor = fogColor\n    self.skyColor = skyColor\n    self.waterColor = waterColor\n    self.waterFogColor = waterFogColor\n    self.category = category\n    self.precipitationType = precipitationType\n  }\n  \n  // MARK: Helper\n  \n  /// Check if the biome fits a criteria.\n  /// - Parameter criteria: The criteria to check.\n  /// - Returns: `true` if the biome fits the criteria.\n  public func satisfies(_ criteria: Criteria) -> Bool {\n    switch criteria {\n      case .identifier(let identifier):\n        return self.identifier == identifier\n      case .category(let category):\n        return self.category == category\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Biome/BiomeCategory.swift",
    "content": "import Foundation\n\nextension Biome {\n  public enum Category: Int, Codable {\n    case none\n    case taiga\n    case extremeHills\n    case jungle\n    case badlands\n    case plains\n    case savanna\n    case icy\n    case theEnd\n    case beach\n    case forest\n    case ocean\n    case desert\n    case river\n    case swamp\n    case mushroom\n    case nether\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Biome/BiomeCriteria.swift",
    "content": "import Foundation\n\nextension Biome {\n  /// Used to specify a type of biome. Sometimes it's required to specify one specific biome, and sometimes a group. That's where this comes in handy.\n  public enum Criteria: Hashable {\n    /// Only match biomes with a specific identifier.\n    case identifier(Identifier)\n    /// Match all biomes in a specific category.\n    case category(Category)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Biome/BiomeModifiers.swift",
    "content": "import Foundation\n\n/// A collection of modifiers targeted at biomes that satisfy criteria.\n///\n/// Always returns the most specific modifier. Used by ``BiomeColors``.\npublic struct BiomeModifiers<Modifier>: ExpressibleByDictionaryLiteral {\n  private var storage: [Biome.Criteria: Modifier] = [:]\n  \n  /// Creates a new empty collection of biome modifiers.\n  public init() {}\n  \n  /// Creates a new collection of biome modifiers from a dictionary literal.\n  /// - Parameter elements: Map of criteria to modifier.\n  public init(dictionaryLiteral elements: (Biome.Criteria, Modifier)...) {\n    for (key, value) in elements {\n      storage[key] = value\n    }\n  }\n  \n  public subscript(_ biome: Biome) -> Modifier? {\n    if let modifier = storage[.identifier(biome.identifier)] {\n      return modifier\n    } else if let modifier = storage[.category(biome.category)] {\n      return modifier\n    } else {\n      return nil\n    }\n  }\n  \n  public subscript(_ criteria: Biome.Criteria) -> Modifier? {\n    get {\n      return storage[criteria]\n    } set(modifier) {\n      storage[criteria] = modifier\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Biome/BiomePrecipitationType.swift",
    "content": "import Foundation\n\nextension Biome {\n  /// The type of precipitation that occurs in a biome.\n  public enum PrecipitationType: Int, Codable {\n    case none\n    case rain\n    case snow\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/BiomeRegistry.swift",
    "content": "import Foundation\n\n/// An error to do with biome loading most likely.\npublic enum BiomeError: LocalizedError {\n  /// Failed to load the foliage color map from the resource pack.\n  case failedToLoadFoliageColorMap(Error)\n  /// Failed to load the grass color map from the resource pack.\n  case failedToLoadGrassColorMap(Error)\n  /// Biome colormaps from resourcepacks ('grass.png' and 'foliage.png') must be 256x256.\n  case colorMapNot256By256Pixels\n  \n  public var errorDescription: String? {\n    switch self {\n      case .failedToLoadFoliageColorMap(let error):\n        return \"\"\"\n        Failed to load the foliage color map from the resource pack.\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n      case .failedToLoadGrassColorMap(let error):\n        return \"\"\"\n        Failed to load the grass color map from the resource pack.\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n      case .colorMapNot256By256Pixels:\n        return \"Biome colormaps from resourcepacks ('grass.png' and 'foliage.png') must be 256x256.\"\n    }\n  }\n}\n\n/// Holds information about biomes.\npublic struct BiomeRegistry: Codable {\n  /// All biomes. Thanks Mojang for having some missing ids and forcing me to use a dictionary.\n  public var biomes: [Int: Biome] = [:]\n  /// Maps biome identifier to biome id.\n  private var identifierToBiomeId: [Identifier: Int] = [:]\n  \n  // MARK: Init\n  \n  /// Creates an empty ``BiomeRegistry``.\n  public init() {}\n  \n  /// Creates a populated ``BiomeRegistry``.\n  public init(biomes: [Int: Biome]) {\n    self.biomes = biomes\n    for (id, biome) in biomes {\n      identifierToBiomeId[biome.identifier] = id\n    }\n  }\n  \n  // MARK: Access\n  \n  /// Get information about the biome specified.\n  /// - Parameter identifier: Biome identifier.\n  /// - Returns: Biome information. `nil` if biome doesn't exist.\n  public func biome(for identifier: Identifier) -> Biome? {\n    if let id = identifierToBiomeId[identifier] {\n      return biomes[id]\n    } else {\n      return nil\n    }\n  }\n  \n  /// Get information about the biome specified.\n  /// - Parameter id: A biome id.\n  /// - Returns: Biome information. `nil` if biome id is out of range.\n  public func biome(withId id: Int) -> Biome? {\n    return biomes[id]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Block/Block.swift",
    "content": "import Foundation\nimport FirebladeMath\n\n/// Information about a specific block state.\npublic struct Block: Codable {\n  /// The vanilla block state id of this block.\n  public var id: Int\n  /// The id of the overarching vanilla block that this state is part of. E.g. the different orientations of oak stairs all have the same parent id.\n  public var vanillaParentBlockId: Int\n  /// The identifier of this block.\n  public var identifier: Identifier\n  /// The name of the java class for this block (from yarn mappings).\n  public var className: String\n  /// The state of the fluid associated with this block.\n  public var fluidState: FluidState?\n  /// A tint to apply when rendering the block.\n  public var tint: Tint?\n  /// A type of random position offset to apply to the block.\n  public var offset: Offset?\n  /// The material identifier that vanilla gives to this block.\n  public var vanillaMaterialIdentifier: Identifier\n  /// Information about the physical properties of the block.\n  public var physicalMaterial: PhysicalMaterial\n  /// Information about the way the block interacts with light.\n  public var lightMaterial: LightMaterial\n  /// Information about the sound properties of the block.\n  public var soundMaterial: SoundMaterial\n  /// Information about the shape of the block.\n  public var shape: Shape\n  /// Properties of the block specific to each block state (e.g. direction of dispenser).\n  public var stateProperties: StateProperties\n\n  /// The id of the fluid in this block.\n  public var fluidId: Int? {\n    return fluidState?.fluidId\n  }\n\n  /// Whether the block is climable or not (e.g. a ladder or a vine).\n  public var isClimbable: Bool {\n    // TODO: Load block tags from Pixlyzer (or some other source if Pixlyzer doesn't have them)\n    return [\"LadderBlock\", \"VineBlock\", \"WeepingVinesBlock\", \"TwistingVinesPlantBlock\"].contains(className)\n  }\n\n  /// Create a new block with the specified properties.\n  public init(\n    id: Int,\n    vanillaParentBlockId: Int,\n    identifier: Identifier,\n    className: String,\n    fluidState: FluidState? = nil,\n    tint: Tint? = nil,\n    offset: Offset? = nil,\n    vanillaMaterialIdentifier: Identifier,\n    physicalMaterial: PhysicalMaterial,\n    lightMaterial: LightMaterial,\n    soundMaterial: SoundMaterial,\n    shape: Shape,\n    stateProperties: StateProperties\n  ) {\n    self.id = id\n    self.vanillaParentBlockId = vanillaParentBlockId\n    self.identifier = identifier\n    self.className = className\n    self.fluidState = fluidState\n    self.tint = tint\n    self.offset = offset\n    self.vanillaMaterialIdentifier = vanillaMaterialIdentifier\n    self.physicalMaterial = physicalMaterial\n    self.lightMaterial = lightMaterial\n    self.soundMaterial = soundMaterial\n    self.shape = shape\n    self.stateProperties = stateProperties\n  }\n\n  /// Returns the offset to apply to the given block at the given position when rendering.\n  public func getModelOffset(at position: BlockPosition) -> Vec3f {\n    if let offset = offset {\n      let seed = Self.getPositionRandom(BlockPosition(x: position.x, y: 0, z: position.z))\n      let y: Float\n      switch offset {\n        case .xyz:\n          y = Float((seed >> 4) & 15) / 75 - 0.2\n        case .xz:\n          y = 0\n      }\n      return Vec3f(\n        x: Float(seed & 15) / 30 - 0.25,\n        y: y,\n        z: Float((seed >> 8) & 15) / 30 - 0.25)\n    } else {\n      return Vec3f()\n    }\n  }\n\n  /// Returns the seed to use for choosing block models. Identical behaviour to vanilla.\n  public static func getPositionRandom(_ position: BlockPosition) -> Int64 {\n    var seed = Int64(position.x &* 3129871) ^ (Int64(position.z) &* 116129781) ^ Int64(position.y)\n    seed = (seed &* seed &* 42317861) &+ (seed &* 11)\n    return seed >> 16\n  }\n\n  /// Used when a block does not exist (e.g. when an invalid block id is received from the server).\n  public static let missing = Block(\n    id: -1,\n    vanillaParentBlockId: -1,\n    identifier: Identifier(name: \"missing\"),\n    className: \"MissingBlock\",\n    fluidState: nil,\n    tint: nil,\n    offset: nil,\n    vanillaMaterialIdentifier: Identifier(name: \"missing\"),\n    physicalMaterial: PhysicalMaterial.default,\n    lightMaterial: LightMaterial.default,\n    soundMaterial: SoundMaterial.default,\n    shape: Shape.default,\n    stateProperties: StateProperties.default\n  )\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Block/BlockLightMaterial.swift",
    "content": "import Foundation\n\nextension Block {\n  /// A block's material properties that are relevant to light.\n  public struct LightMaterial: Codable {\n    /// Is the block translucent.\n    public var isTranslucent: Bool\n    /// How much light the block blocks from 0 to 15. 0 blocks no light and 15 blocks all light.\n    public var opacity: Int\n    /// How much light the block emits from 0 to 15. 0 emits no light and 15 emits the maximum possible block light level (15).\n    public var luminance: Int\n    /// Whether the block is only transparent under some conditions. E.g. slabs have conditional\n    /// transparency (light only passes through in certain directions).\n    public var isConditionallyTransparent: Bool\n\n    /// Whether this material is opaque.\n    public var isOpaque: Bool {\n      return opacity == 15\n    }\n\n    public init(\n      isTranslucent: Bool,\n      opacity: Int,\n      luminance: Int,\n      isConditionallyTransparent: Bool\n    ) {\n      self.isTranslucent = isTranslucent\n      self.opacity = opacity\n      self.luminance = luminance\n      self.isConditionallyTransparent = isConditionallyTransparent\n    }\n\n    /// Used for missing blocks.\n    public static var `default` = LightMaterial(\n      isTranslucent: false,\n      opacity: 0,\n      luminance: 0,\n      isConditionallyTransparent: false)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Block/BlockOffset.swift",
    "content": "import Foundation\n\nextension Block {\n  /// Offset types that can be applied to a block's position before rendering.\n  public enum Offset: String, Codable {\n    case xyz\n    case xz\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Block/BlockPhysicalMaterial.swift",
    "content": "extension Block {\n  /// The physical properties of a block.\n  public struct PhysicalMaterial: Codable {\n    /// How resistant the block is to breaking from explosions.\n    public var explosionResistance: Double\n    /// How much friction the block has. Most blocks have a value of 0.6\n    public var slipperiness: Double\n    /// Applied to entity velocity when on this block (e.g. soul sand has a multiplier lower than 1).\n    public var velocityMultiplier: Double\n    /// Applied to entity jump velocity when on this block (e.g. honey has a multiplier lower than 1).\n    public var jumpVelocityMultiplier: Double\n    /// Where the block requires a specific tool to break or not.\n    public var requiresTool: Bool\n    /// How hard the block is to break.\n    public var hardness: Double\n\n    public init(\n      explosionResistance: Double,\n      slipperiness: Double,\n      velocityMultiplier: Double,\n      jumpVelocityMultiplier: Double,\n      requiresTool: Bool,\n      hardness: Double\n    ) {\n      self.explosionResistance = explosionResistance\n      self.slipperiness = slipperiness\n      self.velocityMultiplier = velocityMultiplier\n      self.jumpVelocityMultiplier = jumpVelocityMultiplier\n      self.requiresTool = requiresTool\n      self.hardness = hardness\n    }\n\n    /// Used for missing blocks.\n    public static var `default` = PhysicalMaterial.init(\n      explosionResistance: 0,\n      slipperiness: 0.6,\n      velocityMultiplier: 1,\n      jumpVelocityMultiplier: 1,\n      requiresTool: false,\n      hardness: 0)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Block/BlockShape.swift",
    "content": "extension Block {\n  /// Information about the shape of a block. Used for both occlusion (in lighting) and collisions.\n  public struct Shape: Codable {\n    /// Used for missing blocks.\n    public static var `default` = Shape(\n      isDynamic: false,\n      isLarge: false,\n      collisionShape: CompoundBoundingBox(),\n      outlineShape: CompoundBoundingBox(),\n      occlusionShapeIds: nil,\n      isSturdy: nil\n    )\n\n    /// Whether the block's shape can change dynamically (with an animation). E.g. pistons extending.\n    public var isDynamic: Bool\n    /// Whether the collision shape is bigger than a block.\n    public var isLarge: Bool\n    /// The shape to use as the collision shape for this block.\n    public var collisionShape: CompoundBoundingBox\n    /// The shape that represent the outline to render for this block.\n    public var outlineShape: CompoundBoundingBox\n\n    /// The id of the shapes to use for occlusion. I don't really know how this works or why there are multiple.\n    public var occlusionShapeIds: [Int]?\n    /// Don't really know yet what this is.\n    public var isSturdy: [Bool]?\n\n    /// Create a new block shape with some properties.\n    public init(\n      isDynamic: Bool,\n      isLarge: Bool,\n      collisionShape: CompoundBoundingBox,\n      outlineShape: CompoundBoundingBox,\n      occlusionShapeIds: [Int]? = nil,\n      isSturdy: [Bool]? = nil\n    ) {\n      self.isDynamic = isDynamic\n      self.isLarge = isLarge\n      self.collisionShape = collisionShape\n      self.outlineShape = outlineShape\n      self.occlusionShapeIds = occlusionShapeIds\n      self.isSturdy = isSturdy\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Block/BlockSoundMaterial.swift",
    "content": "extension Block {\n  /// Material sound related properties of a block.\n  public struct SoundMaterial: Codable {\n    /// Volume of sounds emitted by this block.\n    public var volume: Double\n    /// Pitch of sounds emitted by this block.\n    public var pitch: Double\n    /// Sound to play when the block is broken.\n    public var breakSound: Int\n    /// Sound to play when this block is walked on.\n    public var stepSound: Int\n    /// Sound to play when this block is placed.\n    public var placeSound: Int\n    /// Sound to play when this block is hit.\n    public var hitSound: Int\n    /// Sound to play when something falls onto this block?\n    public var fallSound: Int\n\n    public init(\n      volume: Double,\n      pitch: Double,\n      breakSound: Int,\n      stepSound: Int,\n      placeSound: Int,\n      hitSound: Int,\n      fallSound: Int\n    ) {\n      self.volume = volume\n      self.pitch = pitch\n      self.breakSound = breakSound\n      self.stepSound = stepSound\n      self.placeSound = placeSound\n      self.hitSound = hitSound\n      self.fallSound = fallSound\n    }\n\n    /// Used for missing blocks.\n    public static var `default` = SoundMaterial(\n      volume: 0,\n      pitch: 0,\n      breakSound: -1,\n      stepSound: -1,\n      placeSound: -1,\n      hitSound: -1,\n      fallSound: -1)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Block/BlockStateProperties.swift",
    "content": "import Foundation\n\nextension Block {\n  /// Properties of a block which are specific to each block state (e.g. direction of dispenser).\n  public struct StateProperties: Codable {\n    /// The direction which the block is facing.\n    public var facing: Direction?\n    /// Whether the block is open or not (present for blocks such as trapdoors).\n    public var isOpen: Bool?\n\n    /// Used for missing blocks.\n    public static let `default` = StateProperties(facing: nil, isOpen: nil)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Block/BlockTint.swift",
    "content": "extension Block {\n  /// Information about what color to tint a block.\n  public enum Tint: Codable {\n    /// Apply a tint color computed from biome information and the type of block.\n    case computed(ComputedTintType)\n    /// Apply a harcoded tint color to every instance of the block.\n    case hardcoded(RGBColor)\n  }\n\n  /// Types of tints that can be computed.\n  public enum ComputedTintType: String, Codable {\n    case waterTint = \"minecraft:water_tint\"\n    case foliageTint = \"minecraft:foliage_tint\"\n    case grassTint = \"minecraft:grass_tint\"\n    case sugarCaneTint = \"minecraft:sugar_cane_tint\"\n    case lilyPadTint = \"minecraft:lily_pad_tint\"\n    case shearingDoublePlantTint = \"minecraft:shearing_double_plant_tint\"\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/BlockRegistry.swift",
    "content": "import Foundation\n\n/// Holds the information about blocks that isn't affected by resource packs.\npublic struct BlockRegistry {\n  /// Blocks that are hardcoded as waterlogged when loading.\n  public static let waterloggedBlockClasses: Set<String> = [\n    \"SeagrassBlock\",\n    \"TallSeagrassBlock\",\n    \"KelpBlock\",\n    \"KelpPlantBlock\"\n  ]\n\n  /// Blocks indexed by block id.\n  public var blocks: [Block] = []\n  /// Maps block id to an array containing an array for each variant. The array for each variant\n  /// contains the models to render.\n  public var renderDescriptors: [[[BlockModelRenderDescriptor]]] = []\n\n  /// Contains the block ids of all blocks that cull the faces of blocks with the same id that\n  /// aren't opaque (e.g. glass blocks).\n  public var selfCullingBlocks: Set<Int> = []\n  /// Contains the block ids of all blocks that are air (regular air, cave air and void air).\n  public var airBlocks: Set<Int> = []\n\n  // MARK: Init\n\n  /// Creates an empty block registry. It's best to use the other initializer unless you really know\n  /// what you're doing.\n  public init() {}\n\n  /// Creates a populated block registry.\n  /// - Parameters:\n  ///   - blocks: The array of all blocks, indexed by block id.\n  ///   - renderDescriptors: Descriptions of what to render for each block, indexed by block id.\n  ///   - selfCullingBlockClasses: Block classes of blocks that cull blocks of the same id. If\n  ///     `nil`, the vanilla overrides are used.\n  public init(\n    blocks: [Block],\n    renderDescriptors: [[[BlockModelRenderDescriptor]]],\n    selfCullingBlockClasses: Set<String>? = nil\n  ) {\n    self.blocks = blocks\n    self.renderDescriptors = renderDescriptors\n\n    // I'm really struggling to find a good name for this value and everything else around this\n    // stuff. Its basically just a way to hardcode certain blocks that cull the faces of their own\n    // kind (e.g. glass blocks). Fluid blocks are handled separately by the fluid renderer\n    let selfCullingBlockClasses = selfCullingBlockClasses ?? [\n      \"StainedGlassBlock\",\n      \"GlassBlock\",\n      \"LeavesBlock\",\n      \"SlimeBlock\",\n      \"HoneyBlock\"\n    ]\n    for block in blocks {\n      if selfCullingBlockClasses.contains(block.className) {\n        selfCullingBlocks.insert(block.id)\n      }\n      if block.className == \"AirBlock\" {\n        airBlocks.insert(block.id)\n      }\n    }\n  }\n\n  init(\n    blocks: [Block],\n    renderDescriptors: [[[BlockModelRenderDescriptor]]],\n    selfCullingBlocks: Set<Int>,\n    airBlocks: Set<Int>\n  ) {\n    self.blocks = blocks\n    self.renderDescriptors = renderDescriptors\n    self.selfCullingBlocks = selfCullingBlocks\n    self.airBlocks = airBlocks\n  }\n\n  // MARK: Access\n\n  /// Get information about the specified block.\n  /// - Parameter id: The block id.\n  /// - Returns: Information about the block. `nil` if the block doesn't exist.\n  public func block(withId id: Int) -> Block? {\n    guard id >= 0, id < blocks.count else {\n      return nil\n    }\n    return blocks[id]\n  }\n\n  /// Get whether a block is air.\n  /// - Parameter id: Id of the block of interest.\n  /// - Returns: Whether the block is air.\n  public func isAir(_ id: Int) -> Bool {\n    return airBlocks.contains(id)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Entity/EntityAttributeKey.swift",
    "content": "/// Attributes that are the same for every entity of a given kind (e.g. maximum health). Not all are present for a given entity.\npublic enum EntityAttributeKey: String, Codable {\n  case maxHealth = \"minecraft:generic.max_health\"\n  case armor = \"minecraft:generic.armor\"\n  case armorToughness = \"minecraft:generic.armor_toughness\"\n  case attackDamage = \"minecraft:generic.attack_damage\"\n  case attackKnockback = \"minecraft:generic.attack_knockback\"\n  case knockbackResistance = \"minecraft:generic.knockback_resistance\"\n  case movementSpeed = \"minecraft:generic.movement_speed\"\n  case flyingSpeed = \"minecraft:generic.flying_speed\"\n  case followRange = \"minecraft:generic.follow_range\"\n  case attackSpeed = \"minecraft:generic.attack_speed\"\n  case luck = \"minecraft:generic.luck\"\n  case horseJumpStrength = \"minecraft:horse.jump_strength\"\n  case zombieSpawnReinforcement = \"minecraft:zombie.spawn_reinforcements\"\n  \n  /// The default value for the attribute.\n  public var defaultValue: Double {\n    switch self {\n      case .maxHealth: return 20\n      case .armor: return 0\n      case .armorToughness: return 0\n      case .attackDamage: return 2\n      case .attackKnockback: return 0\n      case .knockbackResistance: return 0\n      case .movementSpeed: return 0.7\n      case .flyingSpeed: return 0.4\n      case .followRange: return 32\n      case .attackSpeed: return 4\n      case .luck: return 0\n      case .horseJumpStrength: return 0.7\n      case .zombieSpawnReinforcement: return 0\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Entity/EntityAttributeModifier.swift",
    "content": "import Foundation\n\n/// A modifier for an entity attribute.\npublic struct EntityAttributeModifier {\n  /// A unique identifier for the modifier (don't quite know how Minecraft uses it yet).\n  public var uuid: UUID\n  /// The amount to change the value by. The manner in which this affects the value is decided by ``operation``.\n  public var amount: Double\n  /// The operation used to apply the modifier.\n  public var operation: Operation\n\n  /// The operation used to apply a modifier.\n  public enum Operation: UInt8 {\n    /// Adds an amount to the value.\n    case add = 0\n    /// Increases the value by a percentage (in decimal form, not out of 100).\n    case addPercent = 1\n    /// Multiplies the value by an amount.\n    case multiply = 2\n  }\n\n  /// Creates a new modifier.\n  public init(uuid: UUID, amount: Double, operation: EntityAttributeModifier.Operation) {\n    self.uuid = uuid\n    self.amount = amount\n    self.operation = operation\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Entity/EntityAttributeValue.swift",
    "content": "import Foundation\n\n/// The value of an entity attribute.\npublic struct EntityAttributeValue {\n  /// The value before applying ``modifiers``.\n  public var baseValue: Double\n  /// Modifiers that are currently affecting the value.\n  public var modifiers: [EntityAttributeModifier]\n\n  /// The value after applying ``modifiers``.\n  public var value: Double {\n    var value = baseValue\n\n    let additiveModifiers = modifiers.filter { $0.operation == .add }\n    for modifier in additiveModifiers {\n      value += modifier.amount\n    }\n\n    let relativeAdditiveModifiers = modifiers.filter { $0.operation == .addPercent }\n    for modifier in relativeAdditiveModifiers {\n      value += modifier.amount * baseValue\n    }\n\n    let multiplicativeModifiers = modifiers.filter { $0.operation == .multiply }\n    for modifier in multiplicativeModifiers {\n      value *= modifier.amount + 1\n    }\n\n    return value\n  }\n\n  /// Creates a new value.\n  /// - Parameters:\n  ///   - baseValue: The base value without modifiers applied.\n  ///   - modifiers: The modifiers affecting the value. Defaults to none.\n  public init(baseValue: Double, modifiers: [EntityAttributeModifier] = []) {\n    self.baseValue = baseValue\n    self.modifiers = modifiers\n  }\n\n  /// Gets whether this value has a given modifier.\n  /// - Parameter uuid: The uuid of the modifier to check for.\n  /// - Returns: `true` if the value has a modifier with the given uuid.\n  public func hasModifier(_ uuid: UUID) -> Bool {\n    for modifier in modifiers where modifier.uuid == uuid {\n      return true\n    }\n    return false\n  }\n\n  /// Applies a modifier to the value.\n  /// - Parameter modifier: The modifier to apply.\n  public mutating func apply(_ modifier: EntityAttributeModifier) {\n    if hasModifier(modifier.uuid) {\n      remove(modifier.uuid)\n    }\n    modifiers.append(modifier)\n  }\n\n  /// Removes a modifier from the value if it exists.\n  /// - Parameter uuid: The uuid of the modifier to remove.\n  public mutating func remove(_ uuid: UUID) {\n    modifiers = modifiers.filter { modifier in\n      return modifier.uuid != uuid\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Entity/EntityKind.swift",
    "content": "/// Information about a kind of entity (e.g. facts true for all cows).\npublic struct EntityKind: Codable {\n  /// The identifier for the entity.\n  public var identifier: Identifier\n  /// The entity's id.\n  public var id: Int\n  /// Width of the entity's hitbox (for both the x and z axes).\n  public var width: Float\n  /// Height of the entity's hitbox.\n  public var height: Float\n  /// Attributes that are the same for every entity of this kind (e.g. maximum health).\n  public var attributes: [EntityAttributeKey: Float]\n  /// Whether the entity is living or not. Corresponds to ``inheritanceChain`` containing\n  /// `\"LivingEntity\"`, but precomputed to avoid an array search every time it's accessed.\n  public var isLiving: Bool\n  // TODO: Parse into an array of enum values (strings are slow, and there are only a limited number\n  //   of entity classes).\n  /// The chain of class inheritance for this entity kind in vanilla.\n  public var inheritanceChain: [String]\n\n  /// The default duration of position/rotation linear interpolation (measured in ticks)\n  /// to use for this kind of entity.\n  public var defaultLerpDuration: Int {\n    if identifier == Identifier(name: \"item\") {\n      return 1\n    } else {\n      return 3\n    }\n  }\n\n  /// Whether this entity kind is the ender dragon or not (purely a convenience property,\n  /// just checks the identifier).\n  public var isEnderDragon: Bool {\n    identifier == Identifier(name: \"ender_dragon\")\n  }\n\n  /// Creates a new entity kind with the given properties.\n  public init(\n    identifier: Identifier,\n    id: Int,\n    width: Float,\n    height: Float,\n    attributes: [EntityAttributeKey: Float],\n    isLiving: Bool,\n    inheritanceChain: [String]\n  ) {\n    self.identifier = identifier\n    self.id = id\n    self.width = width\n    self.height = height\n    self.attributes = attributes\n    self.isLiving = isLiving\n    self.inheritanceChain = inheritanceChain\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/EntityRegistry.swift",
    "content": "import Foundation\n\n/// Holds information about kinds of entities.\npublic struct EntityRegistry: Codable {\n  /// Maps entity kind id to information about that kind of entity.\n  public var entities: [Int: EntityKind] = [:]\n  /// Maps entity identifier to an entity kind id.\n  public var identifierToEntityId: [Identifier: Int] = [:]\n  /// The kind id of player entities.\n  public var playerEntityKindId: Int\n\n  /// Returns information about the player entity. Will fatally crash if ``playerEntityKindId`` is invalid.\n  public var playerEntityKind: EntityKind {\n    guard let kind = entities[playerEntityKindId] else {\n      fatalError(\"The player entity kind was missing from the EntityRegistry\")\n    }\n    return kind\n  }\n\n  // MARK: Init\n\n  /// Creates an empty entity registry.\n  public init() {\n    playerEntityKindId = -1\n  }\n\n  /// Creates a populated entity registry.\n  ///\n  /// Throws an error if `entities` does not contain the player entity.\n  public init(entities: [Int: EntityKind]) throws {\n    self.entities = entities\n    for (_, entity) in entities {\n      identifierToEntityId[entity.identifier] = entity.id\n    }\n\n    guard let playerEntityKindId = identifierToEntityId[Identifier(name: \"player\")] else {\n      throw PixlyzerError.entityRegistryMissingPlayer\n    }\n\n    self.playerEntityKindId = playerEntityKindId\n  }\n\n  // MARK: Access\n\n  /// Get information about the entity specified.\n  /// - Parameter identifier: Entity identifier.\n  /// - Returns: Entity information. `nil` if entity doesn't exist.\n  public func entity(for identifier: Identifier) -> EntityKind? {\n    if let index = identifierToEntityId[identifier] {\n      return entities[index]\n    } else {\n      return nil\n    }\n  }\n\n  /// Get information about the entity specified.\n  /// - Parameter id: An entity id.\n  /// - Returns: Entity information. `nil` if entity id is out of range.\n  public func entity(withId id: Int) -> EntityKind? {\n    return entities[id]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Fluid/Fluid.swift",
    "content": "/// A fluid. Vanilla only has water and lava.\npublic final class Fluid: Codable {\n  /// The id of the fluid in the fluid registry.\n  public var id: Int\n  /// The fluids identifier in vanilla.\n  public var identifier: Identifier\n  /// The texture to use when this fluid is flowing.\n  public var flowingTexture: Identifier\n  /// The texture to use when this fluid is still.\n  public var stillTexture: Identifier\n  /// The id of the particle to use when this fluid is seeping through the ground.\n  public var dripParticleType: Int?\n\n  /// Whether the fluid is vanilla water or not.\n  public var isWater: Bool {\n    identifier == Identifier(name: \"water\")\n  }\n\n  /// Whether the fluid is vanilla lava or not.\n  public var isLava: Bool {\n    identifier == Identifier(name: \"lava\")\n  }\n  \n  public init(\n    id: Int,\n    identifier: Identifier,\n    flowingTexture: Identifier,\n    stillTexture: Identifier,\n    dripParticleType: Int?\n  ) {\n    self.id = id\n    self.identifier = identifier\n    self.flowingTexture = flowingTexture\n    self.stillTexture = stillTexture\n    self.dripParticleType = dripParticleType\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Fluid/FluidState.swift",
    "content": "/// The state of a fluid 'block'.\npublic struct FluidState: Codable {\n  /// The type of fluid.\n  public var fluidId: Int\n  /// The height of the fluid from 0 to 7 (lowest to highest).\n  public var height: Int\n  /// Is the fluid part of a waterlogged block.\n  public var isWaterlogged: Bool\n\n  /// The fluid this fluid state is for.\n  public var fluid: Fluid {\n    return RegistryStore.shared.fluidRegistry.fluid(withId: fluidId)\n  }\n\n  public init(fluidId: Int, height: Int, isWaterlogged: Bool) {\n    self.fluidId = fluidId\n    self.height = height\n    self.isWaterlogged = isWaterlogged\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/FluidRegistry.swift",
    "content": "import Foundation\n\n/// Holds information about fluids.\npublic struct FluidRegistry: Codable {\n  /// All fluids. Indexed by fluid id.\n  public var fluids: [Fluid] = []\n  /// Maps biome identifier to an index in `fluids`.\n  private var identifierToFluidId: [Identifier: Int] = [:]\n\n  // MARK: Init\n\n  /// Creates an empty fluid registry.\n  public init() {}\n\n  /// Creates a populated fluid registry.\n  public init(fluids: [Fluid]) {\n    self.fluids = fluids\n    for fluid in fluids {\n      identifierToFluidId[fluid.identifier] = fluid.id\n    }\n  }\n\n  // MARK: Access\n\n  /// Get information about the fluid specified.\n  /// - Parameter identifier: Fluid identifier.\n  /// - Returns: Fluid information. `nil` if fluid doesn't exist.\n  public func fluid(for identifier: Identifier) -> Fluid? {\n    if let index = identifierToFluidId[identifier] {\n      return fluids[index]\n    } else {\n      return nil\n    }\n  }\n\n  /// Get information about the fluid specified.\n  /// - Parameter id: A fluid id.\n  /// - Returns: Fluid information. `nil` if fluid id is out of range.\n  ///\n  /// Will fatally crash if the fluid id doesn't exist. Use wisely.\n  public func fluid(withId id: Int) -> Fluid {\n    // TODO: should this really fatally crash?\n    return fluids[id]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Item/Item.swift",
    "content": "import Foundation\n\n// Information about an item.\npublic struct Item: Codable {\n  /// The item's id.\n  public var id: Int\n  /// The identifier.\n  public var identifier: Identifier\n  /// The item's rarity.\n  public var rarity: ItemRarity\n  /// The maximum size of a stack of this item.\n  public var maximumStackSize: Int\n  /// The maximum damage this item can deal.\n  public var maximumDamage: Int\n  /// Whether the item is fire resistant as an entity.\n  public var isFireResistant: Bool\n  /// The locale translation key to use as the name of this item.\n  public var translationKey: String\n  /// The id of the block corresponding to this item.\n  public var blockId: Int?\n  /// The properties of the item specific to the type of item. `nil` if the item\n  /// a just a plain old item (e.g. a stick) rather than a tool or an armor piece\n  /// etc.\n  public var properties: Properties?\n\n  public enum Properties: Codable {\n    case armor(ArmorProperties)\n    case tool(ToolProperties)\n\n    public var armorProperties: ArmorProperties? {\n      guard case let .armor(properties) = self else {\n        return nil\n      }\n      return properties\n    }\n\n    public var toolProperties: ToolProperties? {\n      guard case let .tool(properties) = self else {\n        return nil\n      }\n      return properties\n    }\n  }\n\n  public struct ArmorProperties: Codable {\n    public var equipmentSlot: EquipmentSlot\n    public var defense: Int\n    public var toughness: Double\n    public var material: Identifier\n    public var knockbackResistance: Double\n\n    public init(\n      equipmentSlot: Item.ArmorProperties.EquipmentSlot,\n      defense: Int,\n      toughness: Double,\n      material: Identifier,\n      knockbackResistance: Double\n    ) {\n      self.equipmentSlot = equipmentSlot\n      self.defense = defense\n      self.toughness = toughness\n      self.material = material\n      self.knockbackResistance = knockbackResistance\n    }\n\n    public enum EquipmentSlot: String, Codable {\n      case head\n      case chest\n      case legs\n      case feet\n\n      /// The index of the slot corresponding\n      public var index: Int {\n        switch self {\n          case .head:\n            return 0\n          case .chest:\n            return 1\n          case .legs:\n            return 2\n          case .feet:\n            return 3\n        }\n      }\n    }\n  }\n\n  public struct ToolProperties: Codable {\n    public var uses: Int\n    public var level: Level\n    public var speed: Double\n    public var attackDamage: Double\n    public var attackDamageBonus: Double\n    public var enchantmentValue: Int\n    /// Blocks that can be mined faster using this tool. Doesn't include\n    /// blocks covered by ``ToolProperties/effectiveMaterials``.\n    public var mineableBlocks: [Int]\n    /// When tools are used to right click blocks, they can cause the block\n    /// to change state, e.g. a log gets stripped if you right click it with\n    /// an axe. This mapping doesn't include blocks which are always right\n    /// clickable.\n    public var blockInteractions: [Int: Int]\n    public var kind: ToolKind\n    /// Materials which this tool is effective on. Used to minimise the length\n    /// of ``BlockInteractions/mineableBlocks`` by covering large categories of\n    /// blocks at a time.\n    public var effectiveMaterials: [Identifier]\n\n    public enum Level: Int, Codable, Equatable, Comparable {\n      case woodOrGold = 0\n      case stone = 1\n      case iron = 2\n      case diamond = 3\n      case netherite = 4\n\n      public static func < (lhs: Level, rhs: Level) -> Bool {\n        lhs.rawValue < rhs.rawValue\n      }\n    }\n\n    public init(\n      uses: Int,\n      level: Level,\n      speed: Double,\n      attackDamage: Double,\n      attackDamageBonus: Double,\n      enchantmentValue: Int,\n      mineableBlocks: [Int],\n      blockInteractions: [Int: Int],\n      kind: Item.ToolProperties.ToolKind,\n      effectiveMaterials: [Identifier]\n    ) {\n      self.uses = uses\n      self.level = level\n      self.speed = speed\n      self.attackDamage = attackDamage\n      self.attackDamageBonus = attackDamageBonus\n      self.enchantmentValue = enchantmentValue\n      self.mineableBlocks = mineableBlocks\n      self.blockInteractions = blockInteractions\n      self.kind = kind\n      self.effectiveMaterials = effectiveMaterials\n    }\n\n    public func destroySpeedMultiplier(for block: Block) -> Double {\n      switch kind {\n        case .sword:\n          let swordSemiEffectiveMaterials = [\"plant\", \"replaceable_plant\"].map(\n            Identifier.init(name:))\n          if block.className == \"CobwebBlock\" {\n            return 0.15\n          } else if swordSemiEffectiveMaterials.contains(block.vanillaMaterialIdentifier) {\n            return 0.015\n          } else {\n            return 0.01\n          }\n        case .pickaxe, .shovel, .hoe, .axe:\n          let isCorrectTool =\n            effectiveMaterials.contains(block.vanillaMaterialIdentifier)\n            || mineableBlocks.contains(block.vanillaParentBlockId)\n          if isCorrectTool {\n            return speed * (1 / 30)\n          } else {\n            return block.physicalMaterial.requiresTool ? 0.01 : (1 / 30)\n          }\n      }\n    }\n\n    public enum ToolKind: String, Codable {\n      case sword\n      case pickaxe\n      case shovel\n      case hoe\n      case axe\n    }\n  }\n\n  public init(\n    id: Int,\n    identifier: Identifier,\n    rarity: ItemRarity,\n    maximumStackSize: Int,\n    maximumDamage: Int,\n    isFireResistant: Bool,\n    translationKey: String,\n    blockId: Int? = nil,\n    properties: Item.Properties? = nil\n  ) {\n    self.id = id\n    self.identifier = identifier\n    self.rarity = rarity\n    self.maximumStackSize = maximumStackSize\n    self.maximumDamage = maximumDamage\n    self.isFireResistant = isFireResistant\n    self.translationKey = translationKey\n    self.blockId = blockId\n    self.properties = properties\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Item/ItemRarity.swift",
    "content": "import Foundation\n\n/// The rarity of an item.\npublic enum ItemRarity: Int, Codable {\n  case common = 0\n  case uncommon = 1\n  case rare = 2\n  case epic = 3\n\n  /// The color of text to use for this rarity.\n  public var color: ChatComponent.Color {\n    switch self {\n      case .common:\n        return .white\n      case .uncommon:\n        return .yellow\n      case .rare:\n        return .aqua\n      case .epic:\n        return .purple\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/ItemRegistry.swift",
    "content": "import Foundation\n\npublic enum ItemRegistryError: LocalizedError {\n  case missingItemId(Int)\n  \n  public var errorDescription: String? {\n    switch self {\n      case .missingItemId(let id):\n        return \"Missing item id: \\(id)\"\n    }\n  }\n}\n\n/// Holds information about items.\npublic struct ItemRegistry: Codable {\n  public var items: [Item] = []\n  private var identifierToId: [Identifier: Int] = [:]\n\n  /// Creates an empty item registry.\n  public init() {}\n\n  /// Creates a populated item registry.\n  /// - Parameter items: The items to put in the registry, keyed by id.\n  /// - Throws: ``ItemRegistryError/missingItem(id:)`` if the item ids don't make up a continuous\n  ///   block starting at 0.\n  public init(items: [Int: Item]) throws {\n    let maximumId = items.count - 1\n    for id in 0..<maximumId {\n      guard let item = items[id] else {\n        throw ItemRegistryError.missingItemId(id)\n      }\n      self.items.append(item)\n\n      identifierToId[item.identifier] = id\n    }\n  }\n\n  /// Gets the specified item.\n  public func item(for identifier: Identifier) -> Item? {\n    if let id = identifierToId[identifier] {\n      return items[id]\n    } else {\n      return nil\n    }\n  }\n\n  /// Gets the specified item.\n  public func item(withId id: Int) -> Item? {\n    guard id >= 0, id < items.count else {\n      return nil\n    }\n\n    return items[id]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Pixlyzer/PixlyzerAABB.swift",
    "content": "import Foundation\nimport FirebladeMath\n\npublic struct PixlyzerAABB: Decodable {\n  public var from: SingleOrMultiple<Double>\n  public var to: SingleOrMultiple<Double>\n\n  /// Convert a single of multiple to a vector.\n  static func singleOrMultipleToVector(_ singleOrMultiple: SingleOrMultiple<Double>) throws -> Vec3d {\n    switch singleOrMultiple {\n      case let .single(value):\n        return Vec3d(repeating: value)\n      case let .multiple(values):\n        guard values.count == 3 else {\n          throw PixlyzerError.invalidAABBVertexLength(values.count)\n        }\n        return Vec3d(values)\n    }\n  }\n}\n\nextension AxisAlignedBoundingBox {\n  public init(from pixlyzerAABB: PixlyzerAABB) throws {\n    let from = try PixlyzerAABB.singleOrMultipleToVector(pixlyzerAABB.from)\n    let to = try PixlyzerAABB.singleOrMultipleToVector(pixlyzerAABB.to)\n\n    self.init(\n      minimum: from,\n      maximum: to\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Pixlyzer/PixlyzerBiome.swift",
    "content": "import Foundation\n\npublic struct PixlyzerBiome: Codable {\n  public var id: Int\n  public var depth: Float\n  public var scale: Float\n  public var waterColor: Int\n  public var waterFogColor: Int\n  public var category: Biome.Category\n  public var precipitation: Biome.PrecipitationType\n  public var temperature: Float\n  public var downfall: Float\n  public var fogColor: Int\n  public var skyColor: Int\n}\n\nextension Biome {\n  /// Convert a pixlyzer biome to this nicer format.\n  /// - Parameters:\n  ///   - pixlyzerBiome: The pixlyzer biome to convert.\n  ///   - identifier: The biome's identifier.\n  public init(from pixlyzerBiome: PixlyzerBiome, identifier: Identifier) {\n    self = Biome(\n      id: pixlyzerBiome.id,\n      identifier: identifier,\n      depth: pixlyzerBiome.depth,\n      scale: pixlyzerBiome.scale,\n      temperature: pixlyzerBiome.temperature,\n      rainfall: pixlyzerBiome.downfall,\n      fogColor: RGBColor(hexCode: pixlyzerBiome.fogColor),\n      skyColor: RGBColor(hexCode: pixlyzerBiome.skyColor),\n      waterColor: RGBColor(hexCode: pixlyzerBiome.waterColor),\n      waterFogColor: RGBColor(hexCode: pixlyzerBiome.waterFogColor),\n      category: pixlyzerBiome.category,\n      precipitationType: pixlyzerBiome.precipitation)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Pixlyzer/PixlyzerBlock.swift",
    "content": "import Foundation\n\n/// Block data from pixlyzer.\npublic struct PixlyzerBlock: Decodable {\n  public var id: Int\n  public var explosionResistance: Double\n  public var item: Int\n  public var friction: Double?\n  public var velocityMultiplier: Double?\n  public var jumpVelocityMultiplier: Double?\n  public var defaultState: Int\n  public var hasDynamicShape: Bool?\n  public var className: String\n  public var stillFluid: Int?\n  public var flowFluid: Int?\n  public var offsetType: Block.Offset?\n  public var tint: Block.ComputedTintType?\n  public var states: [Int: PixlyzerBlockState]\n\n  enum CodingKeys: String, CodingKey {\n    case id\n    case explosionResistance = \"explosion_resistance\"\n    case item\n    case friction\n    case velocityMultiplier = \"velocity_multiplier\"\n    case jumpVelocityMultiplier = \"jump_velocity_multiplier\"\n    case defaultState = \"default_state\"\n    case hasDynamicShape = \"has_dynamic_shape\"\n    case className = \"class\"\n    case stillFluid = \"still_fluid\"\n    case flowFluid = \"flow_fluid\"\n    case offsetType = \"offset_type\"\n    case tint\n    case states\n  }\n}\n\nextension Block {\n  // swiftlint:disable function_body_length\n  public init(\n    _ pixlyzerBlock: PixlyzerBlock,\n    _ pixlyzerState: PixlyzerBlockState,\n    shapes: [[AxisAlignedBoundingBox]],\n    stateId: Int,\n    fluid: Fluid?,\n    isWaterlogged: Bool,\n    identifier: Identifier\n  ) {\n    let fluidState: FluidState?\n    if let fluid = fluid {\n      let height = 7 - (pixlyzerState.properties?.level ?? 0)\n      fluidState = FluidState(\n        fluidId: fluid.id,\n        height: isWaterlogged ? 7 : height,\n        isWaterlogged: isWaterlogged)\n    } else {\n      fluidState = nil\n    }\n\n    let tint: Tint?\n    if let computedTint = pixlyzerBlock.tint {\n      tint = .computed(computedTint)\n    } else if let hardcodedColor = pixlyzerState.tintColor {\n      tint = .hardcoded(RGBColor(hexCode: hardcodedColor))\n    } else {\n      tint = nil\n    }\n\n    let physicalMaterial = Block.PhysicalMaterial(\n      explosionResistance: pixlyzerBlock.explosionResistance,\n      slipperiness: pixlyzerBlock.friction ?? 0.6,\n      velocityMultiplier: pixlyzerBlock.velocityMultiplier ?? 1,\n      jumpVelocityMultiplier: pixlyzerBlock.jumpVelocityMultiplier ?? 1,\n      requiresTool: pixlyzerState.requiresTool,\n      hardness: pixlyzerState.hardness\n    )\n\n    let lightMaterial = Block.LightMaterial(\n      isTranslucent: pixlyzerState.translucent ?? false,\n      opacity: pixlyzerState.lightBlock ?? 1,\n      luminance: pixlyzerState.luminance ?? 0,\n      isConditionallyTransparent: pixlyzerState.hasSidedTransparency ?? false\n    )\n\n    let soundMaterial = Block.SoundMaterial(\n      volume: pixlyzerState.soundVolume,\n      pitch: pixlyzerState.soundPitch,\n      breakSound: pixlyzerState.breakSound,\n      stepSound: pixlyzerState.stepSound,\n      placeSound: pixlyzerState.placeSound,\n      hitSound: pixlyzerState.hitSound,\n      fallSound: pixlyzerState.fallSound\n    )\n\n    var collisionShape = CompoundBoundingBox()\n    if let collisionShapeId = pixlyzerState.collisionShape {\n      collisionShape.addAABBs(shapes[collisionShapeId])\n    }\n\n    var outlineShape = CompoundBoundingBox()\n    if let outlineShapeId = pixlyzerState.outlineShape {\n      outlineShape.addAABBs(shapes[outlineShapeId])\n    }\n\n    let shape = Block.Shape(\n      isDynamic: pixlyzerBlock.hasDynamicShape ?? false,\n      isLarge: pixlyzerState.largeCollisionShape ?? false,\n      collisionShape: collisionShape,\n      outlineShape: outlineShape,\n      occlusionShapeIds: pixlyzerState.occlusionShape?.items,\n      isSturdy: pixlyzerState.isSturdy?.items\n    )\n\n    let stateProperties = Block.StateProperties(\n      facing: pixlyzerState.properties?.facing,\n      isOpen: pixlyzerState.properties?.open\n    )\n\n    self.init(\n      id: stateId,\n      vanillaParentBlockId: pixlyzerBlock.id,\n      identifier: identifier,\n      className: pixlyzerBlock.className,\n      fluidState: fluidState,\n      tint: tint,\n      offset: pixlyzerBlock.offsetType,\n      vanillaMaterialIdentifier: pixlyzerState.material,\n      physicalMaterial: physicalMaterial,\n      lightMaterial: lightMaterial,\n      soundMaterial: soundMaterial,\n      shape: shape,\n      stateProperties: stateProperties\n    )\n  }\n  // swiftlint:enable function_body_length\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Pixlyzer/PixlyzerBlockModelDescriptor.swift",
    "content": "import Foundation\n\n/// A descriptor describing transformations to apply to a model from a resource pack. When rendering it for a specific block state.\npublic struct PixlyzerBlockModelDescriptor: Decodable {\n  /// The block model identifier used to find the model in resource packs.\n  var model: Identifier\n  /// The x rotation to apply in degrees.\n  var xRotation: Int?\n  /// The y rotation to apply in degrees.\n  var yRotation: Int?\n  /// Whether to rotate the textures with the model or not.\n  var uvLock: Bool?\n\n  enum CodingKeys: String, CodingKey {\n    case model\n    case xRotation = \"x\"\n    case yRotation = \"y\"\n    case uvLock = \"uvlock\"\n  }\n}\n\nextension BlockModelRenderDescriptor {\n  public init(from pixlyzerDescriptor: PixlyzerBlockModelDescriptor) {\n    self.init(\n      model: pixlyzerDescriptor.model,\n      xRotationDegrees: pixlyzerDescriptor.xRotation ?? 0,\n      yRotationDegrees: pixlyzerDescriptor.yRotation ?? 0,\n      uvLock: pixlyzerDescriptor.uvLock ?? false)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Pixlyzer/PixlyzerBlockState.swift",
    "content": "import Foundation\n\n/// Block state data from pixlyzer.\npublic struct PixlyzerBlockState: Decodable {\n  public struct Properties: Codable {\n    public var level: Int?\n    public var waterlogged: Bool?\n    public var facing: Direction?\n    public var open: Bool?\n  }\n  \n  public var luminance: Int?\n  public var isRandomlyTicking: Bool?\n  public var hasSidedTransparency: Bool?\n  public var soundVolume: Double\n  public var soundPitch: Double\n  public var breakSound: Int\n  public var stepSound: Int\n  public var placeSound: Int\n  public var hitSound: Int\n  public var fallSound: Int\n  public var requiresTool: Bool\n  public var hardness: Double\n  public var isOpaque: Bool?\n  public var material: Identifier\n  public var properties: Properties?\n  public var tintColor: Int?\n  /// Information about what to render for this block state. It is either a single model or an array of variants.\n  /// Each variant is always an array even if it only has one model. Effectively, the only two possible types\n  /// are `[[PixlyzerBlockModelDescriptor]]` and `PixlyzerBlockModelDescriptor`.\n  public var renderDescriptor: SingleOrMultiple<SingleOrMultiple<PixlyzerBlockModelDescriptor>>?\n  public var collisionShape: Int?\n  public var outlineShape: Int?\n  public var solidRender: Bool?\n  public var translucent: Bool?\n  public var lightBlock: Int?\n  public var largeCollisionShape: Bool?\n  public var isCollisionShapeFullBlock: Bool?\n  public var occlusionShape: SingleOrMultiple<Int>?\n  public var isSturdy: SingleOrMultiple<Bool>?\n  \n  var blockModelVariantDescriptors: [[PixlyzerBlockModelDescriptor]] {\n    if let renderDescriptor = renderDescriptor {\n      return renderDescriptor.items.map { $0.items }\n    } else {\n      return [[]]\n    }\n  }\n  \n  enum CodingKeys: String, CodingKey {\n    case luminance\n    case isRandomlyTicking = \"is_randomly_ticking\"\n    case hasSidedTransparency = \"has_side_transparency\"\n    case soundVolume = \"sound_type_volume\"\n    case soundPitch = \"sound_type_pitch\"\n    case breakSound = \"break_sound_type\"\n    case stepSound = \"step_sound_type\"\n    case placeSound = \"place_sound_type\"\n    case hitSound = \"hit_sound_type\"\n    case fallSound = \"fall_sound_type\"\n    case requiresTool = \"requires_tool\"\n    case hardness\n    case properties\n    case isOpaque = \"is_opaque\"\n    case material\n    case tintColor = \"tint_color\"\n    case renderDescriptor = \"render\"\n    case collisionShape = \"collision_shape\"\n    case outlineShape = \"outline_shape\"\n    case solidRender = \"solid_render\"\n    case translucent\n    case lightBlock = \"light_block\"\n    case largeCollisionShape = \"large_collision_shape\"\n    case isCollisionShapeFullBlock = \"is_collision_shape_full_block\"\n    case occlusionShape = \"occlusion_shape\"\n    case isSturdy = \"is_sturdy\"\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Pixlyzer/PixlyzerEntity.swift",
    "content": "import Foundation\n\n/// Entity data from Pixlyzer.\npublic struct PixlyzerEntity: Decodable {\n  public var id: Int?\n  public var serializable: Bool?\n  public var summonable: Bool?\n  public var isFireImmune: Bool?\n  public var lootTable: Identifier?\n  public var width: Float?\n  public var height: Float?\n  public var sizeFixed: Bool?\n  public var `class`: String?\n  public var attributes: [String: Float]?\n  public var meta: [String: Int]?\n  public var parent: String?\n}\n\nextension EntityKind {\n  /// Returns nil if the pixlyzer entity doesn't correspond to a Vanilla minecraft entity kind.\n  /// Throws on unknown entity attributes.\n  public init?(\n    from pixlyzerEntity: PixlyzerEntity, inheritanceChain: [String], identifier: Identifier\n  ) throws {\n    guard let id = pixlyzerEntity.id else {\n      return nil\n    }\n\n    self.id = id\n    self.identifier = identifier\n    self.isLiving = inheritanceChain.contains(\"LivingEntity\")\n    self.inheritanceChain = inheritanceChain\n\n    width = pixlyzerEntity.width ?? 0\n    height = pixlyzerEntity.height ?? 0\n\n    attributes = [:]\n    for (attribute, value) in pixlyzerEntity.attributes ?? [:] {\n      guard let attribute = EntityAttributeKey(rawValue: attribute) else {\n        throw PixlyzerError.unknownEntityAttribute(attribute)\n      }\n\n      attributes[attribute] = value\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Pixlyzer/PixlyzerFluid.swift",
    "content": "import Foundation\n\npublic struct PixlyzerFluid: Codable {\n  public var id: Int\n  public var bucket: Int?\n  public var dripParticleType: Int?\n  public var `class`: String\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Pixlyzer/PixlyzerFormatter.swift",
    "content": "import Foundation\n\npublic enum PixlyzerError: LocalizedError {\n  /// The block with the specified id is missing.\n  case missingBlockId(Int)\n  /// An AABB's vertex is of invalid length.\n  case invalidAABBVertexLength(Int)\n  /// The entity registry does not contain the player entity.\n  case entityRegistryMissingPlayer\n  /// The block name could not be converted to data using UTF8.\n  case invalidUTF8BlockName(String)\n  /// Failed to get the water fluid from the fluid registry.\n  case failedToGetWaterFluid\n  /// Unknown entity attribute found in Pixlyzer entity registry.\n  case unknownEntityAttribute(String)\n  /// Expected an entity with the given name to be present in the Pixlyzer entity registry.\n  case missingEntity(String)\n\n  public var errorDescription: String? {\n    switch self {\n      case let .missingBlockId(id):\n        return \"The block with id: \\(id) is missing.\"\n      case let .invalidAABBVertexLength(length):\n        return \"\"\"\n          An AABB's vertex is of invalid length.\n          length: \\(length)\n          \"\"\"\n      case .entityRegistryMissingPlayer:\n        return \"The entity registry does not contain the player entity.\"\n      case let .invalidUTF8BlockName(blockName):\n        return \"\"\"\n          The block name could not be converted to data using UTF8.\n          Block name: \\(blockName)\n          \"\"\"\n      case .failedToGetWaterFluid:\n        return \"Failed to get the water fluid from the fluid registry.\"\n      case let .unknownEntityAttribute(attribute):\n        return \"\"\"\n          Unknown entity attribute in Pixlyzer entity registry.\n          Attribute: \\(attribute)\n          \"\"\"\n      case let .missingEntity(name):\n        return \"Expected entity kind '\\(name)' to be present in Pixlyzer entity registry.\"\n    }\n  }\n}\n\n/// A utility for downloading and reformatting data from the Pixlyzer data repository.\npublic enum PixlyzerFormatter {\n  /// Downloads the pixlyzer registries, reformats them, and caches them to an output directory.\n  /// - Parameter version: The minecraft version string (e.g. '1.16.1').\n  public static func downloadAndFormatRegistries(_ version: String) throws -> RegistryStore {\n    let pixlyzerCommit = \"7cceb5481e6f035d274204494030a76f47af9bb5\"\n    let pixlyzerItemCommit = \"c623c21be12aa1f9be3f36f0e32fbc61f8f16bd1\"\n    let baseURL =\n      \"https://gitlab.bixilon.de/bixilon/pixlyzer-data/-/raw/\\(pixlyzerCommit)/version/\\(version)\"\n\n    // swiftlint:disable force_unwrapping\n    let fluidsDownloadURL = URL(string: \"\\(baseURL)/fluids.min.json\")!\n    let blocksDownloadURL = URL(string: \"\\(baseURL)/blocks.min.json\")!\n    let biomesDownloadURL = URL(string: \"\\(baseURL)/biomes.min.json\")!\n    let entitiesDownloadURL = URL(string: \"\\(baseURL)/entities.min.json\")!\n    let shapeRegistryDownloadURL = URL(string: \"\\(baseURL)/shapes.min.json\")!\n    let itemsDownloadURL = URL(\n      string:\n        \"https://gitlab.bixilon.de/bixilon/pixlyzer-data/-/raw/\\(pixlyzerItemCommit)/version/\\(version)/items.min.json\"\n    )!\n    // swiftlint:enable force_unwrapping\n\n    // Load and decode pixlyzer data\n    log.info(\"Downloading and decoding pixlyzer items\")\n    let pixlyzerItems: [String: PixlyzerItem] = try downloadJSON(\n      itemsDownloadURL, convertSnakeCase: false)\n    log.info(\"Downloading and decoding pixlyzer fluids\")\n    let pixlyzerFluids: [String: PixlyzerFluid] = try downloadJSON(\n      fluidsDownloadURL, convertSnakeCase: true)\n    log.info(\"Downloading and decoding pixlyzer biomes\")\n    let pixlyzerBiomes: [String: PixlyzerBiome] = try downloadJSON(\n      biomesDownloadURL, convertSnakeCase: true)\n    log.info(\"Downloading and decoding pixlyzer blocks\")\n    let pixlyzerBlocks: [String: PixlyzerBlock] = try downloadJSON(\n      blocksDownloadURL, convertSnakeCase: false, useZippyJSON: false)\n    log.info(\"Downloading and decoding pixlyzer entities\")\n    let pixlyzerEntities: [String: PixlyzerEntity] = try downloadJSON(\n      entitiesDownloadURL, convertSnakeCase: true)\n    log.info(\"Downloading and decoding pixlyzer shapes\")\n    let pixlyzerShapeRegistry: PixlyzerShapeRegistry = try downloadJSON(\n      shapeRegistryDownloadURL, convertSnakeCase: false)\n\n    // Process fluids\n    log.info(\"Processing pixlyzer fluid registry\")\n    let (fluidRegistry, pixlyzerFluidIdToFluidId) = try Self.createFluidRegistry(\n      from: pixlyzerFluids)\n\n    // Process biomes\n    log.info(\"Processing pixlyzer biome registry\")\n    let biomeRegistry = try Self.createBiomeRegistry(from: pixlyzerBiomes)\n\n    // Process entities\n    log.info(\"Processing pixlyzer entity registry\")\n    let entityRegistry = try Self.createEntityRegistry(from: pixlyzerEntities)\n\n    // Process blocks\n    log.info(\"Processing pixlyzer block registry\")\n    let blockRegistry = try Self.createBlockRegistry(\n      from: pixlyzerBlocks,\n      shapes: pixlyzerShapeRegistry,\n      pixlyzerFluidIdToFluidId: pixlyzerFluidIdToFluidId,\n      fluidRegistry: fluidRegistry\n    )\n\n    // Process items\n    log.info(\"Processing pixlyzer item registry\")\n    let itemRegistry = try Self.createItemRegistry(from: pixlyzerItems)\n\n    return RegistryStore(\n      blockRegistry: blockRegistry,\n      biomeRegistry: biomeRegistry,\n      fluidRegistry: fluidRegistry,\n      entityRegistry: entityRegistry,\n      itemRegistry: itemRegistry\n    )\n  }\n\n  private static func createFluidRegistry(\n    from pixlyzerFluids: [String: PixlyzerFluid]\n  ) throws -> (fluidRegistry: FluidRegistry, pixlyzerFluidIdToFluidId: [Int: Int]) {\n    guard\n      let waterStill = pixlyzerFluids[\"minecraft:water\"],\n      let lavaStill = pixlyzerFluids[\"minecraft:lava\"]\n    else {\n      log.error(\"Failed to locate all required fluids\")\n      Foundation.exit(1)\n    }\n\n    let water = Fluid(\n      id: 0,\n      identifier: Identifier(name: \"water\"),\n      flowingTexture: Identifier(name: \"block/water_flow\"),\n      stillTexture: Identifier(name: \"block/water_still\"),\n      dripParticleType: waterStill.dripParticleType\n    )\n    let lava = Fluid(\n      id: 1,\n      identifier: Identifier(name: \"lava\"),\n      flowingTexture: Identifier(name: \"block/lava_flow\"),\n      stillTexture: Identifier(name: \"block/lava_still\"),\n      dripParticleType: lavaStill.dripParticleType\n    )\n    let fluids = [water, lava]\n\n    var pixlyzerFluidIdToFluidId: [Int: Int] = [:]\n    for (identifier, pixlyzerFluid) in pixlyzerFluids {\n      if identifier.contains(\"water\") {\n        pixlyzerFluidIdToFluidId[pixlyzerFluid.id] = water.id\n      } else if identifier.contains(\"lava\") {\n        pixlyzerFluidIdToFluidId[pixlyzerFluid.id] = lava.id\n      }\n    }\n\n    return (\n      fluidRegistry: FluidRegistry(fluids: fluids),\n      pixlyzerFluidIdToFluidId: pixlyzerFluidIdToFluidId\n    )\n  }\n\n  private static func createBiomeRegistry(\n    from pixlyzerBiomes: [String: PixlyzerBiome]\n  ) throws -> BiomeRegistry {\n    var biomes: [Int: Biome] = [:]\n    for (identifier, pixlyzerBiome) in pixlyzerBiomes {\n      let identifier = try Identifier(identifier)\n      let biome = Biome(from: pixlyzerBiome, identifier: identifier)\n      biomes[biome.id] = biome\n    }\n\n    return BiomeRegistry(biomes: biomes)\n  }\n\n  private static func createEntityRegistry(from pixlyzerEntities: [String: PixlyzerEntity]) throws\n    -> EntityRegistry\n  {\n    var entities: [Int: EntityKind] = [:]\n    for (identifier, pixlyzerEntity) in pixlyzerEntities {\n      if let identifier = try? Identifier(identifier) {\n        var parent = pixlyzerEntity.parent\n        var inheritanceChain: [String] = [pixlyzerEntity.class].compactMap(identity)\n        while let currentParent = parent {\n          inheritanceChain.append(currentParent)\n\n          guard\n            let parentEntity =\n              pixlyzerEntities[currentParent]\n              ?? pixlyzerEntities.values.first(where: { $0.class == currentParent })\n          else {\n            throw PixlyzerError.missingEntity(currentParent)\n          }\n\n          parent = parentEntity.parent\n        }\n\n        // Some entities don't correspond to Vanilla entity kinds (in which case the initializer returns nil,\n        // not an error).\n        if let entity = try EntityKind(\n          from: pixlyzerEntity,\n          inheritanceChain: inheritanceChain,\n          identifier: identifier\n        ) {\n          entities[entity.id] = entity\n        }\n      }\n    }\n\n    return try EntityRegistry(entities: entities)\n  }\n\n  private static func createBlockRegistry(\n    from pixlyzerBlocks: [String: PixlyzerBlock],\n    shapes pixlyzerShapeRegistry: PixlyzerShapeRegistry,\n    pixlyzerFluidIdToFluidId: [Int: Int],\n    fluidRegistry: FluidRegistry\n  ) throws -> BlockRegistry {\n    // Process block shapes\n    var aabbs: [AxisAlignedBoundingBox] = []\n    for pixlyzerAABB in pixlyzerShapeRegistry.aabbs {\n      aabbs.append(try AxisAlignedBoundingBox(from: pixlyzerAABB))\n    }\n\n    var shapes: [[AxisAlignedBoundingBox]] = []\n    for shape in pixlyzerShapeRegistry.shapes {\n      let ids = shape.items\n      var boxes: [AxisAlignedBoundingBox] = []\n      for id in ids {\n        boxes.append(aabbs[id])\n      }\n      shapes.append(boxes)\n    }\n\n    guard let water = fluidRegistry.fluid(for: Identifier(name: \"water\")) else {\n      throw PixlyzerError.failedToGetWaterFluid\n    }\n\n    // Process blocks\n    var blocks: [Int: Block] = [:]\n    var blockModelRenderDescriptors: [Int: [[BlockModelRenderDescriptor]]] = [:]\n    for (identifier, pixlyzerBlock) in pixlyzerBlocks {\n      var identifier = try Identifier(identifier)\n      identifier.name = \"block/\\(identifier.name)\"\n      let fluid: Fluid?\n      if let flowingFluid = pixlyzerBlock.flowFluid {\n        guard let fluidId = pixlyzerFluidIdToFluidId[flowingFluid] else {\n          log.error(\"Failed to get fluid from pixlyzer flowing fluid id\")\n          Foundation.exit(1)\n        }\n\n        fluid = fluidRegistry.fluid(withId: fluidId)\n      } else {\n        fluid = nil\n      }\n\n      for (stateId, pixlyzerState) in pixlyzerBlock.states {\n        let isWaterlogged =\n          pixlyzerState.properties?.waterlogged == true\n          || BlockRegistry.waterloggedBlockClasses.contains(pixlyzerBlock.className)\n        let isBubbleColumn = identifier == Identifier(name: \"block/bubble_column\")\n        let fluid = isWaterlogged || isBubbleColumn ? water : fluid\n        let block = Block(\n          pixlyzerBlock,\n          pixlyzerState,\n          shapes: shapes,\n          stateId: stateId,\n          fluid: fluid,\n          isWaterlogged: isWaterlogged,\n          identifier: identifier\n        )\n\n        let descriptors = pixlyzerState.blockModelVariantDescriptors.map {\n          $0.map {\n            BlockModelRenderDescriptor(from: $0)\n          }\n        }\n\n        blocks[stateId] = block\n        blockModelRenderDescriptors[stateId] = descriptors\n      }\n    }\n\n    var blockArray: [Block] = []\n    var renderDescriptors: [[[BlockModelRenderDescriptor]]] = []\n    for i in 0..<blocks.count {\n      guard let block = blocks[i] else {\n        throw PixlyzerError.missingBlockId(i)\n      }\n\n      blockArray.append(block)\n      renderDescriptors.append(blockModelRenderDescriptors[i] ?? [])\n    }\n\n    return BlockRegistry(blocks: blockArray, renderDescriptors: renderDescriptors)\n  }\n\n  private static func createItemRegistry(\n    from pixlyzerItems: [String: PixlyzerItem]\n  ) throws -> ItemRegistry {\n    var items: [Int: Item] = [:]\n    for (identifierString, pixlyzerItem) in pixlyzerItems {\n      var identifier = try Identifier(identifierString)\n      identifier.name = \"item/\\(identifier.name)\"\n      let item = try Item(from: pixlyzerItem, identifier: identifier)\n      items[item.id] = item\n    }\n\n    return try ItemRegistry(items: items)\n  }\n\n  private static func downloadJSON<T: Decodable>(\n    _ url: URL,\n    convertSnakeCase: Bool,\n    useZippyJSON: Bool = true\n  ) throws -> T {\n    let contents = try RequestUtil.data(contentsOf: url)\n\n    if useZippyJSON {\n      var decoder = CustomJSONDecoder()\n      if convertSnakeCase {\n        decoder.keyDecodingStrategy = .convertFromSnakeCase\n      }\n      return try decoder.decode(T.self, from: contents)\n    } else {\n      let decoder = JSONDecoder()\n      if convertSnakeCase {\n        decoder.keyDecodingStrategy = .convertFromSnakeCase\n      }\n      return try decoder.decode(T.self, from: contents)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Pixlyzer/PixlyzerItem.swift",
    "content": "import Foundation\n\npublic enum PixlyzerItemError: LocalizedError {\n  case missingRequiredPropertiesForEquipment(id: Int)\n  case missingRequiredPropertiesForTool(id: Int)\n  case unhandledToolClass(String)\n  case axeMissingStrippableBlocks(id: Int)\n  case hoeMissingTillableBlocks(id: Int)\n  case shovelMissingFlattenableBlocks(id: Int)\n  case invalidBlockIdInteger(String)\n\n  public var errorDescription: String? {\n    switch self {\n      case let .missingRequiredPropertiesForEquipment(id):\n        return \"Missing required properties for equipment item with id: \\(id).\"\n      case let .missingRequiredPropertiesForTool(id):\n        return \"Missing required properties for tool item with id: \\(id).\"\n      case let .unhandledToolClass(className):\n        return \"Encountered unhandled tool class '\\(className)'.\"\n      case let .axeMissingStrippableBlocks(id):\n        return \"Axe with id '\\(id)' missing strippable blocks.\"\n      case let .hoeMissingTillableBlocks(id):\n        return \"Hoe with id '\\(id)' missing tillable blocks.\"\n      case let .shovelMissingFlattenableBlocks(id):\n        return \"Shovel with id '\\(id)' missing flattenable blocks.\"\n      case let .invalidBlockIdInteger(id):\n        return \"Invalid block id '\\(id)' (expected an integer).\"\n    }\n  }\n}\n\npublic struct PixlyzerItem: Decodable {\n  public var id: Int\n  public var category: Int?\n  public var rarity: ItemRarity\n  public var maximumStackSize: Int\n  public var maximumDamage: Int\n  public var isFireResistant: Bool\n  public var isComplex: Bool\n  public var translationKey: String\n  public var equipmentSlot: Item.ArmorProperties.EquipmentSlot?\n  public var defense: Int?\n  public var toughness: Double?\n  public var armorMaterial: Identifier?\n  public var knockbackResistance: Double?\n  public var uses: Int?\n  public var speed: Double?\n  public var attackDamage: Double?\n  public var attackDamageBonus: Double?\n  public var level: Item.ToolProperties.Level?\n  public var enchantmentValue: Int?\n  public var diggableBlocks: [Int]?\n  public var strippableBlocks: [String: Int]?\n  public var tillableBlocks: [String: Int]?\n  public var flattenableBlocks: [String: Int]?\n  public var effectiveMaterials: [Identifier]?\n  public var block: Int?\n  public var className: String\n\n  private enum CodingKeys: String, CodingKey {\n    case id\n    case category\n    case rarity\n    case maximumStackSize = \"max_stack_size\"\n    case maximumDamage = \"max_damage\"\n    case isFireResistant = \"is_fire_resistant\"\n    case isComplex = \"is_complex\"\n    case translationKey = \"translation_key\"\n    case equipmentSlot = \"equipment_slot\"\n    case defense\n    case toughness\n    case armorMaterial = \"armor_material\"\n    case knockbackResistance = \"knockback_resistance\"\n    case uses\n    case speed\n    case attackDamage = \"attack_damage\"\n    case attackDamageBonus = \"attack_damage_bonus\"\n    case level\n    case enchantmentValue = \"enchantment_value\"\n    case diggableBlocks = \"diggable_blocks\"\n    case strippableBlocks = \"strippables_blocks\"\n    case tillableBlocks = \"tillables_block_states\"\n    case flattenableBlocks = \"flattenables_block_states\"\n    case effectiveMaterials = \"effective_materials\"\n    case block\n    case className = \"class\"\n  }\n}\n\nextension Item {\n  public init(from pixlyzerItem: PixlyzerItem, identifier: Identifier) throws {\n    id = pixlyzerItem.id\n    self.identifier = identifier\n    rarity = pixlyzerItem.rarity\n    maximumStackSize = pixlyzerItem.maximumStackSize\n    maximumDamage = pixlyzerItem.maximumDamage\n    isFireResistant = pixlyzerItem.isFireResistant\n    translationKey = pixlyzerItem.translationKey\n    blockId = pixlyzerItem.block\n\n    if let equipmentSlot = pixlyzerItem.equipmentSlot {\n      guard\n        let defense = pixlyzerItem.defense,\n        let toughness = pixlyzerItem.toughness,\n        let armorMaterial = pixlyzerItem.armorMaterial,\n        let knockbackResistance = pixlyzerItem.knockbackResistance\n      else {\n        throw PixlyzerItemError.missingRequiredPropertiesForEquipment(id: pixlyzerItem.id)\n      }\n\n      properties = .armor(Item.ArmorProperties(\n        equipmentSlot: equipmentSlot,\n        defense: defense,\n        toughness: toughness,\n        material: armorMaterial,\n        knockbackResistance: knockbackResistance\n      ))\n    } else if let uses = pixlyzerItem.uses {\n      guard\n        let speed = pixlyzerItem.speed,\n        let attackDamage = pixlyzerItem.attackDamage,\n        let attackDamageBonus = pixlyzerItem.attackDamageBonus,\n        let level = pixlyzerItem.level,\n        let enchantmentValue = pixlyzerItem.enchantmentValue\n      else {\n        throw PixlyzerItemError.missingRequiredPropertiesForTool(id: pixlyzerItem.id)\n      }\n\n      let interactions: [String: Int]\n      let kind: Item.ToolProperties.ToolKind\n      switch pixlyzerItem.className {\n        case \"SwordItem\":\n          interactions = [:]\n          kind = .sword\n        case \"PickaxeItem\":\n          interactions = [:]\n          kind = .pickaxe\n        case \"AxeItem\":\n          guard let strippableBlocks = pixlyzerItem.strippableBlocks else {\n            throw PixlyzerItemError.axeMissingStrippableBlocks(id: pixlyzerItem.id)\n          }\n          interactions = strippableBlocks\n          kind = .axe\n        case \"ShovelItem\":\n          guard let flattenableBlocks = pixlyzerItem.flattenableBlocks else {\n            throw PixlyzerItemError.shovelMissingFlattenableBlocks(id: pixlyzerItem.id)\n          }\n          interactions = flattenableBlocks\n          kind = .shovel\n        case \"HoeItem\":\n          guard let tillableBlocks = pixlyzerItem.tillableBlocks else {\n            throw PixlyzerItemError.hoeMissingTillableBlocks(id: pixlyzerItem.id)\n          }\n          interactions = tillableBlocks\n          kind = .hoe\n        default:\n          throw PixlyzerItemError.unhandledToolClass(pixlyzerItem.className)\n      }\n\n      var parsedInteractions: [Int: Int] = [:]\n      for (key, value) in interactions {\n        guard let parsedKey = Int(key) else {\n          throw PixlyzerItemError.invalidBlockIdInteger(key)\n        }\n        parsedInteractions[parsedKey] = value\n      }\n\n      properties = .tool(Item.ToolProperties(\n        uses: uses,\n        level: level,\n        speed: speed,\n        attackDamage: attackDamage,\n        attackDamageBonus: attackDamageBonus,\n        enchantmentValue: enchantmentValue,\n        mineableBlocks: pixlyzerItem.diggableBlocks ?? [],\n        blockInteractions: parsedInteractions,\n        kind: kind,\n        effectiveMaterials: pixlyzerItem.effectiveMaterials ?? []\n      ))\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Pixlyzer/PixlyzerShapeRegistry.swift",
    "content": "import Foundation\n\npublic struct PixlyzerShapeRegistry: Decodable {\n  public var shapes: [SingleOrMultiple<Int>]\n  public var aabbs: [PixlyzerAABB]\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/Pixlyzer/SingleOrMultiple.swift",
    "content": "import Foundation\n\n/// An error thrown by ``SingleOrMultiple``.\npublic enum SingleOrMultipleError: Error {\n  case invalidSingleOrMultipleElement\n}\n\n/// A type container for Mojang and Pixlyzer's weird JSON formats. The formats sometimes have\n/// keys that can either contain a single value or an array depending on context.\npublic enum SingleOrMultiple<T: Decodable>: Decodable {\n  /// Contains a single value. In JSON it is just a value.\n  case single(T)\n  /// Contains multiple values. In JSON it is an array.\n  case multiple([T])\n  \n  /// The container's elements as an array.\n  public var items: [T] {\n    switch self {\n      case let .single(item):\n        return [item]\n      case let .multiple(items):\n        return items\n    }\n  }\n  \n  public init(from decoder: Decoder) throws {\n    let container = try decoder.singleValueContainer()\n    // It's a bit dodgy but we have to check for multiple before single otherwise decoding\n    // PixlyzerBlockState.renderDescriptor doesn't work (for a nested SingleOrMultiple)\n    if let items = try? container.decode([T].self) {\n      self = .multiple(items)\n    } else if let item = try? container.decode(T.self) {\n      self = .single(item)\n    } else {\n      throw SingleOrMultipleError.invalidSingleOrMultipleElement\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Registry/RegistryStore.swift",
    "content": "import Foundation\n\npublic enum RegistryStoreError: LocalizedError {\n  case outDatedBlockRegistry\n\n  public var errorDescription: String? {\n    switch self {\n      case .outDatedBlockRegistry:\n        return \"The block registry cache is outdated and must be regenerated.\"\n    }\n  }\n}\n\n/// Holds static Minecraft data such as biomes, fluids and blocks. Delta Client populates it launch.\npublic struct RegistryStore {\n  /// The shared registry instance.\n  public static var shared = RegistryStore() // TODO: Abolish shared registries\n\n  public var blockRegistry = BlockRegistry()\n  public var biomeRegistry = BiomeRegistry()\n  public var fluidRegistry = FluidRegistry()\n  public var entityRegistry = EntityRegistry()\n  public var itemRegistry = ItemRegistry()\n\n  /// Creates an empty registry store.\n  public init() {}\n\n  /// Creates a populated registry store.\n  public init(\n    blockRegistry: BlockRegistry,\n    biomeRegistry: BiomeRegistry,\n    fluidRegistry: FluidRegistry,\n    entityRegistry: EntityRegistry,\n    itemRegistry: ItemRegistry\n  ) {\n    self.blockRegistry = blockRegistry\n    self.biomeRegistry = biomeRegistry\n    self.fluidRegistry = fluidRegistry\n    self.entityRegistry = entityRegistry\n    self.itemRegistry = itemRegistry\n  }\n\n  /// Populate the shared registry store.\n  /// - Parameters:\n  ///   - directory: Directory used for caching registries.\n  ///   - onProgress: Callback triggered whenever the operation progress is updated.\n  public static func populateShared(\n    _ directory: URL,\n    progress: TaskProgress<RegistryLoadingStep>? = nil\n  ) throws {\n    shared = try loadCached(directory, progress: progress)\n  }\n\n  /// Steps to be exectued during the `populateShared` process.\n  public enum RegistryLoadingStep: CaseIterable, TaskStep {\n    case loadBlock, loadBiome, loadFluid, loadEntity, loadItem\n    case cacheBlock, cacheBiome, cacheFluid, cacheEntity, cacheItem\n\n    /// Task progress in respect to the whole loading process\n    public var relativeDuration: Double { 1 }\n\n    /// The task description\n    public var message: String {\n      var prefix: String\n      var identifier: String\n\n      switch self {\n        case .loadBlock, .loadBiome, .loadFluid, .loadEntity, .loadItem: prefix = \"Loading cached\"\n        case .cacheBlock, .cacheBiome, .cacheFluid, .cacheEntity, .cacheItem: prefix = \"Caching\"\n      }\n\n      switch self {\n        case .loadBlock, .cacheBlock: identifier = \"block\"\n        case .loadBiome, .cacheBiome: identifier = \"biome\"\n        case .loadFluid, .cacheFluid: identifier = \"fluid\"\n        case .loadEntity, .cacheEntity: identifier = \"entity\"\n        case .loadItem, .cacheItem: identifier = \"item\"\n      }\n\n      return \"\\(prefix) \\(identifier) registry\"\n    }\n  }\n\n  /// Loads the registries cached in a directory. If any registries are missing, they're all redownloaded and cached.\n  /// - Parameters:\n  ///   - directory: The directory containing the cached registries.\n  ///   - onProgress: Callback triggered whenever the operation progress is updated.\n  /// - Returns: The loaded registry.\n  public static func loadCached(\n    _ directory: URL,\n    progress: TaskProgress<RegistryLoadingStep>? = nil\n  ) throws -> RegistryStore {\n    // TODO: Make caching more fine-grained, only reload the registries for\n    //   which cache loading failed. Also reduce duplication.\n    do {\n      progress?.update(to: .loadBlock)\n      let blockRegistry = try BlockRegistry.loadCached(fromDirectory: directory)\n\n      progress?.update(to: .loadBiome)\n      let biomeRegistry = try BiomeRegistry.loadCached(fromDirectory: directory)\n\n      progress?.update(to: .loadFluid)\n      let fluidRegistry = try FluidRegistry.loadCached(fromDirectory: directory)\n\n      progress?.update(to: .loadEntity)\n      let entityRegistry = try EntityRegistry.loadCached(fromDirectory: directory)\n\n      progress?.update(to: .loadItem)\n      let itemRegistry = try ItemRegistry.loadCached(fromDirectory: directory)\n\n      return RegistryStore(\n        blockRegistry: blockRegistry,\n        biomeRegistry: biomeRegistry,\n        fluidRegistry: fluidRegistry,\n        entityRegistry: entityRegistry,\n        itemRegistry: itemRegistry\n      )\n    } catch {\n      log.info(\"Failed to load cached registries. Loading from Pixlyzer data\")\n\n      // TODO: Track downloading and formatting progress as well for a more precise progress tracking.\n      let registry = try PixlyzerFormatter.downloadAndFormatRegistries(Constants.versionString)\n      try FileManager.default.createDirectory(\n        at: directory,\n        withIntermediateDirectories: true,\n        attributes: nil\n      )\n\n      progress?.update(to: .cacheBlock)\n      try registry.blockRegistry.cache(toDirectory: directory)\n\n      progress?.update(to: .cacheBiome)\n      try registry.biomeRegistry.cache(toDirectory: directory)\n\n      progress?.update(to: .cacheFluid)\n      try registry.fluidRegistry.cache(toDirectory: directory)\n\n      progress?.update(to: .cacheEntity)\n      try registry.entityRegistry.cache(toDirectory: directory)\n\n      progress?.update(to: .cacheItem)\n      try registry.itemRegistry.cache(toDirectory: directory)\n\n      return registry\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/RenderConfiguration.swift",
    "content": "// TODO: RenderConfiguration should probably be moved to the Renderer\n//   module if possible?\n\n/// Configuration for ``RenderCoordinator``.\npublic struct RenderConfiguration: Codable {\n  /// The vertical fov in degrees.\n  public var fovY: Float\n  /// The furthest distance that chunks should be rendered at.\n  ///\n  /// The distance between a chunk and the camera is measured like so:\n  /// ```swift\n  /// let difference = chunk - cameraChunk\n  /// let distance = max(difference.x, difference.z)\n  /// ```\n  public var renderDistance: Int\n  /// The rendering mode to use.\n  public var mode: RenderMode\n  /// Enables the order independent transparency optimization.\n  public var enableOrderIndependentTransparency: Bool\n\n  /// Creates a new render configuration.\n  /// - Parameters:\n  ///   - fovY: See ``fovY``. Defaults to 70 degrees.\n  ///   - renderDistance: See ``renderDistance``. Defaults to 10.\n  ///   - mode: See ``mode``. Defaults to ``RenderMode/normal``.\n  ///   - enableOrderIndependentTransparency: See ``enableOrderIndependentTransparency``. Defaults\n  ///     to `false`.\n  public init(\n    fovY: Float = 70,\n    renderDistance: Int = 10,\n    mode: RenderMode = .normal,\n    enableOrderIndependentTransparency: Bool = false\n  ) {\n    self.fovY = fovY\n    self.renderDistance = renderDistance\n    self.mode = mode\n    self.enableOrderIndependentTransparency = enableOrderIndependentTransparency\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/RenderMode.swift",
    "content": "/// All of the supported rendering modes.\npublic enum RenderMode: String, Codable, CaseIterable, Identifiable {\n  /// Regular rendering.\n  case normal\n  /// Wireframe rendering.\n  case wireframe\n  \n  public var id: Self { self }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Biome/BiomeColorMap.swift",
    "content": "import Foundation\n\n/// A biome's grass or foliage color map.\npublic struct BiomeColorMap {\n  /// Hardcoded override colors for biomes matching certain criteria.\n  public var overrides: BiomeModifiers<RGBColor> = [:]\n  /// Filters to apply colors of biomes matching certain criteria.\n  public var filters: BiomeModifiers<(RGBColor) -> RGBColor> = [:]\n\n  /// The underlying color map.\n  private var colorMap = ColorMap()\n  /// Precalculated colors for each biome indexed by biome id.\n  private var colors: [Int: RGBColor] = [:]\n\n  // MARK: Init\n\n  /// Creates an empty biome color map.\n  public init() {}\n\n  /// Creates a new biome color map and precalculates the colors.\n  /// - Parameters:\n  ///   - colorMapPNG: The PNG color map containing to get biome colors from.\n  ///   - biomes: Biomes to precalculate colors for.\n  ///   - fallbackColor: The color to use when color calculation fails.\n  ///   - overrides: A list of color overrides for biomes matching certain criteria.\n  ///   - filters: Color filters to apply to biomes matching certain criteria. Color filters are applied to the calculated color before saving it to `colors`.\n  init(\n    from colorMapPNG: URL,\n    biomes: [Int: Biome],\n    fallbackColor: RGBColor,\n    overrides: BiomeModifiers<RGBColor> = [:],\n    filters: BiomeModifiers<(RGBColor) -> RGBColor> = [:]\n  ) throws {\n    colorMap = try ColorMap(from: colorMapPNG)\n\n    guard colorMap.width == 256 && colorMap.height == 256 else {\n      throw BiomeError.colorMapNot256By256Pixels\n    }\n\n    self.overrides = overrides\n    self.filters = filters\n\n    precalculate(biomes, fallback: fallbackColor)\n  }\n\n  // MARK: Access\n\n  /// Get the precalculated color value for a biome.\n  /// - Parameter biome: Biome to get color for.\n  /// - Returns: The biome's color. If the look up fails, `nil` is returned.\n  public func color(for biome: Biome) -> RGBColor? {\n    if let color = colors[biome.id] {\n      return color\n    } else {\n      log.warning(\"Biome color map look up failed, returning nil; biome.id=\\(biome.id)\")\n      return nil\n    }\n  }\n\n  // MARK: Color calculation\n\n  /// Precalculates all of the biome's colors. Applies `overrides` and `filters`.\n  /// - Parameters:\n  ///   - biomes: Biomes to precalculate colors for.\n  ///   - fallback: The color to use when color map look ups fail.\n  public mutating func precalculate(_ biomes: [Int: Biome], fallback: RGBColor) {\n    for (_, biome) in biomes {\n      let overrideColor = overrides[biome]\n\n      var color = overrideColor ?? calculateColor(biome, fallback: fallback)\n\n      if let filter = filters[biome] {\n        color = filter(color)\n      }\n\n      colors[biome.id] = color\n    }\n  }\n\n  /// Calculate the color for a biome from the color map. To get the actual biome color (after overrides and filters), use ``color(for:)``.\n  ///\n  /// `overrides` and `filters` do not affect the color returned. The color is just retrieved using\n  /// the biome's climate information (specifically temperature and rainfall) and the color map.\n  ///\n  /// - Parameters:\n  ///   - biome: A biome.\n  ///   - fallback: A color to use if the color lookup somehow ends up out of bounds.\n  /// - Returns: The color for a biome.\n  public func calculateColor(_ biome: Biome, fallback: RGBColor) -> RGBColor {\n    let adjustedTemperature = MathUtil.clamp(biome.temperature, 0, 1)\n    let adjustedRainfall = MathUtil.clamp(biome.rainfall, 0, 1) * adjustedTemperature\n\n    let x = Int((1 - adjustedTemperature) * Float(colorMap.width - 1))\n    let y = Int((1 - adjustedRainfall) * Float(colorMap.height - 1))\n    let color = colorMap.color(atX: x, y: y) ?? fallback\n\n    return color\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Biome/BiomeColors.swift",
    "content": "import Foundation\n\n/// Stores the colors to use for each biome, loaded from resource packs.\npublic struct BiomeColors {\n  // MARK: Vanilla colors, overrides and filters\n\n  public static let vanillaSpruceLeavesColor = RGBColor(r: 97, g: 153, b: 97)\n  public static let vanillaBirchLeavesColor = RGBColor(r: 128, g: 167, b: 85)\n  public static let vanillaLilyPadColor = RGBColor(r: 32, g: 128, b: 48)\n  public static let vanillaBadlandsGrassColor = RGBColor(r: 144, g: 129, b: 77)\n  public static let vanillaBadlandsFoliageColor = RGBColor(r: 158, g: 129, b: 77)\n  public static let vanillaSwampColor = RGBColor(hexCode: 0x6a7039)\n\n  /// The grass color used when something fails.\n  public static let defaultGrassColor = RGBColor(r: 72, g: 181, b: 24)\n  /// The foliage color used when something fails.\n  public static let defaultFoliageColor = RGBColor(r: 72, g: 181, b: 24)\n\n  /// Grass colors that vanilla hardcodes.\n  public static let vanillaGrassColorOverrides: BiomeModifiers<RGBColor> = [\n    .category(.swamp): Self.vanillaSwampColor,\n    .category(.badlands): Self.vanillaBadlandsGrassColor\n  ]\n\n  /// Grass color filters that vanilla applies.\n  public static let vanillaGrassColorFilters: BiomeModifiers<(RGBColor) -> RGBColor> = [\n    .identifier(Identifier(name: \"dark_forest\")): { color in\n      return RGBColor(hexCode: ((color.hexCode & 0xfefefe) + 0x28340a) >> 1)\n    }\n  ]\n\n  /// Foliage colors that vanilla hardcodes.\n  public static let vanillaFoliageColorOverrides: BiomeModifiers<RGBColor> = [\n    .category(.swamp): Self.vanillaSwampColor,\n    .category(.badlands): Self.vanillaBadlandsFoliageColor\n  ]\n\n  /// Foliage color filters that vanilla applies.\n  public static let vanillaFoliageColorFilters: BiomeModifiers<(RGBColor) -> RGBColor> = [:]\n\n  /// Block colors that vanilla hardcodes.\n  public static let vanillaBlockColorOverrides: [Identifier: RGBColor] = [\n    Identifier(name: \"spruce_leaves\"): Self.vanillaSpruceLeavesColor,\n    Identifier(name: \"birch_leaves\"): Self.vanillaBirchLeavesColor\n  ]\n\n  // MARK: Color maps\n\n  /// The color map containing biome foliage colors.\n  public var foliageColorMap: BiomeColorMap\n  /// The color map containing biome grass colors.\n  public var grassColorMap: BiomeColorMap\n  /// Colors to use for certain blocks instead of the colors from the biome color maps.\n  public var blockColorOverrides: [Identifier: RGBColor] = [:]\n\n  // MARK: Init\n\n  /// Creates an empty biome colors resource.\n  public init() {\n    foliageColorMap = BiomeColorMap()\n    grassColorMap = BiomeColorMap()\n  }\n\n  /// Creates a new biome colors resource from the colormaps in a resource pack directory.\n  /// - Parameter colormapDirectory: The colormap directory of a resource pack. Must contain `foliage.png` and `grass.png`.\n  /// - Parameter grassColorOverrides: Overrides for the grass colors of specific biomes. If `nil`, the vanilla ones are used.\n  /// - Parameter grassColorFilters: Filters to apply to the grass colors of specific biomes after retrieving them from the resource pack's\n  ///                                color map. If `nil`, the vanilla ones are used.\n  /// - Parameter foliageColorOverrides: Overrides for the foliage colors of specific biomes. If `nil`, the vanilla ones are used.\n  /// - Parameter foliageColorFilters: Filters to apply to the foliage colors of specific biomes after retrieving them from the resource pack's\n  ///                                  color map. If `nil`, the vanilla ones are used.\n  /// - Parameter blockColorOverrides: Overrides the colors of any blocks specified.\n  /// - Parameter defaultFoliageColor: Color to use for foliage when something fails.\n  /// - Parameter defaultGrassColor: Color to use for grass when something fails.\n  public init(\n    from colormapDirectory: URL,\n    grassColorOverrides: BiomeModifiers<RGBColor>? = nil,\n    grassColorFilters: BiomeModifiers<(RGBColor) -> RGBColor>? = nil,\n    foliageColorOverrides: BiomeModifiers<RGBColor>? = nil,\n    foliageColorFilters: BiomeModifiers<(RGBColor) -> RGBColor>? = nil,\n    blockColorOverrides: [Identifier: RGBColor]? = nil,\n    defaultFoliageColor: RGBColor? = nil,\n    defaultGrassColor: RGBColor? = nil\n  ) throws {\n    let foliageMapFile = colormapDirectory.appendingPathComponent(\"foliage.png\")\n    let grassMapFile = colormapDirectory.appendingPathComponent(\"grass.png\")\n\n    do {\n      foliageColorMap = try BiomeColorMap(\n        from: foliageMapFile,\n        biomes: RegistryStore.shared.biomeRegistry.biomes,\n        fallbackColor: defaultFoliageColor ?? Self.defaultFoliageColor,\n        overrides: foliageColorOverrides ?? Self.vanillaFoliageColorOverrides,\n        filters: foliageColorFilters ?? Self.vanillaFoliageColorFilters)\n    } catch {\n      throw BiomeError.failedToLoadFoliageColorMap(error)\n    }\n\n    do {\n      grassColorMap = try BiomeColorMap(\n        from: grassMapFile,\n        biomes: RegistryStore.shared.biomeRegistry.biomes,\n        fallbackColor: defaultGrassColor ?? Self.defaultGrassColor,\n        overrides: grassColorOverrides ?? Self.vanillaGrassColorOverrides,\n        filters: grassColorFilters ?? Self.vanillaGrassColorFilters)\n    } catch {\n      throw BiomeError.failedToLoadGrassColorMap(error)\n    }\n\n    self.blockColorOverrides = blockColorOverrides ?? Self.vanillaBlockColorOverrides\n  }\n\n  // MARK: Access\n\n  /// Get the tint color for a block.\n  ///\n  /// Respects ``blockColorOverrides``.\n  ///\n  /// - Parameters:\n  ///   - block: The block.\n  ///   - position: The position of the block. Currently ignored.\n  ///   - biome: The biome the block is in.\n  /// - Returns: A tint color. Returns `nil` if the block doesn't need a tint to be applied and in some cases where biome color look ups\n  ///            fail which could happen if plugins mess with this.\n  public func color(for block: Block, at position: BlockPosition, in biome: Biome) -> RGBColor? {\n    if let tintColor = blockColorOverrides[block.identifier] {\n      return tintColor\n    }\n\n    if let tint = block.tint {\n      switch tint {\n        case .computed(let computedTintType):\n          switch computedTintType {\n            case .waterTint:\n              return biome.waterColor\n            case .foliageTint:\n              return foliageColorMap.color(for: biome)\n            case .grassTint, .sugarCaneTint, .shearingDoublePlantTint:\n              return grassColorMap.color(for: biome)\n            case .lilyPadTint:\n              return Self.vanillaLilyPadColor\n          }\n        case .hardcoded(let tintColor):\n          return tintColor\n      }\n    } else {\n      return nil\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Font/BitmapFontProvider.swift",
    "content": "import Foundation\n\n/// A bitmap font atlas backed by a png file.\npublic struct BitmapFontProvider: Decodable {\n  /// PNG file containing font atlas.\n  public var file: Identifier\n  /// Vertical shift of characters in atlas.\n  public var verticalShift: Int\n  /// Characters in atlas.\n  public var characters: [String]\n\n  private enum CodingKeys: String, CodingKey {\n    case file\n    case verticalShift = \"ascent\"\n    case characters = \"chars\"\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Font/CharacterDescriptor.swift",
    "content": "/// A descriptor containing information required to use or render a character from an atlas texture.\npublic struct CharacterDescriptor {\n  /// The texture containing the character.\n  public var texture: Int\n  /// The x coordinate of the character in the atlas.\n  public var x: Int\n  /// The y coordinate of the character in the atlas.\n  public var y: Int\n  /// The width of the character in the atlas.\n  public var width: Int\n  /// The height of the character in the atlas.\n  public var height: Int\n  /// The vertical offset that the character should be offset by when rendering.\n  public var verticalOffset: Int\n  /// A scaling factor to use when rendering the character.\n  public var scalingFactor: Float\n\n  /// The width multiplied by the scaling factor (and rounded).\n  public var renderedWidth: Int {\n    return Int(Float(width) * scalingFactor)\n  }\n  /// The height multiplied by the scaling factor (and rounded).\n  public var renderedHeight: Int {\n    return Int(Float(height) * scalingFactor)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Font/Font.swift",
    "content": "import Foundation\n\n/// An error related to fonts from resource packs.\npublic enum FontError: LocalizedError {\n  case failedToGetArrayTextureWidth\n  case failedToGetArrayTextureHeight\n  case failedToCreateArrayTexture\n  case emptyFont\n  case invalidUnicodePageFileTemplate\n  case invalidUnicodeScalar(page: Int, index: Int)\n  case invalidGlyphSizesFile\n\n  public var errorDescription: String? {\n    switch self {\n      case .failedToGetArrayTextureWidth:\n        return \"Failed to get array texture width.\"\n      case .failedToGetArrayTextureHeight:\n        return \"Failed to get array texture height.\"\n      case .failedToCreateArrayTexture:\n        return \"Failed to create array texture.\"\n      case .emptyFont:\n        return \"Empty font.\"\n      case .invalidUnicodePageFileTemplate:\n        return \"Unicode page file path templates must be an identifier including exactly one occurence of '%s'.\"\n      case let .invalidUnicodeScalar(page, index):\n        return \"Failed to create unicode scalar while loading legacy unicode font: page=\\(page), index=\\(index).\"\n      case .invalidGlyphSizesFile:\n        return \"glyph_sizes.bin must be exactly 65536 bytes long\"\n    }\n  }\n}\n\n/// A font from a resource pack that can be used when rendering text.\npublic struct Font {\n  /// The default character width.\n  public static var defaultCharacterWidth = 8\n  /// The default character height.\n  public static var defaultCharacterHeight = 8\n\n  /// The characters present in this font and the descriptors used to render them.\n  public var characters: [Character: CharacterDescriptor]\n  /// The ascii characters present in this font, indexed by ascii value. Used to avoid dictionary as\n  /// much as possible in performance critical rendering code.\n  public var asciiCharacters: [CharacterDescriptor?]\n  /// The atlas texture that make up the font.\n  public var textures: [Texture]\n\n  /// Creates an empty font.\n  public init(characters: [Character: CharacterDescriptor] = [:], textures: [Texture] = []) {\n    self.characters = characters\n    self.textures = textures\n\n    asciiCharacters = []\n    for i in 0..<128 {\n      // This is safe because all ascii values are valid unicode scalars\n      // swiftlint:disable force_unwrapping\n      let character = Character(Unicode.Scalar(i)!)\n      // swiftlint:enable force_unwrapping\n      asciiCharacters.append(characters[character])\n    }\n  }\n\n  /// An internal initializer used to efficiently construct fonts from caches.\n  init(\n    characters: [Character: CharacterDescriptor],\n    asciiCharacters: [CharacterDescriptor?],\n    textures: [Texture]\n  ) {\n    self.characters = characters\n    self.asciiCharacters = asciiCharacters\n    self.textures = textures\n  }\n\n  /// Loads a font from a font manifest and texture directory.\n  /// - Parameters:\n  ///   - manifestFile: The font's manifest file.\n  ///   - namespaceDirectory: The current resource namespace's root directory.\n  ///   - textureDirectory: The resource pack's texture directory.\n  /// - Throws: An error if texture or manifest loading fails.\n  public static func load(from manifestFile: URL, namespaceDirectory: URL, textureDirectory: URL) throws -> Font {\n    let manifest = try FontManifest.load(from: manifestFile)\n    return try load(from: manifest, namespaceDirectory: namespaceDirectory, textureDirectory: textureDirectory)\n  }\n\n  /// Loads a font from a font manifest and texture directory.\n  /// - Parameters:\n  ///   - manifest: The font's manifest.\n  ///   - namespaceDirectory: The current resource namespace's root directory.\n  ///   - textureDirectory: The resource pack's texture directory.\n  /// - Throws: An error if texture loading fails.\n  public static func load(from manifest: FontManifest, namespaceDirectory: URL, textureDirectory: URL) throws -> Font {\n    var characters: [Character: CharacterDescriptor] = [:]\n    var textures: [Texture] = []\n\n    for provider in manifest.providers {\n      switch provider {\n        case let .bitmap(atlas):\n          // Load texture from file\n          let file = textureDirectory.appendingPathComponent(atlas.file.name)\n          let texture = try Texture(pngFile: file, type: .transparent)\n          textures.append(texture)\n\n          // Calculate bounding boxes of characters in atlas\n          let descriptors = Self.descriptors(\n            from: atlas,\n            texture: texture,\n            textureIndex: textures.count - 1\n          )\n\n          for (character, descriptor) in descriptors {\n            characters[character] = descriptor\n          }\n        case let .legacyUnicode(metadata):\n          // TODO: These identifiers could be referring to resources in another namespace.\n          //   We should support that instead of assuming that they're from the same namespace.\n          //   This is probably a larger issue affecting a bunch of the resource hanling code,\n          //   a generic way to get resource paths from identifiers is probably required.\n          let glyphSizeFilePath = namespaceDirectory.appendingPathComponent(metadata.sizes.name)\n          let glyphSizeBytes = [UInt8](try Data(contentsOf: glyphSizeFilePath))\n          guard glyphSizeBytes.count == 256 * 256 else {\n            throw FontError.invalidGlyphSizesFile\n          }\n\n          let pageFilePathTemplateParts = metadata.template.split(separator: \":\")\n          guard\n            pageFilePathTemplateParts.count <= 2,\n            let pageFilePathTemplate = pageFilePathTemplateParts.last\n          else {\n            throw FontError.invalidUnicodePageFileTemplate\n          }\n\n          guard\n            pageFilePathTemplate.contains(\"%s\"),\n            pageFilePathTemplate.components(separatedBy: \"%s\").count <= 2\n          else {\n            throw FontError.invalidUnicodePageFileTemplate\n          }\n\n          for page in 0..<256 {\n            let pageNumber = String(format: \"%02x\", page)\n            let path = pageFilePathTemplate.replacingOccurrences(of: \"%s\", with: pageNumber)\n            let textureFile = textureDirectory.appendingPathComponent(path)\n\n            guard FileManager.default.fileExists(atPath: textureFile.path) else {\n              continue\n            }\n            \n            let texture = try Texture(pngFile: textureFile, type: .transparent)\n            let textureIndex = textures.count\n            textures.append(texture)\n\n            for x in 0..<16 {\n              for y in 0..<16 {\n                let index = x + y * 16\n                let absoluteIndex = (page << 8) + index\n\n                let glyphSizeByte = glyphSizeBytes[absoluteIndex]\n                let xOffset = Int(glyphSizeByte >> 4)\n                let width = Int(glyphSizeByte & 0xf) - xOffset\n\n                guard let unicodeScalar = UnicodeScalar(UInt16(absoluteIndex)) else {\n                  throw FontError.invalidUnicodeScalar(page: page, index: index)\n                }\n\n                let character = Character(unicodeScalar)\n\n                // The legacy unicode provider is just a fallback so it shouldn't override\n                // characters from other fonts.\n                guard characters[character] == nil else {\n                  continue\n                }\n\n                characters[character] = CharacterDescriptor(\n                  texture: textureIndex,\n                  x: x * 16 + xOffset,\n                  y: y * 16,\n                  width: width,\n                  height: 16,\n                  verticalOffset: 0,\n                  scalingFactor: 0.5\n                )\n              }\n            }\n          }\n        case .trueType:\n          continue\n      }\n    }\n\n    return Font(characters: characters, textures: textures)\n  }\n\n  public func descriptor(for character: Character) -> CharacterDescriptor? {\n    if let asciiValue = character.asciiValue {\n      return asciiCharacters[Int(asciiValue)]\n    } else {\n      return characters[character]\n    }\n  }\n\n  /// Loads the characters descriptors from a bitmap font atlas.\n  /// - Parameters:\n  ///   - atlas: The bitmap font atlas.\n  ///   - texture: The texture containing the characters.\n  ///   - textureIndex: The index of the texture in the font's array texture.\n  /// - Returns: The character descriptors.\n  private static func descriptors(\n    from atlas: BitmapFontProvider,\n    texture: Texture,\n    textureIndex: Int\n  ) -> [Character: CharacterDescriptor] {\n    var descriptors: [Character: CharacterDescriptor] = [:]\n    for (yIndex, line) in atlas.characters.enumerated() {\n      for (xIndex, character) in line.enumerated() {\n        descriptors[character] = descriptor(\n          for: character,\n          xIndex: xIndex,\n          yIndex: yIndex,\n          texture: texture,\n          textureIndex: textureIndex\n        )\n      }\n    }\n    return descriptors\n  }\n\n  /// Computes the descriptor for a character from a texture.\n  /// - Parameters:\n  ///   - character: The character to get the descriptor for.\n  ///   - xIndex: The column the character is in (starting from 0).\n  ///   - yIndex: The row the character is in (starting from 1).\n  ///   - texture: The texture conatining the character.\n  ///   - textureIndex: Tte texture's index.\n  /// - Returns: A character descriptor.\n  private static func descriptor(\n    for character: Character,\n    xIndex: Int,\n    yIndex: Int,\n    texture: Texture,\n    textureIndex: Int\n  ) -> CharacterDescriptor {\n    var maxX = 0\n    var minX = Self.defaultCharacterWidth\n    var maxY = 0\n    var minY = Self.defaultCharacterHeight\n    for x in 0..<Self.defaultCharacterWidth {\n      for y in 0..<Self.defaultCharacterHeight {\n        let pixel = texture[\n          x + xIndex * Self.defaultCharacterWidth,\n          y + yIndex * Self.defaultCharacterHeight\n        ]\n\n        if pixel.alpha != 0 {\n          if x < minX {\n            minX = x\n          }\n          if x > maxX {\n            maxX = x\n          }\n          if y < minY {\n            minY = y\n          }\n          if y > maxY {\n            maxY = y\n          }\n        }\n      }\n    }\n\n    if maxX < minX || maxY < minY {\n      maxX = 0\n      minX = 0\n      maxY = 0\n      minY = 0\n    }\n\n    var width = maxX - minX + 1\n    let height = maxY - minY + 1\n\n    // Hardcode width of space character\n    if character == \" \" {\n      width = 3\n    }\n\n    return CharacterDescriptor(\n      texture: textureIndex,\n      x: xIndex * Self.defaultCharacterWidth + minX,\n      y: yIndex * Self.defaultCharacterHeight + minY,\n      width: width,\n      height: height,\n      verticalOffset: Self.defaultCharacterHeight - maxY - 1,\n      scalingFactor: 1\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Font/FontManifest.swift",
    "content": "import Foundation\n\n/// A font's manifest file containing providers for rendering the font.\npublic struct FontManifest: Decodable {\n  /// Providers for rendering the font.\n  public var providers: [FontProvider] = []\n\n  /// Loads a font manifest.\n  /// - Parameter file: The font manifest file.\n  public static func load(from file: URL) throws -> FontManifest {\n    let data = try Data(contentsOf: file)\n    return try JSONDecoder().decode(FontManifest.self, from: data)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Font/FontPalette.swift",
    "content": "import Foundation\n\n/// A palette containing any number of fonts.\npublic struct FontPalette {\n  /// The palette's fonts, keyed by name.\n  public var fonts: [String: Font] {\n    didSet {\n      defaultFont = fonts[\"default\"] ?? Font()\n    }\n  }\n\n  /// The palette's default font.\n  public private(set) var defaultFont: Font\n\n  /// Creates a new font palette.\n  /// - Parameter fonts: The palette's fonts.\n  public init(_ fonts: [String: Font] = [:]) {\n    self.fonts = fonts\n    defaultFont = fonts[\"default\"] ?? Font()\n  }\n\n  /// Load a font palette from a resource pack.\n  /// - Parameters:\n  ///   - manifestDirectory: The directory containing font manifests.\n  ///   - namespaceDirectory: The root directory of the current resource namespace being loaded.\n  ///   - textureDirectory: The directory containing the namespace's resources.\n  /// - Returns: A font palette.\n  public static func load(from manifestDirectory: URL, namespaceDirectory: URL, textureDirectory: URL) throws -> FontPalette {\n    let contents = try FileManager.default.contentsOfDirectory(\n      at: manifestDirectory,\n      includingPropertiesForKeys: nil\n    )\n    var fonts: [String: Font] = [:]\n    for file in contents where file.pathExtension == \"json\" {\n      let name = file.deletingPathExtension().lastPathComponent\n      let font = try Font.load(from: file, namespaceDirectory: namespaceDirectory, textureDirectory: textureDirectory)\n      fonts[name] = font\n    }\n    return FontPalette(fonts)\n  }\n\n  /// Gets the font with the given name.\n  /// - Parameter name: The name of the font to get.\n  /// - Returns: The requested font if it exists.\n  public func font(named name: String) -> Font? {\n    return fonts[name]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Font/FontProvider.swift",
    "content": "import Foundation\n\n/// A font provider.\npublic enum FontProvider: Decodable {\n  case bitmap(BitmapFontProvider)\n  case legacyUnicode(LegacyUnicodeFontProvider)\n  case trueType(TrueTypeFontProvider)\n\n  private enum CodingKeys: String, CodingKey {\n    case type\n  }\n\n  /// A font provider type.\n  public enum FontProviderType: String, Decodable {\n    case bitmap\n    case legacyUnicode = \"legacy_unicode\"\n    case trueType = \"ttf\"\n  }\n\n  public init(from decoder: Decoder) throws {\n    let container = try decoder.container(keyedBy: CodingKeys.self)\n    let type = try container.decode(FontProviderType.self, forKey: .type)\n\n    switch type {\n      case .bitmap:\n        self = .bitmap(try BitmapFontProvider(from: decoder))\n      case .legacyUnicode:\n        self = .legacyUnicode(try LegacyUnicodeFontProvider(from: decoder))\n      case .trueType:\n        self = .trueType(try TrueTypeFontProvider(from: decoder))\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Font/LegacyUnicodeFontProvider.swift",
    "content": "import Foundation\n\n/// A legacy unicode font.\npublic struct LegacyUnicodeFontProvider: Decodable {\n  public var sizes: Identifier\n  public var template: String\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Font/TrueTypeFontProvider.swift",
    "content": "import Foundation\n\n/// A TrueType font.\npublic struct TrueTypeFontProvider: Decodable {\n  public var file: String\n  public var shift: [Float]\n  public var size: Float\n  public var oversample: Float\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/GUI/GUITexturePalette.swift",
    "content": "import Foundation\n\n/// An error thrown by ``GUITexturePalette``.\npublic enum GUITexturePaletteError: Error {\n  case missingSlice(GUITextureSlice)\n}\n\n/// A palette of GUI textures.\npublic struct GUITexturePalette {\n  /// The palette of GUI textures.\n  public private(set) var palette: TexturePalette\n  /// Slice indices indexed by ``GUITextureSlice/rawValue``.\n  private var sliceIndices: [Int]\n\n  /// Creates a typesafe GUI texture palette from a regular texture palette.\n  public init(_ texturePalette: TexturePalette) throws {\n    palette = texturePalette\n    sliceIndices = [Int](repeating: -1, count: GUITextureSlice.allCases.count)\n    for slice in GUITextureSlice.allCases {\n      if let index = texturePalette.identifierToIndex[slice.identifier] {\n        sliceIndices[slice.rawValue] = index\n      } else {\n        throw GUITexturePaletteError.missingSlice(slice)\n      }\n    }\n  }\n\n  /// Gets the texture index for the specified slice.\n  /// - Parameter slice: The slice to get.\n  /// - Returns: The texture index.\n  public func textureIndex(for slice: GUITextureSlice) -> Int {\n    return sliceIndices[slice.rawValue]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/GUI/GUITextureSlice.swift",
    "content": "/// A slice of the GUI array texture.\npublic enum GUITextureSlice: Int, CaseIterable {\n  case bars\n  case icons\n  case widgets\n  case inventory\n  case craftingTable\n  case genericContainer\n  case dispenser // also covers dropper\n  case anvil\n  case beacon\n  case blastFurnace\n  case brewingStand\n  case enchantingTable\n  case furnace\n  case grindstone\n  case hopper\n  case loom\n  case merchant // Villagers & wandering traders\n  case shulkerBox\n  case smithingTable\n  case smoker\n  case cartographyTable\n  case stonecutter\n\n  /// The path of the slice's underlying texture in a resource pack's textures directory.\n  public var path: String {\n    switch self {\n      case .bars:\n        return \"bars.png\"\n      case .icons:\n        return \"icons.png\"\n      case .widgets:\n        return \"widgets.png\"\n      case .inventory:\n        return \"container/inventory.png\"\n      case .craftingTable:\n        return \"container/crafting_table.png\"\n      case .genericContainer:\n        return \"container/generic_54.png\"\n      case .dispenser:\n        return \"container/dispenser.png\"\n      case .anvil:\n        return \"container/anvil.png\"\n      case .beacon:\n        return \"container/beacon.png\"\n      case .blastFurnace:\n        return \"container/blast_furnace.png\"\n      case .brewingStand:\n        return \"container/brewing_stand.png\"\n      case .enchantingTable:\n        return \"container/enchanting_table.png\"\n      case .furnace:\n        return \"container/furnace.png\"\n      case .grindstone:\n        return \"container/grindstone.png\"\n      case .hopper:\n        return \"container/hopper.png\"\n      case .loom:\n        return \"container/loom.png\"\n      case .merchant:\n        return \"container/villager2.png\"\n      case .shulkerBox:\n        return \"container/shulker_box.png\"\n      case .smithingTable:\n        return \"container/smithing.png\"\n      case .smoker:\n        return \"container/smoker.png\"\n      case .cartographyTable:\n        return \"container/cartography_table.png\"\n      case .stonecutter:\n        return \"container/stonecutter.png\"\n    }\n  }\n\n  /// The identifier of the slice's underlying texture.\n  public var identifier: Identifier {\n    switch self {\n      case .bars:\n        return Identifier(name: \"gui/bars\")\n      case .icons:\n        return Identifier(name: \"gui/icons\")\n      case .widgets:\n        return Identifier(name: \"gui/widgets\")\n      case .inventory:\n        return Identifier(name: \"gui/container/inventory\")\n      case .craftingTable:\n        return Identifier(name: \"gui/container/crafting_table\")\n      case .genericContainer:\n        return Identifier(name: \"gui/container/generic_54\")\n      case .dispenser:\n        return Identifier(name: \"gui/container/dispenser\")\n      case .anvil:\n        return Identifier(name: \"gui/container/anvil\")\n      case .beacon:\n        return Identifier(name: \"gui/container/beacon\")\n      case .blastFurnace:\n        return Identifier(name: \"gui/container/blast_furnace\")\n      case .brewingStand:\n        return Identifier(name: \"gui/container/brewing_stand\")\n      case .enchantingTable:\n        return Identifier(name: \"gui/container/enchanting_table\")\n      case .furnace:\n        return Identifier(name: \"gui/container/furnace\")\n      case .grindstone:\n        return Identifier(name: \"gui/container/grindstone\")\n      case .hopper:\n        return Identifier(name: \"gui/container/hopper\")\n      case .loom:\n        return Identifier(name: \"gui/container/loom\")\n      case .merchant:\n        return Identifier(name: \"gui/container/villager2\")\n      case .shulkerBox:\n        return Identifier(name: \"gui/container/shulker_box\")\n      case .smithingTable:\n        return Identifier(name: \"gui/container/smithing\")\n      case .smoker:\n        return Identifier(name: \"gui/container/smoker\")\n      case .cartographyTable:\n        return Identifier(name: \"gui/container/cartography_table\")\n      case .stonecutter:\n        return Identifier(name: \"gui/container/stonecutter\")\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Locale/LocalizationFormatter.swift",
    "content": "/// A custom format string formatter than can handle format specifiers of the format \"%s\" or \"%x$s\"\n/// where x is a substitution index starting from 1. This is used by the Minecraft localization\n/// mechanism to generate localized strings from templates.\npublic struct LocalizationFormatter {\n  var substitutions: [String]\n\n  var currentSubstitutionIndex = 0\n  var isInFormatSpecifier = false\n  var currentFormatSpecifier = \"\"\n  var specifiedSubstitutionIndex: Int?\n\n  var output = \"\"\n\n  public static func format(_ template: String, withSubstitutions substitutions: [String]) -> String {\n    var formatter = LocalizationFormatter(substitutions: substitutions)\n    for character in template {\n      formatter.update(character)\n    }\n    formatter.finish()\n    return formatter.output\n  }\n\n  private init(substitutions: [String]) {\n    self.substitutions = substitutions\n  }\n\n  private mutating func update(_ character: Character) {\n    if isInFormatSpecifier {\n      let alreadyContainsDollarSymbol = currentFormatSpecifier.contains(\"$\")\n      currentFormatSpecifier.append(character)\n\n      if character == \"%\" {\n        // \"%%\" should result in \"%\"\n        output += \"%\"\n        endFormatSpecifier()\n      } else if character == \"s\" {\n        // \"s\" always ends a substitution\n        handleSubstitution()\n      } else if alreadyContainsDollarSymbol {\n        // \"$\" should always be immediately followed by \"s\"\n        output += currentFormatSpecifier\n        endFormatSpecifier()\n      } else if let digit = Int(String(character)) {\n        // Format specifiers can include an index immediately following the \"%\"\n        if let index = specifiedSubstitutionIndex {\n          specifiedSubstitutionIndex = index * 10 + digit\n        } else {\n          specifiedSubstitutionIndex = digit\n        }\n      } else if character == \"$\" {\n        // \"$\" should always be preceded by a substitution index\n        if specifiedSubstitutionIndex == nil {\n          output += currentFormatSpecifier\n          endFormatSpecifier()\n        }\n      } else {\n        // Invalid format specifier, just include in the output and move on\n        output += currentFormatSpecifier\n        endFormatSpecifier()\n      }\n    } else {\n      if character == \"%\" {\n        beginFormatSpecifier()\n      } else {\n        output += String(character)\n      }\n    }\n  }\n\n  private mutating func finish() {\n    output += currentFormatSpecifier\n  }\n\n  private mutating func beginFormatSpecifier() {\n    isInFormatSpecifier = true\n    currentFormatSpecifier = \"%\"\n  }\n\n  private mutating func endFormatSpecifier() {\n    isInFormatSpecifier = false\n    currentFormatSpecifier = \"\"\n    specifiedSubstitutionIndex = nil\n  }\n\n  private mutating func handleSubstitution() {\n    var index: Int\n    if let specifiedSubstitutionIndex = specifiedSubstitutionIndex {\n      // Indices specified in format specifiers start at 1\n      index = specifiedSubstitutionIndex - 1\n    } else {\n      index = currentSubstitutionIndex\n      currentSubstitutionIndex += 1\n    }\n\n    if index < substitutions.count {\n      output += substitutions[index]\n    } else {\n      output += currentFormatSpecifier\n    }\n\n    endFormatSpecifier()\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Locale/MinecraftLocale.swift",
    "content": "import Foundation\n\npublic enum MinecraftLocaleError: LocalizedError {\n  case unableToParseLocale\n\n  public var errorDescription: String? {\n    switch self {\n      case .unableToParseLocale:\n        return \"Unable to parse locale.\"\n    }\n  }\n}\n\npublic struct MinecraftLocale {\n  var translations: [String: String]\n\n  public init() {\n    self.translations = [:]\n  }\n\n  public init(localeFile: URL) throws {\n    do {\n      let data = try Data(contentsOf: localeFile)\n      if let dict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] {\n        self.translations = dict\n        return\n      }\n    } catch {\n      log.warning(\"failed to parse locale `\\(localeFile.lastPathComponent)`\")\n      throw error\n    }\n    throw MinecraftLocaleError.unableToParseLocale\n  }\n\n  public func getTranslation(for key: String, with content: [String]) -> String {\n    let template = getTemplate(for: key)\n    return LocalizationFormatter.format(template, withSubstitutions: content)\n  }\n\n  public func getTemplate(for key: String) -> String {\n    if let template = translations[key] {\n      return template\n    } else {\n      return key\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/MCMeta/AnimationMCMeta.swift",
    "content": "import Foundation\n\n/// The format of an mcmeta file describing a block texture's animation.\npublic struct AnimationMCMeta: Decodable {\n  public var animation: Animation\n}\n\nextension AnimationMCMeta {\n  public struct Animation: Decodable {\n    public var interpolate: Bool?\n    public var width: Int?\n    public var height: Int?\n    public var frametime: Int?\n    public var frames: [Frame]?\n  }\n}\n\nextension AnimationMCMeta {\n  public struct Frame: Decodable {\n    public var index: Int\n    public var time: Int?\n    \n    private enum CodingKeys: String, CodingKey {\n      case index\n      case time\n    }\n    \n    public init(from decoder: Decoder) throws {\n      if let container = try? decoder.singleValueContainer() {\n        index = try container.decode(Int.self)\n      } else if let container = try? decoder.container(keyedBy: CodingKeys.self) {\n        index = try container.decode(Int.self, forKey: .index)\n        time = try container.decode(Int.self, forKey: .time)\n      } else {\n        throw TextureError.invalidFrameMetadata\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/MCMeta/PackMCMeta.swift",
    "content": "import Foundation\n\nextension ResourcePack {\n  /// The format of a pack.mcmeta file in a mojang resource pack.\n  public struct PackMCMeta: Decodable {\n    var metadata: Metadata\n    var languages: [String: Language]?\n    \n    private enum CodingKeys: String, CodingKey {\n      case metadata = \"pack\"\n      case languages = \"language\"\n    }\n  }\n}\n\n// MARK: Metadata\n\nextension ResourcePack.PackMCMeta {\n  public struct Metadata: Decodable {\n    public var formatVersion: Int\n    \n    public init(formatVersion: Int) {\n      self.formatVersion = formatVersion\n    }\n    \n    private enum CodingKeys: String, CodingKey {\n      case formatVersion = \"pack_format\"\n    }\n  }\n}\n\n// MARK: Language Metadata\n\nextension ResourcePack.PackMCMeta {\n  public struct Language: Decodable {\n    public var name: String\n    public var region: String\n    public var bidirectional: Bool\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Manifest/VersionManifest.swift",
    "content": "import Foundation\n\nstruct VersionManifest: Codable {\n  var assetIndex: AssetIndex\n  var assets: String\n  var downloads: Downloads\n  var id: String\n  var releaseTime: String\n  var time: String\n  var type: VersionsManifest.VersionType\n  \n  struct AssetIndex: Codable {\n    var id: String\n    var sha1: String\n    var size: Int\n    var totalSize: Int\n    var url: URL\n  }\n  \n  struct Downloads: Codable {\n    var client: Download\n    var clientMappings: Download\n    var server: Download\n    var serverMappings: Download\n    \n    enum CodingKeys: String, CodingKey {\n      case client\n      case clientMappings = \"client_mappings\"\n      case server\n      case serverMappings = \"server_mappings\"\n    }\n  }\n  \n  struct Download: Codable {\n    var sha1: String\n    var size: Int\n    var url: URL\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Manifest/VersionsManifest.swift",
    "content": "import Foundation\n\n/// The structure of Mojang's version manifest. I call it the versions manifest because it contains info on all versions not just one.\nstruct VersionsManifest: Codable {\n  var latest: LatestVersions\n  var versions: [Version]\n  \n  struct LatestVersions: Codable {\n    var release: String\n    var snapshot: String\n  }\n  \n  struct Version: Codable {\n    var id: String\n    var type: VersionType\n    var url: URL\n    var time: String\n    var releaseTime: String\n  }\n  \n  enum VersionType: String, Codable {\n    case release\n    case snapshot\n    case oldBeta = \"old_beta\"\n    case oldAlpha = \"old_alpha\"\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/BlockModel.swift",
    "content": "import Foundation\n\n/// A descriptor for a multipart block model.\npublic struct BlockModel: Equatable {\n  /// The parts that make up this block model\n  public var parts: [BlockModelPart]\n  /// The set of all the sides this model has full faces on.\n  public var cullingFaces: DirectionSet\n  /// The set of all sides this model has faces that can be culled.\n  public var cullableFaces: DirectionSet\n  /// The set of all sides this model has faces that can never be culled.\n  public var nonCullableFaces: DirectionSet\n  /// The type of texture that the block has.\n  ///\n  /// If the block has any translucent faces, the type is translucent. If the block has no\n  /// translucent faces but at least one partially transparent face, the type is transparent. If all\n  /// of the faces are fully opaque, the type is opaque.\n  public var textureType: TextureType\n\n  public init(\n    parts: [BlockModelPart],\n    cullingFaces: DirectionSet,\n    cullableFaces: DirectionSet,\n    nonCullableFaces: DirectionSet,\n    textureType: TextureType\n  ) {\n    self.parts = parts\n    self.cullingFaces = cullingFaces\n    self.cullableFaces = cullableFaces\n    self.nonCullableFaces = nonCullableFaces\n    self.textureType = textureType\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/BlockModelElement.swift",
    "content": "import Foundation\nimport FirebladeMath\n\n/// A block model element is a rectangular prism. All block models are built from elements.\npublic struct BlockModelElement: Equatable {\n  /// First of two vertices defining this rectangular prism.\n  public var transformation: Mat4x4f\n  /// Whether to render shadows or not.\n  public var shade: Bool\n  /// The faces present on this element.\n  public var faces: [BlockModelFace]\n\n  public init(transformation: Mat4x4f, shade: Bool, faces: [BlockModelFace]) {\n    self.transformation = transformation\n    self.shade = shade\n    self.faces = faces\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/BlockModelFace.swift",
    "content": "import Foundation\nimport FirebladeMath\n\n/// A descriptor for a block model element's face\npublic struct BlockModelFace: Equatable {\n  /// The direction the face should face before transformations are applied.\n  /// This won't always be the direction the face ends up facing.\n  public var direction: Direction\n  /// The actual direction the face will be facing after transformations are applied.\n  public var actualDirection: Direction\n  /// Face texture uv coordinates. One uv coordinate for each corner of the face.\n  ///\n  /// The order of these UVs should match the vertex ordering of each face defined by\n  /// ``CubeGeometry``. The winding order of course differs per face.\n  public var uvs: UVs\n  /// The index of the texture to use in the texture palette.\n  public var texture: Int\n  /// The direction that a culling block must be in for this face not to be rendered.\n  public var cullface: Direction?\n  /// Whether the face should have a tint color applied or not.\n  public var isTinted: Bool\n\n  /// Basically tuple storage for a face-worth of UVs except that it can conform to ``Equatable``.\n  /// See ``BlockModelFace/uvs`` for information about ordering.\n  public struct UVs: Equatable {\n    public var uv0: Vec2f\n    public var uv1: Vec2f\n    public var uv2: Vec2f\n    public var uv3: Vec2f\n\n    public init(_ uv0: Vec2f, _ uv1: Vec2f, _ uv2: Vec2f, _ uv3: Vec2f) {\n      self.uv0 = uv0\n      self.uv1 = uv1\n      self.uv2 = uv2\n      self.uv3 = uv3\n    }\n\n    public subscript(_ index: Int) -> Vec2f {\n      get {\n        precondition(index < 4 && index >= 0)\n        return withUnsafePointer(to: self) { pointer in\n          return pointer.withMemoryRebound(to: Vec2f.self, capacity: 4) { buffer in\n            return buffer[index]\n          }\n        }\n      }\n    }\n  }\n\n  public init(\n    direction: Direction,\n    actualDirection: Direction,\n    uvs: UVs,\n    texture: Int,\n    cullface: Direction? = nil,\n    isTinted: Bool\n  ) {\n    self.direction = direction\n    self.actualDirection = actualDirection\n    self.uvs = uvs\n    self.texture = texture\n    self.cullface = cullface\n    self.isTinted = isTinted\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/BlockModelPalette.swift",
    "content": "import Foundation\nimport FirebladeMath\n\n// TODO: For plugin API make the block model palette api safe and easy to use (for adding/editing models)\n\n/// Contains block models loaded from a resource pack.\npublic struct BlockModelPalette: Equatable {\n  /// Block models indexed by block state id. Each is an array of block model variants. Any models\n  /// after the max block state id are extra models such as the ones used in the inventory.\n  public var models: [[BlockModel]] = []\n  /// The transforms to use when displaying blocks in different places. Block models specify an\n  /// index into this array.\n  public var displayTransforms: [ModelDisplayTransforms] = []\n  /// Contains true for each block that is full and opaque (e.g. dirt, but not slabs). Indexed by\n  /// block state id.\n  public var fullyOpaqueBlocks: [Bool] = []\n  /// Maps model identifier to index.\n  public var identifierToIndex: [Identifier: Int] = [:]\n\n  // MARK: Init\n\n  /// Create an empty palette.\n  public init() {}\n\n  /// Create a populated palette.\n  /// - Parameters:\n  ///   - models: The models indexed by block state id.\n  ///   - displayTransforms: The display transforms referenced by block models in the palette.\n  ///   - identifierToIndex: A map from identifier to block model index.\n  ///   - fullyOpaqueBlocks: An array indexed by block state id that stores whether each block is\n  ///     fully opaque or not. See ``BlockModelPalette/fullyOpaqueBlocks``. If not supplied\n  ///     (recommended), it is generated from `models`.\n  public init(\n    models: [[BlockModel]],\n    displayTransforms: [ModelDisplayTransforms],\n    identifierToIndex: [Identifier: Int],\n    fullyOpaqueBlocks: [Bool]? = nil\n  ) {\n    self.models = models\n    self.displayTransforms = displayTransforms\n    self.identifierToIndex = identifierToIndex\n\n    if let fullyOpaqueBlocks = fullyOpaqueBlocks {\n      self.fullyOpaqueBlocks = fullyOpaqueBlocks\n    } else {\n      self.fullyOpaqueBlocks.reserveCapacity(models.count)\n      for model in models {\n        var isFull = false\n        for part in model {\n          if part.cullingFaces == DirectionSet.all && part.textureType == .opaque {\n            isFull = true\n            break\n          }\n        }\n        self.fullyOpaqueBlocks.append(isFull)\n      }\n    }\n  }\n\n  // MARK: Access\n\n  /// Returns the model to render for the given model id. The position is used\n  /// to determine which variant to use in the cases where there are multiple. If the model id is\n  /// less than the maximum block state id, then the model returned corresponds to the block state.\n  /// Otherwise it is a model used for a block item's model.\n  ///\n  /// If `position` is nil the first block model is returned. This is used to skip\n  /// random number generation in finding culling faces. We assume that the block\n  /// model variants all have the same general shape.\n  public func model(for id: Int, at position: BlockPosition?) -> BlockModel? {\n    guard id >= 0, id < models.count else {\n      return nil\n    }\n\n    // TODO: correctly select weighted models (not doing that only affects chorus fruit so not a big deal)\n    let variants = models[id]\n    if let position = position {\n      let modelCount = variants.count\n      if modelCount > 1 {\n        var random = Random(Block.getPositionRandom(position))\n        let value = Int32(truncatingIfNeeded: random.nextLong())\n        let absValue = value.signum() * value\n        let index = absValue % Int32(modelCount)\n        return variants[Int(index)]\n      }\n    }\n\n    return variants.first\n  }\n\n  /// Returns whether the given block is fully opaque (cannot be seen through and takes up a full block).\n  ///\n  /// Does not perform any bounds checks (fatally crashes if `id` is out of range.\n  /// - Parameter id: The id of the block to check.\n  /// - Returns: Whether the given block is fully opaque.\n  public func isBlockFullyOpaque(_ id: Int) -> Bool {\n    return fullyOpaqueBlocks[id]\n  }\n\n  // MARK: Loading\n\n  public static func load(\n    from modelDirectory: URL,\n    namespace: String,\n    blockTexturePalette: TexturePalette\n  ) throws -> BlockModelPalette {\n    // Load block models from the pack into an intermediate format\n    let jsonBlockModels = try JSONBlockModel.loadModels(from: modelDirectory, namespace: namespace)\n    let intermediateBlockModelPalette = try IntermediateBlockModelPalette(from: jsonBlockModels)\n\n    // Convert intermediate block models to final format\n    var blockModels = [[BlockModel]](repeating: [], count: RegistryStore.shared.blockRegistry.renderDescriptors.count)\n    for (blockId, variants) in RegistryStore.shared.blockRegistry.renderDescriptors.enumerated() {\n      let blockModelVariants: [BlockModel] = try variants.map { variant in\n        do {\n          let block = RegistryStore.shared.blockRegistry.block(withId: blockId) ?? Block.missing\n          return try blockModel(\n            for: variant,\n            from: intermediateBlockModelPalette,\n            with: blockTexturePalette,\n            isOpaque: block.lightMaterial.isOpaque || block.className == \"LeavesBlock\"\n          )\n        } catch {\n          log.error(\"Failed to create block model for state \\(blockId): \\(error)\")\n          throw error\n        }\n      }\n\n      blockModels[blockId] = blockModelVariants\n    }\n\n    var identifierToIndex: [Identifier: Int] = [:]\n    for (identifier, _) in intermediateBlockModelPalette.identifierToIndex {\n      do {\n        let model = try blockModel(\n          for: [BlockModelRenderDescriptor(\n            model: identifier,\n            xRotationDegrees: 0,\n            yRotationDegrees: 90,\n            uvLock: false\n          )],\n          from: intermediateBlockModelPalette,\n          with: blockTexturePalette,\n          isOpaque: false\n        )\n        identifierToIndex[identifier] = blockModels.count\n        blockModels.append([model])\n      } catch {\n        continue\n      }\n    }\n\n    return BlockModelPalette(\n      models: blockModels,\n      displayTransforms: intermediateBlockModelPalette.displayTransforms,\n      identifierToIndex: identifierToIndex\n    )\n  }\n\n  /// Creates the block model for the given pixlyzer block model descriptor.\n  private static func blockModel(\n    for partDescriptors: [BlockModelRenderDescriptor],\n    from intermediateBlockModelPalette: IntermediateBlockModelPalette,\n    with blockTexturePalette: TexturePalette,\n    isOpaque: Bool\n  ) throws -> BlockModel {\n    var cullingFaces: DirectionSet = []\n    var cullableFaces: DirectionSet = []\n    var nonCullableFaces: DirectionSet = []\n    var textureType = TextureType.opaque\n\n    let parts: [BlockModelPart] = try partDescriptors.map { renderDescriptor in\n      // Get the block model data in its intermediate 'flattened' format\n      guard let intermediateModel = intermediateBlockModelPalette.blockModel(for: renderDescriptor.model) else {\n        throw BlockModelPaletteError.invalidIdentifier\n      }\n\n      let modelMatrix = renderDescriptor.transformationMatrix\n\n      // Convert the elements to the correct format and identify culling faces\n      var rotatedCullingFaces: Set<Direction> = []\n      let elements: [BlockModelElement] = try intermediateModel.elements.map { intermediateElement in\n        // Identify any faces of the elements that can fill a whole side of a block\n        if isOpaque { // TODO: don't hardcode leaves' rendering behaviour\n          rotatedCullingFaces.formUnion(intermediateElement.getCullingFaces())\n        }\n\n        let element = try blockModelElement(\n          from: intermediateElement,\n          with: blockTexturePalette,\n          modelMatrix: modelMatrix,\n          renderDescriptor: renderDescriptor\n        )\n\n        for face in element.faces {\n          let texture = blockTexturePalette.textures[face.texture]\n          if textureType == .opaque && texture.type != .opaque {\n            textureType = texture.type\n          } else if textureType == .transparent && texture.type == .translucent {\n            textureType = .translucent\n          }\n\n          if let cullFace = face.cullface {\n            cullableFaces.insert(cullFace)\n          } else {\n            nonCullableFaces.insert(face.actualDirection)\n          }\n        }\n\n        return element\n      }\n\n      // Rotate the culling face directions to correctly match the block\n      cullingFaces.formUnion(DirectionSet(rotatedCullingFaces.map { direction in\n        rotate(direction, byRotationFrom: renderDescriptor)\n      }))\n\n      return BlockModelPart(\n        ambientOcclusion: intermediateModel.ambientOcclusion,\n        displayTransformsIndex: intermediateModel.displayTransformsIndex,\n        elements: elements\n      )\n    }\n\n    return BlockModel(\n      parts: parts,\n      cullingFaces: cullingFaces,\n      cullableFaces: cullableFaces,\n      nonCullableFaces: nonCullableFaces,\n      textureType: textureType\n    )\n  }\n\n  /// Converts a flattened block model element to a block model element format ready for rendering.\n  private static func blockModelElement(\n    from flatElement: IntermediateBlockModelElement,\n    with blockTexturePalette: TexturePalette,\n    modelMatrix: Mat4x4f,\n    renderDescriptor: BlockModelRenderDescriptor\n  ) throws -> BlockModelElement {\n    // Convert the faces to the correct format\n    let faces: [BlockModelFace] = try flatElement.faces.map { flatFace in\n      // Get the index of the face's texture\n      guard let textureIdentifier = try? Identifier(flatFace.texture) else {\n        throw BlockModelPaletteError.invalidTexture(flatFace.texture)\n      }\n\n      let debugBlock = blockTexturePalette.textureIndex(for: Identifier(name: \"block/debug\"))\n      guard let textureIndex = blockTexturePalette.textureIndex(for: textureIdentifier) ?? debugBlock else {\n        throw BlockModelPaletteError.invalidTextureIdentifier(textureIdentifier)\n      }\n\n      // Update the cullface with the block rotation (ignoring element rotation)\n      var cullface: Direction?\n      if let flatCullface = flatFace.cullface {\n        cullface = rotate(flatCullface, byRotationFrom: renderDescriptor)\n      }\n\n      let uvs = try uvsForFace(\n        flatFace,\n        on: flatElement,\n        from: renderDescriptor\n      )\n\n      // The actual direction the face will be facing after rotations are applied.\n      let actualDirection = rotate(flatFace.direction, byRotationFrom: renderDescriptor)\n\n      return BlockModelFace(\n        direction: flatFace.direction,\n        actualDirection: actualDirection,\n        uvs: uvs,\n        texture: textureIndex,\n        cullface: cullface,\n        isTinted: flatFace.isTinted\n      )\n    }\n\n    return BlockModelElement(\n      transformation: flatElement.transformationMatrix * modelMatrix,\n      shade: flatElement.shouldShade,\n      faces: faces\n    )\n  }\n\n  /// Returns the given direction with the rotations in a model descriptor applied to it.\n  private static func rotate(\n    _ direction: Direction,\n    byRotationFrom renderDescriptor: BlockModelRenderDescriptor\n  ) -> Direction {\n    var newDirection = direction\n    if renderDescriptor.xRotationDegrees != 0 {\n      newDirection = newDirection.rotated(renderDescriptor.xRotationDegrees / 90, clockwiseFacing: Axis.x.negativeDirection)\n    }\n    if renderDescriptor.yRotationDegrees != 0 {\n      newDirection = newDirection.rotated(renderDescriptor.yRotationDegrees / 90, clockwiseFacing: Axis.y.negativeDirection)\n    }\n    return newDirection\n  }\n\n  /// Calculates texture uvs for a face on a specific element and model.\n  ///\n  /// - Returns: One texture coordinate for each face vertex (4 total) starting at the top left\n  ///            and going clockwise (I think).\n  private static func uvsForFace(\n    _ face: IntermediateBlockModelFace,\n    on element: IntermediateBlockModelElement,\n    from renderDescriptor: BlockModelRenderDescriptor\n  ) throws -> BlockModelFace.UVs {\n    let direction = face.direction\n    let minimumPoint = element.from\n    let maximumPoint = element.to\n\n    // If the block model defines uvs we use those, otherwise we generate our own from the geometry\n    var uvs: [Float]\n    if let uvArray = face.uv {\n      guard uvArray.count == 4 else {\n        throw BlockModelPaletteError.invalidUVs\n      }\n      uvs = uvArray.map { Float($0) / 16 }\n    } else {\n      // Here's a big ugly switch statement I made just for you, you're welcome.\n      // It just finds the xy coords of the top left and bottom right of the texture to use.\n      switch direction {\n        case .west:\n          uvs = [\n            minimumPoint.z,\n            1 - maximumPoint.y,\n            maximumPoint.z,\n            1 - minimumPoint.y\n          ]\n        case .east:\n          uvs = [\n            1 - maximumPoint.z,\n            1 - maximumPoint.y,\n            1 - minimumPoint.z,\n            1 - minimumPoint.y\n          ]\n        case .down:\n          uvs = [\n            minimumPoint.x,\n            1 - maximumPoint.z,\n            maximumPoint.x,\n            1 - minimumPoint.z\n          ]\n        case .up:\n          uvs = [\n            minimumPoint.x,\n            minimumPoint.z,\n            maximumPoint.x,\n            maximumPoint.z\n          ]\n        case .south:\n          uvs = [\n            minimumPoint.x,\n            1 - maximumPoint.y,\n            maximumPoint.x,\n            1 - minimumPoint.y\n          ]\n        case .north:\n          uvs = [\n            1 - maximumPoint.x,\n            1 - maximumPoint.y,\n            1 - minimumPoint.x,\n            1 - minimumPoint.y\n          ]\n      }\n    }\n\n    // The uv coordinates for each corner of the face starting at top left going clockwise\n    var coordinates = [\n      Vec2f(uvs[2], uvs[1]),\n      Vec2f(uvs[2], uvs[3]),\n      Vec2f(uvs[0], uvs[3]),\n      Vec2f(uvs[0], uvs[1])\n    ]\n\n    // Rotate the array of coordinates (samples the same part of the texture just changes the rotation of the sampled region on the face\n    let rotation = face.textureRotation\n    coordinates = rotate(coordinates, by: rotation / 90)\n\n    // UV lock makes sure textures don't rotate with the model (like stairs where the planks always face east-west\n    // We rotate counter-clockwise this time\n    if renderDescriptor.uvLock {\n      var uvLockRotationDegrees = 0\n      switch direction.axis {\n        case .x:\n          uvLockRotationDegrees = -renderDescriptor.xRotationDegrees\n        case .y:\n          uvLockRotationDegrees = -renderDescriptor.yRotationDegrees\n        case .z:\n          // The model descriptor can't rotate on the z axis but we should uvlock with the x rotation\n          uvLockRotationDegrees = -renderDescriptor.xRotationDegrees\n      }\n      coordinates = rotateTextureCoordinates(coordinates, by: uvLockRotationDegrees)\n    }\n\n    return BlockModelFace.UVs(\n      coordinates[0],\n      coordinates[1],\n      coordinates[2],\n      coordinates[3]\n    )\n  }\n\n  // TODO: make this an extension of arrays or something\n  /// Rotates the given array. Positive k is right rotation and negative k is left rotation.\n  private static func rotate<T>(_ array: [T], by k: Int) -> [T] {\n    var initialDigits: Int = 0\n    k >= 0 ? (initialDigits = array.count - (k % array.count)) : (initialDigits = (abs(k) % array.count))\n    let elementToPutAtEnd = Array(array[0..<initialDigits])\n    let elementsToPutAtBeginning = Array(array[initialDigits..<array.count])\n    return elementsToPutAtBeginning + elementToPutAtEnd\n  }\n\n  /// Rotates each of the texture coordinates by the specified amount around the center of the texture (clockwise).\n  /// The angle should be a positive multiple of 90 degrees. Used for UV locking (works different to texture rotation).\n  private static func rotateTextureCoordinates(\n    _ coordinates: [Vec2f],\n    by degrees: Int\n  ) -> [Vec2f] {\n    // Check if any rotation is required\n    let angle = MathUtil.mod(degrees, 360)\n    if angle == 0 {\n      return coordinates\n    }\n\n    let center = Vec2f(0.5, 0.5)\n    // The rotation rounded to nearest 90 degrees\n    let rotation = angle - angle % 90\n    let rotatedCoordinates: [Vec2f] = coordinates.map { point in\n      let centerRelativePoint = point - center\n      let rotatedPoint: Vec2f\n      switch rotation {\n        case 90:\n          rotatedPoint = Vec2f(centerRelativePoint.y, -centerRelativePoint.x)\n        case 180:\n          rotatedPoint = -centerRelativePoint\n        case 270:\n          rotatedPoint = Vec2f(-centerRelativePoint.y, centerRelativePoint.x)\n        default:\n          rotatedPoint = centerRelativePoint\n      }\n      return rotatedPoint + center\n    }\n\n    return rotatedCoordinates\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/BlockModelPaletteError.swift",
    "content": "import Foundation\n\nenum BlockModelPaletteError: LocalizedError {\n  /// Block model references non-existent parent.\n  case noSuchParent(Identifier)\n  /// Failed to flatten the block model with this identifier.\n  case failedToFlatten(Identifier)\n  /// No model exists with the given identifier.\n  case invalidIdentifier\n  /// The texture specified does not exist.\n  case invalidTextureIdentifier(Identifier)\n  /// The face UVs in a block model file did not have a length of 4.\n  case invalidUVs\n  /// A Mojang block model json file had an invalid string as a face direction.\n  case invalidDirectionString(String)\n  /// A texture had an invalid identifier string (likely a texture variable).\n  case invalidTexture(String)\n\n  var errorDescription: String? {\n    switch self {\n      case .noSuchParent(let identifier):\n        return \"Block model references non-existent parent with identifier: `\\(identifier.description)`.\"\n      case .failedToFlatten(let identifier):\n        return \"Failed to flatten the block model with identifier: `\\(identifier.description)`\"\n      case .invalidIdentifier:\n        return \"No model exists with the given identifier.\"\n      case .invalidTextureIdentifier(let identifier):\n        return \"The texture with identifier: `\\(identifier.description)` does not exist.\"\n      case .invalidUVs:\n        return \"The face UVs in a block model file did not have a length of 4.\"\n      case .invalidDirectionString(let string):\n        return \"\"\"\n        A Mojang block model json file had an invalid string as a face direction.\n        String: \\(string)\n        \"\"\"\n      case .invalidTexture(let string):\n        return \"\"\"\n        A texture had an invalid identifier string (likely a texture variable):\n        Identifier string: \\(string)\n        \"\"\"\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/BlockModelPart.swift",
    "content": "import Foundation\n\n/// This is what Mojang calls a block model, I use the word block model to refer the models for each block state which can be made of multiple parts.\npublic struct BlockModelPart: Equatable {\n  /// Whether to use ambient occlusion or not.\n  public var ambientOcclusion: Bool\n  /// Index of the transforms to use when displaying this block.\n  public var displayTransformsIndex: Int?\n  /// The elements that make up this block model.\n  public var elements: [BlockModelElement]\n\n  public init(\n    ambientOcclusion: Bool,\n    displayTransformsIndex: Int? = nil,\n    elements: [BlockModelElement]\n  ) {\n    self.ambientOcclusion = ambientOcclusion\n    self.displayTransformsIndex = displayTransformsIndex\n    self.elements = elements\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/BlockModelRenderDescriptor.swift",
    "content": "import Foundation\nimport FirebladeMath\n\n/// Stores the transforms and properties to apply to a model before rendering it. For changes to the\n/// block registry's render descriptors to take effect you must force the block model palette to be\n/// regenerated, or regenerate it yourself.\n///\n/// A neater format for ``PixlyzerBlockModelDescriptor``.\npublic struct BlockModelRenderDescriptor: Codable {\n  public var model: Identifier\n  public var xRotationDegrees: Int\n  public var yRotationDegrees: Int\n  public var uvLock: Bool\n\n  public init(model: Identifier, xRotationDegrees: Int, yRotationDegrees: Int, uvLock: Bool) {\n    self.model = model\n    self.xRotationDegrees = xRotationDegrees\n    self.yRotationDegrees = yRotationDegrees\n    self.uvLock = uvLock\n  }\n\n  /// Transformation matrix that rotates around the center of the block (`[0.5, 0.5, 0.5]`).\n  var transformationMatrix: Mat4x4f {\n    // Apply the rotation, rotating around the center of the block\n    let origin = Vec3f(repeating: 0.5)\n    let matrix = MatrixUtil.translationMatrix(-origin)\n      * rotationMatrix\n      * MatrixUtil.translationMatrix(origin)\n\n    return matrix\n  }\n\n  /// Only the rotation component of the transformation.\n  var rotationMatrix: Mat4x4f {\n    // Create a vector for the rotation\n    let rotationDegrees = Vec3f(\n      Float(xRotationDegrees),\n      Float(yRotationDegrees),\n      0\n    )\n\n    let rotation = MathUtil.radians(from: rotationDegrees)\n    return MatrixUtil.rotationMatrix(rotation)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/Intermediate/IntermediateBlockModel.swift",
    "content": "import Foundation\n\n/// Flattened mojang block model format.\nstruct IntermediateBlockModel {\n  /// Whether to use ambient occlusion or not.\n  var ambientOcclusion: Bool\n  /// Index of the transforms to use when displaying this block.\n  var displayTransformsIndex: Int?\n  /// The elements that make up this block model.\n  var elements: [IntermediateBlockModelElement]\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/Intermediate/IntermediateBlockModelElement.swift",
    "content": "import Foundation\nimport FirebladeMath\n\n/// Flattened mojang block model element format.\nstruct IntermediateBlockModelElement {\n  /// The minimum vertex of the element. For a dirt block this would be (0, 0, 0).\n  var from: Vec3f\n  /// The maximum vertex of the element. For a dirt block this would be (1, 1, 1).\n  var to: Vec3f\n  /// The rotation matrix for this block model.\n  var rotation: IntermediateBlockModelElementRotation?\n  /// Whether to render shadows or not.\n  var shouldShade: Bool\n  /// The faces present on this element.\n  var faces: [IntermediateBlockModelFace]\n\n  /// Creates a neater and flattened version of a Mojang formatted block model.\n  init(from mojangElement: JSONBlockModelElement, with textureVariables: [String: String]) throws {\n    let blockSize: Float = 16\n\n    // Convert the arrays of doubles to vectors and scale so that they are the same scale as world space\n    let from = try MathUtil.vectorFloat3(from: mojangElement.from) / blockSize\n    let to = try MathUtil.vectorFloat3(from: mojangElement.to) / blockSize\n\n    // I don't trust mojang so this makes sure from and to are actually the minimum and maximum vertices of the element\n    self.from = MathUtil.min(from, to)\n    self.to = MathUtil.max(from, to)\n\n    if let mojangRotation = mojangElement.rotation {\n      rotation = try IntermediateBlockModelElementRotation(from: mojangRotation)\n    }\n\n    shouldShade = mojangElement.shade ?? true\n\n    faces = try mojangElement.faces.map { (directionString, mojangFace) in\n      guard let mojangDirection = JSONBlockModelFaceName(rawValue: directionString) else {\n        throw BlockModelPaletteError.invalidDirectionString(directionString)\n      }\n      return IntermediateBlockModelFace(\n        from: mojangFace,\n        facing: mojangDirection,\n        with: textureVariables)\n    }\n  }\n\n  /// Updates the element's faces' textures with a dictionary of texture variables.\n  /// This is the main part of the flattening process (to remove texture variable lookups later on.\n  mutating func updateTextures(with textureVariables: [String: String]) {\n    for (index, var face) in faces.enumerated() {\n      face.updateTexture(with: textureVariables)\n      faces[index] = face\n    }\n  }\n\n  /// The transformation matrix to apply to a 1x1x1 cube to get this element.\n  var transformationMatrix: Mat4x4f {\n    let scale = to - from\n    var matrix = MatrixUtil.scalingMatrix(scale)\n    matrix *= MatrixUtil.translationMatrix(from)\n    if let rotation = rotation {\n      matrix *= rotation.matrix\n    }\n\n    return matrix\n  }\n\n  /// Returns which directions this block has full faces in.\n  func getCullingFaces() -> Set<Direction> {\n    // There cannot be a full face if the element has rotation not a multiple of 90 degrees.\n    // The only possible multiple of 90 degrees is 0 in this case\n    if (rotation?.radians ?? 0) != 0 {\n      return []\n    }\n\n    // Since rotation is 0 we can just ignore it now.\n    var cullFaces: Set<Direction> = []\n\n    // Checking north, down and west faces (negative directions)\n    if from == Vec3f(repeating: 0) {\n      if to.x == 1 && to.y == 1 {\n        cullFaces.insert(.north)\n      }\n      if to.x == 1 && to.z == 1 {\n        cullFaces.insert(.down)\n      }\n      if to.y == 1 && to.z == 1 {\n        cullFaces.insert(.west)\n      }\n    }\n\n    // Checking south, up and east faces (positive directions)\n    if to == Vec3f(repeating: 1) {\n      if from.x == 0 && from.y == 0 {\n        cullFaces.insert(.south)\n      }\n      if from.x == 0 && from.z == 0 {\n        cullFaces.insert(.up)\n      }\n      if from.y == 0 && from.z == 0 {\n        cullFaces.insert(.east)\n      }\n    }\n\n    return cullFaces\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/Intermediate/IntermediateBlockModelElementRotation.swift",
    "content": "import Foundation\nimport FirebladeMath\n\n/// A neatened format for `JSONBlockModelElementRotation`.\nstruct IntermediateBlockModelElementRotation {\n  /// The point to rotate around.\n  var origin: Vec3f\n  /// The axis of the rotation.\n  var axis: Axis\n  /// The angle of the rotation in radians.\n  var radians: Float\n  /// Whether to scale block to fit original space after rotation or not.\n  var rescale: Bool\n\n  /// Converts a mojang formatted rotation to this nicer format.\n  init(from mojangRotation: JSONBlockModelElementRotation) throws {\n    origin = try MathUtil.vectorFloat3(from: mojangRotation.origin) / 16\n    axis = mojangRotation.axis.axis\n    rescale = mojangRotation.rescale ?? false\n\n    // Clamp angle to between -45 and 45 then round to nearest 22.5\n    var degrees = min(max(-45, Float(mojangRotation.angle)), 45)\n    degrees -= (degrees.truncatingRemainder(dividingBy: 22.5))\n    radians = MathUtil.radians(from: Float(degrees))\n\n    // For some reason in our renderer we need x and y rotation to be reversed but only\n    // for rotations from mojang block model files everything else is fine?\n    switch axis {\n      case .x, .y:\n        radians = -radians\n      case .z:\n        break\n    }\n  }\n\n  /// Returns a transformation matrix representing this rotation.\n  var matrix: Mat4x4f {\n    var matrix = MatrixUtil.translationMatrix(-origin)\n\n    matrix *= MatrixUtil.rotationMatrix(radians, around: axis)\n    if rescale {\n      let scale = 1 / Foundation.cos(radians)\n      matrix *= MatrixUtil.scalingMatrix(\n        axis == .x ? 1 : scale,\n        axis == .y ? 1 : scale,\n        axis == .z ? 1 : scale\n      )\n    }\n\n    matrix *= MatrixUtil.translationMatrix(origin)\n    return matrix\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/Intermediate/IntermediateBlockModelFace.swift",
    "content": "import Foundation\n\n/// Flattened mojang block model face format.\nstruct IntermediateBlockModelFace {\n  /// The direction the face should face before transformations are applied.\n  /// This won't always be the direction the face ends up facing.\n  var direction: Direction\n  /// Face texture uv coordinates (if they're not present we have to make them up).\n  var uv: [Double]?\n  /// The identifier of the texture to use for this face.\n  var texture: String\n  /// The direction that a culling block must be in for this face not to be rendered.\n  var cullface: Direction?\n  /// The amount of rotation for the texture (multiples of 90 degrees).\n  var textureRotation: Int\n  /// Whether a tint color should be applied to the face or not.\n  var isTinted: Bool\n\n  /// Returns a neater and flattened version of a Mojang formatted block model face.\n  init(\n    from jsonFace: JSONBlockModelFace,\n    facing jsonDirection: JSONBlockModelFaceName,\n    with textureVariables: [String: String]\n  ) {\n    uv = jsonFace.uv\n    direction = jsonDirection.direction\n    cullface = jsonFace.cullface?.direction\n    isTinted = (jsonFace.tintIndex ?? -1) != -1\n\n    // Round textureRotation to the nearest 90\n    textureRotation = jsonFace.rotation ?? 0\n    textureRotation -= textureRotation % 90\n\n    texture = jsonFace.texture\n\n    updateTexture(with: textureVariables)\n  }\n\n  /// Substitutes the texture of this face with a replacement from some texture variables if there is a valid replacement.\n  mutating func updateTexture(with textureVariables: [String: String]) {\n    // Substitute in a texture variable if a relevant one is present\n    if texture.starts(with: \"#\") {\n      // A texture variable always starts with a hashtag\n      let textureVariable = texture\n      // Get rid of the hashtag\n      let textureVariableName = String(textureVariable.dropFirst(1))\n      // If there is a substitution for this variable use it instead, otherwise keep the variable\n      texture = textureVariables[textureVariableName] ?? textureVariable\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/Intermediate/IntermediateBlockModelPalette.swift",
    "content": "import Foundation\n\n/// This format just simplifies processing of the mojang block models into DeltaClient block models and serves as an temporary intermediate format.\n/// It flattens the complex dependency tree of block models so that all relevant information is in each block model. This prevents repetitive parent lookups.\nstruct IntermediateBlockModelPalette {\n  /// Map from identifier to index in `blockModels`.\n  var identifierToIndex: [Identifier: Int] = [:]\n  /// Array of flattened mojang block models indexed by `identifierToIndex`.\n  var blockModels: [IntermediateBlockModel] = []\n  /// Array of transforms to use when displaying blocks. Indexed by `displayTransformsIndex` on `FlatJSONBlockModel`s.\n  var displayTransforms: [ModelDisplayTransforms] = []\n\n  /// Creates a new block model palette by flattening the given mojang block models.\n  init(from jsonBlockModelPalette: [Identifier: JSONBlockModel]) throws {\n    for (identifier, blockModel) in jsonBlockModelPalette {\n      // If the block model hasn't already been flattened, flatten it.\n      // It will already have been flattened if it is the parent of an already flattened block model.\n      if !identifierToIndex.keys.contains(identifier) {\n        do {\n          let flattened = try flatten(blockModel, with: jsonBlockModelPalette)\n          append(flattened, as: identifier)\n        } catch {\n          log.error(\"Failed to flatten mojang block model: \\(error)\")\n          throw BlockModelPaletteError.failedToFlatten(identifier)\n        }\n      }\n    }\n  }\n\n  /// Returns the flattened block model from the palette for the given identifier if present.\n  func blockModel(for identifier: Identifier) -> IntermediateBlockModel? {\n    if let index = identifierToIndex[identifier] {\n      return blockModels[index]\n    }\n    return nil\n  }\n\n  /// Flattens a mojang formatted block model. Also flattens any parents of the model and adds them to the palette.\n  private mutating func flatten(\n    _ jsonBlockModel: JSONBlockModel,\n    with jsonBlockPalette: [Identifier: JSONBlockModel]\n  ) throws -> IntermediateBlockModel {\n    // Flatten the parent first if this model has a parent.\n    var parent: IntermediateBlockModel?\n    if let parentIdentifier = jsonBlockModel.parent {\n      guard let parentJSONBlockModel = jsonBlockPalette[parentIdentifier] else {\n        throw BlockModelPaletteError.noSuchParent(parentIdentifier)\n      }\n\n      // Use a cached version of the parent if it has already been processed, otherwise flatten it normally\n      if let flattenedParent = blockModel(for: parentIdentifier) {\n        parent = flattenedParent\n      } else {\n        let flattened = try flatten(parentJSONBlockModel, with: jsonBlockPalette)\n        append(flattened, as: parentIdentifier)\n        parent = flattened\n      }\n    }\n\n    // Flatten the block model elements if present, otherwise use the parent's elements or [] if neither are present\n    let textureVariables = jsonBlockModel.textures ?? [:]\n    var flattenedElements: [IntermediateBlockModelElement] = []\n    if let jsonElements = jsonBlockModel.elements {\n      flattenedElements = try jsonElements.map { jsonElement in\n        try IntermediateBlockModelElement(from: jsonElement, with: textureVariables)\n      }\n    } else {\n      // If this model doesn't have any elements use its parents and update them with this model's texture variables\n      flattenedElements = parent?.elements ?? []\n      if !textureVariables.isEmpty {\n        for (index, var element) in flattenedElements.enumerated() {\n          element.updateTextures(with: textureVariables)\n          flattenedElements[index] = element\n        }\n      }\n    }\n\n    // Flatten the display transforms\n    var displayTransformsIndex: Int?\n    if let jsonDisplayTransforms = jsonBlockModel.display {\n      let flattenedDisplayTransforms = try ModelDisplayTransforms(from: jsonDisplayTransforms)\n      displayTransformsIndex = displayTransforms.count\n      displayTransforms.append(flattenedDisplayTransforms)\n    }\n\n    let ambientOcclusion = jsonBlockModel.ambientOcclusion ?? (parent?.ambientOcclusion ?? true)\n    return IntermediateBlockModel(\n      ambientOcclusion: ambientOcclusion,\n      displayTransformsIndex: displayTransformsIndex,\n      elements: flattenedElements)\n  }\n\n  /// Appends the given flattened block model to the palette, and it's it to the palette lookup under the given identifier.\n  private mutating func append(_ flattened: IntermediateBlockModel, as identifier: Identifier) {\n    let index = blockModels.count\n    identifierToIndex[identifier] = index\n    blockModels.append(flattened)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/JSON/JSONBlockModel.swift",
    "content": "import Foundation\n\n/// The structure of a block model as read from a resource pack.\nstruct JSONBlockModel: Codable {\n  /// The identifier of the parent of this block model.\n  var parent: Identifier?\n  /// Whether to use ambient occlusion or not.\n  var ambientOcclusion: Bool?\n  /// Transformations to use when displaying this block in certain situations.\n  var display: JSONModelDisplayTransforms?\n  /// Texture variables used in this block model.\n  var textures: [String: String]?\n  /// The elements that make up this block model.\n  var elements: [JSONBlockModelElement]?\n\n  enum CodingKeys: String, CodingKey {\n    case parent\n    case ambientOcclusion = \"ambientocclusion\"\n    case display\n    case textures\n    case elements\n  }\n}\n\nextension JSONBlockModel {\n  /// Loads the json formatted block models from the given directory.\n  static func loadModels(\n    from directory: URL,\n    namespace: String\n  ) throws -> [Identifier: JSONBlockModel] {\n    // swiftlint:disable force_unwrapping\n    // Constants used when composing JSON object from models\n    let doubleQuote = \"\\\"\".data(using: .utf8)!\n    let doubleQuoteColon = \"\\\":\".data(using: .utf8)!\n    let comma = \",\".data(using: .utf8)!\n\n    // JSON object starts with opening brace\n    var json = \"{\".data(using: .utf8)!\n    // swiftlint:enable force_unwrapping\n\n    // Reserves the approximate size of the block model folder of a vanilla resource pack (with a bit of head room).\n    // Seems to cut down times by about 5 to 10%.\n    json.reserveCapacity(540000)\n\n    // Get list of all files in block model directory\n    let files = try FileManager.default.contentsOfDirectory(\n      at: directory,\n      includingPropertiesForKeys: nil,\n      options: .skipsSubdirectoryDescendants\n    )\n\n    // All file reading operations are performed at once which is best for performance apparently\n    // The models are combined into one big JSON object which should also minimise losses from\n    // switching between Swift and ZippyJSON's cpp. It seems to cut down load time of the json\n    // files by about 25% (from 780ms to 570ms).\n    var isFirst = true\n    for file in files where file.pathExtension == \"json\" {\n      // Add comma separator between model entries\n      if isFirst {\n        isFirst = false\n      } else {\n        json.append(comma)\n      }\n\n      let blockName = file.deletingPathExtension().lastPathComponent\n\n      // Append `\"blockName\":`\n      json.append(doubleQuote)\n      guard let blockNameData = blockName.data(using: .utf8) else {\n        throw PixlyzerError.invalidUTF8BlockName(blockName)\n      }\n      json.append(blockNameData)\n      json.append(doubleQuoteColon)\n\n      let data = try Data(contentsOf: file)\n\n      // Append model JSON object\n      json.append(data)\n    }\n\n    // Finish JSON object with a closing brace\n    // swiftlint:disable force_unwrapping\n    json.append(\"}\".data(using: .utf8)!)\n    // swiftlint:enable force_unwrapping\n\n    // Load JSON\n    let models: [String: JSONBlockModel] = try CustomJSONDecoder()\n      .decode([String: JSONBlockModel].self, from: json)\n\n    // Convert from [String: JSONBlockModel] to [Identifier: JSONBlockModel]\n    var identifiedModels: [Identifier: JSONBlockModel] = [:]\n    for (blockName, model) in models {\n      let identifier = Identifier(namespace: namespace, name: \"block/\\(blockName)\")\n      identifiedModels[identifier] = model\n    }\n\n    return identifiedModels\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/JSON/JSONBlockModelAxis.swift",
    "content": "import Foundation\n\n/// An enum used when decoding Mojang formatted block models.\nenum JSONBlockModelAxis: String, Codable {\n  case x\n  case y\n  case z\n\n  /// This axis in the normal format.\n  var axis: Axis {\n    switch self {\n      case .x:\n        return .x\n      case .y:\n        return .y\n      case .z:\n        return .z\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/JSON/JSONBlockModelElement.swift",
    "content": "import Foundation\n\n/// A block model element as read from a Mojang formatted block model file.\nstruct JSONBlockModelElement: Codable {\n  /// The starting point of the element.\n  var from: [Double]\n  /// The finishing point of the element.\n  var to: [Double]\n  /// The rotation of the element.\n  var rotation: JSONBlockModelElementRotation?\n  /// Whether to render shadows or not, if nil assume true.\n  var shade: Bool?\n  /// The present faces of the element. The keys are face direction and should be one of;\n  /// `down`, `up`, `north`, `south`, `west` or `east`\n  var faces: [String: JSONBlockModelFace]\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/JSON/JSONBlockModelElementRotation.swift",
    "content": "import Foundation\n\n/// The rotation of a block model element as read from a Mojang formatted block model file.\nstruct JSONBlockModelElementRotation: Codable {\n  /// The point to rotate around.\n  var origin: [Double]\n  /// The axis of the rotation.\n  var axis: JSONBlockModelAxis\n  /// The angle of the rotaiton.\n  var angle: Double\n  /// Whether to scale block to fit original space after rotation or not, if nil assume false.\n  var rescale: Bool?\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/JSON/JSONBlockModelFace.swift",
    "content": "import Foundation\n\n/// A block model element's face as read from a Mojang formatted block model file.\nstruct JSONBlockModelFace: Codable {\n  /// The texture uv coordinates of the face. Should be in the form `[u1, v1, u2, v2]`.\n  /// If nil the uv coordinates are calculated from the face's position in the block.\n  var uv: [Double]?\n  /// The identifier or texture variable representing the texture this face uses.\n  var texture: String\n  /// The direction a culling block must be in for this face not to be rendered.\n  var cullface: JSONBlockModelFaceName?\n  /// The amount to rotate the face's texture. Should be a multiple of 90 degrees.\n  var rotation: Int?\n  /// The index of the tint to use. I'm not exactly sure how this is used yet.\n  var tintIndex: Int?\n\n  enum CodingKeys: String, CodingKey {\n    case uv\n    case texture\n    case cullface\n    case rotation\n    case tintIndex = \"tintindex\"\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Block/JSON/JSONBlockModelFaceName.swift",
    "content": "import Foundation\n\n/// An enum used when decoding Mojang formatted block models from JSON.\nenum JSONBlockModelFaceName: String, Codable {\n  case down\n  case up\n  case north\n  case south\n  case west\n  case east\n\n  init?(rawValue: String) {\n    // Why Mojang, did you have to make both 'down' and 'bottom' valid and on top of that,\n    // only use bottom once in all of the vanilla assets?\n    switch rawValue {\n      case \"down\", \"bottom\":\n        self = .down\n      case \"up\":\n        self = .up\n      case \"north\":\n        self = .north\n      case \"south\":\n        self = .south\n      case \"west\":\n        self = .west\n      case \"east\":\n        self = .east\n      default:\n        return nil\n    }\n  }\n\n  var direction: Direction {\n    switch self {\n      case .down:\n        return .down\n      case .up:\n        return .up\n      case .north:\n        return .north\n      case .south:\n        return .south\n      case .west:\n        return .west\n      case .east:\n        return .east\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Entity/EntityModelPalette.swift",
    "content": "import Foundation\n\npublic enum EntityModelPaletteError: LocalizedError {\n  case failedToDownloadJSONEntityModelPack(Error)\n  case failedToUnzipJSONEntityModelPack(Error)\n  case failedToDeserializeJSONEntityModel(URL, Error)\n  case failedToCopyJSONEntityModels(sourceDirectory: URL, destinationDirectory: URL, Error)\n\n  public var errorDescription: String? {\n    switch self {\n      case let .failedToDownloadJSONEntityModelPack(error):\n        return \"Failed to download default JSON Entity Model pack (\\(error)).\"\n      case let .failedToUnzipJSONEntityModelPack(error):\n        return \"Failed to unzip default JSON Entity Model pack (\\(error)).\"\n      case let .failedToDeserializeJSONEntityModel(file, error):\n        return \"\"\"\n          Failed to deserialize JSON entity model (\\(error)).\n          File: \\(file)\n          \"\"\"\n      case let .failedToCopyJSONEntityModels(sourceDirectory, destinationDirectory, error):\n        return \"\"\"\n          Failed to copy JSON entity models (\\(error)).\n          Source directory: \\(sourceDirectory)\n          Destination directory: \\(destinationDirectory)\n          \"\"\"\n    }\n  }\n}\n\npublic struct EntityModelPalette {\n  // swiftlint:disable force_unwrapping\n  static let jsonEntityModelPackDownloadURL = URL(\n    string: \"https://www.curseforge.com/api/v1/mods/360910/files/3268537/download\"\n  )!\n\n  public var models: [Identifier: JSONEntityModel] = [:]\n\n  /// Creates an empty entity model palette.\n  public init(models: [Identifier: JSONEntityModel] = [:]) {\n    self.models = models\n  }\n\n  /// Loads the JSON Entity Models contained in a specified directory.\n  public static func load(from directory: URL, namespace: String) throws -> Self {\n    let models = try JSONEntityModel.loadModels(from: directory, namespace: namespace)\n    return EntityModelPalette(models: models)\n  }\n\n  /// Downloads the required JSON Entity Model files to the specified directory. Downloads them\n  /// from the Template CEM project on CurseForge.\n  public static func downloadJSONEntityModels(to directory: URL) throws {\n    let temporaryDirectory = FileManager.default.temporaryDirectory\n    let packZipFile = temporaryDirectory.appendingPathComponent(\"json_entity_models.zip\")\n    do {\n      let data = try RequestUtil.data(contentsOf: Self.jsonEntityModelPackDownloadURL)\n      try data.write(to: packZipFile)\n    } catch {\n      throw EntityModelPaletteError.failedToDownloadJSONEntityModelPack(error)\n    }\n\n    let packDirectory = temporaryDirectory.appendingPathComponent(\"json_entity_models\")\n    try? FileManager.default.removeItem(at: packDirectory)\n    do {\n      try FileManager.default.unzipItem(at: packZipFile, to: packDirectory, skipCRC32: true)\n    } catch {\n      throw EntityModelPaletteError.failedToUnzipJSONEntityModelPack(error)\n    }\n\n    let entityModelsDirectory = packDirectory.appendingPathComponent(\n      \"assets/minecraft/optifine/cem\"\n    )\n    do {\n      let files = try FileManager.default.contentsOfDirectory(\n        at: entityModelsDirectory,\n        includingPropertiesForKeys: nil,\n        options: [.skipsSubdirectoryDescendants]\n      )\n      for file in files where file.pathExtension == \"jem\" {\n        try FileManager.default.copyItem(\n          at: file,\n          to: directory.appendingPathComponent(file.lastPathComponent)\n        )\n      }\n    } catch {\n      throw EntityModelPaletteError.failedToCopyJSONEntityModels(\n        sourceDirectory: entityModelsDirectory,\n        destinationDirectory: directory,\n        error\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Entity/JSON/JSONEntityModel.swift",
    "content": "import Foundation\n\n/// An entity model represented in the standard JSON Entity Model format (from `.jem` files).\npublic struct JSONEntityModel: Codable {\n  public var textureSize: Vec2f\n  public var models: [Submodel]\n\n  public struct Submodel: Codable {\n    public var part: String?\n    public var id: String?\n    public var invertAxis: String?\n    public var mirrorTexture: String?\n    /// Translation to apply post-rotation (ignored if ``rotate`` is `nil`).\n    public var translate: Vec3f?\n    /// Rotation in degrees.\n    public var rotate: Vec3f?\n    public var boxes: [Box]?\n    public var submodels: [Submodel]?\n    public var animations: [[String: Either<String, Int>]]?\n  }\n\n  public struct Box: Codable {\n    public var coordinates: [Float]\n    public var textureOffset: Vec2i?\n    public var uvNorth: Vec4i?\n    public var uvEast: Vec4i?\n    public var uvSouth: Vec4i?\n    public var uvWest: Vec4i?\n    public var uvUp: Vec4i?\n    public var uvDown: Vec4i?\n    public var sizeAdd: Float?\n  }\n\n  /// Loads all JSON Entity Models from\n  public static func loadModels(\n    from directory: URL,\n    namespace: String\n  ) throws -> [Identifier: JSONEntityModel] {\n    let files = try FileManager.default.contentsOfDirectory(\n      at: directory,\n      includingPropertiesForKeys: nil,\n      options: .skipsSubdirectoryDescendants\n    )\n\n    var models: [Identifier: JSONEntityModel] = [:]\n    for file in files where file.pathExtension == \"jem\" {\n      let identifier = Identifier(\n        namespace: namespace,\n        name: file.deletingPathExtension().lastPathComponent\n      )\n\n      let model: JSONEntityModel\n      do {\n        let data = try Data(contentsOf: file)\n        model = try CustomJSONDecoder().decode(JSONEntityModel.self, from: data)\n      } catch {\n        throw EntityModelPaletteError.failedToDeserializeJSONEntityModel(file, error)\n      }\n      models[identifier] = model\n    }\n\n    return models\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Item/ItemModel.swift",
    "content": "/// A renderable model of an item.\npublic enum ItemModel {\n  case layered(textureIndices: [ItemModelTexture], transforms: ModelDisplayTransforms)\n  case blockModel(id: Int)\n  case entity(Identifier, transforms: ModelDisplayTransforms)\n  case empty\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Item/ItemModelPalette.swift",
    "content": "import Foundation\n\n/// A palette containing item models loaded from a texture pack.\npublic struct ItemModelPalette {\n  /// Item models indexed by item id.\n  public var models: [ItemModel]\n\n  /// Creates an item model palette with the given models (indexed by id).\n  public init(_ models: [ItemModel] = []) {\n    self.models = models\n  }\n\n  /// Gets the item model for a given item id.\n  /// - Parameter id: The item id.\n  /// - Returns: The item's model if the item exists.\n  public func model(for id: Int) -> ItemModel? {\n    guard id >= 0, id < models.count else {\n      return nil\n    }\n\n    return models[id]\n  }\n\n  /// Loads the item models from the item models directory of a resource pack.\n  /// - Parameters:\n  ///   - directory: The directory to load item models from.\n  ///   - itemTexturePalette: The palette containing all of the item textures.\n  ///   - blockTexturePalette: The palette containing all of the block textures.\n  ///   - blockModelPalette: The palette containing all of the block models.\n  ///   - namespace: The namespace these models are within.\n  /// - Throws: An error if any item models are missing or invalid.\n  public static func load(\n    from directory: URL,\n    itemTexturePalette: TexturePalette,\n    blockTexturePalette: TexturePalette,\n    blockModelPalette: BlockModelPalette,\n    namespace: String\n  ) throws -> ItemModelPalette {\n    let files = try FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil)\n      .filter { file in\n        return file.pathExtension == \"json\"\n      }\n\n    var jsonModels: [Identifier: JSONItemModel] = [:]\n    for file in files {\n      let model: JSONItemModel\n      do {\n        let data = try Data(contentsOf: file)\n        model = try JSONDecoder().decode(JSONItemModel.self, from: data)\n      } catch {\n        throw ItemModelPaletteError.failedToLoadJSON(file: file, error)\n      }\n\n      let identifier = Identifier(\n        namespace: namespace,\n        name: \"item/\" + file.deletingPathExtension().lastPathComponent\n      )\n      jsonModels[identifier] = model\n    }\n\n    var models: [ItemModel] = []\n    for item in RegistryStore.shared.itemRegistry.items {\n      let identifier = item.identifier\n      guard let jsonModel = jsonModels[identifier] else {\n        throw ItemModelPaletteError.missingModel(identifier)\n      }\n\n      let model: ItemModel\n      do {\n        model = try self.model(\n          from: jsonModel,\n          identifier: identifier,\n          jsonModels: jsonModels,\n          itemTexturePalette: itemTexturePalette,\n          blockTexturePalette: blockTexturePalette,\n          blockModelPalette: blockModelPalette\n        )\n      } catch {\n        log.warning(\"Failed to load item model \\(identifier): \\(error)\")\n        model = .empty\n      }\n\n      models.append(model)\n    }\n\n    return ItemModelPalette(models)\n  }\n\n  private static func model(\n    from jsonModel: JSONItemModel,\n    identifier: Identifier,\n    jsonModels: [Identifier: JSONItemModel],\n    itemTexturePalette: TexturePalette,\n    blockTexturePalette: TexturePalette,\n    blockModelPalette: BlockModelPalette\n  ) throws -> ItemModel {\n    guard let parent = jsonModel.parent else {\n      return ItemModel.empty\n    }\n\n    let jsonTransforms = jsonModel.display ?? JSONModelDisplayTransforms()\n    let transforms = try ModelDisplayTransforms(from: jsonTransforms)\n\n    if parent.name == \"item/generated\" {\n      // Load generated model (an array of layered textures)\n      guard let textures = jsonModel.textures else {\n        throw ItemModelPaletteError.generatedModelMissingValuesForTextures\n      }\n\n      var texturesIndices: [ItemModelTexture] = []\n      for i in 0... {\n        guard let value = textures[\"layer\\(i)\"] else {\n          break\n        }\n\n        let identifier = try Identifier(value)\n\n        if let textureIndex = itemTexturePalette.textureIndex(for: identifier) {\n          texturesIndices.append(.item(textureIndex))\n        } else if let textureIndex = blockTexturePalette.textureIndex(for: identifier) {\n          texturesIndices.append(.block(textureIndex))\n        } else {\n          throw ItemModelPaletteError.missingTexture(identifier)\n        }\n      }\n\n      return .layered(textureIndices: texturesIndices, transforms: transforms)\n    } else if parent.name == \"builtin/entity\" {\n      return .entity(identifier, transforms: transforms)\n    } else if parent.name.hasPrefix(\"block/\") {\n      guard let modelId = blockModelPalette.identifierToIndex[parent] else {\n        throw ItemModelPaletteError.missingBlock(parent)\n      }\n      return .blockModel(id: modelId)\n    } else {\n      guard let parentJSONModel = jsonModels[parent] else {\n        throw ItemModelPaletteError.missingParent(parent)\n      }\n\n      let jsonModel = parentJSONModel.merge(withChild: jsonModel)\n      return try model(\n        from: jsonModel,\n        identifier: identifier,\n        jsonModels: jsonModels,\n        itemTexturePalette: itemTexturePalette,\n        blockTexturePalette: blockTexturePalette,\n        blockModelPalette: blockModelPalette\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Item/ItemModelPaletteError.swift",
    "content": "import Foundation\n\n/// An error thrown by ``ItemModelPalette``.\npublic enum ItemModelPaletteError: LocalizedError {\n  case failedToLoadJSON(file: URL, Error)\n  case missingTexture(Identifier)\n  case missingBlock(Identifier)\n  case generatedModelMissingValuesForTextures\n  case failedToLoadModel(Identifier, Error)\n  case missingParent(Identifier)\n  case missingModel(Identifier)\n  \n  public var errorDescription: String? {\n    switch self {\n      case .failedToLoadJSON(let file, let error):\n        return \"\"\"\n        Failed to load JSON.\n        File URL: \\(file.absoluteString)\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n      case .missingTexture(let identifier):\n        return \"Missing texture with identifier: `\\(identifier.description)`\"\n      case .missingBlock(let identifier):\n        return \"Missing block with identifier: `\\(identifier.description)`\"\n      case .generatedModelMissingValuesForTextures:\n        return \"Generated model missing values for textures\"\n      case .failedToLoadModel(let identifier, let error):\n        return \"\"\"\n        Failed to load model with identifier: `\\(identifier.description)`.\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n      case .missingParent(let identifier):\n        return \"Missing parent with identifier: `\\(identifier.description)`\"\n      case .missingModel(let identifier):\n        return \"Missing model with identifier: `\\(identifier.description)`\"\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Item/ItemModelTexture.swift",
    "content": "public enum ItemModelTexture {\n  case item(Int)\n  case block(Int)\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Item/JSON/JSONItemModel.swift",
    "content": "import Foundation\n\n/// An item model as found in a resource pack.\nstruct JSONItemModel: Decodable {\n  /// The parent model.\n  var parent: Identifier?\n  /// The display transforms to use when rendering the item in different locations.\n  var display: JSONModelDisplayTransforms?\n  /// The item's textures.\n  var textures: [String: String]?\n  /// The type of shading to apply.\n  var guiLight: JSONItemModelGUILight?\n  /// Replacements for this model used under certain conditions.\n  var overrides: [JSONItemModelOverride]?\n\n  func merge(withChild child: JSONItemModel) -> JSONItemModel {\n    let display = display?.merge(withChild: child.display ?? JSONModelDisplayTransforms()) ?? child.display\n    var textures: [String: String] = textures ?? [:]\n    for (key, value) in child.textures ?? [:] {\n      textures[key] = value\n    }\n    let guiLight = child.guiLight ?? guiLight\n    let overrides = (child.overrides ?? []) + (overrides ?? [])\n\n    return JSONItemModel(\n      parent: parent,\n      display: display,\n      textures: textures,\n      guiLight: guiLight,\n      overrides: overrides\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Item/JSON/JSONItemModelGUILight.swift",
    "content": "import Foundation\n\n/// The type of shading used for an item model when rendered in the gui (e.g. in the inventory).\nenum JSONItemModelGUILight: String, Decodable {\n  /// No shading.\n  case front\n  /// Shaded like a block.\n  case side\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/Item/JSON/JSONItemModelOverride.swift",
    "content": "import Foundation\n\n/// A conditional replacement of an item model with another. Used for items such as compasses and\n/// clocks (which change depending on heading and time respectively).\nstruct JSONItemModelOverride: Decodable {\n  /// Conditions to be met.\n  var predicate: [String: Float]\n  /// Replacement model.\n  var model: Identifier\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/JSON/JSONModelDisplayTransforms.swift",
    "content": "import Foundation\n\n/// Transforms to use when displaying models in certain situations.\nstruct JSONModelDisplayTransforms: Codable {\n  /// Transform to use for displaying in right hand in third person.\n  var thirdPersonRightHand: JSONModelTransform?\n  /// Transform to use for displaying in left hand in third person.\n  var thirdPersonLeftHand: JSONModelTransform?\n  /// Transform to use for displaying in right hand in first person.\n  var firstPersonRightHand: JSONModelTransform?\n  /// Transform to use for displaying in left hand in first person.\n  var firstPersonLeftHand: JSONModelTransform?\n  /// Transform to use for displaying in inventory.\n  var gui: JSONModelTransform?\n  /// Transform to use for displaying on head?\n  var head: JSONModelTransform?\n  /// Transform to use for displaying on the ground.\n  var ground: JSONModelTransform?\n  /// Transform to use for displaying in item frames.\n  var fixed: JSONModelTransform?\n\n  enum CodingKeys: String, CodingKey {\n    case thirdPersonRightHand = \"thirdperson_righthand\"\n    case thirdPersonLeftHand = \"thirdperson_lefthand\"\n    case firstPersonRightHand = \"firstperson_righthand\"\n    case firstPersonLeftHand = \"firstperson_lefthand\"\n    case gui\n    case head\n    case ground\n    case fixed\n  }\n\n  /// Child takes precedence.\n  func merge(withChild child: JSONModelDisplayTransforms) -> JSONModelDisplayTransforms {\n    let thirdPersonRightHand = child.thirdPersonRightHand ?? thirdPersonRightHand\n    let thirdPersonLeftHand = child.thirdPersonLeftHand ?? thirdPersonLeftHand\n    let firstPersonRightHand = child.firstPersonRightHand ?? firstPersonRightHand\n    let firstPersonLeftHand = child.firstPersonLeftHand ?? firstPersonLeftHand\n    let gui = child.gui ?? gui\n    let head = child.head ?? head\n    let ground = child.ground ?? ground\n    let fixed = child.fixed ?? fixed\n\n    return JSONModelDisplayTransforms(\n      thirdPersonRightHand: thirdPersonRightHand,\n      thirdPersonLeftHand: thirdPersonLeftHand,\n      firstPersonRightHand: firstPersonRightHand,\n      firstPersonLeftHand: firstPersonLeftHand,\n      gui: gui,\n      head: head,\n      ground: ground,\n      fixed: fixed\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/JSON/JSONModelTransform.swift",
    "content": "import FirebladeMath\nimport Foundation\n\n/// Transformation that can be applied to a model, as read from JSON files in resource packs.\n/// Translation is applied before rotation.\nstruct JSONModelTransform: Codable {\n  /// The rotation (should be `[x, y, z]`).\n  var rotation: [Double]?\n  /// The translation (should be `[x, y, z]`). Clamp to between -80 and 80. Measured in 16ths of a block.\n  var translation: [Double]?\n  /// The scale (should be `[x, y, z]`). Maximum 4.\n  var scale: [Double]?\n\n  /// Returns a transformation matrix representing this transform.\n  func toMatrix() throws -> Mat4x4f {\n    var matrix = MatrixUtil.identity\n\n    if let translation = self.translation {\n      var translationVector = try MathUtil.vectorFloat3(from: translation)\n      translationVector = MathUtil.clamp(translationVector, min: -80, max: 80) / 16\n      matrix *= MatrixUtil.translationMatrix(translationVector)\n    }\n\n    if let rotation = self.rotation {\n      var rotationVector = try MathUtil.vectorFloat3(from: rotation)\n      rotationVector = MathUtil.radians(from: rotationVector)\n      // matrix *= MatrixUtil.rotationMatrix(rotationVector)\n      matrix *=\n        MatrixUtil.rotationMatrix(z: rotationVector.z)\n        * MatrixUtil.rotationMatrix(y: rotationVector.y)\n        * MatrixUtil.rotationMatrix(x: rotationVector.x)\n    }\n\n    if let scale = self.scale {\n      var scaleVector = try MathUtil.vectorFloat3(from: scale)\n      scaleVector = MathUtil.clamp(scaleVector, min: -Float.greatestFiniteMagnitude, max: 4)\n      matrix *= MatrixUtil.scalingMatrix(scaleVector)\n    }\n\n    return matrix\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Model/ModelDisplayTransforms.swift",
    "content": "import Foundation\nimport FirebladeMath\n\n/// The transforms to apply when displaying rendering a model in different situations.\npublic struct ModelDisplayTransforms: Equatable {\n  /// Transform to use for displaying in right hand in third person.\n  public var thirdPersonRightHand: Mat4x4f\n  /// Transform to use for displaying in left hand in third person.\n  public var thirdPersonLeftHand: Mat4x4f\n  /// Transform to use for displaying in right hand in first person.\n  public var firstPersonRightHand: Mat4x4f\n  /// Transform to use for displaying in left hand in first person.\n  public var firstPersonLeftHand: Mat4x4f\n  /// Transform to use for displaying in inventory.\n  public var gui: Mat4x4f\n  /// Transform to use for displaying on head?\n  public var head: Mat4x4f\n  /// Transform to use for displaying on the ground.\n  public var ground: Mat4x4f\n  /// Transform to use for displaying in item frames.\n  public var fixed: Mat4x4f\n\n  public init(\n    thirdPersonRightHand: Mat4x4f,\n    thirdPersonLeftHand: Mat4x4f,\n    firstPersonRightHand: Mat4x4f,\n    firstPersonLeftHand: Mat4x4f,\n    gui: Mat4x4f,\n    head: Mat4x4f,\n    ground: Mat4x4f,\n    fixed: Mat4x4f\n  ) {\n    self.thirdPersonRightHand = thirdPersonRightHand\n    self.thirdPersonLeftHand = thirdPersonLeftHand\n    self.firstPersonRightHand = firstPersonRightHand\n    self.firstPersonLeftHand = firstPersonLeftHand\n    self.gui = gui\n    self.head = head\n    self.ground = ground\n    self.fixed = fixed\n  }\n\n  init(from jsonDisplayTransforms: JSONModelDisplayTransforms) throws {\n    thirdPersonRightHand = try jsonDisplayTransforms.thirdPersonRightHand?.toMatrix() ?? MatrixUtil.identity\n    thirdPersonLeftHand = try jsonDisplayTransforms.thirdPersonLeftHand?.toMatrix() ?? MatrixUtil.identity\n    firstPersonRightHand = try jsonDisplayTransforms.firstPersonRightHand?.toMatrix() ?? MatrixUtil.identity\n    firstPersonLeftHand = try jsonDisplayTransforms.firstPersonLeftHand?.toMatrix() ?? MatrixUtil.identity\n    gui = try jsonDisplayTransforms.gui?.toMatrix() ?? MatrixUtil.identity\n    head = try jsonDisplayTransforms.head?.toMatrix() ?? MatrixUtil.identity\n    ground = try jsonDisplayTransforms.ground?.toMatrix() ?? MatrixUtil.identity\n    fixed = try jsonDisplayTransforms.fixed?.toMatrix() ?? MatrixUtil.identity\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/ResourcePack.swift",
    "content": "import Foundation\nimport ZIPFoundation\n\nenum ResourcePackError: LocalizedError {\n  /// The specified resource pack directory does not exist.\n  case noSuchDirectory\n  /// The resource pack's `pack.mcmeta` is invalid.\n  case failedToReadMCMeta\n  /// Failed to list the contents of a texture directory.\n  case failedToEnumerateTextures\n  /// Failed to figure out what namespaces are included in this resource pack.\n  case failedToEnumerateNamespaces\n  /// Failed to convert an image into a texture.\n  case failedToLoadTexture(Identifier)\n  /// Failed to read the image for the given texture from a file.\n  case failedToReadTextureImage(URL)\n  /// Failed to create a `CGDataProvider` for the given image file.\n  case failedToCreateImageProvider(URL)\n  /// Failed to download the specified client jar.\n  case clientJarDownloadFailure\n  /// Failed to extract assets from a client jar.\n  case clientJarExtractionFailure\n  /// Failed to copy the vanilla assets from the extracted client jar.\n  case assetCopyFailure\n  /// Failed to create a dummy pack.mcmeta for the downloaded vanilla assets.\n  case failedToCreatePackMCMeta\n  /// No client jar is available to download for the specified version.\n  case noURLForVersion(String)\n  /// Failed to decode a version manifest.\n  case versionManifestFailure\n  /// Failed to decode the versions manifest.\n  case versionsManifestFailure\n  /// Failed to download default JSON entity models (downloaded separately from the default\n  /// vanilla resources).\n  case failedToDownloadJSONEntityModels\n\n  var errorDescription: String? {\n    switch self {\n      case .noSuchDirectory:\n        return \"The specified resource pack directory does not exist.\"\n      case .failedToReadMCMeta:\n        return \"The resource pack's `pack.mcmeta` is invalid.\"\n      case .failedToEnumerateTextures:\n        return \"Failed to list the contents of a texture directory.\"\n      case .failedToEnumerateNamespaces:\n        return \"Failed to figure out what namespaces are included in this resource pack.\"\n      case .failedToLoadTexture(let identifier):\n        return\n          \"Failed to convert an image into a texture with identifier: `\\(identifier.description)`.\"\n      case .failedToReadTextureImage(let url):\n        return \"\"\"\n          Failed to read the image for the given texture from a file.\n          File URL: \\(url.absoluteString)\n          \"\"\"\n      case .failedToCreateImageProvider(let url):\n        return \"\"\"\n          Failed to create a `CGDataProvider` for the given image file.\n          File URL: \\(url.absoluteString)\n          \"\"\"\n      case .clientJarDownloadFailure:\n        return \"Failed to download the specified client jar.\"\n      case .clientJarExtractionFailure:\n        return \"Failed to extract assets from a client jar.\"\n      case .assetCopyFailure:\n        return \"Failed to copy the vanilla assets from the extracted client jar.\"\n      case .failedToCreatePackMCMeta:\n        return \"Failed to create a dummy pack.mcmeta for the downloaded vanilla assets\"\n      case .noURLForVersion(let version):\n        return \"No client jar is available to download for version: \\(version).\"\n      case .versionManifestFailure:\n        return \"Failed to decode a version manifest.\"\n      case .versionsManifestFailure:\n        return \"Failed to decode the versions manifest.\"\n      case .failedToDownloadJSONEntityModels:\n        return \"Failed to download default JSON entity models.\"\n    }\n  }\n}\n\n// TODO: Version resource pack caches so that we can easily invalidate outdated caches\n//   when the format changes.\n/// A resource pack.\npublic struct ResourcePack {\n  /// The metadata of languages contained within this resource pack.\n  public var languages: [String: PackMCMeta.Language]\n\n  /// All resources contained in this resource pack, keyed by namespace.\n  public var resources: [String: Resources] = [:] {\n    didSet {\n      vanillaResources = resources[\"minecraft\"] ?? Resources()\n    }\n  }\n\n  /// Resources in the 'minecraft' namespace.\n  public private(set) var vanillaResources: Resources\n\n  // MARK: Init\n\n  /// Creates a resource pack with the given resources. Defaults to no resources.\n  /// - Parameters:\n  ///   - languages: The metadata of languages to include in the resource pack.\n  ///   - resources: The resources contained in the pack, keyed by namespace.\n  public init(\n    languages: [String: ResourcePack.PackMCMeta.Language] = [:],\n    resources: [String: ResourcePack.Resources] = [:]\n  ) {\n    self.languages = languages\n    self.resources = resources\n    self.vanillaResources = resources[\"minecraft\"] ?? Resources()\n  }\n\n  // MARK: Access\n\n  public func getBlockModel(for stateId: Int, at position: BlockPosition) -> BlockModel? {\n    return vanillaResources.blockModelPalette.model(for: stateId, at: position)\n  }\n\n  public func getBlockTexturePalette() -> TexturePalette {\n    return vanillaResources.blockTexturePalette\n  }\n\n  public func getDefaultLocale() -> MinecraftLocale {\n    return vanillaResources.locales[Constants.locale] ?? MinecraftLocale()\n  }\n\n  // MARK: Loading\n\n  /// Loads the resource pack in the given directory. ``RegistryStore/shared`` must be populated for this to work.\n  ///\n  /// If provided, cached resources are loaded from the given cache directory if present. To create a resource pack cache use ``cache(to:)``.\n  /// Resource pack caches do not cache the whole pack yet, only the most resource intensive parts to load.\n  public static func load(from directory: URL, cacheDirectory: URL?) throws -> ResourcePack {\n    // Check resource pack exists\n    guard FileManager.default.directoryExists(at: directory) else {\n      throw ResourcePackError.noSuchDirectory\n    }\n\n    // Read pack.mcmeta\n    let mcMetaFile = directory.appendingPathComponent(\"pack.mcmeta\")\n    let mcMeta = try readPackMCMeta(at: mcMetaFile)\n\n    // Read resources from present namespaces\n    guard\n      let contents = try? FileManager.default.contentsOfDirectory(\n        at: directory,\n        includingPropertiesForKeys: nil,\n        options: []\n      )\n    else {\n      throw ResourcePackError.failedToEnumerateNamespaces\n    }\n\n    var namespacedResources: [String: ResourcePack.Resources] = [:]\n    for directory in contents where FileManager.default.directoryExists(at: directory) {\n      let namespace = directory.lastPathComponent\n      let resources = try Resources.load(\n        from: directory,\n        inNamespace: namespace,\n        cacheDirectory: cacheDirectory?.appendingPathComponent(namespace)\n      )\n      namespacedResources[namespace] = resources\n    }\n\n    // Create pack\n    return ResourcePack(\n      languages: mcMeta.languages ?? [:],\n      resources: namespacedResources\n    )\n  }\n\n  /// Reads a pack.mcmeta file.\n  public static func readPackMCMeta(at mcMetaFile: URL) throws -> ResourcePack.PackMCMeta {\n    let mcMeta: ResourcePack.PackMCMeta\n    do {\n      let data = try Data(contentsOf: mcMetaFile)\n      mcMeta = try CustomJSONDecoder().decode(ResourcePack.PackMCMeta.self, from: data)\n    } catch {\n      throw ResourcePackError.failedToReadMCMeta.becauseOf(error)\n    }\n\n    return mcMeta\n  }\n\n  // MARK: Caching\n\n  /// Caches the parts of the pack that are most resource intensive to process (such as block models).\n  public func cache(to directory: URL) throws {\n    for (namespace, resources) in resources {\n      log.debug(\"Caching resources from '\\(namespace)' namespace\")\n      let cacheDirectory = directory.appendingPathComponent(namespace)\n      try FileManager.default.createDirectory(\n        at: cacheDirectory,\n        withIntermediateDirectories: true,\n        attributes: nil\n      )\n\n      try resources.cache(to: cacheDirectory)\n    }\n  }\n\n  // MARK: Download\n\n  /// Tasks to be exectued during the `downloadVanillaAssets` process\n  public enum DownloadStep: CaseIterable, TaskStep {\n    case fetchManifest\n    case downloadJar\n    case extractJar\n    case copyingAssets\n    case downloadEntityModels\n    case creatingMcmeta\n\n    public var relativeDuration: Double { 1 }\n\n    public var message: String {\n      switch self {\n        case .fetchManifest: return \"Fetching version manifest\"\n        case .downloadJar: return \"Downloading client jar\"\n        case .extractJar: return \"Extracting client jar\"\n        case .copyingAssets: return \"Copying assets\"\n        case .downloadEntityModels: return \"Download entity models\"\n        case .creatingMcmeta: return \"Creating pack.mcmeta\"\n      }\n    }\n  }\n\n  /// Downloads the vanilla client and extracts its assets (textures, block models, etc.).\n  public static func downloadVanillaAssets(\n    forVersion version: String,\n    to directory: URL,\n    progress: TaskProgress<DownloadStep>? = nil\n  ) throws {\n    // Get the url for the client jar\n    progress?.update(to: .fetchManifest)\n    let versionManifest = try getVersionManifest(for: version)\n    let clientJarURL = versionManifest.downloads.client.url\n\n    // Download the client jar\n    progress?.update(to: .downloadJar)\n    let temporaryDirectory = FileManager.default.temporaryDirectory\n    let clientJarTempFile = temporaryDirectory.appendingPathComponent(\"client.jar\")\n    do {\n      let data = try RequestUtil.data(contentsOf: clientJarURL)\n      try data.write(to: clientJarTempFile)\n    } catch {\n      throw ResourcePackError.clientJarDownloadFailure.becauseOf(error)\n    }\n\n    // Extract the contents of the client jar (jar files are just zip archives)\n    progress?.update(to: .extractJar)\n    let extractedClientJarDirectory =\n      temporaryDirectory\n      .appendingPathComponent(\"client\", isDirectory: true)\n    try? FileManager.default.removeItem(at: extractedClientJarDirectory)\n    do {\n      try FileManager.default.unzipItem(\n        at: clientJarTempFile,\n        to: extractedClientJarDirectory,\n        skipCRC32: true\n      )\n    } catch {\n      throw ResourcePackError.clientJarExtractionFailure.becauseOf(error)\n    }\n\n    // Copy the assets from the extracted client jar to application support\n    progress?.update(to: .copyingAssets)\n    do {\n      try FileManager.default.copyItem(\n        at: extractedClientJarDirectory.appendingPathComponent(\"assets\"),\n        to: directory\n      )\n    } catch {\n      throw ResourcePackError.assetCopyFailure.becauseOf(error)\n    }\n\n    // Download default JSON entity models\n    progress?.update(to: .downloadEntityModels)\n    do {\n      let entityModelsDirectory = directory.appendingPathComponent(\"minecraft/models/entity\")\n      try FileManager.default.createDirectory(\n        at: entityModelsDirectory,\n        withIntermediateDirectories: false\n      )\n      try EntityModelPalette.downloadJSONEntityModels(to: entityModelsDirectory)\n    } catch {\n      throw ResourcePackError.failedToDownloadJSONEntityModels.becauseOf(error)\n    }\n\n    // Create a default pack.mcmeta for it\n    progress?.update(to: .creatingMcmeta)\n    let contents = #\"{\"pack\": {\"pack_format\": 5, \"description\": \"The default vanilla assets\"}}\"#\n    guard let data = contents.data(using: .utf8) else {\n      throw ResourcePackError.failedToCreatePackMCMeta\n    }\n\n    do {\n      try data.write(to: directory.appendingPathComponent(\"pack.mcmeta\"))\n    } catch {\n      throw ResourcePackError.failedToCreatePackMCMeta.becauseOf(error)\n    }\n  }\n\n  /// Get the manifest describing all versions.\n  private static func getVersionsManifest() throws -> VersionsManifest {\n    let versionsManifestURL = URL(\n      string: \"https://launchermeta.mojang.com/mc/game/version_manifest.json\"\n    )!\n\n    let versionsManifest: VersionsManifest\n    do {\n      let data = try RequestUtil.data(contentsOf: versionsManifestURL)\n      versionsManifest = try CustomJSONDecoder().decode(VersionsManifest.self, from: data)\n    } catch {\n      throw ResourcePackError.versionsManifestFailure.becauseOf(error)\n    }\n\n    return versionsManifest\n  }\n\n  /// Get the manifest for the specified version.\n  private static func getVersionManifest(for versionString: String) throws -> VersionManifest {\n    let versionURLs = try getVersionURLs()\n\n    guard let versionURL = versionURLs[versionString] else {\n      log.error(\"Failed to find manifest download url for version \\(versionString)\")\n      throw ResourcePackError.noURLForVersion(versionString)\n    }\n\n    let versionManifest: VersionManifest\n    do {\n      let data = try RequestUtil.data(contentsOf: versionURL)\n      versionManifest = try CustomJSONDecoder().decode(VersionManifest.self, from: data)\n    } catch {\n      throw ResourcePackError.versionManifestFailure.becauseOf(error)\n    }\n\n    return versionManifest\n  }\n\n  /// Returns a map from version name to the version's manifest url.\n  private static func getVersionURLs() throws -> [String: URL] {\n    let manifest = try getVersionsManifest()\n    var urls: [String: URL] = [:]\n    for version in manifest.versions {\n      urls[version.id] = version.url\n    }\n    return urls\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Resources.swift",
    "content": "import Foundation\n\nextension ResourcePack {\n  /// A namespace of resources.\n  public struct Resources {\n    public static let blockTextureCacheFileName = \"BlockTexturePalette.bin\"\n    public static let itemTextureCacheFileName = \"ItemTexturePalette.bin\"\n    public static let entityTextureCacheFileName = \"EntityTexturePalette.bin\"\n    public static let guiTextureCacheFileName = \"GUITexturePalette.bin\"\n    public static let environmentTextureCacheFileName = \"EnvironmentTexturePalette.bin\"\n    /// The palette holding block textures.\n    public var blockTexturePalette = TexturePalette()\n    /// The palette holding block models.\n    public var blockModelPalette = BlockModelPalette()\n    /// The palette holding item textures.\n    public var itemTexturePalette = TexturePalette()\n    /// The palette holding item models.\n    public var itemModelPalette = ItemModelPalette()\n    /// The palette holding entity textures.\n    public var entityTexturePalette = TexturePalette()\n    /// The palette holding entity models.\n    public var entityModelPalette = EntityModelPalette()\n    /// The GUI texture palette.\n    public var guiTexturePalette = TexturePalette()\n    /// The environment texture palette (containing textures for the sun and moon etc.).\n    public var environmentTexturePalette = TexturePalette()\n    /// The locales.\n    public var locales: [String: MinecraftLocale] = [:]\n    /// The colors of biomes.\n    public var biomeColors = BiomeColors()\n    /// The fonts.\n    public var fontPalette = FontPalette()\n\n    /// Creates a new empty namespace of resources.\n    public init() {}\n\n    public func cache(to cacheDirectory: URL) throws {\n      // Cache models\n      try blockModelPalette.cache(toDirectory: cacheDirectory)\n      try itemModelPalette.cache(toDirectory: cacheDirectory)\n\n      // Cache textures\n      try blockTexturePalette.cache(\n        to: cacheDirectory.appendingPathComponent(Self.blockTextureCacheFileName)\n      )\n      try itemTexturePalette.cache(\n        to: cacheDirectory.appendingPathComponent(Self.itemTextureCacheFileName)\n      )\n      try entityTexturePalette.cache(\n        to: cacheDirectory.appendingPathComponent(Self.entityTextureCacheFileName)\n      )\n      try guiTexturePalette.cache(\n        to: cacheDirectory.appendingPathComponent(Self.guiTextureCacheFileName)\n      )\n      try environmentTexturePalette.cache(\n        to: cacheDirectory.appendingPathComponent(Self.environmentTextureCacheFileName)\n      )\n\n      // Cache fonts\n      try fontPalette.cache(toDirectory: cacheDirectory)\n    }\n\n    public static func loadCached(from cacheDirectory: URL) throws -> ResourcePack.Resources {\n      var resources = Resources()\n      resources.blockTexturePalette = try TexturePalette.loadCached(\n        from: cacheDirectory.appendingPathComponent(Self.blockTextureCacheFileName)\n      )\n      resources.itemTexturePalette = try TexturePalette.loadCached(\n        from: cacheDirectory.appendingPathComponent(Self.itemTextureCacheFileName)\n      )\n      resources.entityTexturePalette = try TexturePalette.loadCached(\n        from: cacheDirectory.appendingPathComponent(Self.entityTextureCacheFileName)\n      )\n      resources.guiTexturePalette = try TexturePalette.loadCached(\n        from: cacheDirectory.appendingPathComponent(Self.guiTextureCacheFileName)\n      )\n      resources.environmentTexturePalette = try TexturePalette.loadCached(\n        from: cacheDirectory.appendingPathComponent(Self.environmentTextureCacheFileName)\n      )\n      resources.blockModelPalette = try BlockModelPalette.loadCached(fromDirectory: cacheDirectory)\n      resources.itemModelPalette = try ItemModelPalette.loadCached(fromDirectory: cacheDirectory)\n      resources.fontPalette = try FontPalette.loadCached(fromDirectory: cacheDirectory)\n      return resources\n    }\n\n    /// Loads the resources in the given directory and gives them the specified namespace.\n    public static func load(\n      from directory: URL,\n      inNamespace namespace: String,\n      cacheDirectory: URL?\n    ) throws -> ResourcePack.Resources {\n      log.debug(\"Loading resources from '\\(namespace)' namespace\")\n\n      var resources = Resources()\n      var loadedCachedResources = false\n      if let cacheDirectory = cacheDirectory,\n        FileManager.default.directoryExists(at: cacheDirectory)\n      {\n        do {\n          resources = try loadCached(from: cacheDirectory)\n          loadedCachedResources = true\n        } catch {\n          do {\n            try FileManager.default.removeItem(at: cacheDirectory.deletingLastPathComponent())\n          } catch {\n            log.warning(\"Failed to delete invalid resource caches\")\n          }\n        }\n      }\n\n      let textureDirectory = directory.appendingPathComponent(\"textures\")\n      let modelDirectory = directory.appendingPathComponent(\"models\")\n\n      // Load biome colors\n      let colorMapDirectory = textureDirectory.appendingPathComponent(\"colormap\")\n      if FileManager.default.directoryExists(at: colorMapDirectory) {\n        log.debug(\"Loading biome colors\")\n        resources.biomeColors = try BiomeColors(from: colorMapDirectory)\n      }\n\n      // Load locales\n      let localeDirectory = directory.appendingPathComponent(\"lang\")\n      if FileManager.default.directoryExists(at: localeDirectory) {\n        log.debug(\"Loading locales\")\n        let contents = try FileManager.default.contentsOfDirectory(\n          at: localeDirectory, includingPropertiesForKeys: nil)\n        for file in contents where file.pathExtension == \"json\" {\n          let locale = try MinecraftLocale(localeFile: file)\n          resources.locales[file.deletingPathExtension().lastPathComponent] = locale\n        }\n      }\n\n      // Load entity models\n      let entityModelDirectory = modelDirectory.appendingPathComponent(\"entity\")\n      if FileManager.default.directoryExists(at: entityModelDirectory) {\n        log.debug(\"Loading entity models\")\n        resources.entityModelPalette = try EntityModelPalette.load(\n          from: entityModelDirectory,\n          namespace: namespace\n        )\n      }\n\n      if !loadedCachedResources {\n        log.debug(\"Loading textures\")\n\n        // Load block textures\n        let blockTextureDirectory = textureDirectory.appendingPathComponent(\"block\")\n        if FileManager.default.directoryExists(at: blockTextureDirectory) {\n          resources.blockTexturePalette = try TexturePalette.load(\n            from: blockTextureDirectory,\n            inNamespace: namespace,\n            withType: \"block\"\n          )\n        }\n\n        /// Load item textures\n        let itemTextureDirectory = textureDirectory.appendingPathComponent(\"item\")\n        if FileManager.default.directoryExists(at: itemTextureDirectory) {\n          resources.itemTexturePalette = try TexturePalette.load(\n            from: itemTextureDirectory,\n            inNamespace: namespace,\n            withType: \"item\"\n          )\n        }\n\n        /// Load entity textures\n        let entityTextureDirectory = textureDirectory.appendingPathComponent(\"entity\")\n        if FileManager.default.directoryExists(at: entityTextureDirectory) {\n          resources.entityTexturePalette = try TexturePalette.load(\n            from: entityTextureDirectory,\n            inNamespace: namespace,\n            withType: \"entity\",\n            recursive: true,\n            isAnimated: false\n          )\n        }\n\n        // Load GUI textures\n        let guiTextureDirectory = textureDirectory.appendingPathComponent(\"gui\")\n        if FileManager.default.directoryExists(at: guiTextureDirectory) {\n          resources.guiTexturePalette = try TexturePalette.load(\n            from: guiTextureDirectory,\n            inNamespace: namespace,\n            withType: \"gui\",\n            recursive: true,\n            isAnimated: false\n          )\n        }\n\n        // Load GUI textures\n        let environmentTextureDirectory = textureDirectory.appendingPathComponent(\"environment\")\n        if FileManager.default.directoryExists(at: environmentTextureDirectory) {\n          resources.environmentTexturePalette = try TexturePalette.load(\n            from: environmentTextureDirectory,\n            inNamespace: namespace,\n            withType: \"environment\",\n            isAnimated: false\n          )\n        }\n\n        // Load block models\n        let blockModelDirectory = modelDirectory.appendingPathComponent(\"block\")\n        if FileManager.default.directoryExists(at: blockModelDirectory) {\n          log.debug(\"Loading block models\")\n          resources.blockModelPalette = try BlockModelPalette.load(\n            from: blockModelDirectory,\n            namespace: namespace,\n            blockTexturePalette: resources.blockTexturePalette\n          )\n        }\n\n        // Load item models\n        let itemModelDirectory = modelDirectory.appendingPathComponent(\"item\")\n        if FileManager.default.directoryExists(at: itemModelDirectory) {\n          log.debug(\"Loading item models\")\n          resources.itemModelPalette = try ItemModelPalette.load(\n            from: itemModelDirectory,\n            itemTexturePalette: resources.itemTexturePalette,\n            blockTexturePalette: resources.blockTexturePalette,\n            blockModelPalette: resources.blockModelPalette,\n            namespace: namespace\n          )\n        }\n\n        // Load fonts\n        let fontDirectory = directory.appendingPathComponent(\"font\")\n        if FileManager.default.directoryExists(at: fontDirectory) {\n          log.debug(\"Loading fonts\")\n          resources.fontPalette = try FontPalette.load(\n            from: fontDirectory,\n            namespaceDirectory: directory,\n            textureDirectory: textureDirectory\n          )\n        }\n      }\n\n      return resources\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Texture/ColorMap.swift",
    "content": "import Foundation\n\npublic struct ColorMap {\n  /// The width of the color map.\n  public var width: Int\n  /// The height of the color map.\n  public var height: Int\n  /// The pixels of the color map. Indexed by `y * width + x`.\n  public var colors: [RGBColor] = []\n\n  /// Creates an empty color map.\n  public init() {\n    width = 0\n    height = 0\n  }\n\n  /// Loads the colormap from a file\n  /// - Parameter pngFile: The png file containing the colormap texture.\n  public init(from pngFile: URL) throws {\n    let texture = try Texture(pngFile: pngFile, type: .opaque)\n    width = texture.width\n    height = texture.height\n\n    for y in 0..<height {\n      for x in 0..<width {\n        let pixel = texture[x, y]\n        colors.append(RGBColor(\n          r: Int(pixel.red),\n          g: Int(pixel.green),\n          b: Int(pixel.blue)\n        ))\n      }\n    }\n  }\n\n  /// Get the color at some coordinates.\n  /// - Parameters:\n  ///   - x: The x coordinate.\n  ///   - y: The y coordinate.\n  /// - Returns: The color at the given coordinates. Returns nil if the coordinates are out of bounds.\n  public func color(atX x: Int, y: Int) -> RGBColor? {\n    if x >= width || y >= height || x < 0 || y < 0 {\n      return nil\n    }\n\n    let index = y * width + x\n    return colors[index]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Texture/Texture.swift",
    "content": "import Foundation\nimport SwiftImage\n\npublic enum TextureError: LocalizedError {\n  /// The texture's height must be a multiple of the width.\n  case invalidDimensions(width: Int, height: Int)\n  /// Texture dimensions must be powers of two.\n  case widthNotPowerOfTwo(width: Int)\n  /// Failed to create an image provider to read the texture from the given file.\n  case failedToCreateImageProvider\n  /// Failed to read the texture from the given file.\n  case failedToReadTextureImage\n  /// Failed to get the raw bytes of the texture.\n  case failedToGetTextureBytes(Error)\n  /// Failed to create a `CGContext` for the given texture.\n  case failedToCreateContext\n  /// Failed to get the bytes of the `CGContext` created to format the texture.\n  case failedToGetContextBytes\n  /// The animation mcmeta file for the texture contains invalid frame metadata.\n  case invalidFrameMetadata\n  /// The animation data for an animated texture was invalid or missing.\n  case failedToLoadTextureAnimation(Error)\n  /// The target width for the texture is not a power of two.\n  case targetWidthNotPowerOfTwo(targetWidth: Int)\n\n  public var errorDescription: String? {\n    switch self {\n      case .invalidDimensions(let width, let height):\n        return \"\"\"\n        The texture's height must be a multiple of the width.\n        Width: \\(width)\n        Height: \\(height)\n        \"\"\"\n      case .widthNotPowerOfTwo(let width):\n        return \"\"\"\n        Texture dimensions must be powers of two.\n        Width: \\(width)\n        \"\"\"\n      case .failedToCreateImageProvider:\n        return \"Failed to create an image provider to read the texture from the given file.\"\n      case .failedToReadTextureImage:\n        return \"Failed to read the texture from the given file.\"\n      case .failedToGetTextureBytes(let error):\n        return \"\"\"\n        Failed to get the raw bytes of the texture.\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n      case .failedToCreateContext:\n        return \"Failed to create a `CGContext` for the given texture.\"\n      case .failedToGetContextBytes:\n        return \"Failed to get the bytes of the `CGContext` created to format the texture.\"\n      case .invalidFrameMetadata:\n        return \"The animation mcmeta file for the texture contains invalid frame metadata.\"\n      case .failedToLoadTextureAnimation(let error):\n        return \"\"\"\n        The animation data for an animated texture was invalid or missing.\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n      case .targetWidthNotPowerOfTwo(let targetWidth):\n        return \"\"\"\n        The target width for the texture is not a power of two.\n        Target width: \\(targetWidth)\n        \"\"\"\n    }\n  }\n}\n\n/// A texture and its metadata.\npublic struct Texture {\n  /// the width of the texture in pixels.\n  public var width: Int {\n    return image.width\n  }\n\n  /// The height of the texture in pixels.\n  public var height: Int {\n    return image.height\n  }\n\n  /// The texture's type.\n  public var type: TextureType\n  /// The image representation.\n  public var image: Image<BGRA<UInt8>>\n  /// The animation to use when rendering this texture.\n  public var animation: Animation?\n  /// The number of frames present in the texture. May differ from\n  /// `animation?.frames.count` because a single texture frame may be\n  /// used by multiple animation frames. Will be 1 if the texture isn't\n  /// animated.\n  public var frameCount: Int\n\n  /// The pixel format used by Apple devices.\n  public struct BGRA<Channel> {\n    var blue: Channel\n    var green: Channel\n    var red: Channel\n    var alpha: Channel\n  }\n\n  /// Loads a texture.\n  /// - Parameters:\n  ///   - pngFile: The png file containing the texture.\n  ///   - type: The type of the texture. Calculated if not specified.\n  ///   - targetWidth: Scales the image to this width.\n  ///   - checkDimensions: If `true`, the texture's width must be a power of two, and the height must be a multiple of the width.\n  ///                      `targetWidth` must also be a power of two if present.\n  public init(\n    pngFile: URL,\n    type: TextureType? = nil,\n    scaledToWidth targetWidth: Int? = nil,\n    checkDimensions: Bool = false,\n    animationMetadataFile: URL? = nil\n  ) throws {\n    let image = try Image<RGBA<UInt8>>(fromPNGFile: pngFile)\n    try self.init(\n      image: image,\n      type: type,\n      scaledToWidth: targetWidth,\n      checkDimensions: checkDimensions,\n      animationMetadataFile: animationMetadataFile\n    )\n  }\n\n  /// Loads a texture with an optional animation.\n  /// - Parameters:\n  ///   - image: The image containing the texture.\n  ///   - type: The type of the texture. Calculated if not specified.\n  ///   - targetWidth: Scales the image to this width.\n  ///   - checkDimensions: If `true`, the texture's width must be a power of two,\n  ///     and the height must be a multiple of the width. `targetWidth` must also\n  ///     be a power of two if present.\n  ///   - animationMetadataFile: A metadata file containing information about the\n  ///     texture's animation\n  public init(\n    image: Image<RGBA<UInt8>>,\n    type: TextureType? = nil,\n    scaledToWidth targetWidth: Int? = nil,\n    checkDimensions: Bool = false,\n    animationMetadataFile: URL?\n  ) throws {\n    if checkDimensions {\n      // The height of the texture must be a multiple of the width\n      guard image.height % image.width == 0 else {\n        throw TextureError.invalidDimensions(width: image.width, height: image.height)\n      }\n\n      // The width must be a power of two\n      guard image.width.isPowerOfTwo else {\n        throw TextureError.widthNotPowerOfTwo(width: image.width)\n      }\n\n      // The target width must be a power of two\n      if let targetWidth = targetWidth {\n        guard targetWidth.isPowerOfTwo else {\n          throw TextureError.targetWidthNotPowerOfTwo(targetWidth: targetWidth)\n        }\n      }\n    }\n\n    // Calculate new dimensions\n    let scaleFactor: Int\n    if let targetWidth = targetWidth {\n      scaleFactor = targetWidth / image.width\n    } else {\n      scaleFactor = 1\n    }\n\n    let width = scaleFactor * image.width\n    let height = scaleFactor * image.height\n\n    // TODO: Don't scale if the scale factor is 1 (waste of cpu)\n    let resized = image.resizedTo(\n      width: width,\n      height: height,\n      interpolatedBy: .nearestNeighbor\n    )\n\n    // BGRA is more efficient for Apple GPUs apparently so we convert all textures to that\n    let pixelCount = width * height\n    let pixels = [BGRA<UInt8>](unsafeUninitializedCapacity: pixelCount) { buffer, count in\n      resized.withUnsafeBufferPointer { pixels in\n        for i in 0..<pixelCount {\n          let pixel = pixels[i]\n          buffer[i] = BGRA<UInt8>(\n            blue: pixel.blue,\n            green: pixel.green,\n            red: pixel.red,\n            alpha: pixel.alpha\n          )\n        }\n      }\n      count = pixelCount\n    }\n\n    self.image = Image(width: resized.width, height: resized.height, pixels: pixels)\n    self.type = type ?? Self.typeOfTexture(self.image)\n\n    if let animationMetadataFile = animationMetadataFile {\n      frameCount = height / width\n      do {\n        let data = try Data(contentsOf: animationMetadataFile)\n        let animationMCMeta = try CustomJSONDecoder().decode(AnimationMCMeta.self, from: data)\n        animation = Animation(from: animationMCMeta, maximumFrameIndex: frameCount)\n      } catch {\n        throw TextureError.failedToLoadTextureAnimation(error)\n      }\n    } else {\n      frameCount = 1\n    }\n  }\n\n  /// An internal initializer used to create texture from data stored in the binary cache.\n  init(type: TextureType, image: Image<BGRA<UInt8>>, animation: Animation?, frameCount: Int) {\n    self.type = type\n    self.image = image\n    self.animation = animation\n    self.frameCount = frameCount\n  }\n\n  /// Accesses the pixel at the given coordinates in the image and crashes if the coordinates are\n  /// out of bounds.\n  public subscript(_ x: Int, _ y: Int) -> BGRA<UInt8> {\n    get {\n      image[x, y]\n    }\n    set {\n      image[x, y] = newValue\n    }\n  }\n\n  /// Fixes the colour values of transparent pixels to vaguely represent the colours of the pixels around them to help with mipmapping.\n  /// This function is probably really slow, I haven't checked yet. Nope, it doesn't seem to be too slow.\n  public mutating func fixTransparentPixels() {\n    let width = image.width\n    let height = image.height\n\n    func index(_ x: Int, _ y: Int) -> Int {\n      return x + y * width\n    }\n\n    image.withUnsafeMutableBufferPointer { pixels in\n      for x in 0..<width {\n        // For each transparent pixel copy the color values from above\n        for y in 0..<height {\n          var pixel = pixels[index(x, y)]\n          if pixel.alpha == 0 && y != 0 {\n            pixel = pixels[index(x, y - 1)]\n            pixel.alpha = 0\n            pixels[index(x, y)] = pixel\n          }\n        }\n\n        // Do the same but the other way\n        for y in 1...height {\n          let y = height - y\n          var pixel = pixels[index(x, y)]\n          if pixel.alpha == 0 && y != height - 1 {\n            pixel = pixels[index(x, y + 1)]\n            pixel.alpha = 0\n            pixels[index(x, y)] = pixel\n          }\n        }\n      }\n\n      // Do the whole thing again but horizontally\n      for y in 0..<height {\n        // For each transparent pixel copy the color values from the left\n        for x in 0..<width {\n          var pixel = pixels[index(x, y)]\n          if pixel.alpha == 0 && x != 0 {\n            pixel = pixels[index(x - 1, y)]\n            pixel.alpha = 0\n            pixels[index(x, y)] = pixel\n          }\n        }\n\n        // Do the same but the other way\n        for x in 1...width {\n          let x = width - x\n          var pixel = pixels[index(x, y)]\n          if pixel.alpha == 0 && x != width - 1 {\n            pixel = pixels[index(x + 1, y)]\n            pixel.alpha = 0\n            pixels[index(x, y)] = pixel\n          }\n        }\n      }\n    }\n  }\n\n  /// Sets the alpha components of the texutre to a specified value. Alpha value is bound to a range (0...255 inclusive).\n  public mutating func setAlpha(_ alpha: UInt8) {\n    let clampedAlpha = min(max(alpha, 0), 255)\n    let pixelCount = image.width * image.height\n    image.withUnsafeMutableBufferPointer { pixels in\n      for i in 0..<pixelCount {\n        pixels[i].alpha = clampedAlpha\n      }\n    }\n  }\n\n  /// Finds the type of a texture by inspecting its pixels.\n  private static func typeOfTexture(\n    _ image: Image<BGRA<UInt8>>\n  ) -> TextureType {\n    var type = TextureType.opaque\n\n    let width = image.width\n    let height = image.height\n    image.withUnsafeBufferPointer { pixels in\n      outer: for x in 0..<width {\n        for y in 0..<height {\n          let alpha = pixels[x + y * width].alpha\n\n          if alpha == 0 {\n            type = .transparent\n            // We don't break here because it can still be overidden by translucent\n          } else if alpha < 255 {\n            type = .translucent\n            break outer\n          }\n        }\n      }\n    }\n\n    return type\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Texture/TextureAnimation.swift",
    "content": "import Foundation\n\nextension Texture {\n  /// Metadata for an animated texture.\n  public struct Animation {\n    public var interpolate: Bool\n    public var frames: [Frame]\n\n    public init(interpolate: Bool, frames: [Frame]) {\n      self.interpolate = interpolate\n      self.frames = frames\n    }\n\n    /// - Parameter `maximumFrameIndex`: is the number of frames present in\n    ///   the corresponding texture.\n    public init(from mcMeta: AnimationMCMeta, maximumFrameIndex: Int) {\n      interpolate = mcMeta.animation.interpolate ?? false\n\n      // Reformat frames\n      frames = []\n      let defaultFrameTime = mcMeta.animation.frametime ?? 1\n      if let mcMetaFrames = mcMeta.animation.frames {\n        for mcMetaFrame in mcMetaFrames {\n          let frame = Frame(\n            index: mcMetaFrame.index,\n            time: mcMetaFrame.time ?? defaultFrameTime\n          )\n          frames.append(frame)\n        }\n      } else {\n        for i in 0..<maximumFrameIndex {\n          let frame = Frame(\n            index: i,\n            time: defaultFrameTime\n          )\n          frames.append(frame)\n        }\n      }\n    }\n  }\n}\n\nextension Texture.Animation {\n  public struct Frame {\n    public var index: Int\n    public var time: Int\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Texture/TextureAnimationState.swift",
    "content": "import Foundation\n\nextension Texture {\n  public struct AnimationState {\n    public var animation: Animation\n\n    public var index: Int\n    public var ticksRemaining: Int\n\n    /// The current frame being shown for this texture.\n    public var currentFrame: Int {\n      animation.frames[index].index\n    }\n\n    public init(for animation: Texture.Animation) {\n      self.animation = animation\n      index = 0\n      ticksRemaining = animation.frames[0].time\n    }\n\n    /// Progresses the animation by one tick. Returns whether the current frame changed or not.\n    public mutating func tick() -> Bool {\n      ticksRemaining -= 1\n      if ticksRemaining == 0 {\n        index = (index + 1) % animation.frames.count\n        ticksRemaining = animation.frames[index].time\n        return true\n      } else {\n        return false\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Texture/TexturePalette.swift",
    "content": "import Foundation\nimport SwiftImage\n\n/// A palette containing textures that can be animated. All of the textures must be the same size or\n/// multiples of 2 of eachother. Textures are assumed to be square.\npublic struct TexturePalette {\n  /// The palette's textures, indexed by ``identifierToIndex``.\n  public var textures: [Texture]\n\n  /// The width of the textures in this palette.\n  public var width: Int\n  /// The height of a single texture frame in this palette. The heights of the textures will be multiples of this number.\n  public var height: Int\n\n  /// An index for ``textures``.\n  public var identifierToIndex: [Identifier: Int]\n\n  /// The default animation state for the texture palette. Defaults to the first frame for every\n  /// animated texture.\n  public var defaultAnimationState: AnimationState {\n    return AnimationState(for: textures)\n  }\n\n  // MARK: Init\n\n  /// Creates an empty texture palette.\n  public init() {\n    textures = []\n    width = 0\n    height = 0\n    identifierToIndex = [:]\n  }\n\n  /// Creates a texture palette containing the given textures which all share the same specified width.\n  public init(_ textures: [(Identifier, Texture)], width: Int, height: Int) {\n    self.textures = []\n    self.width = width\n    self.height = height\n    identifierToIndex = [:]\n\n    for (index, (identifier, texture)) in textures.enumerated() {\n      identifierToIndex[identifier] = index\n      self.textures.append(texture)\n    }\n  }\n\n  /// Creates a texture palette by providing all of its properties. Used internally for caching, and\n  /// not very useful outside of that usecase.\n  init(textures: [Texture], width: Int, height: Int, identifierToIndex: [Identifier: Int]) {\n    self.textures = textures\n    self.width = width\n    self.height = height\n    self.identifierToIndex = identifierToIndex\n  }\n\n  // MARK: Access\n\n  /// Returns the index of the texture referred to by the given identifier if it exists.\n  public func textureIndex(for identifier: Identifier) -> Int? {\n    return identifierToIndex[identifier]\n  }\n\n  /// Returns the texture referred to by the given identifier if it exists.\n  public func texture(for identifier: Identifier) -> Texture? {\n    if let index = textureIndex(for: identifier) {\n      return textures[index]\n    } else {\n      return nil\n    }\n  }\n\n  // MARK: Loading\n\n  /// Loads the texture palette present in the given directory. `type` refers to the part before the\n  /// slash in the name. Like `block` in `minecraft:block/dirt`.\n  /// - Parameters:\n  ///   - recursive: If recursive, textures will also be loaded from\n  ///     subdirectories of the given directory.\n  ///   - isAnimated: If `true`, all textures are resized to the same width and their height must be a multiple\n  ///     of their width. Also, the palette height (frame height) will be set to the palette width.\n  ///     Used for palettes like the block and item texture palettes.\n  public static func load(\n    from directory: URL,\n    inNamespace namespace: String,\n    withType type: String,\n    recursive: Bool = false,\n    isAnimated: Bool = true\n  ) throws -> TexturePalette {\n    // I hate the remnants of ObjC left in Swift, this is such a stupid file enumeration API\n    guard\n      let enumerator = FileManager.default.enumerator(\n        at: directory,\n        includingPropertiesForKeys: [],\n        options: recursive ? [] : [.skipsSubdirectoryDescendants]\n      )\n    else {\n      throw ResourcePackError.failedToEnumerateTextures\n    }\n\n    let files = enumerator.compactMap { file in\n      return file as? URL\n    }\n\n    // Textures are loaded in two phases so that we know what the widest texture is before we do\n    // work scaling them all to the right widths.\n\n    // Load the images\n    var maxWidth = 0 // The width of the widest texture in the palette\n    var maxHeight = 0\n    var images: [(Identifier, Image<RGBA<UInt8>>)] = []\n    for file in files where file.pathExtension == \"png\" {\n      var name = file.deletingPathExtension().path.dropFirst(directory.path.count)\n      if name.hasPrefix(\"/\") {\n        name = name.dropFirst()\n      }\n      let identifier = Identifier(namespace: namespace, name: \"\\(type)/\\(name)\")\n\n      let image = try Image<RGBA<UInt8>>(fromPNGFile: file)\n      if image.width > maxWidth {\n        maxWidth = image.width\n      }\n      if image.height > maxHeight {\n        maxHeight = image.height\n      }\n\n      images.append((identifier, image))\n    }\n\n    // Convert the images to textures\n    var textures: [(Identifier, Texture)] = []\n    for (identifier, image) in images {\n      let name = identifier.name.split(separator: \"/\")[1]\n\n      let animationMetadataFile: URL?\n      if FileSystem.fileExists(directory.appendingPathComponent(\"\\(name).png.mcmeta\")) {\n        animationMetadataFile = directory.appendingPathComponent(\"\\(name).png.mcmeta\")\n      } else {\n        animationMetadataFile = nil\n      }\n\n      // Hardcode leaves as opaque for performance reasons\n      let hardcodeOpaque = identifier.name.hasSuffix(\"leaves\")\n\n      do {\n        // Only check dimensions if we need to resize\n        var texture = try Texture(\n          image: image,\n          type: hardcodeOpaque ? .opaque : nil,\n          scaledToWidth: isAnimated ? maxWidth : image.width,\n          checkDimensions: isAnimated,\n          animationMetadataFile: animationMetadataFile\n        )\n\n        if texture.type == .opaque {\n          texture.setAlpha(255)\n        } else {\n          // Change the color of transparent pixels to make mipmaps look more natural.\n          texture.fixTransparentPixels()\n        }\n\n        textures.append((identifier, texture))\n      } catch {\n        throw ResourcePackError.failedToLoadTexture(identifier).becauseOf(error)\n      }\n    }\n\n    return TexturePalette(textures, width: maxWidth, height: isAnimated ? maxWidth : maxHeight)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Texture/TexturePaletteAnimationState.swift",
    "content": "import Foundation\n\nextension TexturePalette {\n  public struct AnimationState {\n    /// The last tick processed.\n    public var lastTick: Int?\n    /// The states of all animated textures.\n    public var animationStates: [Int: Texture.AnimationState]\n\n    public init(for textures: [Texture]) {\n      animationStates = [:]\n      for (index, texture) in textures.enumerated() {\n        if let animation = texture.animation {\n          animationStates[index] = Texture.AnimationState(for: animation)\n        }\n      }\n    }\n\n    /// Updates all animation states and returns the indices of all textures that changed frames.\n    public mutating func update(tick: Int) -> [(index: Int, tick: Int)] {\n      var numTicks = 0\n      if let lastTick = lastTick {\n        numTicks = tick - lastTick\n      }\n      lastTick = tick\n\n      if numTicks <= 0 {\n        return []\n      }\n\n      var updatedTextures: [(index: Int, tick: Int)] = []\n      for i in 0..<numTicks {\n        for (index, var state) in animationStates {\n          let hasChangedFrames = state.tick()\n          if hasChangedFrames {\n            updatedTextures.append((index: index, tick: tick + i))\n          }\n          animationStates[index] = state\n        }\n      }\n\n      var seenIndices: Set<Int> = []\n      let filtered = updatedTextures.reversed().filter { (index, tick) in\n        return seenIndices.insert(index).inserted\n      }\n      return filtered\n    }\n\n    /// Returns the frame to render for the given texture.\n    public func frame(forTextureAt index: Int) -> Int {\n      if let state = animationStates[index] {\n        return state.currentFrame\n      } else {\n        return 0\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Resources/Texture/TextureType.swift",
    "content": "import Foundation\n\n/// The possible texture types.\npublic enum TextureType: Int {\n  /// The texture only contains opaque pixels.\n  case opaque\n  /// The texture contains some fully transparent pixels but no translucent pictures.\n  case transparent\n  /// The texture contains translucent pixels (semi-transparent).\n  case translucent\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Server/Ping/PingError.swift",
    "content": "import Foundation\n\npublic enum PingError: LocalizedError {\n  case connectionFailed(Error)\n\n  public var errorDescription: String? {\n    switch self {\n      case .connectionFailed(let error):\n        return \"\"\"\n        Connection failed.\n        Reason: \\(error.localizedDescription)\n        \"\"\"\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Server/Ping/Pinger.swift",
    "content": "import Foundation\n\n#if !canImport(Combine)\nimport OpenCombine\n#endif\n\npublic class Pinger: ObservableObject {\n  @Published public var response: Result<StatusResponse, PingError>?\n\n  public var connection: ServerConnection?\n  public let descriptor: ServerDescriptor\n\n  public var shouldPing = false\n  public var isConnecting = false\n\n  // MARK: Init\n\n  public init(_ descriptor: ServerDescriptor) {\n    self.descriptor = descriptor\n    Task {\n      await connect()\n    }\n  }\n\n  // MARK: Interface\n\n  private func handleNetworkEvent(_ event: Event) {\n    switch event {\n      case let event as ConnectionFailedEvent:\n        ThreadUtil.runInMain {\n          self.response = Result.failure(PingError.connectionFailed(event.networkError))\n        }\n      default:\n        break\n    }\n  }\n\n  public func ping() async throws {\n    if let connection = connection {\n      ThreadUtil.runInMain {\n        self.response = nil\n      }\n      try connection.ping()\n      shouldPing = false\n    } else if !isConnecting {\n      shouldPing = true\n      await connect()\n    } else {\n      shouldPing = true\n    }\n  }\n\n  private func connect() async {\n    isConnecting = true\n    do {\n      let connection = try await ServerConnection(descriptor: self.descriptor)\n      connection.setPacketHandler(self.handlePacket)\n      connection.eventBus.registerHandler(self.handleNetworkEvent)\n      self.connection = connection\n      self.isConnecting = false\n      if self.shouldPing {\n        try? await self.ping()\n      }\n    } catch {\n      self.isConnecting = false\n      log.trace(\"Failed to create server connection\")\n      ThreadUtil.runInMain {\n        self.response = Result.failure(.connectionFailed(error))\n      }\n    }\n  }\n\n  public func closeConnection() {\n    connection?.close()\n    connection = nil\n  }\n\n  // MARK: Networking\n\n  private func handlePacket(_ packet: ClientboundPacket) {\n    do {\n      try packet.handle(for: self)\n    } catch {\n      closeConnection()\n      log.error(\"Failed to handle packet: \\(error)\")\n    }\n  }\n}\n\nextension Pinger: Equatable {\n  public static func == (lhs: Pinger, rhs: Pinger) -> Bool {\n    return lhs.descriptor == rhs.descriptor\n  }\n}\n\nextension Pinger: Hashable {\n  public func hash(into hasher: inout Hasher) {\n    hasher.combine(descriptor)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Server/Ping/StatusResponse.swift",
    "content": "import Foundation\n\n/// A status response from the server. See ``StatusResponsePacket``.\npublic struct StatusResponse: Decodable {\n  /// The server's version.\n  public struct Version: Decodable {\n    /// The name of the version. For example \"1.16.1\".\n    public var name: String\n    /// The protocol version. For example 758.\n    public var protocolVersion: Int\n\n    public enum CodingKeys: String, CodingKey {\n      case name\n      case protocolVersion = \"protocol\"\n    }\n  }\n\n  /// Information about the online players.\n  public struct PlayerList: Decodable {\n    /// The maximum number of online players allowed.\n    public var max: Int\n    /// The number of online players.\n    public var online: Int\n    /// A sample of the online players (does not necessarily contain all online players).\n    public var sample: [OnlinePlayer]?\n\n    /// Information about an online player.\n    public struct OnlinePlayer: Decodable {\n      /// The player's username.\n      public var name: String\n      /// The player's uuid.\n      public var id: String\n    }\n  }\n\n  /// The server's description.\n  public struct Description: Decodable {\n    /// The description styled as legacy text.\n    public var text: String\n\n    public enum CodingKeys: String, CodingKey {\n      case text\n    }\n\n    public init(from decoder: Decoder) throws {\n      if let container = try? decoder.container(keyedBy: CodingKeys.self) {\n        text = try container.decode(String.self, forKey: .text)\n      } else if let container = try? decoder.singleValueContainer() {\n        text = try container.decode(String.self)\n      } else {\n        text = \"A Minecraft Server\"\n      }\n    }\n  }\n\n  /// The server's version\n  public var version: Version\n  /// Information about the online players.\n  public var players: PlayerList\n  /// The server's message of the day.\n  public var description: Description\n  /// The server's icon.\n  public var favicon: String?\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Server/ServerDescriptor.swift",
    "content": "import Foundation\n\npublic struct ServerDescriptor: Equatable, Hashable, Codable, CustomStringConvertible {\n  public var name: String\n  public var host: String\n  public var port: UInt16?\n  \n  public var description: String {\n    if let port = port {\n      return \"\\(host):\\(port)\"\n    } else {\n      return host\n    }\n  }\n  \n  public init(name: String, host: String, port: UInt16?) {\n    self.name = name\n    self.host = host\n    self.port = port\n  }\n  \n  public func hash(into hasher: inout Hasher) {\n    hasher.combine(name)\n    hasher.combine(host)\n    hasher.combine(port)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Server/TabList.swift",
    "content": "import Foundation\n\n/// A list of all players currently connected to a server.\npublic struct TabList {\n  /// The currently connected players.\n  public private(set) var players: [UUID: PlayerInfo] = [:]\n\n  /// Adds a player to the tab list.\n  public mutating func addPlayer(_ playerInfo: PlayerInfo) {\n    players[playerInfo.uuid] = playerInfo\n  }\n\n  /// Updates the gamemode of a player in the tab list.\n  public mutating func updateGamemode(_ gamemode: Gamemode?, uuid: UUID) {\n    players[uuid]?.gamemode = gamemode\n  }\n\n  /// Updates the latency of a player in the tab list.\n  public mutating func updateLatency(_ ping: Int, uuid: UUID) {\n    players[uuid]?.ping = ping\n  }\n\n  /// Updates the display name of a player in the tab list.\n  public mutating func updateDisplayName(_ displayName: ChatComponent?, uuid: UUID) {\n    players[uuid]?.displayName = displayName\n  }\n\n  /// Removes the specified player from the tab list.\n  public mutating func removePlayer(uuid: UUID) {\n    players.removeValue(forKey: uuid)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/Array.swift",
    "content": "extension Array where Element: BinaryFloatingPoint {\n  /// - Returns: The average of an array of floating point values.\n  public func average() -> Element {\n    isEmpty ? .zero : reduce(into: 0, { $0 += $1 }) / Element(count)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/ArrayBinding.swift",
    "content": "/// Similar idea to SwiftUI bindings, but for arrays. Allows prolonged mutable access to an array.\n/// Made it a class so that you're not forced to unnecessarily always store the binding as a `var`\n/// just cause you want to assign to it. That would be unnecessary because there's no actual mutation\n/// happening to the binding and by nature it's meant to be an opaque two way binding.\npublic class ArrayBinding<Element> {\n  public let getElement: (_ index: Int) -> Element\n  public let setElement: (_ index: Int, _ value: Element) -> Void\n\n  public init(get getElement: @escaping (Int) -> Element, set setElement: @escaping (Int, Element) -> Void) {\n    self.getElement = getElement\n    self.setElement = setElement\n  }\n\n  public subscript(_ index: Int) -> Element {\n    get {\n      getElement(index)\n    }\n    set {\n      setElement(index, newValue)\n    }\n  }\n\n  public func swapAt(_ firstIndex: Int, _ secondIndex: Int) {\n    let first = self[firstIndex]\n    let second = self[secondIndex]\n    self[firstIndex] = second\n    self[secondIndex] = first\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/BinaryFloatingPoint.swift",
    "content": "import FirebladeMath\n\npublic extension Double {\n  func rounded(toPlaces places: Int) -> Double {\n    let shift = pow(10, Double(places))\n    return (self * shift).rounded() / shift\n  }\n}\n\npublic extension Float {\n  func rounded(toPlaces places: Int) -> Float {\n    let shift = pow(10, Float(places))\n    return (self * shift).rounded() / shift\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/BinaryUtil.swift",
    "content": "import Foundation\n\n/// A namespace for utility binary operations\nenum BinaryUtil {\n  /// Get a list of the indices of bits set in the first n bits of `bitmask`\n  static func setBits(of bitmask: Int, n numberOfBits: Int) -> [Int] {\n    let indices = (0..<numberOfBits).filter { index in\n      return (bitmask >> index) & 0x1 == 0x1\n    }\n    return indices\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/Box.swift",
    "content": "import FirebladeECS\n\npublic class Box<T> {\n  public var value: T\n\n  public init(_ value: T) {\n    self.value = value\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/Character.swift",
    "content": "extension Unicode.Scalar {\n  var isPrintable: Bool {\n    switch properties.generalCategory {\n      case .closePunctuation, .connectorPunctuation, .currencySymbol, .dashPunctuation, .decimalNumber, .enclosingMark, .finalPunctuation, .initialPunctuation, .letterNumber, .lineSeparator, .lowercaseLetter, .mathSymbol, .modifierLetter, .modifierSymbol, .nonspacingMark, .openPunctuation, .otherLetter, .otherNumber, .otherPunctuation, .otherSymbol, .paragraphSeparator, .spaceSeparator, .spacingMark, .titlecaseLetter, .uppercaseLetter:\n        return true\n      default:\n        return false\n    }\n  }\n}\n\nextension Character {\n  var isPrintable: Bool {\n    return unicodeScalars.contains(where: { $0.isPrintable })\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/ColorUtil.swift",
    "content": "enum ColorUtil {}\n\n// TODO: Move ColorUtil to Client target (instead of Core) because it's not cross-platform\n\n#if canImport(AppKit)\nimport AppKit\n\nextension NSColor {\n  /// Creates `NSColor` from hex string\n  ///\n  /// - Parameters:\n  ///   - hexString: the hex string representation of the color\n  ///   - alpha: the color alpha\n  convenience init?(hexString: String, alpha: CGFloat = 1) {\n    var chars = Array(hexString.hasPrefix(\"#\") ? hexString.dropFirst() : hexString[...])\n\n    switch chars.count {\n      case 3: chars = chars.flatMap { [$0, $0] }\n      case 6: break\n      default: return nil\n    }\n\n    guard\n      let red = Int(String(chars[0..<2]), radix: 16),\n      let green = Int(String(chars[2..<4]), radix: 16),\n      let blue = Int(String(chars[4..<6]), radix: 16)\n    else {\n      return nil\n    }\n\n    self.init(\n      red: CGFloat(red) / 255,\n      green: CGFloat(green) / 255,\n      blue: CGFloat(blue) / 255,\n      alpha: alpha\n    )\n  }\n}\n\nextension ColorUtil {\n  static func color(fromHexString hexString: String, alpha: CGFloat = 1) -> NSColor? {\n    return NSColor(hexString: hexString)\n  }\n}\n#elseif canImport(UIKit)\nimport UIKit\n\nextension UIColor {\n  /// Creates `UIColor` from hex string\n  ///\n  /// - Parameters:\n  ///   - hexString: the hex string representation of the color\n  ///   - alpha: the color alpha\n  convenience init?(hexString: String, alpha: CGFloat = 1) {\n    var chars = Array(hexString.hasPrefix(\"#\") ? hexString.dropFirst() : hexString[...])\n\n    switch chars.count {\n      case 3: chars = chars.flatMap { [$0, $0] }\n      case 6: break\n      default: return nil\n    }\n\n    guard\n      let red = Int(String(chars[0..<2]), radix: 16),\n      let green = Int(String(chars[2..<4]), radix: 16),\n      let blue = Int(String(chars[4..<6]), radix: 16)\n    else {\n      return nil\n    }\n\n    self.init(\n      red: CGFloat(red) / 255,\n      green: CGFloat(green) / 255,\n      blue: CGFloat(blue) / 255,\n      alpha: alpha\n    )\n  }\n}\n\nextension ColorUtil {\n  static func color(fromHexString hexString: String, alpha: CGFloat = 1) -> UIColor? {\n    return UIColor(hexString: hexString)\n  }\n}\n#endif\n"
  },
  {
    "path": "Sources/Core/Sources/Util/CompressionUtil.swift",
    "content": "import Foundation\nimport Z\n\nenum CompressionError: LocalizedError {\n  case compressionFailed(error: Int)\n  case decompressionFailed(error: Int)\n\n  var errorDescription: String? {\n    switch self {\n      case .compressionFailed(let error):\n        return \"\"\"\n        Zlib compression failed.\n        Error code: \\(error)\n        \"\"\"\n      case .decompressionFailed(let error):\n        return \"\"\"\n        Zlib decompression failed.\n        Error code: \\(error)\n        \"\"\"\n    }\n  }\n}\n\nstruct CompressionUtil {\n  static func decompress(_ bytes: [UInt8], decompressedLength: Int) throws -> [UInt8] {\n    let compressedData = Data(bytes: bytes, count: bytes.count)\n\n    // Decompression only works if the first two bytes are removed no idea why, it's probably\n    // something to do with headers or magic numbers\n    var compressedBytes = [UInt8](compressedData)\n\n    var length = UInt(decompressedLength)\n    let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: decompressedLength)\n    let err = Z.uncompress(\n      buffer,\n      &length,\n      &compressedBytes,\n      UInt(compressedBytes.count)\n    )\n\n    if err != Z_OK {\n      throw CompressionError.decompressionFailed(error: Int(err))\n    }\n\n    if length != decompressedLength {\n      log.warning(\"actual decompressed length does not match length in packet\")\n    }\n\n    let data = Data(bytes: buffer, count: Int(length))\n    buffer.deallocate()\n    return [UInt8](data)\n  }\n\n  static func compress(_ bytes: [UInt8]) throws -> [UInt8] {\n    var uncompressed = bytes // mutable copy\n    let compressed = UnsafeMutablePointer<UInt8>.allocate(capacity: uncompressed.count)\n    var compressedLength = UInt(uncompressed.count)\n    let err = Z.compress(\n      compressed,\n      &compressedLength,\n      &uncompressed,\n      UInt(uncompressed.count)\n    )\n\n    if err != Z_OK {\n      throw CompressionError.compressionFailed(error: Int(err))\n    }\n\n    let data = Data(bytes: compressed, count: Int(compressedLength))\n    return [UInt8](data)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/ContentType.swift",
    "content": "import Foundation\n\nenum ContentType: String {\n  case text = \"text/plain\"\n  case json = \"application/json\"\n  case form = \"application/x-www-form-urlencoded\"\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/CryptoUtil.swift",
    "content": "import Foundation\nimport ASN1Parser\nimport BigInt\nimport CryptoSwift\n\nenum CryptoError: LocalizedError {\n  case invalidDERCertificate\n  case failedToEncryptRSA\n  case failedToParseDERPublicKey(Error)\n  case plaintextTooLong\n\n  var errorDescription: String? {\n    switch self {\n      case .invalidDERCertificate:\n        return \"Invalid DER certificate.\"\n      case .failedToEncryptRSA:\n        return \"Failed to encrypt RSA.\"\n      case .failedToParseDERPublicKey(let error):\n        return \"\"\"\n        Failed to parse DER-encoded RSA public key.\n        Reason: \\(error)\n        \"\"\"\n      case .plaintextTooLong:\n        return \"The plaintext provided for RSA encryption was too long.\"\n    }\n  }\n}\n\n// TODO: Implement tests for CryptoUtil, it should be very testable\n\nstruct CryptoUtil {\n  static func generateRandomBytes(_ length: Int) throws -> [UInt8] {\n    return AES.randomIV(length)\n  }\n\n  static func sha1MojangDigest(_ contents: [Data]) throws -> String {\n    var digest = SHA1()\n    for data in contents {\n      _ = try digest.update(withBytes: [UInt8](data))\n    }\n    let bytes = try digest.finish()\n\n    // Do some weird stuff to convert to a signed hex string without doing biginteger stuff (Mojang\n    // uses this weird custom type of hash)\n    var isNegative = false\n\n    let firstBit = (bytes[0] & 0x80) >> 7\n    isNegative = firstBit == 1\n\n    let hexStrings: [String] = bytes.enumerated().map { index, inByte in\n      var byte = inByte\n      if isNegative {\n        byte = 255 - byte\n      }\n      if index == 19 && isNegative { // last byte\n        byte += 1\n      }\n      return String(format: \"%02x\", byte)\n    }\n    var string = isNegative ? \"-\" : \"\"\n    string += hexStrings.joined()\n    return string\n  }\n\n  static func generateRSAPadding(_ length: Int) throws -> [UInt8] {\n    // TODO: Make a faster version of this maybe (currently it's technically non-deterministic)\n    var output: [UInt8] = []\n    while output.count < length {\n      output.append(contentsOf: try generateRandomBytes(length - output.count))\n      output = output.filter { $0 != 0 }\n    }\n    return output\n  }\n\n  /// Implements the padded encryption operation outlined by the\n  /// [PKCS1 RFC document](https://datatracker.ietf.org/doc/html/rfc3447#section-7.2.1).\n  static func encryptRSA(data: Data, publicKeyDERData: Data) throws -> Data {\n    let modulus: BigInt\n    let exponent: BigInt\n    do {\n      let der = try DERParser.parse(der: publicKeyDERData)\n      let bitString = try der.asSequence[1].asBitString\n      let sequence = try DERParser.parse(der: Data(bitString.bytes)).asSequence\n\n      modulus = try sequence[0].asInt.value // n\n      exponent = try sequence[1].asInt.value // e\n    } catch {\n      throw CryptoError.failedToParseDERPublicKey(error)\n    }\n\n    // I've implemented RSA (with PKCS1 padding) myself because OpenSSL hates me (it's mutual)\n    let modulusLength = modulus.serialize().count - 1 // k\n    guard data.count <= modulusLength - 11 else {\n      throw CryptoError.plaintextTooLong\n    }\n\n    let plaintext = [UInt8](data)\n    let paddingLength = modulusLength - plaintext.count - 3\n    let padding = try generateRSAPadding(paddingLength)\n    let paddedPlaintextBytes = [0, 2] + padding + [0] + plaintext\n    let paddedPlaintext = BigInt(Data(paddedPlaintextBytes))\n\n    let ciphertext = paddedPlaintext.power(exponent, modulus: modulus)\n    var ciphertextBytes = [UInt8](ciphertext.serialize())\n    if ciphertextBytes[0] == 0 {\n      ciphertextBytes.removeFirst()\n    }\n    if ciphertextBytes.count < modulusLength {\n      let cipherPadding = [UInt8](repeating: 0, count: modulusLength - ciphertextBytes.count)\n      ciphertextBytes = cipherPadding + ciphertextBytes\n    }\n\n    return Data(ciphertextBytes)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/CustomJSONDecoder.swift",
    "content": "import Foundation\n#if canImport(ZippyJSON)\nimport ZippyJSON\n#endif\n\npublic struct CustomJSONDecoder {\n  #if canImport(ZippyJSON)\n  public var keyDecodingStrategy: ZippyJSONDecoder.KeyDecodingStrategy = ZippyJSONDecoder.KeyDecodingStrategy.useDefaultKeys\n  #else\n  public var keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = JSONDecoder.KeyDecodingStrategy.useDefaultKeys\n  #endif\n\n  public init() {}\n\n  public func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T {\n    #if canImport(ZippyJSON)\n    let decoder = ZippyJSONDecoder()\n    #else\n    let decoder = JSONDecoder()\n    #endif\n    decoder.keyDecodingStrategy = self.keyDecodingStrategy\n    return try decoder.decode(type, from: data)\n  }\n}"
  },
  {
    "path": "Sources/Core/Sources/Util/Dictionary.swift",
    "content": "import Foundation\n\nextension Dictionary {\n  init(values: [Value], keyedBy key: KeyPath<Value, Key>) {\n    self.init()\n    for value in values {\n      self[value[keyPath: key]] = value\n    }\n  }\n\n  mutating func mutatingEach(_ update: (Key, inout Value) -> Void) {\n    for key in keys {\n      update(key, &(self[key]!))\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/Either.swift",
    "content": "import Foundation\n\npublic enum Either<Left, Right> {\n  case left(Left)\n  case right(Right)\n}\n\nextension Either: Decodable where Left: Decodable, Right: Decodable {\n  public init(from decoder: Decoder) throws {\n    do {\n      self = .left(try Left(from: decoder))\n    } catch {\n      self = .right(try Right(from: decoder))\n    }\n  }\n}\n\nextension Either: Encodable where Left: Encodable, Right: Encodable {\n  public func encode(to encoder: Encoder) throws {\n    switch self {\n      case let .left(value):\n        try value.encode(to: encoder)\n      case let .right(value):\n        try value.encode(to: encoder)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/FileManager.swift",
    "content": "import Foundation\n\nextension FileManager {\n  func directoryExists(at url: URL) -> Bool {\n    var isDirectory: ObjCBool = false\n    let itemExists = fileExists(atPath: url.path, isDirectory: &isDirectory)\n    return itemExists && isDirectory.boolValue\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/FontUtil.swift",
    "content": "public enum FontUtil {}\n\n// TODO: Move to Delta Client because it's not cross-platform\n\n#if canImport(AppKit)\nimport AppKit\n\nextension NSFont {\n  public func withTraits(_ traits: NSFontDescriptor.SymbolicTraits) -> NSFont? {\n    let descriptor = fontDescriptor.withSymbolicTraits(traits)\n    return NSFont(descriptor: descriptor, size: pointSize)\n  }\n\n  public func italics() -> NSFont? {\n    return withTraits(.italic)\n  }\n\n  public func bold() -> NSFont? {\n    return withTraits(.bold)\n  }\n}\n\nextension FontUtil {\n  public static func systemFont(ofSize size: CGFloat) -> NSFont {\n    return NSFont.systemFont(ofSize: size)\n  }\n\n  public static func systemFontSize(for size: NSControl.ControlSize) -> CGFloat {\n    return NSFont.systemFontSize(for: size)\n  }\n}\n#elseif canImport(UIKit)\nimport UIKit\n\nextension UIFont {\n  public func withTraits(_ traits: UIFontDescriptor.SymbolicTraits) -> UIFont? {\n    guard let descriptor = fontDescriptor.withSymbolicTraits(traits) else {\n      return nil\n    }\n    return UIFont(descriptor: descriptor, size: pointSize)\n  }\n\n  public func italics() -> UIFont? {\n    return withTraits(.traitItalic)\n  }\n\n  public func bold() -> UIFont? {\n    return withTraits(.traitBold)\n  }\n}\n\nextension FontUtil {\n  public enum ControlSize {\n    case regular\n  }\n\n  public static func systemFont(ofSize size: CGFloat) -> UIFont {\n    return UIFont.systemFont(ofSize: size)\n  }\n\n  public static func systemFontSize(for: ControlSize) -> CGFloat {\n    return 14\n  }\n}\n#endif\n"
  },
  {
    "path": "Sources/Core/Sources/Util/FunctionalProgramming.swift",
    "content": "/// The identity function.\nfunc identity<T>(_ value: T) -> T {\n  value\n}\n\nfunc swap<T>(_ left: inout T, _ right: inout T) {\n  let temp = left\n  left = right\n  right = temp\n}\n\nfunc constant<A, B>(_ value: B) -> (A) -> B {\n  { _ in\n    value\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/Image.swift",
    "content": "import Foundation\nimport SwiftImage\nimport PNG\n\npublic enum ImageError: LocalizedError {\n  case failedToReadPNGFile\n\n  public var errorDescription: String? {\n    switch self {\n      case .failedToReadPNGFile:\n        return \"Failed to read PNG file.\"\n    }\n  }\n}\n\nextension Image where Pixel == SwiftImage.RGBA<UInt8> {\n  init(fromPNGFile file: URL) throws {\n    guard let image = try PNG.Data.Rectangular.decompress(path: file.path) else {\n      throw ImageError.failedToReadPNGFile\n    }\n\n    let pngPixels = image.unpack(as: PNG.RGBA<UInt8>.self)\n    let pixels = Array<SwiftImage.RGBA<UInt8>>(unsafeUninitializedCapacity: pngPixels.count) { buffer, count in\n      for (i, pixel) in pngPixels.enumerated() {\n        buffer[i] = SwiftImage.RGBA<UInt8>(\n          red: pixel.r,\n          green: pixel.g,\n          blue: pixel.b,\n          alpha: pixel.a\n        )\n      }\n      count = pngPixels.count\n    }\n    self.init(width: image.size.x, height: image.size.y, pixels: pixels)\n  }\n}\n\n"
  },
  {
    "path": "Sources/Core/Sources/Util/Int.swift",
    "content": "import Foundation\n\nextension Int {\n  /// Whether the integer is a positive power of two or not.\n  public var isPowerOfTwo: Bool {\n    return (self > 0) && (self & (self - 1) == 0)\n  }\n\n  /// The integer as hex (including the `0x` prefix).\n  public var hexWithPrefix: String {\n    \"0x\" + hex\n  }\n\n  /// The integer as hex (without any prefix).\n  public var hex: String {\n    String(self, radix: 16)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/MathUtil.swift",
    "content": "import Foundation\nimport FirebladeMath\n\npublic enum MathError: LocalizedError {\n  case invalidVectorLength(_ length: Int)\n\n  public var errorDescription: String? {\n    switch self {\n      case .invalidVectorLength(let elementsCount):\n        return \"Invalid 3D vector. 3D vector should have exactly 3 elements, but got \\(elementsCount) instead.\"\n    }\n  }\n}\n\n/// Some utility functions to do with maths.\npublic enum MathUtil {\n  /// Converts the given angle to radians from degrees.\n  public static func radians<T: FloatingPoint>(from degrees: T) -> T {\n    return .pi * degrees / 180\n  }\n\n  /// Converts the given angle to degrees from radians.\n  public static func degrees<T: FloatingPoint>(from radians: T) -> T {\n    return 180 * radians / .pi\n  }\n\n  /// Converts the given angle to radians from degrees.\n  public static func radians(from degrees: Vec3f) -> Vec3f {\n    return .pi * degrees / 180\n  }\n\n  /// Converts the given angle to degrees from radians.\n  public static func degrees(from radians: Vec3f) -> Vec3f {\n    return 180 * radians / .pi\n  }\n\n  /// Linearly interpolates between two floating point values.\n  /// - Parameters:\n  ///   - initial: The initial value.\n  ///   - target: The target value.\n  ///   - progress: The progress from the initial value to the target value (gets clamped between 0 and 1).\n  /// - Returns: The current value, linearly interpolated between the initial and target values.\n  public static func lerp<T: FloatingPoint>(from initial: T, to target: T, progress: T) -> T {\n    let progress = MathUtil.clamp(progress, 0, 1)\n    return initial + progress * (target - initial)\n  }\n\n  // Linearly interpolates the individual components of two vectors.\n  public static func lerp<T: FloatingPoint>(from initial: Vec2<T>, to target: Vec2<T>, progress: T) -> Vec2<T> {\n    return Vec2(\n      lerp(from: initial.x, to: target.x, progress: progress),\n      lerp(from: initial.y, to: target.y, progress: progress)\n    )\n  }\n\n  // Linearly interpolates the individual components of two vectors.\n  public static func lerp<T: FloatingPoint>(from initial: Vec3<T>, to target: Vec3<T>, progress: T) -> Vec3<T> {\n    return Vec3(\n      lerp(from: initial.x, to: target.x, progress: progress),\n      lerp(from: initial.y, to: target.y, progress: progress),\n      lerp(from: initial.z, to: target.z, progress: progress)\n    )\n  }\n\n  // Linearly interpolates the individual components of two vectors.\n  public static func lerp<T: FloatingPoint>(from initial: Vec4<T>, to target: Vec4<T>, progress: T) -> Vec4<T> {\n    return Vec4(\n      lerp(from: initial.x, to: target.x, progress: progress),\n      lerp(from: initial.y, to: target.y, progress: progress),\n      lerp(from: initial.z, to: target.z, progress: progress),\n      lerp(from: initial.w, to: target.z, progress: progress)\n    )\n  }\n\n  /// Linearly interpolates between two angles (in radians).\n  /// - Parameters:\n  ///   - initial: The initial angle in radians.\n  ///   - target: The target angle in radians.\n  ///   - progress: The progress from the initial angle to the target angle (gets clamped between 0 and 1).\n  /// - Returns: The current angle, linearly interpolated between the initial and target angles.\n  public static func lerpAngle<T: FloatingPoint>(from initial: T, to target: T, progress: T) -> T {\n    let max: T = .pi * 2\n    // Difference is the smallest angle between the initial and target angles (for example, lerping from 359 degrees to 0, the difference would be 1)\n    var difference = (target - initial).remainder(dividingBy: max)\n    difference = (2 * difference).remainder(dividingBy: max) - difference\n    let progress = MathUtil.clamp(progress, 0, 1)\n    return initial + progress * difference\n  }\n\n  /// Calculates `a` (mod `n`). `n` must be positive.\n  ///\n  /// Swift's `%` isn't actually mod, it is remainder and doesn't behave the\n  /// same way for negative numbers. See https://stackoverflow.com/a/41180619\n  public static func mod(_ a: Int, _ n: Int) -> Int {\n    precondition(n > 0, \"Modulus must be positive\")\n    let r = a % n\n    return r >= 0 ? r : r + n\n  }\n\n  /// Converts an array of doubles to a 3d vector. Throws if the array doesn't have exactly 3 elements.\n  public static func vectorFloat3(from doubleArray: [Double]) throws -> Vec3f {\n    guard doubleArray.count == 3 else {\n      throw MathError.invalidVectorLength(doubleArray.count)\n    }\n\n    return Vec3f(doubleArray.map { Float($0) })\n  }\n\n  /// Checks for approximate equality between two floats.\n  /// - Parameters:\n  ///   - a: The first float.\n  ///   - b: The second float.\n  ///   - absoluteTolerance: The tolerance for what is acceptable equality.\n  /// - Returns: The approximate equality.\n  public static func checkFloatEquality(_ a: Float, _ b: Float, absoluteTolerance: Float) -> Bool {\n    // TODO: use swift numerics package instead\n    return Swift.abs(a - b) <= absoluteTolerance\n  }\n\n  public static func checkFloatLessThan(value a: Float, compareTo b: Float, absoluteTolerance: Float) -> Bool {\n    // TODO: use swift numerics package instead\n    return a - b < absoluteTolerance\n  }\n\n  public static func checkFloatGreaterThan(value a: Float, compareTo b: Float, absoluteTolerance: Float) -> Bool {\n    // TODO: use swift numerics package instead\n    return b - a < absoluteTolerance\n  }\n\n  /// Clamps a given value between a maximum and minimum value.\n  /// - Parameters:\n  ///   - value: The value to clamp.\n  ///   - minValue: The minimum value.\n  ///   - maxValue: The maximum value.\n  /// - Returns: The clamped value.\n  @_specialize(where T:_Trivial)\n  public static func clamp<T>(_ value: T, _ minValue: T, _ maxValue: T) -> T where T: Comparable {\n    // TODO: Add `min` and `max` labels to clamp for clarity\n    return Swift.min(Swift.max(value, minValue), maxValue)\n  }\n\n  /// Clamps the components of a given vector between a maximum and minimum value.\n  /// - Parameters:\n  ///   - vector: The vector to clamp.\n  ///   - minValue: The minimum value.\n  ///   - maxValue: The maximum value.\n  /// - Returns: The clamped value.\n  @_specialize(where T:_Trivial)\n  public static func clamp<T: SIMD>(_ vector: T, min minValue: T.Scalar, max maxValue: T.Scalar) -> T where T.Scalar: Comparable {\n    var vector = vector\n    for i in vector.indices {\n      vector[i] = Swift.min(Swift.max(vector[i], minValue), maxValue)\n    }\n    return vector\n  }\n\n  @_specialize(where T:_Trivial)\n  public static func min<T: SIMD>(_ vector: T, _ other: T) -> T where T.Scalar: Comparable {\n    var out = vector\n    for i in vector.indices {\n      out[i] = Swift.min(vector[i], other[i])\n    }\n    return out\n  }\n\n  @_specialize(where T:_Trivial)\n  public static func max<T: SIMD>(_ vector: T, _ other: T) -> T where T.Scalar: Comparable {\n    var out = vector\n    for i in vector.indices {\n      out[i] = Swift.max(vector[i], other[i])\n    }\n    return out\n  }\n\n  /// Takes the absolute value of each element of a vector.\n  /// - Parameters:\n  ///   - vector: The vector.\n  /// - Returns: The absolute vector.\n  @_specialize(where T:_Trivial)\n  public static func abs<T: SIMD>(_ vector: T) -> T where T.Scalar: Comparable & SignedNumeric {\n    var vector = vector\n    for i in vector.indices {\n      vector[i] = Swift.abs(vector[i])\n    }\n    return vector\n  }\n\n  /// Computes a vector containing the sign of each component.\n  /// - Parameters:\n  ///   - vector: The vector.\n  /// - Returns: The absolute vector.\n  public static func sign<T: SIMD>(_ vector: T) -> T where T.Scalar == Float {\n    var vector = vector\n    for i in vector.indices {\n      vector[i] = FirebladeMath.sign(vector[i])\n    }\n    return vector\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/Matrix.swift",
    "content": "import FirebladeMath\n\nextension Matrix2x2 {\n  public init(diagonal: Value) {\n    self.init(diagonal: Vector(repeating: diagonal))\n  }\n}\n\nextension Matrix3x3 {\n  public init(diagonal: Value) {\n    self.init(diagonal: Vector(repeating: diagonal))\n  }\n}\n\nextension Matrix4x4 {\n  public init(diagonal: Value) {\n    self.init(diagonal: Vector(repeating: diagonal))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/MatrixUtil.swift",
    "content": "import Foundation\nimport FirebladeMath\n\npublic enum MatrixUtil {\n  public static var identity = Mat4x4f(diagonal: 1)\n\n  public static func translationMatrix(_ translation: Vec3f) -> Mat4x4f {\n    var matrix = Mat4x4f(diagonal: 1)\n    matrix[0, 3] = translation.x\n    matrix[1, 3] = translation.y\n    matrix[2, 3] = translation.z\n    return matrix\n  }\n\n  public static func translationMatrix(_ translation: Vec3d) -> Mat4x4d {\n    var matrix = Mat4x4d(diagonal: 1)\n    matrix[0, 3] = translation.x\n    matrix[1, 3] = translation.y\n    matrix[2, 3] = translation.z\n    return matrix\n  }\n\n  public static func scalingMatrix(_ factor: Float) -> Mat4x4f {\n    return scalingMatrix(factor, factor, factor)\n  }\n\n  public static func scalingMatrix(_ factor: Double) -> Mat4x4d {\n    return scalingMatrix(factor, factor, factor)\n  }\n\n  public static func scalingMatrix(_ x: Float, _ y: Float, _ z: Float) -> Mat4x4f {\n    return scalingMatrix(Vec3f(x, y, z))\n  }\n\n  public static func scalingMatrix(_ x: Double, _ y: Double, _ z: Double) -> Mat4x4d {\n    return scalingMatrix(Vec3d(x, y, z))\n  }\n\n  public static func scalingMatrix(_ vector: Vec3f) -> Mat4x4f {\n    return Mat4x4f(diagonal: Vec4f(vector, 1))\n  }\n\n  public static func scalingMatrix(_ vector: Vec3d) -> Mat4x4d {\n    return Mat4x4d(diagonal: Vec4d(vector, 1))\n  }\n\n  public static func projectionMatrix(near: Float, far: Float, aspect: Float, fieldOfViewY: Float) -> Mat4x4f {\n    let scaleY = 1 / Foundation.tan(fieldOfViewY * 0.5)\n    let scaleX = scaleY / aspect\n    let scaleZ = -far / (far - near)\n    let scaleW = -far * near / (far - near)\n    return Mat4x4f([\n      Vec4f([scaleX, 0, 0, 0]),\n      Vec4f([0, scaleY, 0, 0]),\n      Vec4f([0, 0, scaleZ, scaleW]),\n      Vec4f([0, 0, -1, 0])\n    ])\n  }\n\n  public static func projectionMatrix(near: Double, far: Double, aspect: Double, fieldOfViewY: Double) -> Mat4x4d {\n    let scaleY = 1 / Foundation.tan(fieldOfViewY * 0.5)\n    let scaleX = scaleY / aspect\n    let scaleZ = -far / (far - near)\n    let scaleW = -far * near / (far - near)\n    return Mat4x4d([\n      Vec4d([scaleX, 0, 0, 0]),\n      Vec4d([0, scaleY, 0, 0]),\n      Vec4d([0, 0, scaleZ, scaleW]),\n      Vec4d([0, 0, -1, 0])\n    ])\n  }\n\n  /// Returns the rotation matrix applying the rotations in the order of x, then y and then z.\n  public static func rotationMatrix(_ rotation: Vec3f) -> Mat4x4f {\n    let matrix = rotationMatrix(x: rotation.x)\n      * rotationMatrix(y: rotation.y)\n      * rotationMatrix(z: rotation.z)\n    return matrix\n  }\n\n  /// Returns the rotation matrix applying the rotations in the order of x, then y and then z.\n  public static func rotationMatrix(_ rotation: Vec3d) -> Mat4x4d {\n    let matrix = rotationMatrix(x: rotation.x)\n      * rotationMatrix(y: rotation.y)\n      * rotationMatrix(z: rotation.z)\n    return matrix\n  }\n\n  public static func rotationMatrix(_ radians: Float, around axis: Axis) -> Mat4x4f {\n    switch axis {\n      case .x:\n        return rotationMatrix(x: radians)\n      case .y:\n        return rotationMatrix(y: radians)\n      case .z:\n        return rotationMatrix(z: radians)\n    }\n  }\n\n  public static func rotationMatrix(_ radians: Double, around axis: Axis) -> Mat4x4d {\n    switch axis {\n      case .x:\n        return rotationMatrix(x: radians)\n      case .y:\n        return rotationMatrix(y: radians)\n      case .z:\n        return rotationMatrix(z: radians)\n    }\n  }\n\n  public static func rotationMatrix(x: Float) -> Mat4x4f {\n    let matrix = Mat4x4f([\n      Vec4f(1, 0, 0, 0),\n      Vec4f(\n        0,\n        Foundation.cos(x),\n        Foundation.sin(x),\n        0\n      ),\n      Vec4f(\n        0,\n        -Foundation.sin(x),\n        Foundation.cos(x),\n        0\n      ),\n      Vec4f(0, 0, 0, 1)\n    ])\n    return matrix\n  }\n\n  public static func rotationMatrix(x: Double) -> Mat4x4d {\n    let matrix = Mat4x4d([\n      Vec4d(1, 0, 0, 0),\n      Vec4d(\n        0,\n        Foundation.cos(x),\n        Foundation.sin(x),\n        0\n      ),\n      Vec4d(\n        0,\n        -Foundation.sin(x),\n        Foundation.cos(x),\n        0\n      ),\n      Vec4d(0, 0, 0, 1)\n    ])\n    return matrix\n  }\n\n  public static func rotationMatrix(y: Float) -> Mat4x4f {\n    let matrix = Mat4x4f([\n      Vec4f(\n        Foundation.cos(y),\n        0,\n        -Foundation.sin(y),\n        0\n      ),\n      Vec4f(0, 1, 0, 0),\n      Vec4f(\n        Foundation.sin(y),\n        0,\n        Foundation.cos(y),\n        0\n      ),\n      Vec4f(0, 0, 0, 1)\n    ])\n    return matrix\n  }\n\n  public static func rotationMatrix(y: Double) -> Mat4x4d {\n    let matrix = Mat4x4d([\n      Vec4d(\n        Foundation.cos(y),\n        0,\n        -Foundation.sin(y),\n        0\n      ),\n      Vec4d(0, 1, 0, 0),\n      Vec4d(\n        Foundation.sin(y),\n        0,\n        Foundation.cos(y),\n        0\n      ),\n      Vec4d(0, 0, 0, 1)\n    ])\n    return matrix\n  }\n\n  public static func rotationMatrix(z: Float) -> Mat4x4f {\n    let matrix = Mat4x4f([\n      Vec4f(\n        Foundation.cos(z),\n        -Foundation.sin(z),\n        0,\n        0\n      ),\n      Vec4f(\n        Foundation.sin(z),\n        Foundation.cos(z),\n        0,\n        0\n      ),\n      Vec4f(0, 0, 1, 0),\n      Vec4f(0, 0, 0, 1)\n    ])\n    return matrix\n  }\n\n  public static func rotationMatrix(z: Double) -> Mat4x4d {\n    let matrix = Mat4x4d([\n      Vec4d(\n        Foundation.cos(z),\n        -Foundation.sin(z),\n        0,\n        0\n      ),\n      Vec4d(\n        Foundation.sin(z),\n        Foundation.cos(z),\n        0,\n        0\n      ),\n      Vec4d(0, 0, 1, 0),\n      Vec4d(0, 0, 0, 1)\n    ])\n    return matrix\n  }\n\n  public static func rotationMatrix2d(_ angle: Float) -> Mat2x2f {\n    return Mat2x2f(\n      Vec2f(Foundation.cos(angle), -Foundation.sin(angle)),\n      Vec2f(Foundation.sin(angle), Foundation.cos(angle))\n    )\n  }\n\n  public static func rotationMatrix2d(_ angle: Double) -> Mat2x2d {\n    return Mat2x2d(\n      Vec2d(Foundation.cos(angle), -Foundation.sin(angle)),\n      Vec2d(Foundation.sin(angle), Foundation.cos(angle))\n    )\n  }\n\n  public static func matrix4x4to3x3(_ matrix: Mat4x4f) -> Mat3x3f {\n    return Mat3x3f([\n      Vec3f(\n        matrix.columns.0.x,\n        matrix.columns.0.y,\n        matrix.columns.0.z\n      ),\n      Vec3f(\n        matrix.columns.1.x,\n        matrix.columns.1.y,\n        matrix.columns.1.z\n      ),\n      Vec3f(\n        matrix.columns.2.x,\n        matrix.columns.2.y,\n        matrix.columns.2.z\n      )\n    ])\n  }\n\n  public static func matrix4x4to3x3(_ matrix: Mat4x4d) -> Mat3x3d {\n    return Mat3x3d([\n      Vec3d(\n        matrix.columns.0.x,\n        matrix.columns.0.y,\n        matrix.columns.0.z\n      ),\n      Vec3d(\n        matrix.columns.1.x,\n        matrix.columns.1.y,\n        matrix.columns.1.z\n      ),\n      Vec3d(\n        matrix.columns.2.x,\n        matrix.columns.2.y,\n        matrix.columns.2.z\n      )\n    ])\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/Profiler.swift",
    "content": "import Foundation\nimport CoreFoundation\nimport OrderedCollections\n\n/// A simple stack-based profiler with type safe measurement labels if required.\npublic final class Profiler<Label: RawRepresentable & Hashable> where Label.RawValue == String {\n  public var name: String?\n  public var verbose: Bool\n\n  struct Measurement {\n    var label: Label\n    var seconds: Double\n    var children: [Measurement]\n  }\n\n  private var children: [[Measurement]] = []\n  private var measurementStarts: [(label: Label, start: Double)] = []\n  private var measurements: [Measurement] = []\n  private var measurementOrder: [Label] = []\n  private var trials: [[Measurement]] = []\n\n  /// Creates a new profiler.\n  /// - Parameters:\n  ///   - name: The profiler's display name.\n  ///   - verbose: If `true`, measurements are logged as soon as they are finished.\n  public init(_ name: String? = nil, verbose: Bool = false) {\n    self.name = name\n    self.verbose = verbose\n  }\n\n  /// Measures the execution time of a closure.\n  /// - Parameters:\n  ///   - label: The label for the measurement.\n  ///   - task: The task to measure the execution time of.\n  public func measure(_ label: Label, _ task: () throws -> Void) rethrows {\n    push(label)\n    try task()\n    pop()\n  }\n\n  /// Gets the current time in seconds.\n  public func currentSeconds() -> Double {\n    return CFAbsoluteTimeGetCurrent()\n  }\n\n  public func push(_ label: Label) {\n    measurementStarts.append((label: label, start: currentSeconds()))\n    measurementOrder.append(label)\n    children.append([])\n  }\n\n  public func pop() {\n    assert(!measurementStarts.isEmpty, \"pop called without matching push\")\n    guard\n      let (label, start) = measurementStarts.popLast(),\n      let measurementChildren = children.popLast()\n    else {\n      log.warning(\"Invalid use of profiler: pop called without matching push\")\n      return\n    }\n\n    let elapsed = currentSeconds() - start\n    let measurement = Measurement(\n      label: label,\n      seconds: elapsed,\n      children: measurementChildren\n    )\n\n    if children.isEmpty {\n      // If at top level, store the measurement\n      measurements.append(measurement)\n    } else {\n      // Otherwise, store it as a child of the parent for when the parent finishes\n      children[children.count - 1].append(measurement)\n    }\n\n    if verbose {\n      var message = \"\\(label.rawValue): \\(String(format: \"%.5fms\", elapsed * 1000))\"\n      if let name = name {\n        message = \"\\(name), \\(message)\"\n      }\n      log.debug(message)\n    }\n  }\n\n  public func endTrial() {\n    trials.append(measurements)\n    measurements = []\n  }\n\n  public func clear() {\n    children = []\n    measurementStarts = []\n    measurements = []\n    measurementOrder = []\n    trials = []\n  }\n\n  public func printSummary(onlyLatestTrial: Bool = false) {\n    let measurements: [Measurement]\n    if onlyLatestTrial {\n      measurements = trials.last ?? []\n    } else {\n      var trials = trials\n      if !self.measurements.isEmpty {\n        trials.append(self.measurements)\n      }\n      measurements = Self.average(trials)\n    }\n\n    print(\"=== Start profiler summary ===\")\n    printMeasurements(measurements)\n    print(\"===  End profiler summary  ===\")\n  }\n\n  private func printMeasurements(_ measurements: [Measurement], indent: Int = 0) {\n    let longestLabel = measurements.map(\\.label.rawValue.count).max() ?? 0\n    let indentString = String(repeating: \" \", count: indent)\n    for measurement in measurements {\n      let measurementString = String(\n        format: \"%.5fms\",\n        measurement.seconds * 1000\n      )\n      let paddingWidth = longestLabel - measurement.label.rawValue.count\n      let padding = String(repeating: \" \", count: paddingWidth)\n      print(\"\\(indentString)\\(measurement.label.rawValue)\\(padding) \\(measurementString)\")\n\n      printMeasurements(measurement.children, indent: indent + 2)\n    }\n  }\n\n  /// Averages a set of trials, assuming that they all contain the same structure of measurements.\n  private static func average(_ trials: [[Measurement]]) -> [Measurement] {\n    // Get the set of top level labels\n    let labels = OrderedSet(trials.flatMap { trial in\n      return trial.map(\\.label)\n    })\n\n    // Average measurements with the same label\n    var averagedMeasurements: [Measurement] = []\n    for label in labels {\n      let measurements = trials.compactMap { trial in\n        return trial.first { measurement in\n          return measurement.label == label\n        }\n      }\n\n      guard !measurements.isEmpty else {\n        continue\n      }\n\n      let durations = measurements.map(\\.seconds)\n      let averageSeconds = durations.reduce(0, +) / Double(durations.count)\n      let children = measurements.map(\\.children)\n      let averagedChildren = average(children)\n\n      averagedMeasurements.append(Measurement(\n        label: label,\n        seconds: averageSeconds,\n        children: averagedChildren\n      ))\n    }\n\n    return averagedMeasurements\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/RGBColor.swift",
    "content": "import Foundation\nimport FirebladeMath\n\n/// An RGB color, each component is 1 byte (maximum of 255).\npublic struct RGBColor: Codable {\n  public static var white = RGBColor(r: 255, g: 255, b: 255)\n  public static var black = RGBColor(r: 0, g: 0, b: 0)\n\n  /// The int vector\n  public var vector: Vec3i\n\n  /// A float vector containing the rgb representation of the colour.\n  public var floatVector: Vec3f {\n    return Vec3f(vector) / 255\n  }\n\n  /// The integer representation of the hexcode of this color. 1 byte per component.\n  public var hexCode: Int {\n    return (r << 16) | (g << 8) | b\n  }\n\n  /// The red component of the color.\n  public var r: Int {\n    return vector.x\n  }\n\n  /// The green component of the color.\n  public var g: Int {\n    return vector.y\n  }\n\n  /// The blue component of the color.\n  public var b: Int {\n    return vector.z\n  }\n\n  /// Creates a color from rgb components.\n  ///\n  /// Each component should be between 0 and 255 inclusive.\n  public init(r: Int, g: Int, b: Int) {\n    vector = [r, g, b]\n  }\n\n  /// Creates a color from a 3 byte hex code's integer representation.\n  public init(hexCode: Int) {\n    let r = (hexCode >> 16) & 0xff\n    let g = (hexCode >> 8) & 0xff\n    let b = hexCode & 0xff\n    vector = [r, g, b]\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/Random.swift",
    "content": "import Foundation\n\n/// A Java compatible linear conguential generator.\n///\n/// For the same seed, this RNG generates the exact same sequence as Java java.util.Random.\n/// Based on the specification at https://docs.oracle.com/javase/6/docs/api/java/util/Random.html and\n/// the source code at https://developer.classpath.org/doc/java/util/Random-source.html.\npublic struct Random {\n  public private(set) var seed: Int64\n\n  private static var magic: Int64 = 0x5DEECE66D\n  private static var mask: Int64 = (1 << 48) - 1\n  private static var addend: Int64 = 0xB\n\n  private var nextNextGaussian: Double = 0\n  private var haveNextNextGaussian = false\n\n  public init() {\n    self.init(Int64(NSDate().timeIntervalSince1970 * 1000))\n  }\n\n  public init(_ seed: Int64) {\n    self.seed = Self.scrambleSeed(seed)\n  }\n\n  private static func scrambleSeed(_ seed: Int64) -> Int64 {\n    return (seed ^ magic) & mask\n  }\n\n  private mutating func next(_ bits: Int64) -> Int32 {\n    seed = (seed &* Self.magic &+ Self.addend) & Self.mask\n    return Int32(truncatingIfNeeded: seed >>> (48 &- bits))\n  }\n\n  public mutating func setSeed(_ seed: Int64) {\n    self.seed = Self.scrambleSeed(seed)\n    haveNextNextGaussian = false\n  }\n\n  public mutating func nextLong() -> Int64 {\n    return (Int64(next(32)) << 32) &+ Int64(next(32))\n  }\n\n  public mutating func nextInt() -> Int32 {\n    return next(32)\n  }\n\n  /// Returns a random `Int32` with the given exclusive max.\n  public mutating func nextInt(_ max: Int32) -> Int32 {\n    var bits: Int32\n    var val: Int32\n    repeat {\n      bits = next(31)\n      val = bits % max\n    } while (bits &- val &+ (max &- 1) < 0)\n    return val\n  }\n\n  public mutating func nextBool() -> Bool {\n    return next(1) != 0\n  }\n\n  public mutating func nextFloat() -> Float {\n    return Float(next(24)) / (Float(1 << 24))\n  }\n\n  public mutating func nextDouble() -> Double {\n    return Double((Int64(next(26)) << 27) &+ Int64(next(27))) / Double(1 << 53)\n  }\n\n  public mutating func nextGaussian() -> Double {\n    if haveNextNextGaussian {\n      haveNextNextGaussian = false\n      return nextNextGaussian\n    } else {\n      var v1: Double\n      var v2: Double\n      var s: Double\n      repeat {\n        v1 = 2 * nextDouble() - 1\n        v2 = 2 * nextDouble() - 1\n        s = v1 * v1 + v2 * v2\n      } while s >= 1 || s == 0\n      let multiplier = Foundation.sqrt(-2 * Foundation.log(s) / s)\n      nextNextGaussian = v2 * multiplier\n      haveNextNextGaussian = true\n      return v1 * multiplier\n    }\n  }\n}\n\n// MARK: Operators\n\ninfix operator >>> : BitwiseShiftPrecedence\n\n// swiftlint:disable:next:private_over_fileprivate\nfileprivate func >>> (lhs: Int64, rhs: Int64) -> Int64 {\n  return Int64(bitPattern: UInt64(bitPattern: lhs) >> UInt64(rhs))\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/ReadWriteLock.swift",
    "content": "import Atomics\n\n#if canImport(Darwin)\nimport Darwin\n#elseif canImport(Glibc)\nimport Glibc\n#else\n#error(\"Unsupported platform for ReadWriteLock\")\n#endif\n\n// TODO: Figure out why removing the `guiState` parameter to `compileGUI` in `PlayerInputSystem` doesn't\n//   cause a deadlock every tick (it causes crashes but only in specific conditions). It should cause a\n//   deadlock no matter what cause not passing the `guiState` parameter causes `compileGUI` to acquire a\n//   nexus lock. After some debugging it seemed like somehow the lock was successfully getting to a lockCount\n//   of 2 even though the base nexus lock held by tick scheduler each tick is a write lock (and so is the\n//   lock acquired by compileGUI). That just seems like it shouldn't be possible at all???\n\n/// A wrapper around the rwlock C api (`pthread_rwlock_t`).\n///\n/// Add the `DEBUG_LOCKS` custom flag to enable the `lastLockedBy` property which keeps track of the\n/// latest code to acquire the lock, and the `lockCount`/`lastFullyReleasedBy` properties which\n/// track the total number of locks held on the lock and the code which last unlocked the lock\n/// and brought it to a `lockCount` of 0. These are very useful for debugging deadlocks and double unlocks.\npublic final class ReadWriteLock {\n  private var lock = pthread_rwlock_t()\n\n\n  #if DEBUG_LOCKS\n  public var lastLockedBy: String?\n  public var lastFullyReleasedBy: String?\n  public var lockCount = ManagedAtomic<Int>(0)\n  private var stringLock = pthread_rwlock_t()\n  #endif\n\n  /// Creates a new lock.\n  public init() {\n    pthread_rwlock_init(&lock, nil)\n    #if DEBUG_LOCKS\n    pthread_rwlock_init(&stringLock, nil)\n    #endif\n  }\n\n  deinit {\n    pthread_rwlock_destroy(&lock)\n    #if DEBUG_LOCKS\n    pthread_rwlock_destroy(&stringLock)\n    #endif\n  }\n\n  #if DEBUG_LOCKS\n  /// Acquire the lock for reading.\n  @inline(never)\n  public func acquireReadLock(file: String = #file, line: Int = #line, column: Int = #column) {\n    pthread_rwlock_rdlock(&lock)\n    lockCount.wrappingIncrement(ordering: .relaxed)\n\n    pthread_rwlock_wrlock(&stringLock)\n    lastLockedBy = \"\\(file):\\(line):\\(column)\"\n    pthread_rwlock_unlock(&stringLock)\n  }\n  #else\n  /// Acquire the lock for reading.\n  public func acquireReadLock() {\n    pthread_rwlock_rdlock(&lock)\n  }\n  #endif\n\n  #if DEBUG_LOCKS\n  /// Acquire the lock for writing.\n  @inline(never)\n  public func acquireWriteLock(file: String = #file, line: Int = #line, column: Int = #column) {\n    pthread_rwlock_wrlock(&lock)\n    lockCount.wrappingIncrement(ordering: .relaxed)\n\n    pthread_rwlock_wrlock(&stringLock)\n    lastLockedBy = \"\\(file):\\(line):\\(column)\"\n    pthread_rwlock_unlock(&stringLock)\n  }\n  #else\n  /// Acquire the lock for writing.\n  public func acquireWriteLock() {\n    pthread_rwlock_wrlock(&lock)\n  }\n  #endif\n\n  #if DEBUG_LOCKS\n  /// Unlock the lock.\n  public func unlock(file: String = #file, line: Int = #line, column: Int = #column) {\n    let count = lockCount.wrappingDecrementThenLoad(ordering: .relaxed)\n\n    pthread_rwlock_wrlock(&stringLock)\n    precondition(count >= 0, \"Detected unbalanced unlock of ReadWriteLock, unlocked by: \\(file):\\(line):\\(column), last unlocked by: \\(lastFullyReleasedBy ?? \"no one\")\")\n    if count == 0 {\n      lastFullyReleasedBy = \"\\(file):\\(line):\\(column)\"\n    }\n    pthread_rwlock_unlock(&stringLock)\n\n    pthread_rwlock_unlock(&lock)\n  }\n  #else\n  /// Unlock the lock.\n  public func unlock() {\n    pthread_rwlock_unlock(&lock)\n  }\n  #endif\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/Request.swift",
    "content": "import Foundation\n\nstruct Request {\n  var url: URL\n  var body: Data?\n  var method = RequestMethod.get\n  var contentType = ContentType.text\n  var headers: [String: String] = [:]\n  \n  init(_ url: URL) {\n    self.url = url\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/RequestMethod.swift",
    "content": "import Foundation\n\nenum RequestMethod: String {\n  case get = \"GET\"\n  case post = \"POST\"\n  case head = \"HEAD\"\n  case put = \"PUT\"\n  case delete = \"DELETE\"\n  case connect = \"CONNECT\"\n  case options = \"OPTIONS\"\n  case trace = \"TRACE\"\n  case patch = \"PATCH\"\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/RequestUtil.swift",
    "content": "import Foundation\nimport Dispatch\n\n#if os(Linux)\nimport FoundationNetworking\nimport SwiftyRequest\n#endif\n\nenum RequestError: LocalizedError {\n  /// The request's body could not be converted to data.\n  case failedToConvertBodyToData\n  /// The response was not of type HTTP.\n  case invalidURLResponse\n  /// Request failed.\n  case unsuccessfulRequest(_ statusCode: Int)\n  /// Something has gone horribly wrong.\n  case unknownError\n\n  var errorDescription: String? {\n    switch self {\n      case .failedToConvertBodyToData:\n        return \"The request's body could not be converted to data.\"\n      case .invalidURLResponse:\n        return \"The response was not of type HTTP.\"\n      case .unsuccessfulRequest(let statusCode):\n        return \"Request failed with status code \\(statusCode).\"\n      case .unknownError:\n        return \"Something has gone horribly wrong.\"\n    }\n  }\n}\n\nenum RequestUtil {\n  static func performFormRequest(\n    url: URL,\n    body: [String: String],\n    method: RequestMethod\n  ) async throws -> (HTTPURLResponse, Data) {\n    let payload = RequestUtil.encodeParameters(body)\n\n    guard let body = payload.data(using: .utf8) else {\n      throw RequestError.failedToConvertBodyToData\n    }\n\n    var request = Request(url)\n    request.method = method\n    request.contentType = .form\n    request.body = body\n\n    return try await performRequest(request)\n  }\n\n  static func performJSONRequest<T: Encodable>(\n    url: URL,\n    body: T,\n    method: RequestMethod\n  ) async throws -> (HTTPURLResponse, Data) {\n    var request = Request(url)\n    request.method = method\n    request.contentType = .json\n\n    request.body = try JSONEncoder().encode(body)\n\n    return try await performRequest(request)\n  }\n\n  static func performRequest(_ request: Request) async throws -> (HTTPURLResponse, Data) {\n    var urlRequest = URLRequest(url: request.url)\n    urlRequest.httpMethod = request.method.rawValue\n    urlRequest.httpBody = request.body\n\n    urlRequest.addValue(request.contentType.rawValue, forHTTPHeaderField: \"Content-Type\")\n    for (key, value) in request.headers {\n      urlRequest.addValue(value, forHTTPHeaderField: key)\n    }\n\n    return try await withCheckedThrowingContinuation { continuation in\n      let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in\n        if let error = error {\n          continuation.resume(throwing: error)\n          return\n        }\n\n        guard let httpResponse = response as? HTTPURLResponse, let data = data else {\n          continuation.resume(throwing: RequestError.invalidURLResponse)\n          return\n        }\n\n        if httpResponse.statusCode >= 400 {\n          continuation.resume(throwing: RequestError.unsuccessfulRequest(httpResponse.statusCode))\n          log.debug(String(data: data, encoding: .utf8) ?? \"Empty error response received\")\n          return\n        }\n\n        continuation.resume(returning: (httpResponse, data))\n      }\n      task.resume()\n    }\n  }\n\n  /// Replicates `Data.init(contentsOf:)` except it works correctly on Linux (the Data initializer\n  /// seems to fail somwhat randomly). Only works for internet URLs.\n  static func data(contentsOf url: URL) throws -> Data {\n    #if os(Linux)\n    let box: Box<Result<Data, Error>?> = Box(nil)\n    let semaphore = DispatchSemaphore(value: 0)\n\n    let request = RestRequest(method: .get, url: url.absoluteString)\n    request.responseData { result in\n      box.value = result.map(\\.body).mapError { $0 }\n      semaphore.signal()\n    }\n\n    semaphore.wait()\n\n    if let result = box.value {\n      return try result.get()\n    } else {\n      throw RequestError.unknownError\n    }\n    #else\n    return try Data(contentsOf: url)\n    #endif\n  }\n\n  static func urlEncode(_ string: String) -> String {\n    return string.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? \"\"\n  }\n\n  static func encodeParameters(_ parameters: [String: String]) -> String {\n    let parameterArray = parameters.map { (key, value) -> String in\n      return \"\\(urlEncode(key))=\\(urlEncode(value))\"\n    }\n    return parameterArray.joined(separator: \"&\")\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/RichError.swift",
    "content": "import Foundation\n\n/// Wraps an error with an additional ``RichError/richDescription`` property for displaying\n/// rich contextual errors in a UI. The `LocalizedError` implementation simply passes through\n/// the implementation of the wrapped error. Can also be used to wrap a custom error message\n/// instead of a localized error.\npublic struct RichError: LocalizedError {\n  /// The base error.\n  private var wrapped: Wrapped\n  /// Additional key-value context.\n  fileprivate var context: [(String, Any)] = []\n  /// An underlying reason for the error (e.g. the file writing error which causes a resource\n  /// pack caching error).\n  fileprivate var reason: (any Error)?\n\n  /// The rich error either wraps a custom error message or a localized error.\n  private enum Wrapped {\n    case message(String)\n    case error(any LocalizedError)\n\n    var localizedDescription: String {\n      switch self {\n        case .error(let error):\n          return error.localizedDescription\n        case .message(let message):\n          return message\n      }\n    }\n  }\n\n  /// Wraps a localized error with a container for additional context.\n  public init(_ error: any LocalizedError) {\n    wrapped = .error(error)\n  }\n\n  /// Wraps a custom error message with a container for additional context.\n  public init(_ message: String) {\n    wrapped = .message(message)\n  }\n\n  /// Sets the underlying error which caused this error to occur. Calling it a second time\n  /// overwrites the underlying error set by the previous call.\n  public func becauseOf(_ error: any Error) -> RichError {\n    var richError = self\n    richError.reason = error\n    return richError\n  }\n\n  /// Provides additional context for the error. Displayed by ``RichError/richDescription``.\n  public func with(_ key: String, being value: Any) -> RichError {\n    var richError = self\n    richError.context.append((key, value))\n    return richError\n  }\n\n  public var errorDescription: String? {\n    wrapped.localizedDescription\n  }\n\n  /// A rich multi-line error message to display in UIs. Provides additional context and optionally\n  /// an underlying reason.\n  public var richDescription: String {\n    var lines = [wrapped.localizedDescription]\n    for (key, value) in context {\n      lines.append(\"\\(key): \\(value)\")\n    }\n    if let reason = reason {\n      lines.append(\"Reason: \\(reason.localizedDescription)\")\n    }\n    return lines.joined(separator: \"\\n\")\n  }\n}\n\nextension LocalizedError {\n  /// Adds an underlying reason to an error. If the error is already rich, this modifies a copy of the error\n  /// instead of wrapping it in another ``RichError``. Calling this method a second time overwrites the\n  /// underlying error set by the previous call.\n  public func becauseOf(_ error: any Error) -> RichError {\n    var richError: RichError\n    if let self = self as? RichError {\n      richError = self\n    } else {\n      richError = RichError(self)\n    }\n\n    // Don't use `RichError.becauseOf` here even though this is duplicated code (because it creates an infinite loop otherwise)\n    richError.reason = error\n    return richError\n  }\n\n  /// Adds key-value context to an error. If the error is already rich, this modifies a copy of the error\n  /// instead of wrapping it in another ``RichError``.\n  public func with(_ key: String, _ value: Any) -> RichError {\n    var richError: RichError\n    if let self = self as? RichError {\n      richError = self\n    } else {\n      richError = RichError(self)\n    }\n\n    // Don't use `RichError.with` here even though this is duplicated code (because it creates an infinite loop otherwise)\n    richError.context.append((key, value))\n    return richError\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/SIMD.swift",
    "content": "import FirebladeMath\n\nextension Vec3 {\n  /// Returns the requested component of the vector.\n  /// - Parameter axis: The axis of the component.\n  /// - Returns: The value of the requested component.\n  public func component(along axis: Axis) -> Scalar {\n    switch axis {\n      case .x: return x\n      case .y: return y\n      case .z: return z\n    }\n  }\n}\n\nextension Vec3d {\n  /// The magnitude of the vector when disregarding the vertical component.\n  public var horizontalMagnitude: Double {\n    return sqrt(x*x + z*z)\n  }\n}\n\nextension Vec3f {\n  /// The magnitude of the vector when disregarding the vertical component.\n  public var horizontalMagnitude: Float {\n    return sqrt(x*x + z*z)\n  }\n}\n\nextension Vec where Scalar: BinaryFloatingPoint {\n  /// The squared magnitude of the vector (use when you're using magnitude purely for comparison because it's faster).\n  public var magnitudeSquared: Scalar {\n    var magnitudeSquared: Scalar = 0\n    for i in 0..<scalarCount {\n      let component = self[i]\n      magnitudeSquared += component * component\n    }\n    return magnitudeSquared\n  }\n}\n\nextension Vec where Scalar == Float {\n  /// The magnitude of the vector.\n  public var magnitude: Float {\n    sqrt(magnitudeSquared)\n  }\n}\n\nextension Vec where Scalar == Double {\n  /// The magnitude of the vector.\n  public var magnitude: Double {\n    sqrt(magnitudeSquared)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/Stopwatch.swift",
    "content": "import CoreFoundation\n\n/// A stopwatch allows tracking the runtime of a task. Use ``Profiler`` instead if fine-grained\n/// labelled measurements with support for averaging over multiple trials is required.\npublic struct Stopwatch {\n  /// The time (from `CFAbsoluteTimeGetCurrent`) at which the stopwatch was created.\n  let start: Double\n\n  /// Creates a stopwatch starting at the current time.\n  public init() {\n    start = CFAbsoluteTimeGetCurrent()\n  }\n\n  /// The current duration elapsed since the creation of the stopwatch.\n  public var elapsed: Duration {\n    .seconds(CFAbsoluteTimeGetCurrent() - start)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/Task/TaskProgress.swift",
    "content": "import Foundation\n\n/// Tracks a task's progress.\npublic final class TaskProgress<Step: TaskStep> {\n  /// The current progress message.\n  public var message: String {\n    customMessage ?? currentStep?.message ?? \"Loading\"\n  }\n\n  /// The current progress from 0 to 1.\n  public var progress: Double {\n    guard let currentStep = currentStep else {\n      return 0\n    }\n\n    var progress: Double = 0\n    for step in Step.allCases {\n      guard step != currentStep else {\n        break\n      }\n      guard !skippedSteps.contains(step) else {\n        continue\n      }\n      progress += step.relativeDuration\n    }\n\n    progress += currentStep.relativeDuration * stepProgress\n    return progress / totalTaskDuration\n  }\n\n  /// The current step. `nil` when initially created.\n  public private(set) var currentStep: Step?\n  /// The progress of the current step from 0 to 1.\n  public private(set) var stepProgress: Double = 0\n  /// Steps that have been skipped (which get excluded from the total for a more accurate\n  /// indication of progress).\n  public private(set) var skippedSteps: [Step] = []\n  /// A custom message overriding the message of the current step. Useful when\n  /// nesting tasks.\n  public private(set) var customMessage: String?\n\n  /// A handler to call whenever the progress changes.\n  private var changeHandler: ((TaskProgress<Step>) -> Void)?\n\n  #if canImport(Darwin)\n  /// A handle to the observation of a step's nested `Progress`. Required to keep\n  /// the handle alive until the step completes.\n  private var observation: NSKeyValueObservation?\n  #endif\n\n  /// The total combined duration of all steps. There is no unit, it is just the sum\n  /// of relative durations. Excludes ``TaskProgress/skippedSteps``.\n  private var totalTaskDuration: Double {\n    Step.allCases.map { step in\n      if skippedSteps.contains(step) {\n        return 0\n      } else {\n        return step.relativeDuration\n      }\n    }.reduce(into: 0, { $0 += $1 })\n  }\n\n  /// Creates a new task progress tracker. Initially has no current step.\n  public init() {}\n\n  /// Resets the task progress back to the start.\n  public func reset() {\n    currentStep = nil\n    stepProgress = 0\n    customMessage = nil\n    skippedSteps = []\n  }\n\n  /// Adds a step handler to the progress. The handler gets run whenever the current\n  /// step changes (not triggered if the step progress changes while the step remains\n  /// the same). Can be called multiple times to create a chain of handlers.\n  @discardableResult\n  public func onChange(action: @escaping (TaskProgress<Step>) -> Void) -> TaskProgress<Step> {\n    if let existingHandler = changeHandler {\n      changeHandler = { progress in\n        existingHandler(progress)\n        action(progress)\n      }\n    } else {\n      changeHandler = action\n    }\n    return self\n  }\n\n  /// Update the task's progress.\n  public func update(\n    to step: Step,\n    stepProgress: Double = 0,\n    customMessage: String? = nil\n  ) {\n    ThreadUtil.runInMain {\n      currentStep = step\n      self.stepProgress = stepProgress\n      self.customMessage = customMessage\n      changeHandler?(self)\n    }\n  }\n\n  /// Marks a step as skipped. It's not required to mark skipped steps\n  /// as skipped, but it allows for progress to be estimated more accurately.\n  public func skip(_ step: Step) {\n    ThreadUtil.runInMain {\n      skippedSteps.append(step)\n      changeHandler?(self)\n    }\n  }\n\n  /// Performs a step and returns its result.\n  public func perform<R>(_ step: Step, action: () throws -> R) rethrows -> R {\n    update(to: step, stepProgress: 0)\n    return try action()\n  }\n\n  /// Performs a step and returns its result. Skips the step if the\n  /// given condition doesn't hold.\n  public func perform<R>(\n    _ step: Step,\n    if condition: Bool,\n    action: () throws -> R\n  ) rethrows -> R? {\n    guard condition else {\n      skip(step)\n      return nil\n    }\n\n    update(to: step, stepProgress: 0)\n    return try action()\n  }\n\n  /// Performs a step and runs a handler if it fails. Returns the result\n  /// of the step if it succeeds.\n  public func perform<R>(\n    _ step: Step,\n    action: () throws -> R,\n    handleError: (any Error) -> Void\n  ) -> R? {\n    update(to: step, stepProgress: 0)\n    do {\n      return try action()\n    } catch {\n      handleError(error)\n      return nil\n    }\n  }\n\n  /// Performs a step and runs a handler if it fails. Returns the result\n  /// of the step if it succeeds. Skips the step if the given condition\n  /// doesn't hold.\n  public func perform<R>(\n    _ step: Step,\n    if condition: Bool,\n    action: () throws -> R,\n    handleError: (any Error) -> Void\n  ) -> R? {\n    guard condition else {\n      skip(step)\n      return nil\n    }\n\n    update(to: step, stepProgress: 0)\n    do {\n      return try action()\n    } catch {\n      handleError(error)\n      return nil\n    }\n  }\n\n  /// Performs a step with a nested task progress tracker.\n  public func perform<InnerStep: TaskStep, R>(\n    _ step: Step,\n    action: (TaskProgress<InnerStep>) throws -> R\n  ) rethrows -> R {\n    update(to: step, stepProgress: 0)\n    let innerProgress = TaskProgress<InnerStep>()\n    innerProgress.onChange { innerProgress in\n      self.update(\n        to: step,\n        stepProgress: innerProgress.progress,\n        customMessage: innerProgress.message\n      )\n    }\n    return try action(innerProgress)\n  }\n\n  /// Performs a step with a nested task progress tracker. Skips the step\n  /// if the condition isn't met.\n  public func perform<InnerStep: TaskStep, R>(\n    _ step: Step,\n    if condition: Bool,\n    action: (TaskProgress<InnerStep>) throws -> R\n  ) rethrows -> R? {\n    guard condition else {\n      skip(step)\n      return nil\n    }\n\n    update(to: step, stepProgress: 0)\n    let innerProgress = TaskProgress<InnerStep>()\n    innerProgress.onChange { innerProgress in\n      self.update(\n        to: step,\n        stepProgress: innerProgress.progress,\n        customMessage: innerProgress.message\n      )\n    }\n    return try action(innerProgress)\n  }\n\n  #if canImport(Darwin)\n  // TODO: This feels a bit janky and less intuitive than the other methods. Perhaps a more descriptive\n  //   method name would help.\n  /// Performs a step with a nested progress tracker. The step is given a way to register\n  /// a nested progress object.\n  public func perform<R>(\n    _ step: Step,\n    action: ((Progress) -> Void) async throws -> R\n  ) async rethrows -> R {\n    update(to: step, stepProgress: 0)\n\n    let result = try await action() { progress in\n      observation = progress.observe(\\.fractionCompleted, options: [.new]) { progress, change in\n        self.update(to: step, stepProgress: change.newValue!)\n      }\n    }\n    observation = nil\n\n    return result\n  }\n\n  /// Performs a step with a nested progress tracker. The step is given a\n  /// way to register a nested progress object. Skips the step if the condition isn't met.\n  public func perform<R>(\n    _ step: Step,\n    if condition: Bool,\n    action: ((Progress) -> Void) async throws -> R\n  ) async rethrows -> R? {\n    guard condition else {\n      skip(step)\n      return nil\n    }\n\n    update(to: step, stepProgress: 0)\n\n    let result = try await action() { progress in\n      observation = progress.observe(\\.fractionCompleted, options: [.new]) { progress, change in\n        self.update(to: step, stepProgress: change.newValue!)\n      }\n    }\n    observation = nil\n\n    return result\n  }\n  #endif\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/Task/TaskStep.swift",
    "content": "public protocol TaskStep: CaseIterable, Equatable {\n  var message: String { get }\n  var relativeDuration: Double { get }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/ThreadUtil.swift",
    "content": "import Foundation\n\npublic struct ThreadUtil {\n  public static func runInMain(_ closure: () -> Void) {\n    if Thread.isMainThread {\n      closure()\n    } else {\n      DispatchQueue.main.sync {\n        closure()\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/TickScheduler.swift",
    "content": "import Atomics\nimport Foundation\nimport FirebladeECS\nimport CoreFoundation\n\n#if canImport(Darwin)\nimport Darwin\n#elseif canImport(Glibc)\nimport Glibc\n#else\n#error(\"Unsupported platform for TickScheduler\")\n#endif\n\n/// A highly accurate timer used to implement the ticking mechanism. Completely threadsafe.\npublic final class TickScheduler: @unchecked Sendable {\n  // MARK: Static properties\n\n  public static let defaultTicksPerSecond: Double = 20\n  \n  // MARK: Public properties\n\n  /// The number of ticks to perform per second.\n  public let ticksPerSecond: Double\n  /// Incremented each tick.\n  public private(set) var tickNumber = 0\n  /// The time that the most recent tick began, in unix epoch seconds.\n  public private(set) var mostRecentTick: Double = CFAbsoluteTimeGetCurrent()\n\n  // MARK: Private properties\n\n  /// The ECS nexus to perform operations on.\n  private var nexus: Nexus\n  /// The lock to acquire before accessing ``nexus``.\n  private var nexusLock: ReadWriteLock\n  /// The current world.\n  private var world: World\n  /// The lock to acquire before accessing ``world``.\n  private var worldLock: ReadWriteLock\n  /// The systems to run each tick. In execution order.\n  private var systems: [System] = []\n  /// If `true`, the tick loop will be stopped at the start of the next tick.\n  private var shouldCancel = ManagedAtomic<Bool>(false)\n\n  #if canImport(Darwin)\n  /// Time base information used in time calculations.\n  private var timebaseInfo = mach_timebase_info_data_t()\n  #endif\n\n  // MARK: Init\n\n  /// Creates a new tick scheduler.\n  /// - Parameters:\n  ///   - nexus: The nexus to run updates on.\n  ///   - nexusLock: The lock to acquire for each tick to keep the nexus threadsafe.\n  ///   - world: The world to pass to systems (usually used for things such as collisions).\n  ///   - ticksPerSecond: The number of ticks per second to run the scheduler at.\n  public init(\n    _ nexus: Nexus,\n    nexusLock: ReadWriteLock,\n    _ world: World,\n    ticksPerSecond: Double = defaultTicksPerSecond\n  ) {\n    self.nexus = nexus\n    self.nexusLock = nexusLock\n    self.world = world\n    self.ticksPerSecond = ticksPerSecond\n    worldLock = ReadWriteLock()\n  }\n\n  deinit {\n    cancel()\n  }\n\n  // MARK: Public methods\n\n  /// Sets the world to give to the systems each tick.\n  /// - Parameter newWorld: The new world.\n  public func setWorld(to newWorld: World) {\n    worldLock.acquireWriteLock()\n    defer { worldLock.unlock() }\n    world = newWorld\n  }\n\n  /// Adds a system to the tick loop. Systems are run in the order they are added.\n  public func addSystem(_ system: System) {\n    // Acquiring the nexus lock ensures that a tick is not currently in progress\n    nexusLock.acquireWriteLock()\n    defer { nexusLock.unlock() }\n    systems.append(system)\n  }\n\n  /// Cancels the scheduler at the start of the next tick.\n  public func cancel() {\n    shouldCancel.store(true, ordering: .relaxed)\n  }\n\n  /// Should only be called once on a given tick scheduler.\n  public func startTickLoop() {\n    Thread.detachNewThread {\n      self.mostRecentTick = CFAbsoluteTimeGetCurrent()\n      let nanosecondsPerTick = UInt64(1 / self.ticksPerSecond * 1_000_000_000)\n\n      #if canImport(Darwin)\n      autoreleasepool {\n        self.configureThread()\n\n        var when = mach_absolute_time()\n        self.tick()\n        while !self.shouldCancel.load(ordering: .relaxed) {\n          when += self.nanosToAbs(nanosecondsPerTick)\n          mach_wait_until(when)\n          self.mostRecentTick = CFAbsoluteTimeGetCurrent()\n          self.tick()\n        }\n      }\n\n      #elseif canImport(Glibc)\n      let delay = timespec(tv_sec: 0, tv_nsec: Int(nanosecondsPerTick))\n      var nextTick = timespec()\n      clock_gettime(CLOCK_MONOTONIC, &nextTick)\n      self.tick()\n      while !self.shouldCancel.load(ordering: .relaxed) {\n        // Basically just `nextTick += delay`\n        nextTick.tv_sec += delay.tv_sec\n        nextTick.tv_nsec += delay.tv_nsec\n        nextTick.tv_sec += nextTick.tv_nsec / 1_000_000_000\n        nextTick.tv_nsec = nextTick.tv_nsec % 1_000_000_000\n\n        var time = timespec()\n        clock_gettime(CLOCK_MONOTONIC, &time)\n\n        // Basically just `let sleepTime = nextTick - time`\n        var sleepTime = timespec()\n        sleepTime.tv_sec = time.tv_sec - nextTick.tv_sec\n        sleepTime.tv_nsec = time.tv_nsec - nextTick.tv_nsec\n        if sleepTime.tv_nsec < 0 {\n          sleepTime.tv_nsec += 1_000_000_000\n          sleepTime.tv_sec -= 1\n        }\n\n        nanosleep(&sleepTime, nil)\n        self.mostRecentTick = CFAbsoluteTimeGetCurrent()\n        self.tick()\n      }\n      #endif\n    }\n  }\n\n  // MARK: Private methods\n\n  /// Run all of the systems.\n  private func tick() {\n    // The nexus lock must be held for the duration of the tick because it is used elsewhere as a\n    // lock to prevent thread safety issue related to modifying dependencies of the tick method.\n    nexusLock.acquireWriteLock()\n\n    worldLock.acquireReadLock()\n    let world = world\n    worldLock.unlock()\n\n    for system in systems {\n      do {\n        try system.update(nexus, world)\n      } catch {\n        log.error(\"Failed to run \\(type(of: system)): \\(error)\")\n        world.eventBus.dispatch(ErrorEvent(error: error, message: \"Failed to run \\(type(of: system))\"))\n      }\n    }\n\n    tickNumber += 1\n    nexusLock.unlock()\n  }\n\n  #if canImport(Darwin)\n  /// Configures the thread's time constraint policy.\n  private func configureThread() {\n    // TODO: Implement for Linux\n    mach_timebase_info(&timebaseInfo)\n    let clockToAbs = Double(timebaseInfo.denom) / Double(timebaseInfo.numer) * Double(NSEC_PER_SEC)\n\n    let period = UInt32(0.00 * clockToAbs)\n    let computation = UInt32(1 / ticksPerSecond * clockToAbs)\n    let constraint = UInt32(1 / ticksPerSecond * clockToAbs)\n\n    let threadTimeConstraintPolicyCount = mach_msg_type_number_t(MemoryLayout<thread_time_constraint_policy>.size / MemoryLayout<integer_t>.size)\n\n    var policy = thread_time_constraint_policy()\n    var ret: Int32\n    let thread: thread_port_t = pthread_mach_thread_np(pthread_self())\n\n    policy.period = period\n    policy.computation = computation\n    policy.constraint = constraint\n    policy.preemptible = 0\n\n    ret = withUnsafeMutablePointer(to: &policy) {\n      $0.withMemoryRebound(to: integer_t.self, capacity: Int(threadTimeConstraintPolicyCount)) {\n        thread_policy_set(thread, UInt32(THREAD_TIME_CONSTRAINT_POLICY), $0, threadTimeConstraintPolicyCount)\n      }\n    }\n\n    if ret != KERN_SUCCESS {\n      mach_error(\"thread_policy_set:\", ret)\n      exit(1)\n    }\n\n    // TODO: properly handle error\n  }\n  #endif\n\n  #if canImport(Darwin)\n  /// Converts nanoseconds to mach absolute time.\n  private func nanosToAbs(_ nanos: UInt64) -> UInt64 {\n    return nanos * UInt64(timebaseInfo.denom) / UInt64(timebaseInfo.numer)\n  }\n  #endif\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/URL.swift",
    "content": "import Foundation\n\nextension URL {\n  func appendingQueryItems(_ items: [String: String]) -> URL {\n    if var components = URLComponents(url: self, resolvingAgainstBaseURL: true) {\n      var queryItems = components.queryItems ?? []\n      for (key, value) in items {\n        queryItems.append(URLQueryItem(name: key, value: value))\n      }\n      components.queryItems = queryItems\n      \n      if let newURL = components.url {\n        return newURL\n      } else {\n        log.warning(\"failed to append query items to url: \\(self.absoluteString)\")\n        return self\n      }\n    }\n    log.warning(\"failed to append query items to url: \\(self.absoluteString)\")\n    return self\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/Util/Vector.swift",
    "content": "@_exported import FirebladeMath\n\npublic typealias Vec = SIMD\n\npublic typealias Vec2 = SIMD2\npublic typealias Vec3 = SIMD3\npublic typealias Vec4 = SIMD4\n"
  },
  {
    "path": "Sources/Core/Sources/World/BreakingBlock.swift",
    "content": "/// Represents a block getting broken. Includes the stage of the breaking animation,\n/// the entity breaking the block (the perpetrator), and the position.\npublic struct BreakingBlock {\n  public var position: BlockPosition\n  public var perpetratorEntityId: Int\n  public var progress: Double\n\n  /// `nil` if the animation hasn't started yet. Otherwise an integer in the range `0...9`.\n  public var stage: Int? {\n    guard progress >= 0 else {\n      return nil\n    }\n\n    guard progress <= 1 else {\n      return 9\n    }\n\n    let stage = Int(progress * 10) - 1\n    if stage < 0 {\n      return nil\n    } else {\n      return stage\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/Chunk/Chunk.swift",
    "content": "import Foundation\n\n// TODO: Ensure that positions used are within the chunk (maybe have a `ChunkRelativeBlockPosition` type?)\n\n/// Represents a 16x256x16 (x y z) chunk of blocks in a world. Completely thread-safe.\n///\n/// Most of the public methods have an `acquireLock` parameter. To perform manual locking (for\n/// optimisation), you can use ``acquireWriteLock()``, ``acquireReadLock()`` and ``unlock()``,\n/// along with `acquireLock: false`.\n///\n/// Sometimes referred to as a chunk column online. It is a column of ``Chunk/Section``s with\n/// some extra information about block entities, biomes, lighting and heightmaps.\npublic final class Chunk {\n  // MARK: Static properties\n\n  /// The width of a chunk in the x direction.\n  public static let width = 16\n  /// The width of a chunk in the z direction.\n  public static let depth = 16\n  /// The height of a chunk in the y direction.\n  public static let height = 256\n  /// The number of blocks in each 1 block tall layer of a chunk.\n  public static let blocksPerLayer = width * depth\n  /// The total number of blocks per chunk.\n  public static let numBlocks = height * blocksPerLayer\n  /// The total number of sections per chunk.\n  public static let numSections = 16\n\n  // MARK: Public properties\n\n  /// Whether the chunk has lighting data or not.\n  public var hasLighting: Bool {\n    lock.acquireReadLock()\n    defer { lock.unlock() }\n\n    return lighting.isPopulated\n  }\n\n  public var nonEmptySectionCount: Int {\n    lock.acquireReadLock()\n    defer { lock.unlock() }\n\n    var count = 0\n    for section in sections {\n      if !section.isEmpty {\n        count += 1\n      }\n    }\n    return count\n  }\n\n  // MARK: Private properties\n\n  /// Blocks are stored in chunk sections corresponding to 16x16x16 sections of the chunk from lowest to highest.\n  private var sections: [Chunk.Section]\n  /// Block entities for this chunk (i.e. chests, beds etc.)\n  private var blockEntities: [BlockEntity]\n\n  /// 3d biome data in 4x4x4 blocks.\n  private var biomeIds: [UInt8]\n  /// Lighting data that is populated once UpdateLightPacket is receive for this chunk.\n  private var lighting = ChunkLighting()\n  /// Information about the highest blocks in each column of the chunk.\n  private var heightMap: HeightMap\n\n  /// Lock for thread-safe reading and writing.\n  private var lock = ReadWriteLock()\n\n  // MARK: Init\n\n  /// Creates a new chunk\n  /// - Parameters:\n  ///   - sections: An array of 16 chunk sections from lowest to highest.\n  ///   - blockEntities: An array of block entities in the chunk in no particular order.\n  ///   - biomeIds: The biomes of the chunk in 4x4x4 blocks. Indexed in the same order as blocks. (Index is block index divided by 4).\n  ///   - lighting: Lighting data for the chunk\n  ///   - heightMap: Information about the highest blocks in each column of the chunk.\n  public init(\n    sections: [Chunk.Section], blockEntities: [BlockEntity], biomeIds: [UInt8],\n    lighting: ChunkLighting? = nil, heightMap: HeightMap\n  ) {\n    self.sections = sections\n    self.blockEntities = blockEntities\n    self.biomeIds = biomeIds\n    self.lighting = lighting ?? ChunkLighting()\n    self.heightMap = heightMap\n  }\n\n  /// Creates a new chunk from the data contained within a chunk data packet.\n  public init(_ packet: ChunkDataPacket) {\n    self.heightMap = packet.heightMap\n    self.blockEntities = packet.blockEntities\n    self.sections = packet.sections\n    self.biomeIds = packet.biomeIds\n  }\n\n  // MARK: Blocks\n\n  /// Get information about a block.\n  /// - Parameters:\n  ///   - position: A block position relative to the chunk.\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  /// - Returns: Information about block and its state. Returns ``Block/missing`` if block state id is invalid.\n  public func getBlock(at position: BlockPosition, acquireLock: Bool = true) -> Block {\n    let stateId = getBlockId(at: position, acquireLock: acquireLock)\n    return RegistryStore.shared.blockRegistry.block(withId: stateId) ?? Block.missing\n  }\n\n  /// Get the block state id of the block at a position.\n  /// - Parameters:\n  ///   - position: A block position relative to the chunk.\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  /// - Returns: Block id of block. Returns 0 (regular air) if `position` is invalid (outside chunk).\n  public func getBlockId(at position: BlockPosition, acquireLock: Bool = true) -> Int {\n    let blockIndex = position.blockIndex\n    return getBlockId(at: blockIndex, acquireLock: acquireLock)\n  }\n\n  /// Get the block state id of the block at an index.\n  /// - Parameters:\n  ///   - index: Can be obtained using ``Position/blockIndex``. Relative to the chunk.\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  /// - Returns: Block id of block. Returns 0 (air) if `index` is invalid (outside chunk).\n  public func getBlockId(at index: Int, acquireLock: Bool = true) -> Int {\n    if !Self.isValidBlockIndex(index) {\n      log.warning(\n        \"Invalid block index passed to Chunk.getBlockStateId(at:), index=\\(index), returning block id 0 (air)\"\n      )\n      return 0\n    }\n\n    if acquireLock { lock.acquireReadLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    let sectionIndex = index / Section.numBlocks\n    let sectionBlockIndex = index % Section.numBlocks\n    return sections[sectionIndex].getBlockId(at: sectionBlockIndex)\n  }\n\n  /// Sets the block at the given position to a new value.\n  ///\n  /// Updates the height map. **Does not update lighting**.\n  ///\n  /// - Parameters:\n  ///   - position: A position relative to the chunk.\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  ///   - newState: A new block state. Not validated.\n  public func setBlockId(at position: BlockPosition, to state: Int, acquireLock: Bool = true) {\n    if acquireLock { lock.acquireWriteLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    // TODO: Validate block state\n    let blockIndex = position.blockIndex\n    let sectionIndex = blockIndex / Section.numBlocks\n    let sectionBlockIndex = blockIndex % Section.numBlocks\n    sections[sectionIndex].setBlockId(at: sectionBlockIndex, to: state)\n\n    heightMap.handleBlockUpdate(at: position, in: self, acquireChunkLock: false)\n  }\n\n  // MARK: Block entities\n\n  /// Gets the chunk's block entities.\n  /// - Parameter acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  /// - Returns: The chunk's block entities.\n  public func getBlockEntities(acquireLock: Bool = true) -> [BlockEntity] {\n    if acquireLock { lock.acquireReadLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    return blockEntities\n  }\n\n  /// Mutates the chunk's block entities with a closure.\n  /// - Parameters:\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  ///   - action: Action that mutates the chunk's block entities.\n  /// - Returns: The chunk's block entities.\n  public func mutateBlockEntities(acquireLock: Bool = true, action: (inout [BlockEntity]) -> Void) {\n    if acquireLock { lock.acquireWriteLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    action(&blockEntities)\n  }\n\n  // MARK: Biomes\n\n  /// Gets the biome of the block at the given position.\n  /// - Parameters:\n  ///   - position: Position of block in chunk relative coordinates.\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  /// - Returns: The id of the biome or `nil` if above or below the chunk.\n  public func biomeId(at position: BlockPosition, acquireLock: Bool = true) -> Int? {\n    if acquireLock { lock.acquireReadLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    let position = position.relativeToChunk\n    guard Self.isValidBlockPosition(position) else {\n      return nil\n    }\n\n    let index = position.biomeIndex\n    return Int(biomeIds[index])\n  }\n\n  /// Get the biome of the block at the given position.\n  /// - Parameters:\n  ///   - position: Position of block in chunk relative coordinates.\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  /// - Returns: Data about the biome.\n  public func biome(at position: BlockPosition, acquireLock: Bool = true) -> Biome? {\n    guard let biomeId = biomeId(at: position, acquireLock: acquireLock) else {\n      return nil\n    }\n\n    return RegistryStore.shared.biomeRegistry.biome(withId: biomeId)\n  }\n\n  /// Gets the chunk's biomes.\n  /// - Parameter acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  /// - Returns: The chunk's biomes.\n  public func getBiomeIds(acquireLock: Bool = true) -> [UInt8] {\n    if acquireLock { lock.acquireReadLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    return biomeIds\n  }\n\n  /// Mutates the chunk's biomes with a closure.\n  /// - Parameters:\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  ///   - action: Action that mutates the chunk's biomes.\n  /// - Returns: The chunk's biomes.\n  public func mutateBiomeIds(acquireLock: Bool = true, action: (inout [UInt8]) -> Void) {\n    if acquireLock { lock.acquireWriteLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    action(&biomeIds)\n  }\n\n  // MARK: Sections\n\n  /// Updates the chunk with data sent from the server.\n  /// - Parameters:\n  ///   - packet: Packet containing data to update this chunk with.\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  public func update(with packet: ChunkDataPacket, acquireLock: Bool = true) {\n    // TODO: take a lock for the duration of this function\n    if acquireLock {\n      lock.acquireWriteLock()\n    }\n\n    blockEntities = packet.blockEntities\n    heightMap = packet.heightMap\n\n    if acquireLock {\n      lock.unlock()\n    }\n\n    for sectionIndex in packet.presentSections {\n      setSection(atIndex: sectionIndex, to: packet.sections[sectionIndex], acquireLock: acquireLock)\n    }\n  }\n\n  /// Replaces a section with a new one.\n  /// - Parameters:\n  ///   - index: A section index (from 0 to 15 inclusive). Not validated.\n  ///   - section: The replacement section.\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  public func setSection(atIndex index: Int, to section: Section, acquireLock: Bool = true) {\n    if acquireLock { lock.acquireWriteLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    sections[index] = section\n  }\n\n  /// Gets the chunk's sections.\n  /// - Parameter acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  /// - Returns: The chunk's sections.\n  public func getSections(acquireLock: Bool = true) -> [Section] {\n    if acquireLock { lock.acquireReadLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    let sections = sections\n    return sections\n  }\n\n  /// Gets the section with the given y coordinate.\n  /// - Parameters:\n  ///   - y: The y coordinate of the section to get.\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  /// - Returns: The section. `nil` if the `y` coordinate is invalid.\n  public func getSection(at y: Int, acquireLock: Bool = true) -> Section? {\n    if acquireLock { lock.acquireReadLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    guard y >= 0 && y < Self.numSections else {\n      return nil\n    }\n\n    let section = sections[y]\n    return section\n  }\n\n  /// Mutates the chunk's sections with a closure.\n  /// - Parameters:\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  ///   - action: Action that mutates the chunk's sections.\n  /// - Returns: The chunk's sections.\n  public func mutateSections(acquireLock: Bool = true, action: (inout [Section]) -> Void) {\n    if acquireLock { lock.acquireWriteLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    action(&sections)\n  }\n\n  // MARK: Lighting\n\n  /// Returns the block light level for the given block.\n  /// - Parameters:\n  ///   - position: Position of block.\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  /// - Returns: The requested block light level.\n  public func blockLightLevel(at position: BlockPosition, acquireLock: Bool = true) -> Int {\n    if acquireLock { lock.acquireReadLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    return lighting.getBlockLightLevel(at: position)\n  }\n\n  /// Sets the block light level for the given block. Does not propagate the change and does not verify the level is valid.\n  /// - Parameters:\n  ///   - position: Position of block.\n  ///   - level: The new block light level.\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  public func setBlockLightLevel(\n    at position: BlockPosition, to level: Int, acquireLock: Bool = true\n  ) {\n    if acquireLock { lock.acquireWriteLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    lighting.setBlockLightLevel(at: position, to: level)\n  }\n\n  /// Returns the sky light level for the given block.\n  /// - Parameters:\n  ///   - position: Position of block.\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  /// - Returns: The requested sky light level.\n  public func skyLightLevel(at position: BlockPosition, acquireLock: Bool = true) -> Int {\n    if acquireLock { lock.acquireReadLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    return lighting.getSkyLightLevel(at: position)\n  }\n\n  /// Sets the sky light level for the given block. Does not propagate the change and does not verify the level is valid.\n  /// - Parameters:\n  ///   - position: Position of block.\n  ///   - level: The new sky light level.\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  public func setSkyLightLevel(at position: BlockPosition, to level: Int, acquireLock: Bool = true)\n  {\n    if acquireLock { lock.acquireWriteLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    lighting.setSkyLightLevel(at: position, to: level)\n  }\n\n  // TODO: Make method naming consistent (chunk lighting uses `getLightLevel` but chunk uses `lightLevel`).\n  /// Returns the block and sky light levels for the given block.\n  /// - Parameters:\n  ///   - position: Position of block.\n  ///   - acquireLock: Position of block.\n  /// - Returns: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  public func lightLevel(at position: BlockPosition, acquireLock: Bool = true) -> LightLevel {\n    if acquireLock { lock.acquireReadLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    return lighting.getLightLevel(at: position)\n  }\n\n  /// Updates the chunk's lighting with data received from the server.\n  /// - Parameters:\n  ///   - data: Data received from the server.\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  public func updateLighting(with data: ChunkLightingUpdateData, acquireLock: Bool = true) {\n    if acquireLock { lock.acquireWriteLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    lighting.update(with: data)\n  }\n\n  /// Gets the chunk's lighting.\n  /// - Parameter acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  /// - Returns: The chunk's lighting.\n  public func getLighting(acquireLock: Bool = true) -> ChunkLighting {\n    if acquireLock { lock.acquireReadLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    return lighting\n  }\n\n  /// Mutates the chunk's lighting with a closure.\n  /// - Parameters:\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  ///   - action: Action that mutates the chunk's lighting.\n  /// - Returns: The chunk's lighting.\n  public func mutateLighting(acquireLock: Bool = true, action: (inout ChunkLighting) -> Void) {\n    if acquireLock { lock.acquireWriteLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    action(&lighting)\n  }\n\n  // MARK: Height map\n\n  /// Gets the height of the highest block that blocks light at the specified x and z coordinates.\n  /// - Parameters:\n  ///   - x: x coordinate of column.\n  ///   - z: z coordinate of column.\n  /// - Returns: Height of the highest block in the specified column that blocks light.\n  ///            Returns -1 if `x` or `z` are out of bounds or if the column is fully transparent.\n  public func highestLightBlockingBlock(atX x: Int, andZ z: Int, acquireLock: Bool = true) -> Int {\n    guard x >= 0, x < Self.width, z >= 0, z < Self.depth else {\n      return -1\n    }\n\n    if acquireLock { lock.acquireReadLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    return heightMap.getHighestLightBlockingBlock(x, z)\n  }\n\n  /// Gets the chunk's height map.\n  /// - Parameter acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  /// - Returns: The chunk's height map.\n  public func getHeightMap(acquireLock: Bool = true) -> HeightMap {\n    if acquireLock { lock.acquireReadLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    return heightMap\n  }\n\n  /// Mutates the chunk's height map with a closure.\n  /// - Parameters:\n  ///   - acquireLock: Whether to acquire a lock or not. Only set to false if you know what you're doing. See ``Chunk``.\n  ///   - action: Action that mutates the chunk's height map.\n  /// - Returns: The chunk's height map.\n  public func mutateHeightMap(acquireLock: Bool = true, action: (inout HeightMap) -> Void) {\n    if acquireLock { lock.acquireWriteLock() }\n    defer { if acquireLock { lock.unlock() } }\n\n    action(&heightMap)\n  }\n\n  // MARK: Locking\n\n  /// Acquire a lock for manually writing data to the chunk (e.g. writing to the sections directly).\n  ///\n  /// Do not call any of the public methods of this chunk until you call ``unlock()`` because that\n  /// might create a deadlock (unless you pass `acquireLock: false` to the method).\n  public func acquireWriteLock() {\n    lock.acquireWriteLock()\n  }\n\n  /// Acquire a lock for manually reading data from the chunk (e.g. accessing the sections directly).\n  ///\n  /// Do not call any of the public methods of this chunk until you call ``unlock()`` because that\n  /// might create a deadlock.\n  public func acquireReadLock() {\n    lock.acquireReadLock()\n  }\n\n  /// Release the lock after calling ``acquireReadLock()`` or ``acquireWriteLock()``.\n  public func unlock() {\n    lock.unlock()\n  }\n\n  // MARK: Static methods\n\n  /// - Returns: `true` if the block index is contained within a chunk.\n  private static func isValidBlockIndex(_ index: Int) -> Bool {\n    return index >= 0 && index < Chunk.numBlocks\n  }\n\n  /// - Returns: `true` if the block position is contained within the a chunk.\n  private static func isValidBlockPosition(_ position: BlockPosition) -> Bool {\n    return\n      (position.x < Chunk.width && position.x >= 0 && position.z < Chunk.depth && position.z >= 0\n      && position.y < Chunk.height && position.y >= 0)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/Chunk/ChunkNeighbours.swift",
    "content": "/// Stores a chunk's neighbours.\n///\n/// All 4 neighbours must be present.\npublic struct ChunkNeighbours {\n  /// The neighbour chunk to the North.\n  public var north: Chunk\n  /// The neighbour chunk to the East.\n  public var east: Chunk\n  /// The neighbour chunk to the South.\n  public var south: Chunk\n  /// The neighbour chunk to the West.\n  public var west: Chunk\n  \n  /// All four neighbouring chunks.\n  public var all: [CardinalDirection: Chunk] {\n    return [\n      .north: north,\n      .east: east,\n      .south: south,\n      .west: west\n    ]\n  }\n  \n  public init(north: Chunk, east: Chunk, south: Chunk, west: Chunk) {\n    self.north = north\n    self.east = east\n    self.south = south\n    self.west = west\n  }\n  \n  /// Returns the neighbour in the given direction\n  public func neighbour(in direction: CardinalDirection) -> Chunk {\n    switch direction {\n      case .north:\n        return north\n      case .east:\n        return east\n      case .south:\n        return south\n      case .west:\n        return west\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/Chunk/ChunkPosition.swift",
    "content": "import Foundation\nimport FirebladeMath\n\n/// The position of a chunk.\npublic struct ChunkPosition {\n  // MARK: Public properties\n\n  /// The chunk's world x divided by 16 and rounded down.\n  public var chunkX: Int\n  /// The chunk's world z divided by 16 and rounded down.\n  public var chunkZ: Int\n\n  // MARK: Public computed properties\n\n  /// A map from each cardinal direction to each of this position's neighbours.\n  public var allNeighbours: [CardinalDirection: ChunkPosition] {\n    return [\n      .north: neighbour(inDirection: .north),\n      .east: neighbour(inDirection: .east),\n      .south: neighbour(inDirection: .south),\n      .west: neighbour(inDirection: .west)\n    ]\n  }\n\n  /// An array of containing this position and its neighbours.\n  public var andNeighbours: [ChunkPosition] {\n    var positionAndNeighbours = [self]\n    positionAndNeighbours.append(contentsOf: allNeighbours.values)\n    return positionAndNeighbours\n  }\n\n  /// The axis aligned bounding box for this chunk.\n  public var axisAlignedBoundingBox: AxisAlignedBoundingBox {\n    return AxisAlignedBoundingBox(\n      position: [\n        Double(chunkX * Chunk.width),\n        0.0,\n        Double(chunkZ * Chunk.depth)\n      ],\n      size: [\n        Double(Chunk.width),\n        Double(Chunk.height),\n        Double(Chunk.depth)\n      ]\n    )\n  }\n\n  /// A vector representing this position.\n  public var vector: Vec2i {\n    return Vec2i(chunkX, chunkZ)\n  }\n\n  // MARK: Init\n\n  public init(chunkX: Int, chunkZ: Int) {\n    self.chunkX = chunkX\n    self.chunkZ = chunkZ\n  }\n\n  // MARK: Public methods\n\n  /// Gets whether this chunk is within the given render distance of a given chunk.\n  /// - Parameters:\n  ///   - renderDistance: The render distance.\n  ///   - other: The other chunk.\n  /// - Returns: Whether the chunk is within render distance of the other chunk.\n  public func isWithinRenderDistance(_ renderDistance: Int, of other: ChunkPosition) -> Bool {\n    let distance = max(\n      abs(self.chunkX - other.chunkX),\n      abs(self.chunkZ - other.chunkZ)\n    )\n    return distance <= renderDistance\n  }\n\n  /// Gets the position of the chunk that neighbours this chunk in the specified direction.\n  public func neighbour(inDirection direction: CardinalDirection) -> ChunkPosition {\n    var position = self\n    switch direction {\n      case .north:\n        position.chunkZ -= 1\n      case .east:\n        position.chunkX += 1\n      case .south:\n        position.chunkZ += 1\n      case .west:\n        position.chunkX -= 1\n    }\n    return position\n  }\n\n  /// Checks if a chunk position is a neighbour of this chunk position.\n  public func neighbours(_ potentialNeighbour: ChunkPosition) -> Bool {\n    let manhattanDistance = abs(potentialNeighbour.chunkX - chunkX) + abs(potentialNeighbour.chunkZ - chunkZ)\n    let isNeighbour = manhattanDistance == 1\n    return isNeighbour\n  }\n\n  /// Checks if this chunk contains the specified chunk section.\n  public func contains(_ sectionPosition: ChunkSectionPosition) -> Bool {\n    return sectionPosition.sectionX == chunkX && sectionPosition.sectionZ == chunkZ\n  }\n}\n\nextension ChunkPosition: Hashable {\n  public func hash(into hasher: inout Hasher) {\n    hasher.combine(chunkX)\n    hasher.combine(chunkZ)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/Chunk/ChunkSection.swift",
    "content": "import Foundation\n\nextension Chunk {\n  /// A 16x16x16 section of a chunk. Just stores an array of block ids. Not thread-safe.\n  public struct Section {\n    /// The number of blocks wide a chunk section is (x axis).\n    public static let width = Chunk.width\n    /// The number of blocks tall a chunk section is (y axis).\n    public static let height = Chunk.height / Chunk.numSections\n    /// The number of blocks deep a chunk section is (z axis).\n    public static let depth = Chunk.depth\n    /// The number of blocks in a chunk section.\n    public static let numBlocks = width * height * depth\n\n    /// Block ids. Use Position.blockIndex to convert a position to an index in this array. The position must be relative to the section.\n    public var blocks: [UInt16]\n    /// The number of non-air blocks in the chunk section.\n    public var blockCount: Int\n\n    /// Whether the section is all air or not.\n    public var isEmpty: Bool {\n      blockCount == 0\n    }\n\n    /// Create an empty chunk section.\n    public init() {\n      blocks = [UInt16](repeating: 0, count: Section.numBlocks)\n      blockCount = 0\n    }\n\n    /// Create a chunk section populated with blocks.\n    /// - Parameters:\n    ///   - blocks: An array of block ids. Length must be equal to ``numBlocks``.\n    ///   - blockCount: The number of non-air blocks in the array.\n    public init(blocks: [UInt16], blockCount: Int) {\n      assert(blocks.count == Self.numBlocks, \"Attempted to initialize Chunk.Section with \\(blocks.count) blocks but it must have \\(Self.numBlocks) blocks\")\n\n      self.blocks = blocks\n      self.blockCount = blockCount\n    }\n\n    /// Create a chunk section populated with blocks.\n    /// - Parameters:\n    ///   - blockIds: Block ids or indices into the palette if the palette isn't empty. Length must be equal to ``numBlocks``.\n    ///   - palette: Used as a look up table to convert palette ids to block ids. If empty, the palette is ignored.\n    ///   - blockCount: The number of non-air blocks in the array.\n    public init(blockIds: [UInt16], palette: [UInt16], blockCount: Int) {\n      assert(blockIds.count == Self.numBlocks, \"Attempted to initialize Chunk.Section with \\(blockIds.count) blocks but it must have \\(Self.numBlocks) blocks\")\n\n      self.blocks = []\n      blocks.reserveCapacity(blockIds.count)\n\n      self.blockCount = blockCount\n\n      // See https://wiki.vg/Chunk_Format\n      if !palette.isEmpty {\n        for blockId in blockIds {\n          if blockId >= palette.count {\n            log.warning(\"Indirect palette lookup failed: block id \\(blockId) out of bounds for palette of length \\(palette.count), defaulting to 0 (air)\")\n            blocks.append(0)\n            continue\n          }\n          blocks.append(palette[Int(blockId)])\n        }\n      } else {\n        self.blocks = blockIds\n      }\n    }\n\n    /// Get the id of the block at the specified position.\n    /// - Parameter position: Position of the block relative to this section.\n    /// - Returns: The block id.\n    ///\n    /// For safety, the position is automatically converted to be relative to whatever section it is in. If\n    /// you're using this in a performance critical loop perhaps you should manually access ``blocks`` instead.\n    public func getBlockId(at position: BlockPosition) -> Int {\n      let index = position.relativeToChunkSection.blockIndex\n      return getBlockId(at: index)\n    }\n\n    /// Get the id of the block at the specified index in the section.\n    /// - Parameter index: The index of the block relative to this section.\n    /// - Returns: The block id, or `0` if the index is invalid\n    public func getBlockId(at index: Int) -> Int {\n      guard index < Section.numBlocks && index >= 0 else {\n        log.warning(\"Invalid position passed to Chunk.Section.getBlockState(at:); index=\\(index)\")\n        return 0\n      }\n      return Int(blocks[index])\n    }\n\n    /// Set the block id at the specified position.\n    /// - Parameters:\n    ///   - position: Position of the block relative to this section.\n    ///   - id: The new block id.\n    ///\n    /// For safety, the position is automatically converted to be relative to whatever section it is in. If\n    /// you're using this in a performance critical loop perhaps you should manually modify ``blocks`` instead.\n    public mutating func setBlockId(at position: BlockPosition, to id: Int) {\n      let index = position.relativeToChunkSection.blockIndex\n      setBlockId(at: index, to: id)\n    }\n\n    /// Set the block id at the specified position. Does nothing if the block index is invalid.\n    /// - Parameters:\n    ///   - index: Index of the block relative to this section.\n    ///   - id: The new block id.\n    public mutating func setBlockId(at index: Int, to id: Int) {\n      guard index < Section.numBlocks && index >= 0 else {\n        log.warning(\"Invalid position passed to Chunk.Section.setBlockId(at:to:); index=\\(index)\")\n        return\n      }\n\n      if RegistryStore.shared.blockRegistry.isAir(Int(blocks[index])) {\n        blockCount += 1\n      }\n\n      if RegistryStore.shared.blockRegistry.isAir(id) {\n        blockCount -= 1\n      }\n\n      blocks[index] = UInt16(id)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/Chunk/ChunkSectionPosition.swift",
    "content": "import Foundation\nimport FirebladeMath\n\n/// The position of a chunk section.\npublic struct ChunkSectionPosition {\n  /// The world x of the section divided by 16 and rounded down.\n  public var sectionX: Int\n  /// The world y of the section divided by 16 and rounded down.\n  public var sectionY: Int\n  /// The world z of the section divided by 16 and rounded down.\n  public var sectionZ: Int\n\n  /// The position of the chunk this section is in.\n  public var chunk: ChunkPosition {\n    return ChunkPosition(chunkX: sectionX, chunkZ: sectionZ)\n  }\n\n  /// The axis aligned bounding box for this chunk section.\n  public var axisAlignedBoundingBox: AxisAlignedBoundingBox {\n    AxisAlignedBoundingBox(\n      position: Vec3d(\n        Double(sectionX * Chunk.Section.width),\n        Double(sectionY * Chunk.Section.height),\n        Double(sectionZ * Chunk.Section.depth)\n      ),\n      size: Vec3d(\n        Double(Chunk.Section.width),\n        Double(Chunk.Section.height),\n        Double(Chunk.Section.depth)\n      )\n    )\n  }\n\n  /// Checks that the section's Y value is valid.\n  public var isValid: Bool {\n    return sectionY >= 0 && sectionY < Chunk.numSections\n  }\n\n  /// Create a new chunk section position.\n  public init(sectionX: Int, sectionY: Int, sectionZ: Int) {\n    self.sectionX = sectionX\n    self.sectionY = sectionY\n    self.sectionZ = sectionZ\n  }\n\n  /// Create a new `ChunkSectionPosition` in the chunk at `chunkPosition` located at `sectionY`.\n  ///\n  /// `sectionY` is not the world Y of the `Chunk.Section`. It is the world Y divided\n  /// by 16 and rounded down. This means it should be from 0 to 15.\n  ///\n  /// - Parameter chunkPosition: The position of the chunk this section is in\n  /// - Parameter sectionY: The section Y of the section (from 0 to 15 inclusive).\n  public init(_ chunkPosition: ChunkPosition, sectionY: Int) {\n    sectionX = chunkPosition.chunkX\n    self.sectionY = sectionY\n    sectionZ = chunkPosition.chunkZ\n  }\n\n  /// Gets the position of the section that neighbours this section in the specified direction.\n  /// - Parameter direction: Direction to find neighbour in.\n  /// - Returns: The position of the neighbour.\n  public func neighbour(inDirection direction: Direction) -> ChunkSectionPosition {\n    var position = self\n    switch direction {\n      case .north:\n        position.sectionZ -= 1\n      case .east:\n        position.sectionX += 1\n      case .south:\n        position.sectionZ += 1\n      case .west:\n        position.sectionX -= 1\n      case .up:\n        position.sectionY += 1\n      case .down:\n        position.sectionY -= 1\n    }\n\n    return position\n  }\n\n  /// Gets the position of the section that neighbours this section in the specified direction if it's valid.\n  ///\n  /// Returns `nil` if the neighbour position isn't valid (see ``isValid``).\n  /// - Parameter direction: Direction to find neighbour in.\n  /// - Returns: The position of the neighbour unless the neighbour is above or below the world.\n  public func validNeighbour(inDirection direction: Direction) -> ChunkSectionPosition? {\n    let position = neighbour(inDirection: direction)\n    if position.isValid {\n      return position\n    } else {\n      return nil\n    }\n  }\n}\n\nextension ChunkSectionPosition: Hashable {\n  public func hash(into hasher: inout Hasher) {\n    hasher.combine(sectionX)\n    hasher.combine(sectionY)\n    hasher.combine(sectionZ)\n  }\n}\n\nextension ChunkSectionPosition: Equatable {\n  public static func == (_ lhs: ChunkSectionPosition, _ rhs: ChunkSectionPosition) -> Bool {\n    return lhs.sectionX == rhs.sectionX && lhs.sectionZ == rhs.sectionZ && lhs.sectionY == rhs.sectionY\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/Chunk/HeightMap.swift",
    "content": "/// The height map for a chunk.\n///\n/// Only used for lighting and as such it holds the height of the highest non-transparent block in each column.\n/// Not the height of highest visible block as might be expected.\npublic struct HeightMap {\n  /// The highest block with a non-zero opacity in each column of the chunk.\n  public var heightMap: [Int]\n\n  /// Creates a new height map.\n  /// - Parameter heightMap: The height for each column of the chunk in order of increasing x and then increasing z.\n  public init(heightMap: [Int]) {\n    self.heightMap = heightMap\n  }\n\n  /// Gets the highest non-transparent block in the specified column.\n  /// - Parameters:\n  ///   - x: The x coordinate of the column.\n  ///   - z: The z coordinate of the column.\n  /// - Returns: The height of the highest non-transparent block in the specified column. Returns `-1` for a fully empty column.\n  public func getHighestLightBlockingBlock(_ x: Int, _ z: Int) -> Int {\n    return heightMap[columnIndex(x, z)]\n  }\n\n  /// Gets the highest non-transparent block in the specified column.\n  /// - Parameters:\n  ///   - position: The position of a block in the column to check.\n  /// - Returns: The height of the highest non-transparent block in the specified column. Returns `-1` for a fully empty column.\n  public func getHighestLightBlockingBlock(_ position: BlockPosition) -> Int {\n    return heightMap[columnIndex(position)]\n  }\n\n  /// Updates the height maps with regards to the given block update.\n  ///\n  /// The block should already be updated in the chunk.\n  public mutating func handleBlockUpdate(at position: BlockPosition, in chunk: Chunk, acquireChunkLock: Bool = true) {\n    let newBlock = chunk.getBlock(at: position, acquireLock: acquireChunkLock)\n    let columnIndex = self.columnIndex(position)\n\n    let highestLightBlockingBlock = heightMap[columnIndex]\n    if position.y > highestLightBlockingBlock {\n      if newBlock.lightMaterial.opacity != 0 {\n        heightMap[columnIndex] = Int(position.y)\n      }\n    } else if position.y == highestLightBlockingBlock && newBlock.lightMaterial.opacity == 0 {\n      // If the highest block has changed and doesn't block direct sky light anymore, find the next highest valid block\n      var position = position\n      var foundBlock = false\n      for _ in 0..<position.y {\n        position.y -= 1\n        let block = chunk.getBlock(at: position, acquireLock: acquireChunkLock)\n        if block.lightMaterial.opacity != 0 {\n          heightMap[columnIndex] = Int(position.y)\n          foundBlock = true\n          break\n        }\n      }\n\n      // If the column is empty set the height to -1\n      if !foundBlock {\n        heightMap[columnIndex] = -1\n      }\n    }\n  }\n\n  /// Calculates the index of the specified column in ``heightMap``.\n  private func columnIndex(_ position: BlockPosition) -> Int {\n    return columnIndex(position.x, position.z)\n  }\n\n  /// Calculates the index of the specified column in ``heightMap``.\n  private func columnIndex(_ x: Int, _ z: Int) -> Int {\n    return Int(z * 16 + x)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/DaylightCyclePhase.swift",
    "content": "import FirebladeMath\n\n// TODO: Figure out whether finer distinctions are required for any features\n// (e.g. knowing whether it's noon).\n/// A phase of the daylight cycle along with any important information about\n/// that phase (e.g. sunrise color).\npublic enum DaylightCyclePhase {\n  case sunrise(color: Vec4f)\n  case day\n  case sunset(color: Vec4f)\n  case night\n\n  /// Whether the phase is a sunrise or not.\n  public var isSunrise: Bool {\n    switch self {\n      case .sunrise:\n        return true\n      case .day, .sunset, .night:\n        return false\n    }\n  }\n\n  /// Whether the phase is a sunset or not.\n  public var isSunset: Bool {\n    switch self {\n      case .sunset:\n        return true\n      case .sunrise, .day, .night:\n        return false\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/Dimension/Dimension.swift",
    "content": "// TODO: Update the property names once I understand what they each actually do\npublic struct Dimension {\n  public var identifier: Identifier\n  public var ambientLight: Float\n  public var infiniburn: Identifier?\n  public var fixedTime: Int?\n  public var logicalHeight: Int\n  public var isNatural: Bool\n  public var hasCeiling: Bool\n  public var hasSkyLight: Bool\n  public var shrunk: Bool\n  public var ultrawarm: Bool\n  public var hasRaids: Bool\n  public var respawnAnchorWorks: Bool\n  public var bedWorks: Bool\n  public var piglinSafe: Bool\n\n  /// Whether the dimension is the vanilla overworld or not.\n  public var isOverworld: Bool {\n    identifier == Identifier(name: \"overworld\")\n  }\n\n  /// Whether the dimension is the vanilla nether or not.\n  public var isNether: Bool {\n    identifier == Identifier(name: \"the_nether\")\n  }\n\n  /// Whether the dimension is the vanilla end or not.\n  public var isEnd: Bool {\n    identifier == Identifier(name: \"the_end\")\n  }\n\n  public static let overworld = Dimension(\n    identifier: Identifier(name: \"overworld\"),\n    ambientLight: 0,\n    infiniburn: Identifier(name: \"infiniburn_overworld\"),\n    fixedTime: nil,\n    logicalHeight: 256,\n    isNatural: true,\n    hasCeiling: false,\n    hasSkyLight: true,\n    shrunk: false,\n    ultrawarm: false,\n    hasRaids: true,\n    respawnAnchorWorks: false,\n    bedWorks: true,\n    piglinSafe: false\n  )\n\n  public init(\n    identifier: Identifier,\n    ambientLight: Float,\n    infiniburn: Identifier? = nil,\n    fixedTime: Int? = nil,\n    logicalHeight: Int,\n    isNatural: Bool,\n    hasCeiling: Bool,\n    hasSkyLight: Bool,\n    shrunk: Bool,\n    ultrawarm: Bool,\n    hasRaids: Bool,\n    respawnAnchorWorks: Bool,\n    bedWorks: Bool,\n    piglinSafe: Bool\n  ) {\n    self.identifier = identifier\n    self.ambientLight = ambientLight\n    self.infiniburn = infiniburn\n    self.fixedTime = fixedTime\n    self.logicalHeight = logicalHeight\n    self.isNatural = isNatural\n    self.hasCeiling = hasCeiling\n    self.hasSkyLight = hasSkyLight\n    self.shrunk = shrunk\n    self.ultrawarm = ultrawarm\n    self.hasRaids = hasRaids\n    self.respawnAnchorWorks = respawnAnchorWorks\n    self.bedWorks = bedWorks\n    self.piglinSafe = piglinSafe\n  }\n\n  public init(from compound: NBT.Compound) throws {\n    identifier = try Identifier(compound.get(\"name\"))\n    ambientLight = try compound.get(\"ambient_light\")\n\n    let infiniburn: String = try compound.get(\"infiniburn\")\n    self.infiniburn = infiniburn == \"\" ? nil : try Identifier(infiniburn)\n\n    let fixedTime: Int64? = try? compound.get(\"fixed_time\")\n    self.fixedTime = fixedTime.map(Int.init)\n\n    let isNatural: Int8 = try compound.get(\"natural\")\n    self.isNatural = isNatural == 1\n    let hasCeiling: Int8 = try compound.get(\"has_ceiling\")\n    self.hasCeiling = hasCeiling == 1\n    let hasSkyLight: Int8 = try compound.get(\"has_skylight\")\n    self.hasSkyLight = hasSkyLight == 1\n    let shrunk: Int8 = try compound.get(\"shrunk\")\n    self.shrunk = shrunk == 1\n    let ultrawarm: Int8 = try compound.get(\"ultrawarm\")\n    self.ultrawarm = ultrawarm == 1\n    let hasRaids: Int8 = try compound.get(\"has_raids\")\n    self.hasRaids = hasRaids == 1\n    let respawnAnchorWorks: Int8 = try compound.get(\"respawn_anchor_works\")\n    self.respawnAnchorWorks = respawnAnchorWorks == 1\n    let bedWorks: Int8 = try compound.get(\"bed_works\")\n    self.bedWorks = bedWorks == 1\n    let piglinSafe: Int8 = try compound.get(\"piglin_safe\")\n    self.piglinSafe = piglinSafe == 1\n    let logicalHeight: Int32 = try compound.get(\"logical_height\")\n    self.logicalHeight = Int(logicalHeight)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/Fog.swift",
    "content": "/// The distance fog experienced by a player.\npublic struct Fog {\n  public var color: Vec3f\n  public var style: Style\n\n  public enum Style {\n    case exponential(density: Float)\n    case linear(startDistance: Float, endDistance: Float)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/Light/ChunkLighting.swift",
    "content": "import Foundation\n\n/// A store for chunk lighting data. Not thread-safe.\n///\n/// It may also include lighting for the chunk sections above and below the world.\npublic struct ChunkLighting {\n  /// Sky light levels for each chunk section. Each array is indexed by block index.\n  public private(set) var skyLightData: [Int: [UInt8]] = [:]\n  /// Block light levels for each chunk section. Each array is indexed by block index.\n  public private(set) var blockLightData: [Int: [UInt8]] = [:]\n\n  /// Whether this lighting has been populated with initial data or not.\n  public private(set) var isPopulated = false\n\n  /// Creates an empty chunk lighting store. ``isPopulated`` gets set to `false`.\n  public init() {\n    isPopulated = false\n  }\n\n  /// Updates the lighting data with data received from the server.\n  /// - Parameter data: The data received from the server.\n  public mutating func update(with data: ChunkLightingUpdateData) {\n    for index in data.emptySkyLightSections {\n      skyLightData.removeValue(forKey: index)\n    }\n\n    for index in data.emptyBlockLightSections {\n      blockLightData.removeValue(forKey: index)\n    }\n\n    for (index, array) in data.skyLightArrays {\n      skyLightData[index] = array\n    }\n\n    for (index, array) in data.blockLightArrays {\n      blockLightData[index] = array\n    }\n\n    isPopulated = true\n  }\n\n  /// Gets the light level at the given position. Includes both the block light and sky light level.\n  /// - Parameter position: The position to get the light level at.\n  /// - Returns: The requested light level. If the position is not loaded, the default light level is returned.\n  public func getLightLevel(at position: BlockPosition) -> LightLevel {\n    let skyLightLevel = getSkyLightLevel(at: position)\n    let blockLightLevel = getBlockLightLevel(at: position)\n    return LightLevel(sky: skyLightLevel, block: blockLightLevel)\n  }\n\n  /// Gets the light level at the given position. Includes both the block light and sky light level.\n  /// - Parameters:\n  ///   - position: The position to get the light level at (relative to the specified section).\n  ///   - sectionIndex: The chunk section containing the light level to get.\n  /// - Returns: The requested light level. If the position is not loaded, the default light level is returned.\n  public func getLightLevel(at position: BlockPosition, inSectionAt sectionIndex: Int) -> LightLevel {\n    var position = position\n    position.y += sectionIndex * Chunk.Section.height\n    let skyLightLevel = getSkyLightLevel(at: position)\n    let blockLightLevel = getBlockLightLevel(at: position)\n    return LightLevel(sky: skyLightLevel, block: blockLightLevel)\n  }\n\n  /// Gets the light level at the given position. Includes both the block light and sky light level.\n  /// - Parameters:\n  ///   - index: The index of the block to get the light level for in the specified section.\n  ///   - sectionIndex: The chunk section containing the light level to get.\n  /// - Returns: The requested light level. If the position is not loaded, the default light level is returned.\n  public func getLightLevel(atIndex index: Int, inSectionAt sectionIndex: Int) -> LightLevel {\n    let skyLightLevel = getSkyLightLevel(atIndex: index, inSectionAt: sectionIndex)\n    let blockLightLevel = getBlockLightLevel(atIndex: index, inSectionAt: sectionIndex)\n    return LightLevel(sky: skyLightLevel, block: blockLightLevel)\n  }\n\n  /// Gets the light level at the given block index. Includes both the block light and sky light level.\n  /// - Parameter index: The index of the block to get the light level for in the specified section.\n  /// - Returns: The requested light level. If the position is not loaded, the default light level is returned.\n  public func getLightLevel(at index: Int) -> LightLevel {\n    let skyLightLevel = getSkyLightLevel(atIndex: index)\n    let blockLightLevel = getBlockLightLevel(atIndex: index)\n    return LightLevel(sky: skyLightLevel, block: blockLightLevel)\n  }\n\n  /// Gets the sky light level at the given position.\n  /// - Parameter position: The position to get the light level at.\n  /// - Returns: The requested light level. If the position is not loaded, ``LightLevel/defaultSkyLightLevel`` is returned.\n  public func getSkyLightLevel(at position: BlockPosition) -> Int {\n    if !Self.isValidPosition(position) {\n      return LightLevel.defaultSkyLightLevel\n    }\n    let blockIndex = position.relativeToChunkSection.blockIndex\n    return getSkyLightLevel(atIndex: blockIndex, inSectionAt: position.sectionIndex)\n  }\n\n  /// Gets the sky light level at the given position.\n  /// - Parameter index: The index of the block to get the sky light level for in the specified section.\n  /// - Returns: The requested light level. If the position is not loaded, ``LightLevel/defaultSkyLightLevel`` is returned.\n  public func getSkyLightLevel(atIndex blockIndex: Int) -> Int {\n    if blockIndex < 0 || blockIndex >= Chunk.numBlocks {\n      return LightLevel.defaultSkyLightLevel\n    }\n    let sectionRelativeBlockIndex = blockIndex % Chunk.Section.numBlocks\n    let sectionIndex = blockIndex / Chunk.Section.numBlocks\n    return getSkyLightLevel(atIndex: sectionRelativeBlockIndex, inSectionAt: sectionIndex)\n  }\n\n  /// Gets the sky light level at the given position.\n  /// - Parameters:\n  ///   - index: The index of the block to get the sky light level for in the specified section.\n  ///   - sectionIndex: The chunk section containing the sky light level to get.\n  /// - Returns: The requested light level. If the position is not loaded, ``LightLevel/defaultSkyLightLevel`` is returned.\n  public func getSkyLightLevel(atIndex blockIndex: Int, inSectionAt sectionIndex: Int) -> Int {\n    if let skyLightArray = skyLightData[sectionIndex] {\n      return Int(skyLightArray[blockIndex])\n    } else {\n      return LightLevel.defaultSkyLightLevel\n    }\n  }\n\n  /// Gets the block light level at the given position.\n  /// - Parameter position: The position to get the light level at.\n  /// - Returns: The requested light level. If the position is not loaded, ``LightLevel/defaultBlockLightLevel`` is returned.\n  public func getBlockLightLevel(at position: BlockPosition) -> Int {\n    if !Self.isValidPosition(position) {\n      return LightLevel.defaultBlockLightLevel\n    }\n    let blockIndex = position.relativeToChunkSection.blockIndex\n    return getBlockLightLevel(atIndex: blockIndex, inSectionAt: position.sectionIndex)\n  }\n\n  /// Gets the block light level at the given position.\n  /// - Parameter index: The index of the block to get the block light level for in the specified section.\n  /// - Returns: The requested light level. If the position is not loaded, ``LightLevel/defaultBlockLightLevel`` is returned.\n  public func getBlockLightLevel(atIndex blockIndex: Int) -> Int {\n    if blockIndex < 0 || blockIndex >= Chunk.numBlocks {\n      return LightLevel.defaultBlockLightLevel\n    }\n    let sectionRelativeBlockIndex = blockIndex % Chunk.Section.numBlocks\n    let sectionIndex = blockIndex / Chunk.Section.numBlocks\n    return getBlockLightLevel(atIndex: sectionRelativeBlockIndex, inSectionAt: sectionIndex)\n  }\n\n  /// Gets the block light level at the given position.\n  /// - Parameters:\n  ///   - index: The index of the block to get the block light level for in the specified section.\n  ///   - sectionIndex: The chunk section containing the block light level to get.\n  /// - Returns: The requested light level. If the position is not loaded, ``LightLevel/defaultBlockLightLevel`` is returned.\n  public func getBlockLightLevel(atIndex blockIndex: Int, inSectionAt sectionIndex: Int) -> Int {\n    if let blockLightArray = blockLightData[sectionIndex] {\n      return Int(blockLightArray[blockIndex])\n    } else {\n      return LightLevel.defaultBlockLightLevel\n    }\n  }\n\n  // TODO: Properly light sections when creating new ones\n\n  /// Sets the block light level at the given position.\n  /// - Parameters:\n  ///   - position: The position to set the block light level for.\n  ///   - newLevel: The new block light level from 0 to 15\n  public mutating func setBlockLightLevel(at position: BlockPosition, to newLevel: Int) {\n    guard Self.isValidPosition(position) else {\n      return\n    }\n\n    let newLevel = MathUtil.clamp(newLevel, 0, LightLevel.maximumLightLevel)\n\n    let sectionIndex = position.sectionIndex\n    let blockIndex = position.relativeToChunkSection.blockIndex\n    if blockLightData[sectionIndex] != nil {\n      // It was done this way to prevent copies\n      // swiftlint:disable force_unwrapping\n      blockLightData[sectionIndex]![blockIndex] = UInt8(newLevel)\n      // swiftlint:enable force_unwrapping\n    } else {\n      var blockLighting: [UInt8] = [UInt8](repeating: UInt8(LightLevel.defaultBlockLightLevel), count: Chunk.Section.numBlocks)\n      blockLighting[blockIndex] = UInt8(newLevel)\n      blockLightData[sectionIndex] = blockLighting\n    }\n  }\n\n  /// Sets the sky light level at the given position.\n  /// - Parameters:\n  ///   - position: The position to set the sky light level for.\n  ///   - newLevel: The new sky light level from 0 to 15\n  public mutating func setSkyLightLevel(at position: BlockPosition, to newLevel: Int) {\n    guard Self.isValidPosition(position) else {\n      return\n    }\n\n    let newLevel = MathUtil.clamp(newLevel, 0, LightLevel.maximumLightLevel)\n\n    let sectionIndex = position.sectionIndex\n    let blockIndex = position.relativeToChunkSection.blockIndex\n    if skyLightData[sectionIndex] != nil {\n      // It was done this way to prevent copies\n      // swiftlint:disable force_unwrapping\n      skyLightData[sectionIndex]![blockIndex] = UInt8(newLevel)\n      // swiftlint:enable force_unwrapping\n    } else {\n      // TODO: properly light empty sections when creating lighting data for them\n      var skyLighting: [UInt8] = [UInt8](repeating: UInt8(LightLevel.maximumLightLevel), count: Chunk.Section.numBlocks)\n      skyLighting[blockIndex] = UInt8(newLevel)\n      skyLightData[sectionIndex] = skyLighting\n    }\n  }\n\n  /// Checks whether a position is within the chunk (including 1 chunk section above and below).\n  /// - Parameter position: The position to check.\n  /// - Returns: Whether the position is valid or not.\n  private static func isValidPosition(_ position: BlockPosition) -> Bool {\n    return position.x >= 0 && position.y >= -16 && position.z >= 0 && position.x < Chunk.width && position.y < Chunk.height + 16 && position.z < Chunk.depth\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/Light/ChunkLightingUpdateData.swift",
    "content": "import Foundation\n\n// TODO: just use the packet instead of this thing\npublic struct ChunkLightingUpdateData {\n  public var trustEdges: Bool\n  public var emptySkyLightSections: [Int]\n  public var emptyBlockLightSections: [Int]\n  public var skyLightArrays: [Int: [UInt8]]\n  public var blockLightArrays: [Int: [UInt8]]\n  \n  public var updatedSections: [Int] {\n    var sections: Set<Int> = []\n    sections.formUnion(emptySkyLightSections)\n    sections.formUnion(emptyBlockLightSections)\n    sections.formUnion(skyLightArrays.keys)\n    sections.formUnion(blockLightArrays.keys)\n    return Array(sections)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/Light/LightLevel.swift",
    "content": "import Foundation\n\n/// A light level. Includes both the block light and sky light level.\npublic struct LightLevel {\n  /// The sky light level used for unloaded chunks.\n  public static let defaultSkyLightLevel = 0\n  /// The block light level used for unloaded chunks.\n  public static let defaultBlockLightLevel = 0\n  /// The maximum light level.\n  public static let maximumLightLevel = 15\n  /// The number of light levels.\n  public static let levelCount = maximumLightLevel + 1\n  /// The default light level used for uninitialized chunks etc.\n  public static let `default` = Self(sky: defaultSkyLightLevel, block: defaultBlockLightLevel)\n\n  /// The sky light level.\n  public var sky: Int\n  /// The block light level.\n  public var block: Int\n\n  /// Creates a new light level value.\n  /// - Parameters:\n  ///   - sky: The sky light level.\n  ///   - block: The block light level.\n  public init(sky: Int, block: Int) {\n    self.sky = sky\n    self.block = block\n  }\n\n  /// Gets the highest sky light level and block light level from two light levels.\n  /// - Parameters:\n  ///   - a: The first light level.\n  ///   - b: The second light level.\n  /// - Returns: A light level with the maximum of both sky light levels and the maximum of both block light levels.\n  public static func max(_ a: LightLevel, _ b: LightLevel) -> LightLevel {\n    return LightLevel(sky: Swift.max(a.sky, b.sky), block: Swift.max(a.block, b.block))\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/Light/LightingEngine.swift",
    "content": "import Foundation\nimport Collections\n\n/// The engine used to update a world's lighting data when a block changes.\n///\n/// The algorithm used in this lighting engine is a simplified version of the one found in\n/// [Starlight](https://github.com/PaperMC/Starlight/tree/fabric), a highly optimised replacement for the vanilla lighting system.\npublic struct LightingEngine {\n  // MARK: Private types\n\n  /// An entry in the increase queue.\n  private struct IncreaseQueueEntry {\n    var position: BlockPosition\n    var lightLevel: Int\n    var flags: IncreaseFlags\n    var skipDirections: [Direction]\n  }\n\n  /// An entry in the decrease queue.\n  private struct DecreaseQueueEntry {\n    var position: BlockPosition\n    var lightLevel: Int\n    var skipDirections: [Direction]\n  }\n\n  /// Flags for entries in the increase queue.\n  private struct IncreaseFlags: OptionSet {\n    var rawValue: UInt8\n\n    init(rawValue: UInt8) {\n      self.rawValue = rawValue\n    }\n\n    static let writeLevel = IncreaseFlags(rawValue: 1)\n    static let recheckLevels = IncreaseFlags(rawValue: 2)\n  }\n\n  // MARK: Private properties\n\n  /// Queue of blocks to check when increasing light levels.\n  private var increaseQueue: Deque<IncreaseQueueEntry> = []\n  /// Queue of blocks to check when decreasing light levels.\n  private var decreaseQueue: Deque<DecreaseQueueEntry> = []\n\n  // MARK: Init\n\n  /// Create a new lighting engine instance with its own queues.\n  public init() { }\n\n  // MARK: Public methods\n\n  /// Propagates lighting changes from an updated block. `position` is in world coordinates.\n  public mutating func updateLighting(at position: BlockPosition, in world: World) {\n    updateLighting(at: [position], in: world)\n  }\n\n  /// Propagates lighting changes from multiple updated blocks. `positions` are in world coordinates.\n  public mutating func updateLighting(at positions: [BlockPosition], in world: World) {\n    increaseQueue = []\n    decreaseQueue = []\n    updateBlockLight(at: positions, in: world)\n    updateSkyLight(at: positions, in: world)\n  }\n\n  // MARK: Private methods\n\n  /// Propagates block light changes from multiple updated blocks. `positions` are in world coordinates.\n  private mutating func updateBlockLight(at positions: [BlockPosition], in world: World) {\n    for position in positions {\n      queueBlockLightUpdate(at: position, in: world)\n    }\n\n    performLightLevelDecrease(\n      in: world,\n      getLightLevel: world.getBlockLightLevel(at:),\n      setLightLevel: world.setBlockLightLevel(at:to:)\n    )\n  }\n\n  /// Propagates sky light changes from multiple updated blocks. `positions` are in world coordinates.\n  ///\n  /// All positions provided must be in existent chunks.\n  private mutating func updateSkyLight(at positions: [BlockPosition], in world: World) {\n    for position in positions {\n      let chunkRelativePosition = position.relativeToChunk\n      guard let highest = world.chunk(at: position.chunk)?.highestLightBlockingBlock(atX: chunkRelativePosition.x, andZ: chunkRelativePosition.z) else {\n        log.error(\"Sky light update attempted in non-existent chunk\")\n        return\n      }\n\n      if position.y > highest {\n        // This could possibly have changed from a block that used to be the highest so update the light level 15 blocks below accordingly\n        var position = position\n        for _ in 0..<position.y {\n          position.y -= 1\n          if world.getBlock(at: position).lightMaterial.opacity == 0 && world.getSkyLightLevel(at: position) != 15 {\n            world.setSkyLightLevel(at: position, to: 15)\n            increaseQueue.append(IncreaseQueueEntry(position: position, lightLevel: 15, flags: [], skipDirections: [.up, .down]))\n          } else {\n            break\n          }\n        }\n      } else if position.y == highest {\n        // This could possibly be a new highest block so we must check the blocks below.\n        var position = position\n        for _ in 0..<position.y {\n          position.y -= 1\n          let currentLightLevel = world.getSkyLightLevel(at: position)\n          if world.getBlock(at: position).lightMaterial.opacity == 0 && currentLightLevel == 15 {\n            world.setSkyLightLevel(at: position, to: 0)\n            decreaseQueue.append(DecreaseQueueEntry(position: position, lightLevel: currentLightLevel, skipDirections: []))\n          }\n        }\n      }\n\n      queueSkyLightUpdate(at: position, in: world)\n    }\n\n    performLightLevelDecrease(\n      in: world,\n      getLightLevel: world.getSkyLightLevel(at:),\n      setLightLevel: world.setSkyLightLevel(at:to:)\n    )\n  }\n\n  /// Checks an updated block and queues any block light level propagation required.\n  private mutating func queueBlockLightUpdate(at position: BlockPosition, in world: World) {\n    let currentLightLevel = world.getBlockLightLevel(at: position)\n    let block = world.getBlock(at: position)\n    let emittedLight = block.lightMaterial.luminance\n\n    world.setBlockLightLevel(at: position, to: emittedLight)\n\n    if emittedLight != 0 {\n      increaseQueue.append(IncreaseQueueEntry(position: position, lightLevel: emittedLight, flags: [], skipDirections: []))\n    }\n\n    decreaseQueue.append(DecreaseQueueEntry(position: position, lightLevel: currentLightLevel, skipDirections: []))\n  }\n\n  /// Checks an updated block and queues any sky light level propagation required.\n  private mutating func queueSkyLightUpdate(at position: BlockPosition, in world: World) {\n    let currentLightLevel = world.getSkyLightLevel(at: position)\n\n    if currentLightLevel == 15 {\n      increaseQueue.append(IncreaseQueueEntry(position: position, lightLevel: 15, flags: [], skipDirections: []))\n    } else {\n      world.setSkyLightLevel(at: position, to: 0)\n    }\n\n    decreaseQueue.append(DecreaseQueueEntry(position: position, lightLevel: currentLightLevel, skipDirections: []))\n  }\n\n  private mutating func performLightLevelIncrease(\n    in world: World,\n    getLightLevel: (_ position: BlockPosition) -> Int,\n    setLightLevel: (_ position: BlockPosition, _ level: Int) -> Void\n  ) {\n    while let entry = increaseQueue.popFirst() {\n      if entry.flags.contains(.recheckLevels) {\n        if getLightLevel(entry.position) != entry.lightLevel {\n          continue\n        }\n      } else if entry.flags.contains(.writeLevel) {\n        setLightLevel(entry.position, entry.lightLevel)\n      }\n\n      for direction in Direction.allDirections where !entry.skipDirections.contains(direction) {\n        let neighbourPosition = entry.position + direction.intVector\n\n        if !world.isPositionLoaded(neighbourPosition) {\n          continue\n        }\n\n        let neighbourLight = getLightLevel(neighbourPosition)\n\n        if neighbourLight >= entry.lightLevel {\n          continue\n        }\n\n        let neighbourBlock = world.getBlock(at: neighbourPosition)\n        let opacity = max(neighbourBlock.lightMaterial.opacity, 1)\n        let newNeighbourLight = max(entry.lightLevel - opacity, 0)\n\n        if newNeighbourLight > neighbourLight {\n          setLightLevel(neighbourPosition, newNeighbourLight)\n\n          if newNeighbourLight > 1 {\n            increaseQueue.append(\n              IncreaseQueueEntry(\n                position: neighbourPosition,\n                lightLevel: newNeighbourLight,\n                flags: [],\n                skipDirections: [direction.opposite]\n              )\n            )\n          }\n        }\n      }\n    }\n  }\n\n  private mutating func performLightLevelDecrease(\n    in world: World,\n    getLightLevel: (_ position: BlockPosition) -> Int,\n    setLightLevel: (_ position: BlockPosition, _ level: Int) -> Void\n  ) {\n    while let entry = decreaseQueue.popFirst() {\n      for direction in Direction.allDirections where !entry.skipDirections.contains(direction) {\n        let neighbourPosition = entry.position + direction.intVector\n\n        if !world.isPositionLoaded(neighbourPosition) {\n          continue\n        }\n\n        let neighbourLight = getLightLevel(neighbourPosition)\n\n        if neighbourLight == 0 {\n          continue\n        }\nlet neighbourBlock = world.getBlock(at: neighbourPosition)\n        let opacity = max(neighbourBlock.lightMaterial.opacity, 1)\n        let newNeighbourLight = max(entry.lightLevel - opacity, 0)\n\n        if neighbourLight > newNeighbourLight {\n          increaseQueue.append(IncreaseQueueEntry(position: neighbourPosition, lightLevel: neighbourLight, flags: [.recheckLevels], skipDirections: []))\n          continue\n        }\n\n        let neighbourLuminance = neighbourBlock.lightMaterial.luminance\n        if neighbourLuminance != 0 {\n          increaseQueue.append(IncreaseQueueEntry(position: neighbourPosition, lightLevel: neighbourLuminance, flags: [.writeLevel], skipDirections: []))\n        }\n\n        setLightLevel(neighbourPosition, neighbourLuminance)\n\n        if newNeighbourLight > 0 {\n          decreaseQueue.append(DecreaseQueueEntry(position: neighbourPosition, lightLevel: newNeighbourLight, skipDirections: [direction.opposite]))\n        }\n      }\n    }\n\n    performLightLevelIncrease(in: world, getLightLevel: getLightLevel, setLightLevel: setLightLevel)\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/Targeted.swift",
    "content": "/// Something targeted by the player. Often a ``Block``, ``Entity`` or ``Thing``.\npublic struct Targeted<T> {\n  /// The underlying thing getting targeted.\n  public var target: T\n  /// The distance from the player to the point of intersection.\n  public var distance: Float\n  /// The face of the bounding box which the player's ray intersects with.\n  public var face: Direction\n  /// The position at which the player's ray intersects the thing's bounding box.\n  public var targetedPosition: Vec3f\n\n  /// The targeted position relative to the block that it's in. All coordinates will be in\n  /// the range `0...1`.\n  public var cursor: Vec3f {\n    var cursor = targetedPosition\n    cursor.x = cursor.x.truncatingRemainder(dividingBy: 1)\n    cursor.y = cursor.y.truncatingRemainder(dividingBy: 1)\n    cursor.z = cursor.z.truncatingRemainder(dividingBy: 1)\n    return cursor\n  }\n\n  /// Maps the targeted value, useful for changing the representation of the wrapped target.\n  public func map<U>(_ mapTarget: (T) -> U) -> Targeted<U> {\n    Targeted<U>(\n      target: mapTarget(target),\n      distance: distance,\n      face: face,\n      targetedPosition: targetedPosition\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/Thing.swift",
    "content": "/// A block or an entity. Starting to run out of ambiguous nouns!\npublic enum Thing {\n  case block(position: BlockPosition)\n  case entity(id: Int)\n}\n"
  },
  {
    "path": "Sources/Core/Sources/World/World.swift",
    "content": "import FirebladeMath\nimport Foundation\nimport Logging\n\n/// Represents a Minecraft world. Completely thread-safe.\n///\n/// Includes chunks, lighting and some other metadata.\npublic class World {\n  /// The color of fog when in lava.\n  public static let lavaFogColor = Vec3f(0.6, 0.1, 0)\n\n  // MARK: Public properties\n\n  /// The bus the world will emit events to.\n  public var eventBus: EventBus {\n    get {\n      eventBusLock.acquireReadLock()\n      defer { eventBusLock.unlock() }\n      return _eventBus\n    }\n    set {\n      eventBusLock.acquireWriteLock()\n      defer { eventBusLock.unlock() }\n      _eventBus = newValue\n    }\n  }\n\n  /// The positions of all loaded chunks.\n  public var loadedChunkPositions: [ChunkPosition] {\n    terrainLock.acquireReadLock()\n    defer { terrainLock.unlock() }\n    return [ChunkPosition](chunks.keys)\n  }\n\n  // MARK: Public metadata properties\n\n  /// The name of this world.\n  public let name: Identifier\n  /// The world's dimension.\n  public let dimension: Dimension\n  /// The hashed seed of this world.\n  public let hashedSeed: Int\n  /// Whether this world is a debug world or not.\n  public let isDebug: Bool\n  /// Whether this world is superflat or not.\n  public let isFlat: Bool\n\n  // MARK: Private properties\n\n  /// Lock for managing thread-safe read and write of ``age`` and ``timeOfDay``.\n  private var timeLock = ReadWriteLock()\n  /// The world's age.\n  private var age = 0\n  /// The time of day.\n  private var timeOfDay = 0\n\n  /// Lock for managing thread-safe read and write of ``chunks``, ``chunklessLightingData`` and ``unlitChunks``.\n  private var terrainLock = ReadWriteLock()\n  /// The world's chunks.\n  private var chunks: [ChunkPosition: Chunk] = [:]\n  /// Lighting data that arrived before its respective chunk or was sent for a non-existent chunk.\n  private var chunklessLightingData: [ChunkPosition: ChunkLightingUpdateData] = [:]\n  /// Chunks that don't have lighting data yet.\n  private var unlitChunks: [ChunkPosition: Chunk] = [:]\n\n  /// Used to update world lighting.\n  private var lightingEngine = LightingEngine()\n\n  /// Used to manage thread safe access of `_eventBus`.\n  private var eventBusLock = ReadWriteLock()\n  /// Not thread safe. Use `eventBus`.\n  private var _eventBus: EventBus\n\n  /// Lock for protecting access to ``breakingBlocks``.\n  private var blockBreakingLock = ReadWriteLock()\n  /// All blocks currently getting broken.\n  private var breakingBlocks: [BreakingBlock] = []\n\n  // MARK: Init\n\n  /// Create an empty world.\n  public init(eventBus: EventBus) {\n    _eventBus = eventBus\n    name = Identifier(name: \"world\")\n    dimension = Dimension.overworld\n    hashedSeed = 0\n    isFlat = false\n    isDebug = false\n  }\n\n  /// Create a new world with the given properties.\n  public init(\n    name: Identifier,\n    dimension: Dimension,\n    hashedSeed: Int,\n    isFlat: Bool,\n    isDebug: Bool,\n    eventBus: EventBus\n  ) {\n    _eventBus = eventBus\n    self.name = name\n    self.dimension = dimension\n    self.hashedSeed = hashedSeed\n    self.isFlat = isFlat\n    self.isDebug = isDebug\n  }\n\n  // MARK: Time\n\n  /// - Returns: The current age of the world in ticks.\n  public func getAge() -> Int {\n    timeLock.acquireReadLock()\n    defer { timeLock.unlock() }\n    return age\n  }\n\n  /// - Returns: The current time of day in ticks.\n  public func getTimeOfDay() -> Int {\n    timeLock.acquireReadLock()\n    defer { timeLock.unlock() }\n\n    if let time = dimension.fixedTime {\n      return time\n    } else {\n      // Negative time of day is used to indicate that doDaylightCycle is false (weird)\n      return timeOfDay < 0 ? -timeOfDay : timeOfDay\n    }\n  }\n\n  /// Sets the age of the world in ticks.\n  /// - Parameter age: The new value.\n  public func setAge(_ age: Int) {\n    timeLock.acquireWriteLock()\n    defer { timeLock.unlock() }\n    self.age = age\n  }\n\n  /// Sets the time of day in ticks.\n  /// - Parameter timeOfDay: The new value.\n  public func setTimeOfDay(_ timeOfDay: Int) {\n    timeLock.acquireWriteLock()\n    defer { timeLock.unlock() }\n    self.timeOfDay = timeOfDay\n  }\n\n  // MARK: Sky\n\n  /// Gets the sky color seen when viewed from a given position.\n  /// - Parameters:\n  ///   - position: The position that the world is being viewed from.\n  public func getSkyColor(at position: BlockPosition) -> Vec3f {\n    // TODO: Avoid the force unwrap. Possibly by updating the BiomeRegistry to ensure that\n    //   a plains biome is always present to use as a default (perhaps as a defaultBiome\n    //   property).\n    let biome =\n      getBiome(at: position) ?? RegistryStore.shared.biomeRegistry.biome(\n        for: Identifier(name: \"plains\"))!\n\n    let skyColor = biome.skyColor.floatVector\n    let skyBrightness = getSkyBrightness()\n\n    return skyColor * skyBrightness\n  }\n\n  // TODO: Does this only make sense for the overworld?\n  /// Gets the brightness of the sky due to the time of day.\n  public func getSkyBrightness() -> Float {\n    // The sun angle is used to calculate sun height, which is then adjusted to account\n    // for the fact that the sun still brightens the sky for a while after it sets.\n    let sunAngle = getSunAngleRadians()\n    // The sun's height from `-1` at midnight to `1` at midday.\n    let sunHeight = Foundation.cos(sunAngle)\n    return MathUtil.clamp(sunHeight * 2 + 0.5, 0, 1)\n  }\n\n  /// Gets the sun's angle in the sky with 0 being directly overhead, and the angle increasing\n  /// into the afternoon. Always in the interval `[0, 2π)`.\n  public func getSunAngleRadians() -> Float {\n    let time = getTimeOfDay()\n\n    // The progress of the day starting at 6am\n    let dayProgress = Float(time) / 24000\n    let dayProgressSinceNoon = Foundation.fmod(dayProgress - 0.25, 1)\n\n    // Due to modelling the world similarly to a sphere (in terms of its sky), the sun\n    // should spend less time above the horizontal than below it, which this extra factor\n    // takes care of.\n    let dayShorteningFactor = 0.5 - Foundation.cos(dayProgressSinceNoon * .pi) / 2\n    // The sun's progress around the sky from 0 to 1, with 0 being noon.\n    let sunProgress = (2 * dayProgressSinceNoon + dayShorteningFactor) / 3\n\n    return 2 * .pi * sunProgress\n  }\n\n  /// Gets the color of fog that is seen when viewed from a given position and looking\n  /// in a specific direction.\n  /// - Parameters:\n  ///   - ray: The ray defining the position and look direction of the viewer.\n  ///   - renderDistance: The render distance that the fog will be rendered at. Often\n  ///     the true render distance minus 1 is used when above 2 render distance (to conceal\n  ///     more of the edge of the world).\n  ///   - acquireLock: Whether to acquire a lock or not before reading the value. Don't\n  ///     touch this unless you know what you're doing.\n  public func getFogColor(\n    forViewerWithRay ray: Ray,\n    withRenderDistance renderDistance: Int,\n    acquireLock: Bool = true\n  ) -> Vec3f {\n    let position = ray.origin\n    let blockPosition = BlockPosition(x: Int(position.x), y: Int(position.y), z: Int(position.z))\n\n    let biome =\n      getBiome(at: blockPosition)\n      ?? RegistryStore.shared.biomeRegistry.biome(for: Identifier(name: \"plains\"))!\n\n    let fluidOnEyes = getFluidState(at: position, acquireLock: acquireLock)\n      .map(\\.fluidId)\n      .map(RegistryStore.shared.fluidRegistry.fluid(withId:))\n\n    var fogColor: Vec3f\n    if fluidOnEyes?.isWater == true {\n      // TODO: Slowly adjust the water fog color as the player's 'eyes' adjust.\n      fogColor = biome.waterFogColor.floatVector\n    } else if fluidOnEyes?.isLava == true {\n      fogColor = Self.lavaFogColor\n    } else {\n      fogColor = MathUtil.lerp(\n        from: getSkyColor(at: blockPosition),\n        to: biome.fogColor.floatVector,\n        progress: FirebladeMath.pow(0.25 + 0.75 * min(32, Float(renderDistance)) / 32, 0.25)\n      )\n\n      // Take sky brightness into account.\n      if dimension.isEnd {\n        fogColor *= 0.15\n      } else if dimension.isOverworld {\n        let skyBrightness = getSkyBrightness()\n        fogColor *= Vec3f(\n          MathUtil.lerp(from: 0.06, to: 1, progress: skyBrightness),\n          MathUtil.lerp(from: 0.06, to: 1, progress: skyBrightness),\n          MathUtil.lerp(from: 0.09, to: 1, progress: skyBrightness)\n        )\n      }\n\n      // TODO: This is actually render distance 4, but decreased by 1 to cover more terrain,\n      //   move this adjustment into the fog calculation for more clarity\n      if renderDistance >= 3 {\n        let sunHemisphereDirection = Vec3f(\n          Foundation.sin(getSunAngleRadians()) > 0 ? -1 : 1,\n          0,\n          0\n        )\n        let sunriseFogAmount = FirebladeMath.dot(ray.direction, sunHemisphereDirection)\n        if sunriseFogAmount > 0 {\n          switch getDaylightCyclePhase() {\n            case let .sunrise(sunriseColor), let .sunset(sunriseColor):\n              // The more see through the sunrise/sunset color, the less intense the directional\n              // fog color is.\n              let progress = sunriseFogAmount * sunriseColor.w\n              let sunriseColorRGB = Vec3f(sunriseColor.x, sunriseColor.y, sunriseColor.z)\n              fogColor = MathUtil.lerp(from: fogColor, to: sunriseColorRGB, progress: progress)\n            case .day, .night:\n              break\n          }\n        }\n      }\n    }\n\n    // As the player nears the\n    let voidFadeStart: Float = isFlat ? 1 : 32\n    if position.y < voidFadeStart {\n      let amount = max(0, position.y / voidFadeStart)\n      fogColor *= amount * amount\n    }\n\n    return fogColor\n  }\n\n  /// Gets the fog experienced by a player viewing the world from a given position and looking\n  /// in a specific direction.\n  /// - Parameters:\n  ///   - ray: The ray defining the position and look direction of the viewer.\n  ///   - renderDistance: The render distance that the fog will be rendered at. Often\n  ///     the true render distance minus 1 is used when above 2 render distance (to conceal\n  ///     more of the edge of the world).\n  ///   - acquireLock: Whether to acquire a lock or not before reading the value. Don't\n  ///     touch this unless you know what you're doing.\n  public func getFog(\n    forViewerWithRay ray: Ray,\n    withRenderDistance renderDistance: Int,\n    acquireLock: Bool = true\n  ) -> Fog {\n    // TODO: Check fog reverse engineering document for any other adjustments\n    //   to implement.\n    let fogColor = getFogColor(forViewerWithRay: ray, withRenderDistance: renderDistance)\n\n    let renderDistanceInBlocks = Float(renderDistance * Chunk.width)\n\n    let fluidOnEyes = getFluidState(at: ray.origin, acquireLock: acquireLock)\n      .map(\\.fluidId)\n      .map(RegistryStore.shared.fluidRegistry.fluid(withId:))\n\n    guard fluidOnEyes?.isWater != true else {\n      // TODO: Calculate density as per reverse engineering document\n      return Fog(color: fogColor, style: .exponential(density: 0.05))\n    }\n\n    // TODO: If player has blindness, the fog starts at 5/4 and ends at 5, lerping up to\n    //   starting at renderDistance/4 and ending at renderDistance over the last second of blindness\n\n    let fogStart: Float\n    let fogEnd: Float\n    if fluidOnEyes?.isLava == true {\n      // TODO: Should start at 0 and end at 3 if the player has fire resistance\n      fogStart = 0.25\n      fogEnd = 1\n    } else if dimension.isNether {\n      // TODO: This should also happen if there is a boss present which has the fog creation effect\n      //   (determined by flags of BossBarPacket)\n      fogStart = renderDistanceInBlocks / 20\n      fogEnd = min(96, renderDistanceInBlocks / 2)\n    } else {\n      fogStart = 0.75 * renderDistanceInBlocks\n      fogEnd = renderDistanceInBlocks\n    }\n\n    return Fog(\n      color: fogColor,\n      style: .linear(startDistance: fogStart, endDistance: fogEnd)\n    )\n  }\n\n  /// Gets the phase of the daylight cycle (sunrise, sunset, etc.).\n  ///\n  /// The color associated with sunrises and sunsets changes constantly throughout\n  /// that phase of the daylight cycle. To get the latest color you must call this\n  /// method again.\n  public func getDaylightCyclePhase() -> DaylightCyclePhase {\n    let sunAngleRadians = getSunAngleRadians()\n    let sunHeight = Foundation.cos(sunAngleRadians)\n    if sunHeight < -0.4 {\n      return .night\n    } else if sunHeight > 0.4 {\n      return .day\n    } else {\n      // The sunrise or sunset's current brightness from 0 to 1 (not true brightness,\n      // but it is correlated with actual brightness).\n      let brightnessFactor = sunHeight / 0.4 * 0.5 + 0.5\n      let sqrtAlpha = 0.01 + 0.99 * Foundation.sin(brightnessFactor * .pi)\n      let color = Vec4f(\n        brightnessFactor * 0.3 + 0.7,\n        brightnessFactor * brightnessFactor * 0.7 + 0.2,\n        0.2,\n        sqrtAlpha * sqrtAlpha\n      )\n\n      let isSunrise = Foundation.sin(sunAngleRadians) < 0\n      if isSunrise {\n        return .sunrise(color: color)\n      } else {\n        return .sunset(color: color)\n      }\n    }\n  }\n\n  /// Gets the phase of the moon (an integer in the range `0..<8`). The moon's\n  /// phase progresses by 1 each day.\n  public func getMoonPhase() -> Int {\n    (getTimeOfDay() / 24000) % 8\n  }\n\n  /// Gets the brightness of the stars in the current dimension at the current time.\n  public func getStarBrightness() -> Float {\n    guard dimension.isOverworld else {\n      return 0\n    }\n\n    let brightness = 0.75 - 2 * Foundation.cos(getSunAngleRadians())\n    let clampedBrightness = MathUtil.clamp(brightness, 0, 1)\n    return clampedBrightness * clampedBrightness / 2\n  }\n\n  // MARK: Blocks\n\n  /// Sets the block at the specified position to the specified block id.\n  ///\n  /// This will trigger lighting to be updated.\n  public func setBlockId(at position: BlockPosition, to state: Int) {\n    if let chunk = chunk(at: position.chunk) {\n      chunk.setBlockId(at: position.relativeToChunk, to: state)\n      lightingEngine.updateLighting(at: position, in: self)\n\n      blockBreakingLock.acquireWriteLock()\n      // Use removeAll instead of filter to minimize cost in the case that the set block\n      // wasn't associated with a breaking block (probably the most likely case?)\n      breakingBlocks.removeAll { block in\n        block.position == position\n      }\n      blockBreakingLock.unlock()\n\n      eventBus.dispatch(\n        Event.SingleBlockUpdate(\n          position: position,\n          newState: state\n        ))\n    } else {\n      log.warning(\"Cannot set block in non-existent chunk, chunkPosition=\\(position.chunk)\")\n    }\n  }\n\n  /// Sets the blocks at the specified positions to the specified block ids.\n  ///\n  /// Using this method is preferred over just using setBlockId within a for loop because it\n  /// processes lighting updates in batch which is much more efficient.\n  /// - Parameters:\n  ///   - updates: The positions and new states of affected blocks.\n  ///   - chunkPosition: If all updates occur within a single chunk provide this parameter for more\n  ///     efficient batching.\n  public func processMultiBlockUpdate(\n    _ updates: [Event.SingleBlockUpdate],\n    inChunkAt chunkPosition: ChunkPosition? = nil\n  ) {\n    let positions = updates.map(\\.position)\n    if let chunkPosition = chunkPosition {\n      if let chunk = chunk(at: chunkPosition) {\n        for update in updates {\n          chunk.setBlockId(at: update.position.relativeToChunk, to: update.newState)\n        }\n        lightingEngine.updateLighting(at: positions, in: self)\n      } else {\n        log.warning(\n          \"Cannot handle multi-block change in non-existent chunk, chunkPosition=\\(chunkPosition)\")\n        return\n      }\n    } else {\n      for update in updates {\n        if let chunk = chunk(at: update.position.chunk) {\n          chunk.setBlockId(at: update.position.relativeToChunk, to: update.newState)\n        } else {\n          log.warning(\n            \"Cannot handle multi-block change in non-existent chunk, chunkPosition=\\(update.position.chunk)\"\n          )\n          return\n        }\n      }\n      lightingEngine.updateLighting(at: positions, in: self)\n    }\n\n    blockBreakingLock.acquireWriteLock()\n    // Use removeAll instead of filter to minimize cost in the case that the set block\n    // wasn't associated with a breaking block (probably the most likely case?)\n    breakingBlocks.removeAll { block in\n      positions.contains(block.position)\n    }\n    blockBreakingLock.unlock()\n\n    eventBus.dispatch(Event.MultiBlockUpdate(updates: updates))\n  }\n\n  /// Get the block id of the block at the specified position.\n  /// - Parameters:\n  ///   - position: A block position in world coordinates.\n  ///   - acquireLock: Whether to acquire a lock or not before reading the value. Don't touch this unless you know what you're doing.\n  /// - Returns: A block state id. If `position` is in a chunk that isn't loaded, `0` (regular air) is returned.\n  public func getBlockId(at position: BlockPosition, acquireLock: Bool = true) -> Int {\n    if Self.isValidBlockPosition(position), let chunk = chunk(at: position.chunk) {\n      return chunk.getBlockId(at: position.relativeToChunk, acquireLock: acquireLock)\n    } else {\n      return 0\n    }\n  }\n\n  // TODO: Should these getters be called `block(at:)` etc instead?\n  /// Returns information about the type of block at the specified position.\n  /// - Parameters:\n  ///   - position: Position of block.\n  ///   - acquireLock: Whether to acquire a lock or not before reading the value. Don't touch this unless you know what you're doing.\n  /// - Returns: The block at the given position. ``Block/missing`` if the block doesn't exist.\n  public func getBlock(at position: BlockPosition, acquireLock: Bool = true) -> Block {\n    let blockId = getBlockId(at: position, acquireLock: acquireLock)\n    return RegistryStore.shared.blockRegistry.block(withId: blockId) ?? Block.missing\n  }\n\n  /// Returns information about the fluid state at the specified position.\n  /// - Parameters:\n  ///   - position: Position of fluid.\n  ///   - acquireLock: Whether to acquire a lock or not before reading the value. Don't touch this unless you know what you're doing.\n  /// - Returns: The fluid state at the given position, if any.\n  public func getFluidState(at position: BlockPosition, acquireLock: Bool = true) -> FluidState? {\n    let block = getBlock(at: position)\n    return block.fluidState\n  }\n\n  /// Returns information about the type of fluid that the given point is in.\n  /// - Parameters:\n  ///   - position: Point to get fluid at.\n  ///   - acquireLock: Whether to acquire a lock or not before reading the value. Don't touch this unless you know what you're doing.\n  /// - Returns: The fluid at the given point, if any.\n  public func getFluidState(at position: Vec3f, acquireLock: Bool = true) -> FluidState? {\n    let blockPosition = BlockPosition(x: Int(position.x), y: Int(position.y), z: Int(position.z))\n    guard let fluidState = getFluidState(at: blockPosition) else {\n      return nil\n    }\n\n    let fluidStateAbove = getFluidState(at: blockPosition.neighbour(.up))\n    if fluidStateAbove?.fluidId == fluidState.fluidId {\n      return fluidState\n    }\n\n    let height = Float(fluidState.height + 1) / 9\n    let fluidEnd = Float(blockPosition.y) + height\n    if position.y <= fluidEnd {\n      return fluidState\n    } else {\n      return nil\n    }\n  }\n\n  // MARK: Lighting\n\n  /// Sets the block light level of a block. Does not propagate the change and does not verify the level is valid.\n  ///\n  /// If `position` is in a chunk that isn't loaded or is above y=255 or below y=0, nothing happens.\n  /// - Parameters:\n  ///   - position: A block position relative to the world.\n  ///   - level: The new block light level. Should be from 0 to 15 inclusive. Not validated.\n  public func setBlockLightLevel(at position: BlockPosition, to level: Int) {\n    if let chunk = chunk(at: position.chunk) {\n      chunk.setBlockLightLevel(at: position.relativeToChunk, to: level)\n    }\n  }\n\n  /// Gets the block light level for the given block.\n  /// - Parameter position: Position of block.\n  /// - Returns: The block light level of the block. If the given position isn't loaded, ``LightLevel/defaultBlockLightLevel`` is returned.\n  public func getBlockLightLevel(at position: BlockPosition) -> Int {\n    if let chunk = chunk(at: position.chunk) {\n      return chunk.blockLightLevel(at: position.relativeToChunk)\n    } else {\n      return LightLevel.defaultBlockLightLevel\n    }\n  }\n\n  /// Sets the sky light level of a block. Does not propagate the change and does not verify the level is valid.\n  ///\n  /// If `position` is in a chunk that isn't loaded or is above y=255 or below y=0, nothing happens.\n  /// - Parameters:\n  ///   - position: A block position relative to the world.\n  ///   - level: The new sky light level. Should be from 0 to 15 inclusive. Not validated.\n  public func setSkyLightLevel(at position: BlockPosition, to level: Int) {\n    if let chunk = chunk(at: position.chunk) {\n      chunk.setSkyLightLevel(at: position.relativeToChunk, to: level)\n    }\n  }\n\n  /// Gets the sky light level for the given block.\n  /// - Parameter position: Position of block.\n  /// - Returns: The sky light level of the block. If the given position isn't loaded, ``LightLevel/defaultSkyLightLevel`` is returned.\n  public func getSkyLightLevel(at position: BlockPosition) -> Int {\n    if let chunk = chunk(at: position.chunk) {\n      return chunk.skyLightLevel(at: position.relativeToChunk)\n    } else {\n      return LightLevel.defaultSkyLightLevel\n    }\n  }\n\n  /// Gets the block and sky light levels for the given block.\n  /// - Parameter position: Position of block.\n  /// - Returns: The light levels of the block. If the given position isn't loaded, ``LightLevel/default`` is returned.\n  public func getLightLevel(at position: BlockPosition) -> LightLevel {\n    if let chunk = chunk(at: position.chunk) {\n      return chunk.lightLevel(at: position.relativeToChunk)\n    } else {\n      return .default\n    }\n  }\n\n  /// Updates a chunk's lighting with lighting data received from the server.\n  /// - Parameters:\n  ///   - position: Position of chunk to update.\n  ///   - data: Data about the lighting update.\n  public func updateChunkLighting(at position: ChunkPosition, with data: ChunkLightingUpdateData) {\n    terrainLock.acquireWriteLock()\n\n    // Terrain lock is unlocked before acquiring a lock on a complete chunk, because otherwise this\n    // function locks waiting for the chunk lock, and some code that already has a chunk lock (such\n    // as in `ChunkSectionMeshBuilder`) but needs to wait for a terrain lock.\n    if let chunk = chunks[position] {\n      terrainLock.unlock()\n      chunk.updateLighting(with: data)\n    } else if let chunk = unlitChunks[position] {\n      terrainLock.unlock()\n      chunk.updateLighting(with: data)\n\n      terrainLock.acquireWriteLock()\n      unlitChunks.removeValue(forKey: position)\n      chunks[position] = chunk\n\n      eventBus.dispatch(Event.AddChunk(position: position))\n      terrainLock.unlock()\n    } else {\n      chunklessLightingData[position] = data\n      terrainLock.unlock()\n    }\n\n    eventBus.dispatch(\n      Event.UpdateChunkLighting(\n        position: position,\n        data: data\n      ))\n  }\n\n  // MARK: Biomes\n\n  /// Gets the biome at the specified position.\n  /// - Parameter position: Position to get biome at.\n  /// - Returns: The biome at the requested position, or `nil` if the position is in a non-loaded\n  ///   chunk.\n  public func getBiome(at position: BlockPosition) -> Biome? {\n    return chunk(at: position.chunk)?.biome(at: position.relativeToChunk)\n  }\n\n  // MARK: Chunks\n\n  /// Gets the chunk at the specified position. Does not return unlit chunks.\n  /// - Parameter chunkPosition: Position of chunk.\n  /// - Returns: The requested chunk, or `nil` if the chunk isn't present.\n  public func chunk(at chunkPosition: ChunkPosition) -> Chunk? {\n    terrainLock.acquireReadLock()\n    defer { terrainLock.unlock() }\n    return chunks[chunkPosition]\n  }\n\n  /// Adds a chunk to the world.\n  /// - Parameters:\n  ///   - chunk: Chunk to add.\n  ///   - position: Position chunk should be added at.\n  public func addChunk(_ chunk: Chunk, at position: ChunkPosition) {\n    terrainLock.acquireWriteLock()\n    defer { terrainLock.unlock() }\n\n    if let lightingData = chunklessLightingData.removeValue(forKey: position) {\n      chunk.updateLighting(with: lightingData)\n    }\n\n    if !chunk.hasLighting {\n      unlitChunks[position] = chunk\n    } else {\n      chunks[position] = chunk\n    }\n\n    if chunk.hasLighting {\n      eventBus.dispatch(Event.AddChunk(position: position))\n    }\n  }\n\n  /// Removes the chunk at the specified position if present.\n  /// - Parameter position: Position of chunk to remove.\n  public func removeChunk(at position: ChunkPosition) {\n    terrainLock.acquireWriteLock()\n    defer { terrainLock.unlock() }\n\n    chunks.removeValue(forKey: position)\n    eventBus.dispatch(Event.RemoveChunk(position: position))\n  }\n\n  /// Gets the chunks neighbouring the specified chunk with their respective directions.\n  ///\n  /// Neighbours are any chunk that are next to the current chunk along any of the axes.\n  ///\n  /// - Parameter position: Position of chunk.\n  /// - Returns: All present neighbours of the chunk.\n  public func neighbours(ofChunkAt position: ChunkPosition) -> [CardinalDirection: Chunk] {\n    let neighbourPositions = position.allNeighbours\n    var neighbourChunks: [CardinalDirection: Chunk] = [:]\n    for (direction, neighbourPosition) in neighbourPositions {\n      if let neighbour = chunk(at: neighbourPosition) {\n        neighbourChunks[direction] = neighbour\n      }\n    }\n    return neighbourChunks\n  }\n\n  /// Gets all four neighbours of a chunk.\n  ///\n  /// See ``neighbours(ofChunkAt:)`` for a definition of neighbour.\n  ///\n  /// - Parameter chunkPosition: Position of chunk.\n  /// - Returns: A value containing all 4 neighbouring chunks. `nil` if any of the neighbours are not present.\n  public func allNeighbours(ofChunkAt chunkPosition: ChunkPosition) -> ChunkNeighbours? {\n    let northPosition = chunkPosition.neighbour(inDirection: .north)\n    let eastPosition = chunkPosition.neighbour(inDirection: .east)\n    let southPosition = chunkPosition.neighbour(inDirection: .south)\n    let westPosition = chunkPosition.neighbour(inDirection: .west)\n\n    guard\n      let northNeighbour = chunk(at: northPosition),\n      let eastNeighbour = chunk(at: eastPosition),\n      let southNeighbour = chunk(at: southPosition),\n      let westNeighbour = chunk(at: westPosition)\n    else {\n      return nil\n    }\n\n    return ChunkNeighbours(\n      north: northNeighbour,\n      east: eastNeighbour,\n      south: southNeighbour,\n      west: westNeighbour\n    )\n  }\n\n  /// Gets whether a chunk has been fully received.\n  ///\n  /// To be fully received, a chunk must be present, and must contain lighting data.\n  /// - Parameter position: The position of the chunk to check.\n  /// - Returns: Whether the chunk has been fully received.\n  public func isChunkComplete(at position: ChunkPosition) -> Bool {\n    return chunk(at: position) != nil\n  }\n\n  /// Gets whether a chunk hasn't got any lighting.\n  /// - Parameter position: The position of the chunk to check.\n  /// - Returns: Whether the chunk hasn't got lighting from the server. Returns `false` if the chunk doesn't exist.\n  public func isChunkUnlit(at position: ChunkPosition) -> Bool {\n    terrainLock.acquireReadLock()\n    defer { terrainLock.unlock() }\n    return unlitChunks[position] != nil\n  }\n\n  // MARK: Block breaking\n\n  public func startBreakingBlock(at position: BlockPosition, for entityId: Int) {\n    blockBreakingLock.acquireWriteLock()\n    defer { blockBreakingLock.unlock() }\n    for block in breakingBlocks {\n      if block.position == position {\n        // TODO: Figure out what to do in this situation\n        return\n      }\n    }\n    breakingBlocks.append(\n      BreakingBlock(\n        position: position,\n        perpetratorEntityId: entityId,\n        progress: 0\n      )\n    )\n  }\n\n  /// Sets the block breaking progress of a given block to a value corresponding to a specific\n  /// stage of the block breaking animation. If the block is not already getting broken, it gets\n  /// added to the list of breaking blocks.\n  public func setBlockBreakingStage(at position: BlockPosition, to stage: Int, for entityId: Int) {\n    blockBreakingLock.acquireWriteLock()\n    defer { blockBreakingLock.unlock() }\n\n    let progress = Double(clamp(stage + 1, min: 0, max: 10)) / 10\n\n    for (i, block) in breakingBlocks.enumerated() {\n      if block.position == position {\n        breakingBlocks[i].progress = progress\n        breakingBlocks[i].perpetratorEntityId = entityId\n        return\n      }\n    }\n\n    breakingBlocks.append(\n      BreakingBlock(\n        position: position,\n        perpetratorEntityId: entityId,\n        progress: progress\n      )\n    )\n  }\n\n  /// Does nothing if the specified block isn't getting broken.\n  public func addBreakingProgress(_ progress: Double, toBlockAt position: BlockPosition) {\n    blockBreakingLock.acquireWriteLock()\n    defer { blockBreakingLock.unlock() }\n    for (i, block) in breakingBlocks.enumerated() where block.position == position {\n      breakingBlocks[i].progress += progress\n    }\n  }\n\n  public func getBreakingBlocks() -> [BreakingBlock] {\n    blockBreakingLock.acquireReadLock()\n    defer { blockBreakingLock.unlock() }\n    return breakingBlocks\n  }\n\n  public func getBlockBreakingProgress(at position: BlockPosition) -> Double? {\n    blockBreakingLock.acquireReadLock()\n    defer { blockBreakingLock.unlock() }\n    return breakingBlocks.first { block in\n      block.position == position\n    }?.progress\n  }\n\n  public func endBlockBreaking(at position: BlockPosition) {\n    blockBreakingLock.acquireWriteLock()\n    defer { blockBreakingLock.unlock() }\n    breakingBlocks = breakingBlocks.filter { block in\n      block.position != position\n    }\n  }\n\n  public func endBlockBreaking(for entityId: Int) {\n    blockBreakingLock.acquireWriteLock()\n    defer { blockBreakingLock.unlock() }\n    breakingBlocks = breakingBlocks.filter { block in\n      block.perpetratorEntityId != entityId\n    }\n  }\n\n  // MARK: Helper\n\n  /// Gets whether the a position is in a loaded chunk or not.\n  /// - Parameter position: Position to check.\n  /// - Returns: `true` if the position is in a loaded chunk.\n  public func isPositionLoaded(_ position: BlockPosition) -> Bool {\n    return isChunkComplete(at: position.chunk) && Self.isValidBlockPosition(position)\n  }\n\n  /// - Parameter position: Position to validate.\n  /// - Returns: whether a block position is below the world height limit and above 0.\n  public static func isValidBlockPosition(_ position: BlockPosition) -> Bool {\n    return position.y < Chunk.height && position.y >= 0\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Tests/DeltaCoreUnitTests/BufferTests.swift",
    "content": "import XCTest\nimport Foundation\n\n@testable import struct DeltaCore.Buffer\n\nfinal class BufferTests: XCTestCase {\n  func testValidInput() throws {\n    let input: [UInt8] = [\n      0x80, // -128 (signed byte)\n      0x80, // 128 (unsigned byte)\n      1, 2, // 0x0102 (big endian unsigned short)\n      1, 2, // 0x0201 (little endian unsigned short)\n      0x80, 0x01, // -32767 (big endian signed short)\n      0x80, 0x01, // 0x0180 (little endian signed short)\n      1, 2, 3, 4, // 0x01020304 (big endian unsigned int)\n      1, 2, 3, 0x80, // -2147286527 (little endian signed int)\n      1, 2, 3, 4, 5, 6, 7, 8, // 0x0102030405060708 (big endian signed long)\n      0xff, 0xff, 0xff, 0xff, 0x0f, // -1 (varint)\n      0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0xc0, 0x80, 0x01, // 0x0101010101010101 (varlong)\n      0x00, 0xff, 0x00, 0xff, // 2.34184089e-38 (big endian float)\n      0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0xff, 0x00, // 7.0641714803000305e-304 (little endian double)\n      0x41, 0x61, 0x41, 0x61 // aAaA (string)\n    ]\n\n    var buffer = Buffer(input)\n\n    XCTAssertEqual(try buffer.readSignedByte(), -128)\n    XCTAssertEqual(try buffer.readByte(), 128)\n    XCTAssertEqual(try buffer.readShort(endianness: .big), 0x0102)\n    XCTAssertEqual(try buffer.readShort(endianness: .little), 0x0201)\n    XCTAssertEqual(try buffer.readSignedShort(endianness: .big), -32767)\n    XCTAssertEqual(try buffer.readSignedShort(endianness: .little), 0x0180)\n    XCTAssertEqual(try buffer.readInteger(endianness: .big), 0x01020304)\n    XCTAssertEqual(try buffer.readSignedInteger(endianness: .little), -2147286527)\n    XCTAssertEqual(try buffer.readSignedLong(endianness: .big), 0x0102030405060708)\n    XCTAssertEqual(try buffer.readVariableLengthInteger(), -1)\n    XCTAssertEqual(try buffer.readVariableLengthLong(), 0x0101010101010101)\n    XCTAssertEqual(try buffer.readFloat(endianness: .big), 2.34184089e-38)\n    XCTAssertEqual(try buffer.readDouble(endianness: .big), 1.157756680498041e-279)\n    XCTAssertEqual(try buffer.readString(length: 4), \"AaAa\")\n    XCTAssertEqual(buffer.remaining, 0)\n  }\n\n  func testInvalidInput() throws {\n    let testCases: [(String, [UInt8], (inout Buffer) throws -> Any)] = [\n      (\n        \"insufficient bytes\",\n        [1],\n        { buffer in try buffer.readShort(endianness: .big) }\n      ),\n      (\n        \"oversize varint\",\n        [0xff, 0xff, 0xff, 0xff, 0x1f], // One bit over the maximum\n        { buffer in try buffer.readVariableLengthInteger() }\n      ),\n      (\n        \"oversize varlong\",\n        [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x71], // One bit of the maximum\n        { buffer in try buffer.readVariableLengthLong() }\n      )\n    ]\n\n    for (name, bytes, function) in testCases {\n      do {\n        var buffer = Buffer(bytes)\n        _ = try function(&buffer)\n        XCTFail(\"Invalid input (\\(name)) didn't throw an error\")\n      } catch {\n        continue\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Tests/DeltaCoreUnitTests/ChatComponentTests.swift",
    "content": "import XCTest\nimport Foundation\n\n@testable import struct DeltaCore.ChatComponent\n\nfinal class ChatComponentTests: XCTestCase {\n  func testValidJSON() throws {\n    let testCases: [(String, ChatComponent)] = [\n      (\n        #\"{\"italic\":false,\"extra\":[{\"color\":\"yellow\",\"clickEvent\":{\"action\":\"open_url\",\"value\":\"http://www.hypixel.ne\"},\"text\":\"www.hypixel.ne\"}],\"text\":\"\"}\"#,\n        ChatComponent(\n          style: .init(italic: false),\n          content: .string(\"\"),\n          children: [\n            ChatComponent(\n              style: .init(color: .yellow),\n              content: .string(\"www.hypixel.ne\")\n            )\n          ]\n        )\n      ),\n      (\n        // swiftlint:disable:next line_length\n        #\"{\"italic\":false,\"extra\":[{\"color\":\"white\",\"text\":\" \"},{\"color\":\"dark_gray\",\"text\":\"[\"},{\"color\":\"aqua\",\"text\":\"■\"},{\"color\":\"gray\",\"text\":\"■■■■■■■\"}],\"text\":\"\"}\"#,\n        ChatComponent(\n          style: .init(italic: false),\n          content: .string(\"\"),\n          children: [\n            ChatComponent(\n              style: .init(color: .white),\n              content: .string(\" \")\n            ),\n            ChatComponent(\n              style: .init(color: .darkGray),\n              content: .string(\"[\")\n            ),\n            ChatComponent(\n              style: .init(color: .aqua),\n              content: .string(\"■\")\n            ),\n            ChatComponent(\n              style: .init(color: .gray),\n              content: .string(\"■■■■■■■\")\n            )\n          ]\n        )\n      )\n    ]\n\n    for (json, expected) in testCases {\n      let data = json.data(using: .utf8)!\n      let parsed = try JSONDecoder().decode(ChatComponent.self, from: data)\n      XCTAssertEqual(\n        parsed,\n        expected\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Tests/DeltaCoreUnitTests/IdentifierTests.swift",
    "content": "import XCTest\nimport Foundation\n\n@testable import struct DeltaCore.Identifier\n\nfinal class IdentifierTests: XCTestCase {\n  func testValidIdentifiers() throws {\n    let namespaces = [\"minecraft\", \"delta-client\", \"123delta_-client_\"]\n    let names = [\"block/dirt.png\", \"dirt\", \"diamond_sword\", \"-/_123iron_sword\"]\n    \n    for namespace in namespaces {\n      for name in names {\n        let identifier = \"\\(namespace):\\(name)\"\n        let parsed = try Identifier(identifier)\n        XCTAssertEqual(\n          parsed,\n          Identifier(namespace: namespace, name: name),\n          \"Expected '\\(identifier)' to be parsed correctly. Got '\\(parsed.namespace):\\(parsed.name)'\"\n        )\n      }\n    }\n\n    for name in names {\n      let parsed = try Identifier(name)\n      XCTAssertEqual(\n        parsed,\n        Identifier(namespace: \"minecraft\", name: name),\n        \"Expected '\\(name)' to be parsed correctly. Got '\\(parsed.namespace):\\(parsed.name)'\"\n      )\n    }\n  }\n\n  func testInvalidIdentifiers() throws {\n    let identifiers = [\n      \"minecraft:block:dirt\",\n      \"block:\",\n      \"minecraft:diamond$sword\",\n      \"%\",\n      \":iron_shovel\"\n    ]\n\n    for identifier in identifiers {\n      do {\n        let parsed = try Identifier(identifier)\n        XCTFail(\"Expected parsing '\\(identifier)' to throw an error. Got '\\(parsed.name):\\(parsed.name)'\")\n      } catch {\n        continue\n      }\n    }\n  }\n\n  func testDecoding() throws {\n    struct Payload: Codable {\n      let identifierString: Identifier\n      let identifierParts: Identifier\n    }\n\n    let namespace = \"minecraft\"\n    let name = \"block/dirt.png\"\n    let identifier = Identifier(namespace: namespace, name: name)\n\n    let json = \"\"\"\n{\n  \"identifierString\": \"\\(namespace):\\(name)\",\n  \"identifierParts\": [\"\\(namespace)\", \"\\(name)\"]\n}\n\"\"\".data(using: .utf8)!\n    \n    let decoded = try JSONDecoder().decode(Payload.self, from: json)\n\n    XCTAssertEqual(\n      decoded.identifierString,\n      identifier,\n      \"Failed to decode identifier '\\(identifier)' encoded as a JSON string. Got '\\(decoded.identifierString)'\"\n    )\n\n    XCTAssertEqual(\n      decoded.identifierParts,\n      identifier,\n      \"Failed to decode identifier '\\(identifier)' encoded as a JSON array. Got '\\(decoded.identifierParts)'\"\n    )\n  }\n\n  func testEncoding() throws {\n    let namespace = \"minecraft\"\n    let name = \"block/dirt.png\"\n    let identifier = Identifier(namespace: namespace, name: name)\n\n    let json = String(data: try JSONEncoder().encode(identifier), encoding: .utf8)!\n    \n    let expected = \"[\\\"\\(namespace)\\\",\\\"\\(name.replacingOccurrences(of: \"/\", with: \"\\\\/\"))\\\"]\"\n    XCTAssertEqual(\n      json,\n      expected,\n      \"Incorrectly encoded identifier '\\(identifier)'. Expected '\\(expected)'. Got '\\(json)'\"\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Tests/DeltaCoreUnitTests/LegacyFormattedTextTests.swift",
    "content": "import XCTest\nimport Foundation\n\n@testable import struct DeltaCore.LegacyFormattedText\n\nprivate typealias Token = LegacyFormattedText.Token\n\nfinal class LegacyFormattedTextTests: XCTestCase {\n  func testValidText() throws {\n    let testCases: [(String, [Token])] = [\n      (\"§cX§nY\", [\n        Token(string: \"X\", color: .red, style: nil),\n        Token(string: \"Y\", color: .red, style: .underline)\n      ]),\n      (\"§nX§cY\", [\n        Token(string: \"X\", color: nil, style: .underline),\n        Token(string: \"Y\", color: .red, style: nil)\n      ]),\n      (\"first§csecond§a§lthird\", [\n        Token(string: \"first\", color: nil, style: nil),\n        Token(string: \"second\", color: .red, style: nil),\n        Token(string: \"third\", color: .green, style: .bold)\n      ])\n    ]\n\n    for (input, expectedTokens) in testCases {\n      XCTAssertEqual(\n        LegacyFormattedText(input).tokens,\n        expectedTokens,\n        \"Failed to parse '\\(input)'\"\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Core/Tests/DeltaCoreUnitTests/LocalizationFormatterTests.swift",
    "content": "import XCTest\nimport Foundation\n\n@testable import struct DeltaCore.LocalizationFormatter\n\nfinal class LocalizationFormatterTests: XCTestCase {\n  func testValidInputs() throws {\n    let inputs: [(String, [String], String)] = [\n      (\"%s, world!\", [\"Hello\"], \"Hello, world!\"),\n      (\n        \"%s, %s, %1$s again, %s, %2$s again, %3$s, %1$s again, %1$s%2$s%3$s\",\n        [\"1\", \"2\", \"3\"],\n        \"1, 2, 1 again, 3, 2 again, 3, 1 again, 123\"\n      ),\n      (\"%% %%s %%%s %%%%s %%%%%s\", [\"1\", \"2\"], \"% %s %1 %%s %%2\")\n    ]\n\n    for (template, substitutions, expected) in inputs {\n      XCTAssertEqual(\n        LocalizationFormatter.format(template, withSubstitutions: substitutions),\n        expected\n      )\n    }\n  }\n\n  func testInvalidInputs() throws {\n    let inputs: [(String, [String], String)] = [\n      (\"%\", [\"Hi\"], \"%\"),\n      (\"% s\", [\"Hi\"], \"% s\"),\n      (\"%s %$s\", [\"Hi\", \"Hello\"], \"Hi %$s\"),\n      (\"%s %s %1$s %2$s\", [\"Hi\"], \"Hi %s Hi %2$s\"),\n      (\"%$1s\", [\"Hi\"], \"%$1s\"),\n    ]\n\n    for (template, substitutions, expected) in inputs {\n      XCTAssertEqual(\n        LocalizationFormatter.format(template, withSubstitutions: substitutions),\n        expected\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/Exporters/DynamicShim/Exports.swift",
    "content": "@_exported import DeltaCore\n@_exported import DeltaLogger\n\n#if canImport(DeltaRenderer)\n@_exported import DeltaRenderer\n#endif\n"
  },
  {
    "path": "Sources/Exporters/StaticShim/Exports.swift",
    "content": "@_exported import DeltaCore\n@_exported import DeltaLogger\n\n#if canImport(DeltaRenderer)\n@_exported import DeltaRenderer\n#endif\n"
  },
  {
    "path": "build.sh",
    "content": "#!/bin/sh\nswift bundler bundle -c release -o .\n"
  },
  {
    "path": "lint.sh",
    "content": "swift package --disable-sandbox lint # DeltaClient\nswift package --disable-sandbox --package-path Sources/Core lint #DeltaCore\n"
  }
]