From 0a314bca16f6de199c319ffb0d84a5d5c3a61387 Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 24 May 2022 20:28:08 -0500 Subject: rename code-generator to codegen --- code-generator/.gitignore | 3 - code-generator/README.md | 8 -- code-generator/lib/mappings.py | 60 ------------- code-generator/lib/packetcodegen.py | 171 ------------------------------------ code-generator/lib/utils.py | 15 ---- code-generator/lib/version.py | 80 ----------------- code-generator/main.py | 19 ---- 7 files changed, 356 deletions(-) delete mode 100644 code-generator/.gitignore delete mode 100644 code-generator/README.md delete mode 100644 code-generator/lib/mappings.py delete mode 100644 code-generator/lib/packetcodegen.py delete mode 100644 code-generator/lib/utils.py delete mode 100644 code-generator/lib/version.py delete mode 100644 code-generator/main.py (limited to 'code-generator') diff --git a/code-generator/.gitignore b/code-generator/.gitignore deleted file mode 100644 index 2ef6e1be..00000000 --- a/code-generator/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -downloads -__pycache__ -*.tmp diff --git a/code-generator/README.md b/code-generator/README.md deleted file mode 100644 index a6c60c47..00000000 --- a/code-generator/README.md +++ /dev/null @@ -1,8 +0,0 @@ -Tools for automatically generating code to help with updating Minecraft versions. - -The directory name doesn't start with `azalea-` because it's not a Rust crate. - -## Usage - -Generate packet:\ -`python main.py [packet id] [clientbound or serverbound] \[game/handshake/login/status\]` diff --git a/code-generator/lib/mappings.py b/code-generator/lib/mappings.py deleted file mode 100644 index fb3e8bda..00000000 --- a/code-generator/lib/mappings.py +++ /dev/null @@ -1,60 +0,0 @@ -class Mappings: - __slots__ = ('classes', 'fields', 'methods') - - def __init__(self, classes, fields, methods): - self.classes = classes - self.fields = fields - self.methods = methods - - @staticmethod - def parse(mappings_txt): - classes = {} - fields = {} - methods = {} - - 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_name = real_name_with_parameters.split('(')[0] - parameters = real_name_with_parameters.split('(')[1] - - if current_obfuscated_class_name not in methods: - methods[current_obfuscated_class_name] = {} - methods[current_obfuscated_class_name][ - f'{obfuscated_name}({parameters})'] = real_name - else: - # otherwise, it's a field - real_name_with_type, obfuscated_name = line.strip().split(' -> ') - real_name = real_name_with_type.split(' ')[1] - - if current_obfuscated_class_name not in fields: - fields[current_obfuscated_class_name] = {} - fields[current_obfuscated_class_name][obfuscated_name] = real_name - 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) - - 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): - 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})'] diff --git a/code-generator/lib/packetcodegen.py b/code-generator/lib/packetcodegen.py deleted file mode 100644 index 3d453a4e..00000000 --- a/code-generator/lib/packetcodegen.py +++ /dev/null @@ -1,171 +0,0 @@ -from utils import to_snake_case, to_camel_case -from mappings import Mappings -import os - - -def burger_type_to_rust_type(burger_type): - is_var = False - uses = set() - - if burger_type == 'byte': - field_type_rs = 'i8' - elif burger_type == 'short': - field_type_rs = 'i16' - elif burger_type == 'int': - field_type_rs = 'i32' - elif burger_type == 'long': - field_type_rs = 'i64' - elif burger_type == 'float': - field_type_rs = 'f32' - elif burger_type == 'double': - field_type_rs = 'f64' - - elif burger_type == 'varint': - is_var = True - field_type_rs = 'i32' - elif burger_type == 'varlong': - is_var = True - field_type_rs = 'i64' - - elif burger_type == 'boolean': - field_type_rs = 'bool' - elif burger_type == 'string': - field_type_rs = 'String' - - elif burger_type == 'chatcomponent': - field_type_rs = 'Component' - uses.add('azalea_chat::component::Component') - elif burger_type == 'identifier': - field_type_rs = 'ResourceLocation' - uses.add('azalea_core::resource_location::ResourceLocation') - elif burger_type == 'uuid': - field_type_rs = 'Uuid' - uses.add('uuid::Uuid') - elif burger_type == 'position': - field_type_rs = 'BlockPos' - uses.add('azalea_core::BlockPos') - elif burger_type == 'nbtcompound': - field_type_rs = 'azalea_nbt::Tag' - elif burger_type == 'itemstack': - field_type_rs = 'Slot' - uses.add('azalea_core::Slot') - elif burger_type == 'metadata': - field_type_rs = 'EntityMetadata' - uses.add('crate::mc_buf::EntityMetadata') - elif burger_type == 'enum': - # enums are too complicated, leave those to the user - field_type_rs = 'todo!()' - elif burger_type.endswith('[]'): - field_type_rs, is_var, uses = burger_type_to_rust_type( - burger_type[:-2]) - field_type_rs = f'Vec<{field_type_rs}>' - else: - print('Unknown field type:', burger_type) - exit() - return field_type_rs, is_var, uses - - -def write_packet_file(state, packet_name_snake_case, code): - with open(f'../azalea-protocol/src/packets/{state}/{packet_name_snake_case}.rs', 'w') as f: - f.write(code) - - -def generate(burger_packets, mappings: Mappings, target_packet_id, target_packet_direction, target_packet_state): - for packet in burger_packets.values(): - if packet['id'] != target_packet_id: - continue - - direction = packet['direction'].lower() # serverbound or clientbound - state = {'PLAY': 'game'}.get(packet['state'], packet['state'].lower()) - - if state != target_packet_state or direction != target_packet_direction: - continue - - generated_packet_code = [] - uses = set() - generated_packet_code.append( - f'#[derive(Clone, Debug, McBuf, {to_camel_case(state)}Packet)]') - uses.add(f'packet_macros::{{{to_camel_case(state)}Packet, McBuf}}') - - obfuscated_class_name = packet['class'].split('.')[0].split('$')[0] - class_name = mappings.get_class( - obfuscated_class_name).split('.')[-1].split('$')[0] - - generated_packet_code.append( - f'pub struct {to_camel_case(class_name)} {{') - - for instruction in packet.get('instructions', []): - if instruction['operation'] == 'write': - obfuscated_field_name = instruction['field'] - if '.' in obfuscated_field_name or ' ' in obfuscated_field_name or '(' in obfuscated_field_name: - generated_packet_code.append(f'// TODO: {instruction}') - continue - field_name = mappings.get_field( - obfuscated_class_name, obfuscated_field_name) - if not field_name: - generated_packet_code.append( - f'// TODO: unknown field {instruction}') - continue - - field_type = instruction['type'] - field_type_rs, is_var, instruction_uses = burger_type_to_rust_type( - field_type) - if is_var: - generated_packet_code.append('#[var]') - generated_packet_code.append( - f'pub {to_snake_case(field_name)}: {field_type_rs},') - uses.update(instruction_uses) - else: - generated_packet_code.append(f'// TODO: {instruction}') - continue - - generated_packet_code.append('}') - - if uses: - # empty line before the `use` statements - generated_packet_code.insert(0, '') - for use in uses: - generated_packet_code.insert(0, f'use {use};') - - print(generated_packet_code) - write_packet_file(state, to_snake_case(class_name), - '\n'.join(generated_packet_code)) - print() - - mod_rs_dir = f'../azalea-protocol/src/packets/{state}/mod.rs' - with open(mod_rs_dir, 'r') as f: - mod_rs = f.read().splitlines() - - pub_mod_line = f'pub mod {to_snake_case(class_name)};' - if pub_mod_line not in mod_rs: - mod_rs.insert(0, pub_mod_line) - packet_mod_rs_line = f' {hex(packet["id"])}: {to_snake_case(class_name)}::{to_camel_case(class_name)},' - - in_serverbound = False - in_clientbound = False - for i, line in enumerate(mod_rs): - if line.strip() == 'Serverbound => {': - in_serverbound = True - continue - elif line.strip() == 'Clientbound => {': - in_clientbound = True - continue - elif line.strip() in ('}', '},'): - if (in_serverbound and direction == 'serverbound') or (in_clientbound and direction == 'clientbound'): - mod_rs.insert(i, packet_mod_rs_line) - break - in_serverbound = in_clientbound = False - continue - - if line.strip() == '' or line.strip().startswith('//') or (not in_serverbound and direction == 'serverbound') or (not in_clientbound and direction == 'clientbound'): - continue - - line_packet_id_hex = line.strip().split(':')[0] - assert line_packet_id_hex.startswith('0x') - line_packet_id = int(line_packet_id_hex[2:], 16) - if line_packet_id > packet['id']: - mod_rs.insert(i, packet_mod_rs_line) - break - - with open(mod_rs_dir, 'w') as f: - f.write('\n'.join(mod_rs)) diff --git a/code-generator/lib/utils.py b/code-generator/lib/utils.py deleted file mode 100644 index 5336d574..00000000 --- a/code-generator/lib/utils.py +++ /dev/null @@ -1,15 +0,0 @@ -import urllib.request -import gzip -import json -import re -import io - - -def to_snake_case(name): - s = re.sub('([A-Z])', r'_\1', name) - return s.lower().strip('_') - - -def to_camel_case(name): - s = re.sub('_([a-z])', lambda m: m.group(1).upper(), name) - return s[0].upper() + s[1:] diff --git a/code-generator/lib/version.py b/code-generator/lib/version.py deleted file mode 100644 index f50ab461..00000000 --- a/code-generator/lib/version.py +++ /dev/null @@ -1,80 +0,0 @@ -from mappings import Mappings -import requests -import json -import os - - -def download_burger(): - print('\033[92mDownloading Burger...\033[m') - os.system( - 'cd downloads && git clone https://github.com/pokechu22/Burger && cd Burger && git pull') - - print('\033[92mInstalling dependencies...\033[m') - os.system('cd downloads/Burger && pip install six jawa') - - -def get_version_manifest(): - if not os.path.exists(f'downloads/version_manifest.json'): - print( - f'\033[92mDownloading version manifest...\033[m') - version_manifest_data = requests.get( - 'https://launchermeta.mojang.com/mc/game/version_manifest.json').json() - with open(f'downloads/version_manifest.json', 'w') as f: - json.dump(version_manifest_data, f) - else: - with open(f'downloads/version_manifest.json', 'r') as f: - version_manifest_data = json.load(f) - return version_manifest_data - - -def get_version_data(version_id: str): - if not os.path.exists(f'downloads/{version_id}.json'): - version_manifest_data = get_version_manifest() - - print( - f'\033[92mGetting data for \033[1m{version_id}..\033[m') - package_url = next( - filter(lambda v: v['id'] == version_id, version_manifest_data['versions']))['url'] - package_data = requests.get(package_url).json() - with open(f'downloads/{version_id}.json', 'w') as f: - json.dump(package_data, f) - else: - with open(f'downloads/{version_id}.json', 'r') as f: - package_data = json.load(f) - return package_data - - -def get_client_jar(version_id: str): - if not os.path.exists(f'downloads/client-{version_id}.jar'): - package_data = get_version_data(version_id) - print('\033[92mDownloading client jar...\033[m') - client_jar_url = package_data['downloads']['client']['url'] - with open('client.jar', 'wb') as f: - f.write(requests.get(client_jar_url).content) - - -def get_burger_data_for_version(version_id: str): - if not os.path.exists(f'downloads/burger-{version_id}.json'): - get_client_jar(version_id) - - os.system( - f'cd downloads/Burger && python munch.py ../client-{version_id}.jar --output ../burger-{version_id}.json' - ) - with open(f'burger-{version_id}.json', 'r') as f: - return json.load(f) - - -def get_mappings_for_version(version_id: str): - if not os.path.exists(f'downloads/mappings-{version_id}.json'): - 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(f'downloads/mappings-{version_id}.json', 'w') as f: - f.write(mappings_text) - else: - with open(f'downloads/mappings-{version_id}.json', 'r') as f: - mappings_text = f.read() - return Mappings.parse(mappings_text) diff --git a/code-generator/main.py b/code-generator/main.py deleted file mode 100644 index 54ad3946..00000000 --- a/code-generator/main.py +++ /dev/null @@ -1,19 +0,0 @@ -from .lib import version, packetcodegen -import requests -import json -import sys -import os - -mappings = version.get_mappings_for_version('1.18.2') -burger_data = version.get_burger_data_for_version('1.18.2') - -burger_packets_data = burger_data[0]['packets']['packet'] -packet_id, direction, state = int(sys.argv[1]), sys.argv[2], sys.argv[3] -print( - f'Generating code for packet id: {packet_id} with direction {direction} and state {state}') -packetcodegen.generate(burger_packets_data, mappings, - packet_id, direction, state) - -os.system('cd .. && cargo fmt') - -print('Done!') -- cgit v1.2.3