Metadata-Version: 2.4
Name: minecraft-schemas
Version: 0.4.1
Summary: Parse Minecraft-relative JSON to structured objects
Author-email: Izanagi Tokiyuki <izanagitokiyuki@noreply.codeberg.org>
License-Expression: MulanPSL-2.0
Project-URL: Repository, https://codeberg.org/IzanagiTokiyuki/minecraft-schemas
Project-URL: Issues, https://codeberg.org/IzanagiTokiyuki/minecraft-schemas/issues
Project-URL: Changelog, https://codeberg.org/IzanagiTokiyuki/minecraft-schemas/src/branch/master/HISTORY.md
Keywords: minecraft,schema
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: attrs>=25.4.0
Requires-Dist: cattrs>=25.3.0
Requires-Dist: pendulum>=3.1.0
Requires-Dist: typing-extensions>=4.15.0
Dynamic: license-file

# minecraft-schemas

A Python library for help you to parse Minecraft-relative JSON to structured objects.

**Disclaimer:** Although the project name contains "minecraft", this project is not supported by Mojang Studios or Microsoft.

## Notice for users/developers migrated from `minecraft-schemes`

This project is renamed from [`minecraft-schemes`](https://pypi.org/project/minecraft-schemes), with a full package structure reorganization.

For more information about migration, see the [version history file](HISTORY.md) for more details.
(Or scroll down to see the version history if you are reading this on PyPI.)

Due to limited time and energy, I am unable to provide a complete backward compatibility solution for this release. Sorry for the inconvenience.

## Features

### Already implemented

- Easy installing
- Open source
- All public APIs are static typed
- Supports parsing various file structures used by Mojang and Minecraft ([see below](#supported-file-structures))
- Easy-to-use file structure definitions, powered by [`attrs`](https://www.attrs.org)
- Rapidly file parsing, powered by [`cattrs`](https://catt.rs)
- Conditional testing for game/command line options and dependency libraries (in `client.json`)

### Not implemented yet (not a complete list)

- [ ] Parse/build supports for `launcher_profiles.json` (used by official Minecraft Launcher)
- [ ] Game/JVM command line options concatenating and completing

## Supported file structures

### <span id="file-structure-version-manifest"></span>`version_manifest.json` and `version_manifest_v2.json`

**See more:**
[Minecraft Wiki](https://minecraft.wiki/w/Version_manifest.json)

- A JSON file that list Minecraft versions available in the official launcher.

### <span id="file-structure-client-manifest"></span>`client.json`

**See more:**
[Minecraft Wiki](https://minecraft.wiki/w/Client.json)

- A JSON file that accompanies client.jar in `.minecraft/versions/<version>` and lists the version's attributes.
- Usually named `<game version>.json`.
- Don't confuse this file with `version.json`; they are fundamentally different.

### <span id="file-structure-asset-index"></span>Asset index file

**See more:**
[Minecraft Wiki (only Chinese version)](https://zh.minecraft.wiki/w/%E6%95%A3%E5%88%97%E8%B5%84%E6%BA%90%E6%96%87%E4%BB%B6#%E8%B5%84%E6%BA%90%E7%B4%A2%E5%BC%95)

- A series of JSON files used to query the hash value of the corresponding hashed resource file based on the resource path, in order to invoke
  the file.
- Can be downloaded from the URL pointed in the `client.json`: `[Root Tag] > "assetIndex" > "url"`

### <span id="file-structure-version-attributes"></span>`version.json`

**See more:**
[Minecraft Wiki](https://minecraft.wiki/w/Version.json)

- A JSON file that offers some basic information about the version's attributes.
- Embedded within client.jar in `.minecraft/versions/<version>` and `server.jar`.
- Don't confuse this file with `client.json`; they are fundamentally different.

### <span id="file-structure-mojang-java-index-manifest"></span>Mojang Java Runtime index file and manifest files

- A JSON file that list manifest files of Java Runtime provided by Mojang via their "codename".
- Not documented by Minecraft Wiki or Mojang, but it is believed to be for the purposes described above.

### <span id="file-structure-yggdrasil-api-responses"></span>Yggdrasil API Responses

**See more:**
[Unofficial Yggdrasil server technical specification, provided by
`authlib-injector` (only Chinese version)](https://github.com/yushijinhun/authlib-injector/wiki/Yggdrasil-%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%8A%80%E6%9C%AF%E8%A7%84%E8%8C%83)

Included the following things:

- Error response
- Endpoint `/authenticate` response and its parts
- Endpoint `/refresh` response and its parts
- `authlib-injector`-compatible Yggdrasil API metadata
- `authlib-injector`-compatible Yggdrasil Server metadata
    - Included in the `authlib-injector`-compatible Yggdrasil API metadata: `[Root Tag] > "meta"` (JSON) or `api_metadata.serverMetadata` (parsed)
    - According to this [
      `authlib-injector` Wiki](https://github.com/yushijinhun/authlib-injector/wiki/Yggdrasil-%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%8A%80%E6%9C%AF%E8%A7%84%E8%8C%83#meta-%E4%B8%AD%E7%9A%84%E5%85%83%E6%95%B0%E6%8D%AE)
      section about the server metadata, this structure is not mandatory. Regardless of whether the parsing is successful or not, users/developers
      should access and manipulate it as a regular dict.
- Player texture property
    - As a part of Yggdrasil API endpoint `/authenticate` and `/refresh`.
    - Usually encoded in Base64. Users/developers should decode and load it manually.

## Install

Install `minecraft-schemas` using pip:

```commandline
pip install minecraft-schemas
```

The release page also provides various versions of wheel files for manual download and installation.

## API Documentation

`mcschemas`'s most useful functionalities are the following APIs:

- `mcschemas.Schemas`
    - An enum class that declares currently supported schemas. All schemas have parsing support, but no one currently have build support.
- `mcschemas.parse(obj, schema, /, *, converter=None)`
    - Parse `obj` as the given `schema` to the corresponding type.
    - `schema` must be a member of enum `mcschemas.Schemas`.
    - `converter` can be an instance of `cattrs.BaseConverter` or its subclasses.
        - Default is `None`. At this time, a `mcschemas.DedicatedConverter` instance will be automatically created for internal
          structuring.
- `mcschemas.loads(s, schema, /, *, converter=None, **json_loads_kwargs)`
    - Deserialize the string `s`, then parse the deserialized result as the given `schema` to the corresponding type.
    - `schema` and `converter` is identical to `mcschemas.parse()`.
    - `**json_loads_kwargs` will be passed to `json.loads()`, except the keyword `s`.
- `mcschemas.load(fp, schema, /, *, converter=None, **json_load_kwargs)`
    - Identical to `mcschemas.loads()`, but instead of a string, deserialize the file-like object `fp`, then parse the deserialized result as the
      given `schema` to the corresponding type.
        - `fp` must be a `.read()`-supporting text file.
    - `schema` and `converter` is identical to `mcschemas.parse()`.
    - `**json_load_kwargs` will be passed to `json.load()`, except the keyword `fp`.
- `mcschemas.loadVersionAttrsFromClientJar(file, /, *, converter=None, **json_load_kwargs)`
    - A convenience function for loading, deserializing and parsing `version.json` from `client.jar`.
    - The schema is fixed to `mcschemas.Schemas.VERSION_ATTRIBUTES`.
    - `converter` is identical to `mcschemas.parse()`.
    - `**json_load_kwargs` is identical to `mcschemas.load()`.
- _class_ `mcschemas.DedicatedConverter(*, regex_flags=0, detailed_validation=True, forbid_extra_keys=False)`
    - A converter for converting between structured and unstructured data according to the data structures defined in this package.
    - _classmethod_ `mcschemas.DedicatedConverter.configure(converter, /, *, regex_flags=0)`
        - Configure an existing converter to convert some specific types.
            - `converter` must be an instance of `cattrs.BaseConverter` or its subclasses.
    - Full documentation can be found in the code: [src/mcschemas/parser/converters.py](src/mcschemas/parser/converters.py)

And file structure model declarations:

|            Sub Package             |                                                                   `mcschemas.Schemas` Enum Member Name                                                                    |                                  Corresponding File Structure                                   |
|:----------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------:|
| `mcschemas.models.versionmanifest` |                                                                            `VERSION_MANIFEST`                                                                             |   [`version_manifest.json` and `version_manifest_v2.json`](#file-structure-version-manifest)    |
| `mcschemas.models.clientmanifest`  |                                                                             `CLIENT_MANIFEST`                                                                             |                        [`client.json`](#file-structure-client-manifest)                         |
|   `mcschemas.models.assetindex`    |                                                                               `ASSET_INDEX`                                                                               |                         [Asset index file](#file-structure-asset-index)                         |
|  `mcschemas.models.versionattrs`   |                                                                           `VERSION_ATTRIBUTES`                                                                            |                      [`version.json`](#file-structure-version-attributes)                       |
|   `mcschemas.models.mojangjava`    |                                                      `MOJANG_JAVA_RUNTIME_INDEX`<br/>`MOJANG_JAVA_RUNTIME_MANIFEST`                                                       | [Mojang Java Runtime index file and manifest files](#file-structure-mojang-java-index-manifest) |
|    `mcschemas.models.yggdrasil`    | `TEXTURE_PROPERTY`<br/>`ERROR_RESPONSE`<br/>`ENDPOINT_AUTHENTICATE_RESPONSE`<br/>`ENDPOINT_REFRESH_RESPONSE`<br/>`YGGDRASIL_API_METADATA`<br/>`REFERENCE_SERVER_METADATA` |               [Yggdrasil API Responses](#file-structure-yggdrasil-api-responses)                |

## Usage Example

### Parse `version_manifest.json`

Download [at here](https://piston-meta.mojang.com/mc/game/version_manifest_v2.json).

```python
import mcschemas
from mcschemas.models.enums import VersionType

with open('version_manifest.json', mode='r') as f:
    version_manifest = mcschemas.load(f, mcschemas.Schema.VERSION_MANIFEST)

print('Latest release:', version_manifest.latest.release)
print('Latest snapshot:', version_manifest.latest.snapshot)
print('Number of available versions:', len(version_manifest.versions))
print('Show information on the first 5 release versions:')
for idx, entity in enumerate(version_manifest.filterVersions(type=VersionType.RELEASE)):
    if idx >= 5:
        break
    print('  The ID of the release version (at index {0}):'.format(version_manifest.index(entity)), entity.id)
    print('  The release time of the release version (at index {0}):'.format(version_manifest.index(entity)), entity.releaseTime)
    print('  The last update time of the release version (at index {0}):'.format(version_manifest.index(entity)), entity.time)
```

### Parse `client.json`

This example code uses `client.json` from Minecraft Java Edition
1.21.11, download [at here](https://piston-meta.mojang.com/v1/packages/3f42d3ea921915b36c581a435ed03683a7023fb1/1.21.11.json).

```python
import mcschemas

with open('1.21.11.json', mode='r') as f:
    client_manifest_1_21_11 = mcschemas.load(f, mcschemas.Schema.CLIENT_MANIFEST)

print('Version ID:', client_manifest_1_21_11.id)
# The following field is structured as a member of enum mcschemas.enums.VersionType
print('Version Type:', str(client_manifest_1_21_11.type))
print('Asset version ID:', client_manifest_1_21_11.assetIndex.id)
print('Main class:', client_manifest_1_21_11.mainClass)
print('Release time:', client_manifest_1_21_11.releaseTime)
print('Last update time:', client_manifest_1_21_11.time)
print('Number of dependency libraries:', len(client_manifest_1_21_11.libraries))
client_jar_file_info = client_manifest_1_21_11.downloads.get('client')
if client_jar_file_info:
    print('URL to download the client JAR file:', client_jar_file_info.url)
```

### Parse asset index file

This example code uses the asset index file version 29. You can download
it [at here](https://piston-meta.mojang.com/v1/packages/aaf4be9d6e197c384a09b1d9c631c6900d1f077c/29.json).

```python
from pathlib import Path

import mcschemas

with open('29.json', mode='r') as f:
    asset_index = mcschemas.load(f, mcschemas.Schema.ASSET_INDEX)

print('Number of asset files:', len(asset_index.objects))
asset_file_relative_path = Path('icons/icon_256x256.png')
if asset_file_relative_path in asset_index.objects:
    target_asset_file_info = asset_index.objects[asset_file_relative_path]
    print('Information about asset file {0}: hash={1.hash}, size={1.size}'.format(asset_file_relative_path, target_asset_file_info))
```

### Parse `version.json` from a client JAR file

This example code uses the client JAR file from Minecraft Java Edition 1.21.11. You can download it in official Minecraft Launcher
or [at here](https://piston-data.mojang.com/v1/objects/ba2df812c2d12e0219c489c4cd9a5e1f0760f5bd/client.jar).

```python
from pathlib import Path

import mcschemas

version_attrs = mcschemas.loadVersionAttrsFromClientJar(Path.home().joinpath('.minecraft/versions/1.21.11/1.21.11.jar'))

print('Unique identifier of this client JAR:', version_attrs.id)
print('User-friendly name of this client JAR:', version_attrs.name)
print('Data version of this client JAR:', version_attrs.world_version)
print('Protocol version of this client JAR:', version_attrs.protocol_version)
print('Build time of this client JAR:', version_attrs.build_time)
if version_attrs.series_id:
    print('Series ID (branch name) of this client JAR:', version_attrs.series_id)
```

### Load `client.json`, then filter and concatenate command line

This example code uses `client.json` from Minecraft Java Edition
1.21.11, download [at here](https://piston-meta.mojang.com/v1/packages/3f42d3ea921915b36c581a435ed03683a7023fb1/1.21.11.json).

**Note:** this example only demonstrates basic conditional filtering and concatenation operations, and does not consider the replacement of
placeholder parameters (which may be supported in future versions).

```python
import mcschemas
from mcschemas.tools import rules

with open('1.21.11.json', mode='r') as f:
    client_manifest_1_21_11 = mcschemas.load(f, mcschemas.Schema.CLIENT_MANIFEST)

features: dict[str, bool] = {
    'is_demo_user'         : True,
    'has_custom_resolution': True
}
cmdline: list[str] = ['java']
for jvm_arg_entry in client_manifest_1_21_11.arguments.jvm:
    if rules.isArgumentCanBeAppended(jvm_arg_entry, features=features):
        cmdline.extend(jvm_arg_entry.value)
cmdline.append(client_manifest_1_21_11.mainClass)
for game_arg_entry in client_manifest_1_21_11.arguments.game:
    if rules.isArgumentCanBeAppended(game_arg_entry, features=features):
        cmdline.extend(game_arg_entry.value)
print('Concatenated command line (without placeholder replacements):', cmdline)
```

### Fetch API metadata from an `authlib-injector` compatible Yggdrasil Service

This example code requires [`httpx`](https://pypi.org/project/httpx). You can install it through `pip`.

The Yggdrasil service is provided by [Drasl](https://drasl.unmojang.org).

```python
import sys

import cattrs
import httpx

import mcschemas

try:
    resp = httpx.get('https://drasl.unmojang.org/authlib-injector').raise_for_status()
except httpx.HTTPError as exc:
    print('Failed to fetch the Yggdrasil API metadata: {0}'.format(exc))
    sys.exit(1)

# Parse the structure that already unserialized by `resp.json()`
api_metadata = mcschemas.parse(resp.json(), mcschemas.Schema.YGGDRASIL_API_METADATA)
# Or directly load from original response text
api_metadata = mcschemas.loads(resp.text, mcschemas.Schema.YGGDRASIL_API_METADATA)

# Parse the server metadata
# If failed to parse, we can still access/manipulate it as a regular dict
try:
    server_metadata = mcschemas.parse(api_metadata.serverMetadata, mcschemas.Schema.REFERENCE_SERVER_METADATA)
except cattrs.ClassValidationError:
    server_metadata = api_metadata.serverMetadata

print('Skin domain allowlist:')
for allowed_skin_domain in api_metadata.skinDomainAllowlist:
    print('  -', allowed_skin_domain)
print('Player profile signature public key (in PEM format):')
print(api_metadata.signaturePublicKey)
print('Yggdrasil server name (if exists):', server_metadata.get('serverName', ''))
print(
        'Yggdrasil server implementation name and version (if exists):',
        server_metadata.get('implementationName'),
        server_metadata.get('implementationVersion'),
)
print('Yggdrasil server supports non-email account name:', server_metadata.get('feature.non_email_login', False))
```

# History

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.1] - 2026-02-21

### Added

- **Schema:** Supported to parse the response format of Yggdrasil APIs compatible with authlib-injector.
    - Corresponding model declarations at `mcschemas.models.yggdrasil`.
- **Schema:** Added the method `filterVersions()` for `mcschemas.models.versionmanifest.VersionManifest`
  to help filtering version entries by version type/ID.

### Changes

- **Project metadata:** Overhauled and updated the README.
- **Schema:** Registered `mcschemas.models.versionmanifest.VersionManifest` as a `collections.abc.MutableSequence` by implementing required abstract
  methods. In short, `mcschemas.models.versionmanifest.VersionManifest` now is a mutable sequence.
    - Its `__getitem__` method behaves a little differently from a regular sequence: when a string is passed in, it iterates through the internal list
      of version entries, searches for and returns the entry whose version ID **exactly matches** the string.
- **Schema:** Registered `mcschemas.models.assetindex.AssetIndex` as a `collections.abc.MutableMapping` by implementing required abstract methods.
  In short, `mcschemas.models.assetindex.AssetIndex` now is a mutable mapping.
    - Its `__getitem__`, `_setitem__` and `__delitem__` method behaves a little differently from a regular mapping: when a string is passed as the
      first argument after the `self`, it is first converted into a`pathlib.Path` instance before being passed to the internal asset objects mapping.

## [0.4.0] - 2026-02-09

This release focuses on renaming package and reorganizing the package internal structure.

Due to limited time and energy, I am unable to design a complete backward compatibility solution for this release. Sorry for the inconvenience.

### Backwards-incompatible Changes

- **Organizational:** The package is renamed to `mcschemas` (formally `mcschemes`),
  and this project is renamed to `minecraft-schemas` (formally `minecraft-schemes`).
    - Since the word "scheme" [[Merriam-Webster]](https://www.merriam-webster.com/dictionary/scheme) does not accurately reflect the content and
      objectives of this project, using "schema" [[Merriam-Webster]](https://www.merriam-webster.com/dictionary/schema) is clearly more appropriate.
- **Organizational:** Package structure changed:
    - All sub packages and modules including schema declarations are moved to `mcschemas.models`, detailed below:
        - `mcschemes.assetindex` -> `mcschemas.models.assetindex`
        - `mcschemes.clientmanifest` -> `mcschemas.models.clientmanifest`
        - `mcschemes.mojangjava` -> `mcschemas.models.mojangjava`
        - `mcschemes.versionattrs` -> `mcschemas.models.versionattrs`
        - `mcschemes.versionmanifest` -> `mcschemas.models.versionmanifest`
        - `mcschemes.enums` -> `mcschemas.models.enums`
        - `mcschemes.specials` -> `mcschemas.models.specials`
    - Parser submodule `mcschemes.tools.parser` is moved to `mcschemas.parser`.

### Changes

- **Project metadata:** Fixed typos in this version history file and README: `scheme` -> `schema`.

## [0.3.0.post1] - 2026-02-09

**Deprecated:**

- Announced the abandonment of the old package name `mcschemes` and the old project name `minecraft-schemes`.
    - The obsolete parts are kept in a separate branch `0.3.0-announced-deprecation`.

## [0.3.0] - 2026-01-31

### Added

- **Schema:** Added support for parsing version information files (the `version.json` embedded within `client.jar`).
- **Schema:** Added parse support for index file of Mojang Java Runtimes, and file manifest of each java runtime.
- **Parsing:** Added a dedicated converter class in `mcschemes.tools.parser.Converter.DedicatedConverter`.
    - This is intended to replace the `mcschemes.tools.parser.createConverter()`.

### Backwards-incompatible Changes

- **Schema:** `mcschemes.assetindex.AssetIndex` now use the `Path` object from the standard library's `pathlib` module to represent file relative
  paths in asset index files.

    1. Previously, it will use `str` to represent file relative paths, so you can access information (e.g. hash, size) by the following way:

        ```python
        from mcschemes.models.assetindex import AssetIndex
  
        asset_index: AssetIndex = ...  # Some operations to obtain the json and structure it to the AssetIndex instance
        file_info = asset_index.objects['icons/icon_128x128.png']
  
        [...]  # Do your operations for file_info
        ```

    2. Now, you need to use a `pathlib.Path` object as the key to access the corresponding information:

        ```python
        from pathlib import Path
        from mcschemes.models.assetindex import AssetIndex
      
        asset_index: AssetIndex = ...  # Some operations to obtain the json and structure it to the AssetIndex instance
        file_info = asset_index.objects[Path('icons/icon_128x128.png')]
      
        [...]  # Do your operations for file_info
        ```

### Deprecations

- **Parsing:** `mcschemes.tools.parser.createConverter()` is now marked as deprecated and will be removed in future versions.
    - Now pass a converter class based on `cattrs.Converter` to the kw-only argument ``converter_class`` is no longer determines the type of the
      returned dedicated converter instance.

### Changes

- **Project metadata:** This version history file has been revised to conform to the format described
  in [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- **Project metadata:** Fully updated the [README file](README.md):
    - Added a summary of the main features and benefits.
    - Added a summary of file structures supported by this library.
    - Usage example are now more useful and better represent typical use cases.
- **Organizational:** Reorganized the project structure:
    - `mcschemes.tools.parser` now is a package.
        - Sub-package `mcschemes.tools.parser.converters` is added to contains dedicated converters.
- **Typing:** `typing-extensions` was used instead of stdlib `typing` for better backward-compatibility for type annotation.
- **Schema:** Several changes for SHA-1 hexdigest container:
    - The comparison between two `mcsehemes.specials.Sha1Sum` instances now is based on the case-insensitive form of the `hexdigest` attribute of
      both.
    - The exception class `mcschemes.specials.ChecksumMismatch` is now exposed.
- **Parsing:** `mcschemes.tools.parser.parse()` now will check the second argument `scheme` in more robust way.

### Fixed

- **Tooling:** Fixed a mistake when comparing the OS name in the function `mcschemes.tools.rules.isAllow()`.

## [0.2.0] - 2025-12-11

### Added

- **Project metadata:** Added `MANIFEST.in` for setuptools.
- **Schema:** Added an SHA-1 hexdigest container type for `sha1`/`hash` fields (un-)structuring. Its definition can be found at:
  `mcschemes.specials.Sha1Sum`.
- **Tooling:** Added some tool functions to calculate a set of rules (iterable of `mcschemes.clientmanifest.nodes.RuleEntry` instances) means allow or
  disallow some operation, such as append an argument or download a library file.

### Changes

- **Project metadata:** Declared build backend `setuptools` into `pyproject.toml`.
- **Project metadata:** According to [PEP 561](https://peps.python.org/pep-0561), an empty `py.typed` is added into the root directory of package.
- **Project metadata:** Corrected the date format for all tier-2 titles in this version history file.
- **Organizational:** Moved `typings.py` to the root directory of package.

## [0.1.0.post1] - 2025-12-05

### Changes

- **Project metadata:** Added project urls into `pyproject.toml`.
- **Project metadata:** Added disclaimer in `README.md`.

## [0.1.0] - 2025-12-04

### Added

The initial release.
