From eeaf1435e81d9cbd8daa0efa22029c1f259a64b5 Mon Sep 17 00:00:00 2001 From: mat <27899617+mat-1@users.noreply.github.com> Date: Tue, 24 Mar 2026 11:15:56 -0500 Subject: 26.1 (#316) * start updating to 26.1 * start updating to 26.1-snapshot-6 * 26.1-snapshot-6 * 26.1-snapshot-10 * 26.1-rc-1 * fix tests * 26.1-rc-2 and sort default components * 26.1 * update changelog --- codegen/lib/code/data_components.py | 27 +++++---- codegen/lib/code/entity.py | 49 ++++++++--------- codegen/lib/code/packet.py | 106 ++++++++++-------------------------- codegen/lib/code/registry.py | 2 +- codegen/lib/code/tags.py | 39 ++++++++++--- codegen/lib/code/utils.py | 72 ++++-------------------- codegen/lib/download.py | 45 +++++---------- codegen/lib/extract.py | 83 ++++++++-------------------- codegen/lib/mappings.py | 93 ------------------------------- 9 files changed, 148 insertions(+), 368 deletions(-) delete mode 100644 codegen/lib/mappings.py (limited to 'codegen/lib') diff --git a/codegen/lib/code/data_components.py b/codegen/lib/code/data_components.py index e6de6201..992ba83f 100644 --- a/codegen/lib/code/data_components.py +++ b/codegen/lib/code/data_components.py @@ -360,7 +360,7 @@ use crate::{ ) elif target_rust_type == "ItemStack": item_rust_value = python_to_rust_value(python_value["id"], "ItemKind") - count = python_value["count"] + count = python_value.get("count", 1) if count == 1: return f"ItemStack::from({item_rust_value})" else: @@ -411,9 +411,6 @@ use crate::{ fields_for_rust_type = enum_and_struct_fields.get(target_rust_type, []) if "Referenced(Identifier)" in fields_for_rust_type: return f"{target_rust_type}::Referenced({python_to_rust_value(python_value, 'Identifier')})" - elif "Registry(data::Instrument)" in fields_for_rust_type: - # TODO - return f"{target_rust_type}::Registry(azalea_registry::data::Instrument::new_raw(0))" elif target_rust_type.startswith("HolderSet<"): holderset_type = target_rust_type.split("<", 1)[1].split(",", 1)[0] main_vec = python_to_rust_value( @@ -429,10 +426,10 @@ use crate::{ elif target_rust_type == "Identifier": # convert minecraft:air into Identifier::from_static("minecraft:air") return f'"{python_value}".into()' - elif target_rust_type == "DamageType": + elif target_rust_type.startswith("azalea_registry::data::"): # TODO: this is intentionally incorrect, see the comment in # azalea-registry/src/data.rs to see how to fix this properly - return "DamageType::Registry(azalea_registry::data::DamageKind::new_raw(0))" + return f"{target_rust_type}::new_raw(0)" else: # enum variant return f"{target_rust_type}::{to_camel_case(identifier_to_path(python_value))}" @@ -472,9 +469,17 @@ use crate::{ tag_module = "blocks" else: tag_module = "FIXME_UNKNOWN_MODULE" - vectors.append( - f"azalea_registry::tags::{tag_module}::{tag_name}.clone().into_iter().collect()" - ) + + # TODO: it's not currently possible to have a holderset for data registry items + # (because registries would need to be translated during packet parsing/writing), + # so we leave this empty for now. + if inner_type in {"BannerPatternKind", "DamageKind"}: + pass + else: + vectors.append( + f"azalea_registry::tags::{tag_module}::{tag_name}.clone().into_iter().collect()" + ) + continue main_vec += python_to_rust_value(v, inner_type) + "," main_vec = main_vec.rstrip(",") + "]" @@ -533,11 +538,11 @@ use crate::{ item_defaults_original = item_defaults item_defaults = {} - for k, v in item_defaults_original.items(): + for k, v in sorted(item_defaults_original.items(), key=lambda i: i[0]): item_defaults[k] = python_to_rust_value(v, field_type) default_values_frequency = {} - for value in item_defaults.values(): + for value in sorted(item_defaults.values()): if value not in default_values_frequency: default_values_frequency[value] = 0 default_values_frequency[value] += 1 diff --git a/codegen/lib/code/entity.py b/codegen/lib/code/entity.py index d10a159f..992d9b19 100644 --- a/codegen/lib/code/entity.py +++ b/codegen/lib/code/entity.py @@ -1,5 +1,4 @@ from lib.utils import to_camel_case, to_snake_case, get_dir_location, upper_first_letter -from lib.mappings import Mappings from typing import Optional import re @@ -8,18 +7,19 @@ DATA_RS_DIR = get_dir_location("../azalea-entity/src/data.rs") DIMENSIONS_RS_DIR = get_dir_location("../azalea-entity/src/dimensions.rs") -def generate_metadata_names(burger_dataserializers: dict, mappings: Mappings): +def generate_metadata_names(burger_dataserializers: dict): serializer_names: list[Optional[str]] = [None] * len(burger_dataserializers) for burger_serializer in burger_dataserializers.values(): print(burger_serializer) + # TODO: remove these comments # burger gives us the wrong class, so we do this instead - data_serializers_class = mappings.get_class_from_deobfuscated_name( - "net.minecraft.network.syncher.EntityDataSerializers" - ) - mojmap_name = mappings.get_field( - data_serializers_class, burger_serializer["field"] - ).lower() + # data_serializers_class = "net/minecraft/network/syncher/EntityDataSerializers" + # mojmap_name = mappings.get_field( + # data_serializers_class, burger_serializer["field"] + # ).lower() + + mojmap_name = burger_serializer["field"].lower() if mojmap_name == "component": mojmap_name = "formatted_text" @@ -58,11 +58,11 @@ def parse_metadata_types_from_code(): return data -def generate_entity_metadata(burger_entities_data: dict, mappings: Mappings): +def generate_entity_metadata(burger_entities_data: dict): burger_entity_metadata = burger_entities_data["entity"] new_metadata_names = generate_metadata_names( - burger_entities_data["dataserializers"], mappings + burger_entities_data["dataserializers"] ) parsed_metadata_types = parse_metadata_types_from_code() @@ -173,7 +173,7 @@ impl From for UpdateMetadataError { for entity_id in burger_entity_metadata.keys(): field_name_map[entity_id] = {} for field_name_or_bitfield in get_entity_metadata_names( - entity_id, burger_entity_metadata, mappings + entity_id, burger_entity_metadata ).values(): if isinstance(field_name_or_bitfield, str): if field_name_or_bitfield in previous_field_names: @@ -204,7 +204,7 @@ impl From for UpdateMetadataError { # and now figure out what to rename them to for entity_id in burger_entity_metadata.keys(): for index, field_name_or_bitfield in get_entity_metadata_names( - entity_id, burger_entity_metadata, mappings + entity_id, burger_entity_metadata ).items(): if isinstance(field_name_or_bitfield, str): new_field_name = field_name_or_bitfield @@ -245,7 +245,7 @@ impl From for UpdateMetadataError { parents = get_entity_parents(entity_id, burger_entity_metadata) for parent_id in list(reversed(parents)): for index, name_or_bitfield in get_entity_metadata_names( - parent_id, burger_entity_metadata, mappings + parent_id, burger_entity_metadata ).items(): assert index == len(all_field_names_or_bitfields) all_field_names_or_bitfields.append(name_or_bitfield) @@ -462,7 +462,7 @@ impl From for UpdateMetadataError { if parent_struct_name: code.append(f" parent: {parent_struct_name}MetadataBundle,") for index, name_or_bitfield in get_entity_metadata_names( - entity_id, burger_entity_metadata, mappings + entity_id, burger_entity_metadata ).items(): if isinstance(name_or_bitfield, str): name_or_bitfield = maybe_rename_field(name_or_bitfield, index) @@ -514,7 +514,7 @@ impl From for UpdateMetadataError { code.append(" parent: Default::default(),") for index, name_or_bitfield in get_entity_metadata_names( - this_entity_id, burger_entity_metadata, mappings + this_entity_id, burger_entity_metadata ).items(): default = next( filter(lambda i: i["index"] == index, entity_metadatas) @@ -792,22 +792,19 @@ def get_entity_metadata(entity_id: str, burger_entity_metadata: dict): # returns a dict of {index: (name or bitfield)} -def get_entity_metadata_names( - entity_id: str, burger_entity_metadata: dict, mappings: Mappings -): +def get_entity_metadata_names(entity_id: str, burger_entity_metadata: dict): entity_metadata = burger_entity_metadata[entity_id]["metadata"] mapped_metadata_names = {} for metadata_item in entity_metadata: if "data" in metadata_item: - obfuscated_class = metadata_item["class"] + # obfuscated_class = metadata_item["class"] # mojang_class = mappings.get_class(obfuscated_class) first_byte_index = None for metadata_attribute in metadata_item["data"]: - obfuscated_field = metadata_attribute["field"] - mojang_field = mappings.get_field(obfuscated_class, obfuscated_field) + mojang_field = metadata_attribute["field"] pretty_mojang_name = prettify_mojang_field(mojang_field) mapped_metadata_names[metadata_attribute["index"]] = pretty_mojang_name @@ -820,12 +817,10 @@ def get_entity_metadata_names( if metadata_item["bitfields"] and first_byte_index is not None: clean_bitfield = {} for bitfield_item in metadata_item["bitfields"]: - bitfield_item_obfuscated_class = bitfield_item.get( - "class", obfuscated_class - ) - mojang_bitfield_item_name = mappings.get_method( - bitfield_item_obfuscated_class, bitfield_item["method"], "" - ) + # bitfield_item_obfuscated_class = bitfield_item.get( + # "class", obfuscated_class + # ) + mojang_bitfield_item_name = bitfield_item["method"] bitfield_item_name = prettify_mojang_method( mojang_bitfield_item_name ) diff --git a/codegen/lib/code/packet.py b/codegen/lib/code/packet.py index 9ce5c137..1131cb49 100644 --- a/codegen/lib/code/packet.py +++ b/codegen/lib/code/packet.py @@ -1,6 +1,5 @@ from lib.utils import identifier_to_path, to_snake_case, to_camel_case, get_dir_location from lib.code.utils import burger_type_to_rust_type, write_packet_file -from lib.mappings import Mappings from typing import Optional import os import re @@ -175,7 +174,6 @@ def burger_instruction_to_code( instructions: list[dict], index: int, generated_packet_code: list[str], - mappings: Mappings, obfuscated_class_name: str, uses: set, extra_code: list[str], @@ -208,8 +206,7 @@ def burger_instruction_to_code( and next_next_instruction and next_next_instruction["operation"] == "loop" ): - obfuscated_field_name = instruction["field"].split(".")[0] - field_name = mappings.get_field(obfuscated_class_name, obfuscated_field_name) + field_name = instruction["field"].split("/")[0] # figure out what kind of iterator it is loop_instructions = next_next_instruction["instructions"] @@ -218,7 +215,6 @@ def burger_instruction_to_code( loop_instructions[1]["type"], None, loop_instructions[1], - mappings, obfuscated_class_name, ) field_type_rs = f"Vec<{entry_type_rs}>" @@ -235,7 +231,6 @@ def burger_instruction_to_code( loop_instructions[1]["type"], None, loop_instructions[1], - mappings, obfuscated_class_name, ) ) @@ -248,7 +243,6 @@ def burger_instruction_to_code( loop_instructions[2]["type"], None, loop_instructions[2], - mappings, obfuscated_class_name, ) ) @@ -277,16 +271,14 @@ def burger_instruction_to_code( ) ): print("ok is option") - obfuscated_field_name = instruction["field"].split(".")[0].split(" ")[0] + field_name = instruction["field"].split(".")[0].split(" ")[0] - if obfuscated_field_name in known_variable_types: - # just use the known name since it's not gonna be in the mappings - obfuscated_field_name = known_variable_types[obfuscated_field_name] - - field_name = mappings.get_field(obfuscated_class_name, obfuscated_field_name) + # if obfuscated_field_name in known_variable_types: + # just use the known name since it's not gonna be in the mappings + # obfuscated_field_name = known_variable_types[obfuscated_field_name] if field_name is None: - field_name = obfuscated_field_name.split("/")[-1] + field_name = field_name.split("/")[-1] if "<" in field_name: field_name = "value" @@ -306,7 +298,6 @@ def burger_instruction_to_code( condition_instruction["type"], None, condition_instruction, - mappings, obfuscated_class_name, ) ) @@ -321,54 +312,35 @@ def burger_instruction_to_code( skip = 1 else: field_type = instruction["type"] - obfuscated_field_name = instruction["field"] + field_name = instruction["field"] - if obfuscated_field_name.startswith("(float)"): - obfuscated_field_name = obfuscated_field_name[len("(float)") :] - - field_name = mappings.get_field( - obfuscated_class_name, obfuscated_field_name - ) or mappings.get_field( - obfuscated_class_name.split("$")[0], obfuscated_field_name - ) + if field_name.startswith("(float)"): + field_name = field_name[len("(float)") :] field_type_rs, is_var, instruction_uses, instruction_extra_code = ( burger_type_to_rust_type( - field_type, field_name, instruction, mappings, obfuscated_class_name + field_type, field_name, instruction, obfuscated_class_name ) ) - if obfuscated_field_name in known_variable_types: + if field_name in known_variable_types: # just use the known name since it's not gonna be in the mappings - field_name = obfuscated_field_name + pass - elif ( - "." in obfuscated_field_name - or " " in obfuscated_field_name - or "(" in obfuscated_field_name - ): - field_type_rs2, obfuscated_field_name, field_comment = burger_field_to_type( - obfuscated_field_name, - mappings, + elif "." in field_name or " " in field_name or "(" in field_name: + field_type_rs2, field_name, field_comment = burger_field_to_type( + field_name, obfuscated_class_name, known_variable_types, ) if not field_type_rs2: generated_packet_code.append(f"// TODO: {instruction}") return - if obfuscated_field_name in known_variable_types: + if field_name in known_variable_types: # just use the known name since it's not gonna be in the mappings - obfuscated_field_name = known_variable_types[obfuscated_field_name] - print("got obfuscated_field_name", obfuscated_field_name) - - # try to get the field name again with the new stuff we know - field_name = mappings.get_field( - obfuscated_class_name, obfuscated_field_name - ) or mappings.get_field( - obfuscated_class_name.split("$")[0], obfuscated_field_name - ) - if field_name is None: - field_name = obfuscated_field_name.split("/")[-1] + field_name = known_variable_types[field_name] + print("got obfuscated_field_name", field_name) + uses.update(instruction_uses) extra_code.extend(instruction_extra_code) @@ -387,7 +359,7 @@ def burger_instruction_to_code( def burger_field_to_type( - field, mappings: Mappings, obfuscated_class_name: str, known_variable_types={} + field, obfuscated_class_name: str, known_variable_types={} ) -> tuple[Optional[str], str, Optional[str]]: """ Returns field_type_rs, obfuscated_field_name, field_comment @@ -399,38 +371,18 @@ def burger_field_to_type( match = re.match(r"^\w+\.\w+\(\)$", field) if match: print("field", field) - obfuscated_first = field.split(".")[0] - obfuscated_second = field.split(".")[1].split("(")[0] + first_type = field.split(".")[0] + second_type = field.split(".")[1].split("(")[0] # first = mappings.get_field(obfuscated_class_name, obfuscated_first) - if obfuscated_first in known_variable_types: - first_type = known_variable_types[obfuscated_first] - else: - try: - first_type = mappings.get_field_type( - obfuscated_class_name, obfuscated_first - ) - except Exception: - first_type = "TODO" - first_obfuscated_class_name: Optional[str] = ( - mappings.get_class_from_deobfuscated_name(first_type) - ) - if first_obfuscated_class_name: - try: - second = mappings.get_method( - first_obfuscated_class_name, obfuscated_second, "" - ) - except Exception: - # if this happens then the field is probably from a super class - second = obfuscated_second - else: - second = obfuscated_second - first_type_short = first_type.split(".")[-1] - if second in {"byteValue"}: - return (first_type_short, obfuscated_first, None) + if first_type in known_variable_types: + first_type = known_variable_types[first_type] + first_type_short = first_type.split("/")[-1] + if second_type in {"byteValue"}: + return (first_type_short, first_type, None) return ( first_type_short, - obfuscated_first, - f"TODO: Does {first_type_short}::{second}, may not be implemented", + first_type, + f"TODO: Does {first_type_short}::{second_type}, may not be implemented", ) return None, field, None diff --git a/codegen/lib/code/registry.py b/codegen/lib/code/registry.py index 01462e1f..9c30a273 100644 --- a/codegen/lib/code/registry.py +++ b/codegen/lib/code/registry.py @@ -154,7 +154,7 @@ def registry_name_to_enum_name(registry_name: str) -> str: # change _type to _kind because that's Rustier (and because _type # is a reserved keyword) registry_name = registry_name[:-5] + "_kind" - elif registry_name in {"menu", "block", "item"}: + elif registry_name in {"menu", "block", "item", "banner_pattern"}: registry_name += "_kind" return to_camel_case(registry_name) diff --git a/codegen/lib/code/tags.py b/codegen/lib/code/tags.py index b6e225ac..2a44d35c 100644 --- a/codegen/lib/code/tags.py +++ b/codegen/lib/code/tags.py @@ -1,25 +1,45 @@ from lib.code.registry import registry_name_to_enum_name -from lib.utils import identifier_to_namespace, identifier_to_path, to_snake_case, upper_first_letter, get_dir_location, to_camel_case +from lib.utils import ( + identifier_to_namespace, + identifier_to_path, + to_snake_case, + upper_first_letter, + get_dir_location, + to_camel_case, +) TAGS_DIR = get_dir_location("../azalea-registry/src/tags") -def generate_tags(registries: dict, tags: dict, file_name: str, registry_name: str): +def generate_tags( + registries: dict, + tags: dict, + file_name: str, + registry_name: str, + is_data_registry: bool = False, +): struct_name = registry_name_to_enum_name(registry_name) tags_dir = f"{TAGS_DIR}/{file_name}.rs" + registry_module = "data" if is_data_registry else "builtin" + if is_data_registry: + struct_name += "Key" + generated = f"""// This file was @generated by codegen/lib/code/tags.py, don't edit it manually! use std::sync::LazyLock; -use crate::{{builtin::{struct_name}, tags::RegistryTag}}; +use crate::{{{registry_module}::{struct_name}, tags::RegistryTag}}; """ - protocol_ids = {} - for k, v in registries["minecraft:" + registry_name]["entries"].items(): - protocol_ids[identifier_to_path(k)] = v["protocol_id"] + if is_data_registry: + protocol_ids = None + else: + protocol_ids = {} + for k, v in registries[f"minecraft:{registry_name}"]["entries"].items(): + protocol_ids[identifier_to_path(k)] = v["protocol_id"] for tag_name, tag in sorted(tags.items(), key=lambda x: x[0]): entries = [] @@ -41,9 +61,10 @@ use crate::{{builtin::{struct_name}, tags::RegistryTag}}; generated += f"pub static {static_set_name}: LazyLock> = LazyLock::new(|| RegistryTag::new(vec![" - # this is important because we binary search registries in some cases - # and they need to be sorted by their rust Ord order - entries.sort(key=lambda e: protocol_ids[e]) + if not is_data_registry: + # this is important because we binary search registries in some cases + # and they need to be sorted by their rust Ord order + entries.sort(key=lambda e: protocol_ids[e]) for entry_name in entries: generated += ( diff --git a/codegen/lib/code/utils.py b/codegen/lib/code/utils.py index 62b4627b..9bfb3504 100644 --- a/codegen/lib/code/utils.py +++ b/codegen/lib/code/utils.py @@ -1,7 +1,6 @@ # utilities specifically for codegen from lib.utils import to_camel_case, to_snake_case, get_dir_location -from lib.mappings import Mappings from typing import Optional import os @@ -10,8 +9,7 @@ def burger_type_to_rust_type( burger_type, field_name: Optional[str] = None, instruction=None, - mappings: Optional[Mappings] = None, - obfuscated_class_name: Optional[str] = None, + class_name: Optional[str] = None, ): is_var = False uses = set() @@ -90,7 +88,7 @@ def burger_type_to_rust_type( # depends on context field_type_rs = "todo!()" elif burger_type == "enum": - if not instruction or not mappings or not obfuscated_class_name: + if not instruction or not class_name: field_type_rs = 'todo!("enum")' else: # generate the whole enum :) @@ -98,57 +96,20 @@ def burger_type_to_rust_type( enum_field = instruction["field"] # enums with a.b() as the field if "." in enum_field: - enum_first_part_name = mappings.get_field_type( - obfuscated_class_name, enum_field.split(".")[0] - ) - enum_first_part_obfuscated_name = ( - mappings.get_class_from_deobfuscated_name(enum_first_part_name) - ) - print( - "enum_first_part_obfuscated_name", enum_first_part_obfuscated_name - ) + enum_first_part_name = enum_field.split("/")[0] + print("enum_first_part_name", enum_first_part_name) print("enum field", enum_field.split(".")[1].split("(")[0]) - try: - enum_name = mappings.get_method_type( - enum_first_part_obfuscated_name, - enum_field.split(".")[1].split("(")[0], - "", - ) - except KeyError: - # sometimes enums are fields instead of methods - enum_name = mappings.get_field_type( - enum_first_part_obfuscated_name, - enum_field.split(".")[1].split("(")[0], - ) + enum_name = enum_field.split("$")[1].split("(")[0] print("hm", enum_name) else: - try: - enum_name = mappings.get_field_type( - obfuscated_class_name, enum_field - ) - except Exception: - enum_name = mappings.get_class(obfuscated_class_name) - print( - f"failed getting {obfuscated_class_name}.{enum_field} but continuing with {enum_name} anyways" - ) + enum_name = enum_field or class_name print("enum_name", enum_name) - enum_obfuscated_name = mappings.get_class_from_deobfuscated_name(enum_name) + enum_obfuscated_name = enum_name print("enum_obfuscated_name", enum_obfuscated_name) enum_variants = [] - for obfuscated_field_name in mappings.fields[enum_obfuscated_name]: - field_name = mappings.get_field( - enum_obfuscated_name, obfuscated_field_name - ) - - # get the type just to make sure it's actually a variant and not something else - field_type = mappings.get_field_type( - enum_obfuscated_name, obfuscated_field_name - ) - if field_type != enum_name: - continue - - enum_variants.append(field_name) + raise RuntimeError("TODO: extracting enum variants") + enum_variants.append(field_name) field_type_rs = to_camel_case(enum_name.split(".")[-1].split("$")[-1]) extra_code.append("") @@ -165,28 +126,19 @@ def burger_type_to_rust_type( field_type_rs = f"Vec<{field_type_rs}>" # sometimes burger gives us a slightly incorrect type - if mappings and instruction: + if instruction: if field_type_rs == "Vec": field = instruction["field"] if field.endswith(".copy()"): field = field[:-7] - try: - array_type = mappings.get_field_type(obfuscated_class_name, field) - except KeyError: - print("Error getting array type", field) - return field_type_rs, is_var, uses, extra_code + array_type = field if array_type == "net.minecraft.network.FriendlyByteBuf": field_type_rs = "UnsizedByteArray" uses.add("azalea_buf::UnsizedByteArray") else: print("instruction that we errored on:", instruction) - deobfuscated_class_name = ( - mappings.get_class(obfuscated_class_name) if obfuscated_class_name else None - ) - raise Exception( - f"Unknown field type: {burger_type} ({deobfuscated_class_name or obfuscated_class_name})" - ) + raise Exception(f"Unknown field type: {burger_type} ({class_name})") return field_type_rs, is_var, uses, extra_code diff --git a/codegen/lib/download.py b/codegen/lib/download.py index 8819cf61..43b42aa5 100644 --- a/codegen/lib/download.py +++ b/codegen/lib/download.py @@ -1,12 +1,11 @@ -from lib.utils import get_dir_location +from .utils import get_dir_location import xml.etree.ElementTree as ET -from .mappings import Mappings import requests import json import os -PUMPKIN_EXTRACTOR_COMMIT = "82926545925baf5f50414cc9374f1cc340b7de0f" -BURGER_COMMIT = "366d6e4bed0e9e6505d9c40c83628ab80a5fe001" +PUMPKIN_EXTRACTOR_COMMIT = "f3019d598c06f0d6fd4f3568fbf2d5bebae71173q" +BURGER_COMMIT = "bb84700e43bf7090877d9a4eb5d87c3125a8d22e" # make sure the cache directory exists print("Making __cache__") @@ -16,26 +15,26 @@ if not os.path.exists(get_dir_location("__cache__")): def get_burger(): - if not os.path.exists(get_dir_location("__cache__/Burger")): - print("\033[92mDownloading Burger...\033[m") + if not os.path.exists(get_dir_location("__cache__/azalea-burger")): + print("\033[92mDownloading azalea-burger...\033[m") os.system( - f"cd {get_dir_location('__cache__')} && git clone https://github.com/mat-1/Burger && cd Burger && git pull && git reset --hard {BURGER_COMMIT}" + f"cd {get_dir_location('__cache__')} && git clone https://github.com/azalea-rs/azalea-burger && cd azalea-burger && git pull && git reset --hard {BURGER_COMMIT}" ) print("\033[92mInstalling dependencies...\033[m") os.system( - f"cd {get_dir_location('__cache__')}/Burger && python -m venv venv && venv/bin/pip install six jawa" + f"cd {get_dir_location('__cache__')}/azalea-burger && python -m venv venv && venv/bin/pip install six jawa" ) def get_pumpkin_extractor(): - if not os.path.exists(get_dir_location("__cache__/pumpkin-extractor")): - print("\033[92mDownloading Pumpkin-MC/Extractor...\033[m") + if not os.path.exists(get_dir_location("__cache__/azalea-pumpkin-extractor")): + print("\033[92mDownloading mat-1/azalea-pumpkin-extractor...\033[m") os.system( - f"cd {get_dir_location('__cache__')} && git clone https://github.com/Pumpkin-MC/Extractor pumpkin-extractor && cd pumpkin-extractor && git pull && git reset --hard {PUMPKIN_EXTRACTOR_COMMIT}" + f"cd {get_dir_location('__cache__')} && git clone https://github.com/azalea-rs/azalea-pumpkin-extractor && cd azalea-pumpkin-extractor && git pull && git reset --hard {PUMPKIN_EXTRACTOR_COMMIT}" ) - return get_dir_location("__cache__/pumpkin-extractor") + return get_dir_location("__cache__/azalea-pumpkin-extractor") def get_version_manifest(): @@ -94,29 +93,13 @@ def get_server_jar(version_id: str): f.write(requests.get(server_jar_url).content) -def get_mappings_for_version(version_id: str): - if not os.path.exists(get_dir_location(f"__cache__/mappings-{version_id}.txt")): - package_data = get_version_data(version_id) - - client_mappings_url = package_data["downloads"]["client_mappings"]["url"] - - mappings_text = requests.get(client_mappings_url).text - - with open(get_dir_location(f"__cache__/mappings-{version_id}.txt"), "w") as f: - f.write(mappings_text) - else: - with open(get_dir_location(f"__cache__/mappings-{version_id}.txt"), "r") as f: - mappings_text = f.read() - return Mappings.parse(mappings_text) - - def get_fabric_data(version_id: str): # https://meta.fabricmc.net/v2/versions/yarn path = get_dir_location(f"__cache__/fabric-{version_id}.json") if not os.path.exists(path): print(f"\033[92mDownloading Fabric metadata for {version_id}...\033[m") - url = f"https://meta.fabricmc.net/v1/versions/loader/{version_id}" + url = f"https://meta.fabricmc.net/v2/versions/loader/{version_id}" yarn_versions_data = requests.get(url).json() with open(path, "w") as f: json.dump(yarn_versions_data, f) @@ -247,10 +230,10 @@ def clear_version_cache(): if os.path.exists(get_dir_location(f"__cache__/{file}")): os.remove(get_dir_location(f"__cache__/{file}")) - burger_path = get_dir_location("__cache__/Burger") + burger_path = get_dir_location("__cache__/azalea-burger") if os.path.exists(burger_path): os.system(f"cd {burger_path} && git pull && git reset --hard {BURGER_COMMIT}") - pumpkin_path = get_dir_location("__cache__/pumpkin-extractor") + pumpkin_path = get_dir_location("__cache__/azalea-pumpkin-extractor") if os.path.exists(pumpkin_path): os.system( f"cd {pumpkin_path} && git add . && git stash && git checkout master && git pull && git stash pop && git reset --hard {PUMPKIN_EXTRACTOR_COMMIT}" diff --git a/codegen/lib/extract.py b/codegen/lib/extract.py index f2f7f4a2..9faa2e22 100644 --- a/codegen/lib/extract.py +++ b/codegen/lib/extract.py @@ -1,18 +1,16 @@ # Extracting data from the Minecraft jars import shutil -from lib.download import ( +from .download import ( get_fabric_api_version, get_latest_fabric_kotlin_version, - get_latest_fabric_loom_version, - get_mappings_for_version, get_pumpkin_extractor, get_server_jar, get_burger, get_client_jar, get_fabric_data, ) -from lib.utils import get_dir_location, to_camel_case, upper_first_letter +from .utils import get_dir_location from zipfile import ZipFile import subprocess import json @@ -43,7 +41,23 @@ def get_packets_report(version_id: str): def get_items_report(version_id: str): - return get_report(version_id, "items") + generate_data_from_server_jar(version_id) + items_dir = get_dir_location( + f"__cache__/generated-{version_id}/reports/minecraft/components/item" + ) + if not os.path.exists(items_dir): + return {} + items = {} + for root, dirs, files in os.walk(items_dir, topdown=False): + for name in files: + file = os.path.join(root, name) + relative_path = file.replace(items_dir, "")[1:] + if not file.endswith(".json"): + continue + with open(file, "r") as f: + items[relative_path[:-5]] = json.load(f) + + return items def get_report(version_id: str, name: str): @@ -143,14 +157,12 @@ def get_burger_data_for_version(version_id: str): if not os.path.exists(get_dir_location(f"__cache__/burger-{version_id}.json")): get_burger() get_client_jar(version_id) - get_mappings_for_version(version_id) - print("\033[92mRunning Burger...\033[m") + print("\033[92mRunning azalea-burger...\033[m") run_python_command_and_download_deps( - f"cd {get_dir_location('__cache__/Burger')} && " + f"cd {get_dir_location('__cache__/azalea-burger')} && " f"venv/bin/python munch.py {get_dir_location('__cache__')}/client-{version_id}.jar " f"--output {get_dir_location('__cache__')}/burger-{version_id}.json " - f"--mappings {get_dir_location('__cache__')}/mappings-{version_id}.txt" ) with open(get_dir_location(f"__cache__/burger-{version_id}.json"), "r") as f: return json.load(f) @@ -183,12 +195,11 @@ def get_pumpkin_data(version_id: str, category: str): fabric_kotlin_version = get_latest_fabric_kotlin_version() gradle_properties = f"""# Done to increase the memory available to gradle. -org.gradle.jvmargs=-Xmx1G +org.gradle.jvmargs=-Xmx2G org.gradle.parallel=true # Fabric Properties -# check these on https://modmuss50.me/fabric.html +# check these on https://fabricmc.net/develop/ minecraft_version={version_id} -yarn_mappings={fabric_data["mappings"]["version"]} loader_version={fabric_data["loader"]["version"]} kotlin_loader_version={fabric_kotlin_version} # Mod Properties @@ -200,24 +211,9 @@ fabric_version={fabric_api_version} with open(f"{pumpkin_dir}/gradle.properties", "w") as f: f.write(gradle_properties) - # update the minecraft version dependency in src/main/resources/fabric.mod.json - fabric_mod_json_path = f"{pumpkin_dir}/src/main/resources/fabric.mod.json" - with open(fabric_mod_json_path, "r") as f: - fabric_mod_json = f.read() - with open(f"{pumpkin_dir}/build.gradle.kts", "r") as f: - build_gradle_kts = f.read() - with open(f"{pumpkin_dir}/build.gradle.kts", "w") as f: - # build_gradle_kts = re.sub( - # r'(id\("fabric-loom"\) version )"[^"]+"', - # rf'\1"{fabric_loom_version}"', - # build_gradle_kts, - # ) - f.write(build_gradle_kts) - # run ./gradlew runServer until it logs "(pumpkin_extractor) Done" p = subprocess.Popen( - # the gradle wrapper (./gradlew) is sometimes on the wrong version so just prefer the system's gradle installation - f"cd {pumpkin_dir} && gradle clean && gradle runServer", + f"cd {pumpkin_dir} && ./gradlew clean && ./gradlew runServer", stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=True, @@ -253,34 +249,3 @@ def get_file_from_jar(version_id: str, file_dir: str): def get_en_us_lang(version_id: str): return json.loads(get_file_from_jar(version_id, "assets/minecraft/lang/en_us.json")) - - -# burger packet id extraction is broken since 1.20.5 (always returns -1, so we have to determine packet id ourselves from the mappings). -# this is very much not ideal. - - -def get_packet_list(version_id: str): - if version_id != "1.21": - return [] - - generate_data_from_server_jar(version_id) - with open( - get_dir_location(f"__cache__/generated-{version_id}/reports/packets.json"), "r" - ) as f: - packets_report = json.load(f) - packet_list = [] - for state, state_value in packets_report.items(): - for direction, direction_value in state_value.items(): - for packet_identifier, packet_value in direction_value.items(): - assert packet_identifier.startswith("minecraft:") - packet_identifier = upper_first_letter( - to_camel_case(packet_identifier[len("minecraft:") :]) - ) - packet_list.append( - { - "state": state, - "direction": direction, - "name": packet_identifier, - "id": packet_value["protocol_id"], - } - ) diff --git a/codegen/lib/mappings.py b/codegen/lib/mappings.py deleted file mode 100644 index 9c39fc2b..00000000 --- a/codegen/lib/mappings.py +++ /dev/null @@ -1,93 +0,0 @@ -from typing import Optional - - -class Mappings: - __slots__ = ('classes', 'fields', 'methods', 'field_types', 'method_types') - - def __init__(self, classes, fields, methods, field_types, method_types): - self.classes = classes - self.fields = fields - self.methods = methods - self.field_types = field_types - self.method_types = method_types - - @staticmethod - def parse(mappings_txt): - classes = {} - fields = {} - methods = {} - field_types = {} - method_types = {} - - current_obfuscated_class_name = None - - for line in mappings_txt.splitlines(): - if line.startswith('#') or line == '': - continue - - if line.startswith(' '): - # if a line starts with 4 spaces, that means it's a method or a field - if '(' in line: - # if it has an opening parenthesis, it's a method - real_name_with_parameters_and_line, obfuscated_name = line.strip().split(' -> ') - real_name_with_parameters = real_name_with_parameters_and_line.split( - ':')[-1] - - real_type, real_name = real_name_with_parameters.split('(')[ - 0].split(' ') - parameters = real_name_with_parameters.split('(')[1].split(')')[ - 0] - - if current_obfuscated_class_name not in methods: - methods[current_obfuscated_class_name] = {} - method_types[current_obfuscated_class_name] = {} - methods[current_obfuscated_class_name][ - f'{obfuscated_name}({parameters})'] = real_name - method_types[current_obfuscated_class_name][ - f'{obfuscated_name}({parameters})'] = real_type - else: - # otherwise, it's a field - real_name_with_type, obfuscated_name = line.strip().split(' -> ') - real_type, real_name = real_name_with_type.split(' ') - - if current_obfuscated_class_name not in fields: - fields[current_obfuscated_class_name] = {} - field_types[current_obfuscated_class_name] = {} - fields[current_obfuscated_class_name][obfuscated_name] = real_name - field_types[current_obfuscated_class_name][obfuscated_name] = real_type - else: - # otherwise it's a class - real_name, obfuscated_name = line.strip(':').split(' -> ') - current_obfuscated_class_name = obfuscated_name - - classes[obfuscated_name] = real_name - - return Mappings(classes, fields, methods, field_types, method_types) - - def get_field(self, obfuscated_class_name, obfuscated_field_name): - return self.fields.get(obfuscated_class_name, {}).get(obfuscated_field_name) - - def get_class(self, obfuscated_class_name): - if '<' in obfuscated_class_name: - first_part, args = obfuscated_class_name.split('<') - args = args.rstrip('>').strip(';').split(';') - print(args) - assert len(args) == 1 - arg = self.get_class(args[0][1:]) - return f'{first_part}<{arg}>' - return self.classes[obfuscated_class_name] - - def get_method(self, obfuscated_class_name, obfuscated_method_name, obfuscated_signature): - return self.methods[obfuscated_class_name][f'{obfuscated_method_name}({obfuscated_signature})'] - - def get_field_type(self, obfuscated_class_name, obfuscated_field_name) -> str: - return self.field_types[obfuscated_class_name][obfuscated_field_name] - - def get_method_type(self, obfuscated_class_name, obfuscated_method_name, obfuscated_signature) -> str: - return self.method_types[obfuscated_class_name][f'{obfuscated_method_name}({obfuscated_signature})'] - - def get_class_from_deobfuscated_name(self, deobfuscated_name) -> Optional[str]: - for obfuscated_name, real_name in self.classes.items(): - if real_name == deobfuscated_name: - return obfuscated_name - return None -- cgit v1.2.3