diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2023-03-14 16:33:03 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-14 16:33:03 -0500 |
| commit | 12a9c8ce65b58f0c600fd7b9fc5d454ce228b420 (patch) | |
| tree | 9bada4164ea12fa533d413c0c7090f4779b519f1 /codegen/lib | |
| parent | b792e21d1c2b7dba04d88dba479ed451104a6514 (diff) | |
| download | azalea-drasl-12a9c8ce65b58f0c600fd7b9fc5d454ce228b420.tar.xz | |
1.19.4 (#57)
* 23w03a
* 23w04a
* 23w05a
* 23w06a
* fix
* 23w07a
mojang broke their json data generator so some stuff is missing
* didn't mean to commit that file here
* 1.19.4-pre2
* fix
* 1.19.4-pre3
* fix
* how did these packets get here
* 1.19.4-pre4
* 1.19.4-rc1
* 1.19.4-rc2
* 1.19.4-rc3
* merge main
* remove debugging code
* 1.19.4
Diffstat (limited to 'codegen/lib')
| -rw-r--r-- | codegen/lib/code/entity.py | 178 | ||||
| -rwxr-xr-x | codegen/lib/code/packet.py | 53 | ||||
| -rwxr-xr-x | codegen/lib/code/utils.py | 8 | ||||
| -rwxr-xr-x | codegen/lib/download.py | 12 | ||||
| -rwxr-xr-x | codegen/lib/extract.py | 4 | ||||
| -rwxr-xr-x | codegen/lib/mappings.py | 7 |
6 files changed, 189 insertions, 73 deletions
diff --git a/codegen/lib/code/entity.py b/codegen/lib/code/entity.py index e3e88edf..23a94c52 100644 --- a/codegen/lib/code/entity.py +++ b/codegen/lib/code/entity.py @@ -1,4 +1,6 @@ from lib.utils import to_camel_case, to_snake_case, get_dir_location, upper_first_letter +from lib.code.packet import burger_instruction_to_code +from lib.code.utils import burger_type_to_rust_type from lib.mappings import Mappings from typing import Optional import re @@ -6,35 +8,91 @@ import re METADATA_RS_DIR = get_dir_location( '../azalea-world/src/entity/metadata.rs') - -def generate_entity_metadata(burger_entity_data: dict, mappings: Mappings): - # TODO: auto generate this and use it for generating the EntityDataValue enum - metadata_types = [ - {'name': 'Byte', 'type': 'u8'}, - {'name': 'Int', 'type': 'i32', 'var': True}, - {'name': 'Long', 'type': 'i64'}, - {'name': 'Float', 'type': 'f32'}, - {'name': 'String', 'type': 'String'}, - {'name': 'FormattedText', 'type': 'FormattedText'}, - {'name': 'OptionalFormattedText', 'type': 'Option<FormattedText>'}, - {'name': 'ItemStack', 'type': 'Slot'}, - {'name': 'Boolean', 'type': 'bool'}, - {'name': 'Rotations', 'type': 'Rotations'}, - {'name': 'BlockPos', 'type': 'BlockPos'}, - {'name': 'OptionalBlockPos', 'type': 'Option<BlockPos>'}, - {'name': 'Direction', 'type': 'Direction'}, - {'name': 'OptionalUuid', 'type': 'Option<Uuid>'}, - {'name': 'BlockState', 'type': 'BlockState'}, - {'name': 'CompoundTag', 'type': 'azalea_nbt::Tag'}, - {'name': 'Particle', 'type': 'Particle'}, - {'name': 'VillagerData', 'type': 'VillagerData'}, - {'name': 'OptionalUnsignedInt', 'type': 'OptionalUnsignedInt'}, - {'name': 'Pose', 'type': 'Pose'}, - {'name': 'CatVariant', 'type': 'azalea_registry::CatVariant'}, - {'name': 'FrogVariant', 'type': 'azalea_registry::FrogVariant'}, - {'name': 'GlobalPos', 'type': 'GlobalPos'}, - {'name': 'PaintingVariant', 'type': 'azalea_registry::PaintingVariant'} - ] +DATA_RS_DIR = get_dir_location( + '../azalea-world/src/entity/data.rs') + +def generate_metadata_names(burger_dataserializers: dict, mappings: Mappings): + serializer_names = [None] * len(burger_dataserializers) + for burger_serializer in burger_dataserializers.values(): + print(burger_serializer) + + # 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() + + if mojmap_name == 'component': + mojmap_name = 'formatted_text' + elif mojmap_name == 'optional_component': + mojmap_name = 'optional_formatted_text' + + serializer_names[burger_serializer['id']] = upper_first_letter(to_camel_case(mojmap_name)) + return serializer_names + +def parse_metadata_types_from_code(): + with open(DATA_RS_DIR, 'r') as f: + lines = f.read().splitlines() + + data = [] + + in_enum = False + for line in lines: + if line == 'pub enum EntityDataValue {': + in_enum = True + elif line == '}': + in_enum = False + elif in_enum: + line = line.strip() + if line.startswith('//'): continue + name, type = line.rstrip('),').split('(') + is_var = False + if type.startswith('#[var] '): + is_var = True + type = type[len('#[var] '):] + data.append({ + 'name': name, + 'type': type, + 'var': is_var + }) + print(data) + return data + +def generate_entity_metadata(burger_entities_data: dict, mappings: Mappings): + burger_entity_metadata = burger_entities_data['entity'] + + new_metadata_names = generate_metadata_names(burger_entities_data['dataserializers'], mappings) + parsed_metadata_types = parse_metadata_types_from_code() + + parsed_metadata_names = [] + for t in parsed_metadata_types: + parsed_metadata_names.append(t['name']) + + with open(DATA_RS_DIR, 'r') as f: + lines = f.read().splitlines() + # add the metadata names that weren't there before to the end of the enum. + # this technically might cause them to be in the wrong order but i decided + # making it correct while preserving comments was too annoying so i didn't + added_metadata_names = [] + for n in new_metadata_names: + if n not in parsed_metadata_names: + added_metadata_names.append(n) + if added_metadata_names != []: + in_enum = False + for i, line in enumerate(list(lines)): + if line == 'pub enum EntityDataValue {': + in_enum = True + elif in_enum and line == '}': + in_enum = False + for n in added_metadata_names: + lines.insert(i, f'{n}(TODO),') + i += 1 + print(lines) + with open(DATA_RS_DIR, 'w') as f: + f.write('\n'.join(lines)) + print('Expected metadata types:\n' + '\n'.join(new_metadata_names)) + print('Updated metadata types in azalea-world/src/entity/data.rs, go make sure they\'re correct and then press enter') + input() + + metadata_types = parse_metadata_types_from_code() code = [] code.append('''#![allow(clippy::single_match)] @@ -42,10 +100,12 @@ def generate_entity_metadata(burger_entity_data: dict, mappings: Mappings): // This file is generated from codegen/lib/code/entity.py. // Don't change it manually! -use super::{EntityDataItem, EntityDataValue, OptionalUnsignedInt, Pose, Rotations, VillagerData}; -use azalea_block::BlockState; +use super::{ + EntityDataItem, EntityDataValue, OptionalUnsignedInt, Pose, Quaternion, Rotations, + SnifferState, VillagerData +}; use azalea_chat::FormattedText; -use azalea_core::{BlockPos, Direction, Particle, Slot}; +use azalea_core::{BlockPos, Direction, Particle, Slot, Vec3}; use bevy_ecs::{bundle::Bundle, component::Component}; use derive_more::{Deref, DerefMut}; use thiserror::Error; @@ -74,9 +134,9 @@ impl From<EntityDataValue> for UpdateMetadataError { # build the duplicate_field_names set previous_field_names = set() duplicate_field_names = set() - for entity_id in burger_entity_data.keys(): + 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_data, mappings).values(): + for field_name_or_bitfield in get_entity_metadata_names(entity_id, burger_entity_metadata, mappings).values(): if isinstance(field_name_or_bitfield, str): if field_name_or_bitfield in previous_field_names: duplicate_field_names.add(field_name_or_bitfield) @@ -99,8 +159,8 @@ impl From<EntityDataValue> for UpdateMetadataError { raise Exception(f'{name} should only exist once') # and now figure out what to rename them to - for entity_id in burger_entity_data.keys(): - for index, field_name_or_bitfield in get_entity_metadata_names(entity_id, burger_entity_data, mappings).items(): + 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).items(): if isinstance(field_name_or_bitfield, str): new_field_name = field_name_or_bitfield if new_field_name == 'type': @@ -129,14 +189,14 @@ impl From<EntityDataValue> for UpdateMetadataError { return field_name_map[entity_ids_for_all_field_names_or_bitfields[index]][name] return name - parents = get_entity_parents(entity_id, burger_entity_data) + 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_data, mappings).items(): + for index, name_or_bitfield in get_entity_metadata_names(parent_id, burger_entity_metadata, mappings).items(): assert index == len(all_field_names_or_bitfields) all_field_names_or_bitfields.append(name_or_bitfield) entity_ids_for_all_field_names_or_bitfields.append(parent_id) entity_metadatas.extend(get_entity_metadata( - parent_id, burger_entity_data)) + parent_id, burger_entity_metadata)) parent_id = parents[1] if len(parents) > 1 else None # now add all the fields/component structs @@ -264,7 +324,7 @@ impl From<EntityDataValue> 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_data, mappings).items(): + for index, name_or_bitfield in get_entity_metadata_names(entity_id, burger_entity_metadata, mappings).items(): if isinstance(name_or_bitfield, str): name_or_bitfield = maybe_rename_field( name_or_bitfield, index) @@ -310,7 +370,7 @@ impl From<EntityDataValue> for UpdateMetadataError { # if it has a parent, put it (do recursion) # parent: AbstractCreatureBundle { ... }, this_entity_parent_ids = get_entity_parents( - this_entity_id, burger_entity_data) + this_entity_id, burger_entity_metadata) this_entity_parent_id = this_entity_parent_ids[1] if len( this_entity_parent_ids) > 1 else None if this_entity_parent_id: @@ -322,7 +382,7 @@ impl From<EntityDataValue> for UpdateMetadataError { code.append( ' },') - for index, name_or_bitfield in get_entity_metadata_names(this_entity_id, burger_entity_data, mappings).items(): + for index, name_or_bitfield in get_entity_metadata_names(this_entity_id, burger_entity_metadata, mappings).items(): default = next(filter(lambda i: i['index'] == index, entity_metadatas)).get( 'default', 'Default::default()') if isinstance(name_or_bitfield, str): @@ -367,11 +427,21 @@ impl From<EntityDataValue> for UpdateMetadataError { elif type_name == 'ItemStack': default = f'Slot::Present({default})' if default != 'Empty' else 'Slot::Empty' elif type_name == 'BlockState': - default = f'{default}' if default != 'Empty' else 'BlockState::AIR' + default = f'{default}' if default != 'Empty' else 'azalea_block::BlockState::AIR' + elif type_name == 'OptionalBlockState': + default = f'{default}' if default != 'Empty' else 'azalea_block::BlockState::AIR' elif type_name == 'OptionalFormattedText': default = f'Some({default})' if default != 'Empty' else 'None' elif type_name == 'CompoundTag': default = f'azalea_nbt::Tag::Compound({default})' if default != 'Empty' else 'azalea_nbt::Tag::Compound(Default::default())' + elif type_name == 'Quaternion': + default = f'Quaternion {{ x: {float(default["x"])}, y: {float(default["y"])}, z: {float(default["z"])}, w: {float(default["w"])} }}' + elif type_name == 'Vector3': + default = f'Vec3 {{ x: {float(default["x"])}, y: {float(default["y"])}, z: {float(default["z"])} }}' + elif type_name == 'Byte': + # in 1.19.4 TextOpacity is a -1 by default + if default < 0: + default += 128 if name in single_use_imported_types: code.append(f' {name}: {default},') else: @@ -395,7 +465,7 @@ impl From<EntityDataValue> for UpdateMetadataError { code.append('') # parent_field_name = None - for entity_id in burger_entity_data: + for entity_id in burger_entity_metadata: new_entity(entity_id) # and now make the main apply_metadata @@ -419,7 +489,7 @@ impl From<EntityDataValue> for UpdateMetadataError { items: Vec<EntityDataItem>, ) -> Result<(), UpdateMetadataError> {{ match entity_kind {{''') - for entity_id in burger_entity_data: + for entity_id in burger_entity_metadata: if entity_id.startswith('~'): # not actually an entity continue @@ -446,7 +516,7 @@ impl From<EntityDataValue> for UpdateMetadataError { code.append( 'pub fn apply_default_metadata(entity: &mut bevy_ecs::system::EntityCommands, kind: azalea_registry::EntityKind) {') code.append(' match kind {') - for entity_id in burger_entity_data: + for entity_id in burger_entity_metadata: if entity_id.startswith('~'): # not actually an entity continue @@ -464,22 +534,22 @@ impl From<EntityDataValue> for UpdateMetadataError { f.write('\n'.join(code)) -def get_entity_parents(entity_id: str, burger_entity_data: dict): +def get_entity_parents(entity_id: str, burger_entity_metadata: dict): parents = [] while entity_id: parents.append(entity_id) - entity_id = get_entity_parent(entity_id, burger_entity_data) + entity_id = get_entity_parent(entity_id, burger_entity_metadata) return parents -def get_entity_parent(entity_id: str, burger_entity_data: dict): - entity_metadata = burger_entity_data[entity_id]['metadata'] +def get_entity_parent(entity_id: str, burger_entity_metadata: dict): + entity_metadata = burger_entity_metadata[entity_id]['metadata'] first_metadata = entity_metadata[0] return first_metadata.get('entity') -def get_entity_metadata(entity_id: str, burger_entity_data: dict): - entity_metadata = burger_entity_data[entity_id]['metadata'] +def get_entity_metadata(entity_id: str, burger_entity_metadata: dict): + entity_metadata = burger_entity_metadata[entity_id]['metadata'] entity_useful_metadata = [] for metadata_item in entity_metadata: if 'data' in metadata_item: @@ -494,8 +564,8 @@ def get_entity_metadata(entity_id: str, burger_entity_data: dict): # returns a dict of {index: (name or bitfield)} -def get_entity_metadata_names(entity_id: str, burger_entity_data: dict, mappings: Mappings): - entity_metadata = burger_entity_data[entity_id]['metadata'] +def get_entity_metadata_names(entity_id: str, burger_entity_metadata: dict, mappings: Mappings): + entity_metadata = burger_entity_metadata[entity_id]['metadata'] mapped_metadata_names = {} for metadata_item in entity_metadata: diff --git a/codegen/lib/code/packet.py b/codegen/lib/code/packet.py index 48572aed..58e50136 100755 --- a/codegen/lib/code/packet.py +++ b/codegen/lib/code/packet.py @@ -222,7 +222,7 @@ def get_packets(direction: str, state: str): return packet_ids, packet_class_names -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]) -> Optional[int]: +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], known_variable_types={}) -> Optional[int]: ''' Generate a field for an instruction, returns the number of instructions to skip (if any). ''' @@ -237,12 +237,14 @@ def burger_instruction_to_code(instructions: list[dict], index: int, generated_p field_type_rs = None field_comment = None + print('instruction', instruction) + # iterators if instruction['operation'] == 'write' and instruction['field'].endswith('.size()') and next_instruction and next_instruction['type'] == 'Iterator' and next_next_instruction and next_next_instruction['operation'] == 'loop': - field_obfuscated_name = instruction['field'].split('.')[ + obfuscated_field_name = instruction['field'].split('.')[ 0] field_name = mappings.get_field( - obfuscated_class_name, field_obfuscated_name) + obfuscated_class_name, obfuscated_field_name) # figure out what kind of iterator it is loop_instructions = next_next_instruction['instructions'] @@ -282,10 +284,21 @@ def burger_instruction_to_code(instructions: list[dict], index: int, generated_p # Option<T> elif instruction['operation'] == 'write' and (instruction['field'].endswith('.isPresent()') or instruction['field'].endswith(' != null')) and next_instruction and (next_instruction.get('condition', '').endswith('.isPresent()') or next_instruction.get('condition', '').endswith(' != null')): - field_obfuscated_name = instruction['field'].split('.')[ + print('ok is option') + obfuscated_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, field_obfuscated_name) + obfuscated_class_name, obfuscated_field_name) + + if field_name is None: field_name = obfuscated_field_name.split('/')[-1] + if '<' in field_name: + field_name = 'value' + condition_instructions = next_instruction['instructions'] condition_types_rs = [] @@ -312,23 +325,34 @@ def burger_instruction_to_code(instructions: list[dict], index: int, generated_p field_type_rs, is_var, instruction_uses, instruction_extra_code = burger_type_to_rust_type( field_type, field_name, instruction, mappings, obfuscated_class_name) - if '.' in obfuscated_field_name or ' ' in obfuscated_field_name or '(' in 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 + field_name = obfuscated_field_name + + 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, obfuscated_class_name) + obfuscated_field_name, mappings, 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: + # 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] uses.update(instruction_uses) extra_code.extend(instruction_extra_code) if not field_name: generated_packet_code.append( f'// TODO: unknown field {instruction}') - return + return skip if is_var: generated_packet_code.append('#[var]') @@ -340,7 +364,7 @@ def burger_instruction_to_code(instructions: list[dict], index: int, generated_p return skip -def burger_field_to_type(field, mappings: Mappings, obfuscated_class_name: str) -> tuple[Optional[str], str, Optional[str]]: +def burger_field_to_type(field, mappings: Mappings, obfuscated_class_name: str, known_variable_types={}) -> tuple[Optional[str], str, Optional[str]]: ''' Returns field_type_rs, obfuscated_field_name, field_comment ''' @@ -353,9 +377,12 @@ def burger_field_to_type(field, mappings: Mappings, obfuscated_class_name: str) print('field', field) obfuscated_first = field.split('.')[0] obfuscated_second = field.split('.')[1].split('(')[0] - first = mappings.get_field(obfuscated_class_name, obfuscated_first) - first_type = mappings.get_field_type( - obfuscated_class_name, obfuscated_first) + # first = mappings.get_field(obfuscated_class_name, obfuscated_first) + if obfuscated_first in known_variable_types: + first_type = known_variable_types[obfuscated_first] + else: + first_type = mappings.get_field_type( + obfuscated_class_name, obfuscated_first) first_obfuscated_class_name: Optional[str] = mappings.get_class_from_deobfuscated_name( first_type) if first_obfuscated_class_name: @@ -368,6 +395,8 @@ def burger_field_to_type(field, mappings: Mappings, obfuscated_class_name: str) else: second = obfuscated_second first_type_short = first_type.split('.')[-1] + if second in {'byteValue'}: + return (first_type_short, obfuscated_first, None) return (first_type_short, obfuscated_first, f'TODO: Does {first_type_short}::{second}, may not be implemented') return None, field, None diff --git a/codegen/lib/code/utils.py b/codegen/lib/code/utils.py index fe4aca7f..aaa166ff 100755 --- a/codegen/lib/code/utils.py +++ b/codegen/lib/code/utils.py @@ -97,8 +97,12 @@ def burger_type_to_rust_type(burger_type, field_name: Optional[str] = None, inst print('hm', enum_name) else: - enum_name = mappings.get_field_type( - obfuscated_class_name, enum_field) + try: + enum_name = mappings.get_field_type( + obfuscated_class_name, enum_field) + except: + enum_name = mappings.get_class(obfuscated_class_name) + print(f'failed getting {obfuscated_class_name}.{enum_field} but continuing with {enum_name} anyways') print('enum_name', enum_name) enum_obfuscated_name = mappings.get_class_from_deobfuscated_name( enum_name) diff --git a/codegen/lib/download.py b/codegen/lib/download.py index aec3a648..1b22fbcc 100755 --- a/codegen/lib/download.py +++ b/codegen/lib/download.py @@ -177,7 +177,11 @@ def clear_version_cache(): if os.path.exists(get_dir_location(f'downloads/{file}')): os.remove(get_dir_location(f'downloads/{file}')) - os.system( - f'cd {get_dir_location("downloads/Burger")} && git pull') - os.system( - f'cd {get_dir_location("downloads/pixlyzer")} && git pull') + burger_path = get_dir_location("downloads/Burger") + if os.path.exists(burger_path): + os.system( + f'cd {burger_path} && git pull') + pixlyzer_path = get_dir_location('downloads/pixlyzer') + if os.path.exists(pixlyzer_path): + os.system( + f'cd {pixlyzer_path} && git pull')
\ No newline at end of file diff --git a/codegen/lib/extract.py b/codegen/lib/extract.py index 6bf2d57f..f7c78d29 100755 --- a/codegen/lib/extract.py +++ b/codegen/lib/extract.py @@ -117,7 +117,7 @@ def get_pixlyzer_data(version_id: str, category: str): # TODO: right now this False is hard-coded, it should retry with this # enabled if # initially getting the data fails - if False or (os.path.exists(target_dir) and not os.path.exists(f'{target_dir}/{category}.min.json')): + if True or (os.path.exists(target_dir) and not os.path.exists(f'{target_dir}/{category}.min.json')): print('Downloading', category, 'from pixlyzer-data.') data = requests.get(f'https://gitlab.com/Bixilon/pixlyzer-data/-/raw/master/version/{version_id}/{category}.min.json?inline=false').text try: @@ -219,6 +219,8 @@ def get_pixlyzer_data(version_id: str, category: str): pom_xml = open(f'{pixlyzer_dir}/pom.xml', 'r').read() pom_xml = re.sub( '<dependencies>.*?</dependencies>', f'<dependencies>{pom_xml_dependencies}</dependencies>', pom_xml, flags=re.DOTALL) + pom_xml = re.sub( + '<minecraft\.version>.*?</minecraft\.version>', f'<minecraft.version>{version_id}</minecraft.version>', pom_xml, flags=re.DOTALL) open(f'{pixlyzer_dir}/pom.xml', 'w').write(pom_xml) # compile diff --git a/codegen/lib/mappings.py b/codegen/lib/mappings.py index 7cbb863a..22624fac 100755 --- a/codegen/lib/mappings.py +++ b/codegen/lib/mappings.py @@ -68,6 +68,13 @@ class Mappings: 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): |
