aboutsummaryrefslogtreecommitdiff
path: root/codegen/lib/code/utils.py
blob: 9bfb3504fa225e5b4c00712dba6e6b808d0b16bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# utilities specifically for codegen

from lib.utils import to_camel_case, to_snake_case, get_dir_location
from typing import Optional
import os


def burger_type_to_rust_type(
    burger_type,
    field_name: Optional[str] = None,
    instruction=None,
    class_name: Optional[str] = None,
):
    is_var = False
    uses = set()
    # extra code, like enum definitions
    extra_code = []

    should_be_signed = False
    if field_name and any(
        map(
            lambda w: w in {"x", "y", "z", "xa", "ya", "za"},
            to_snake_case(field_name).split("_"),
        )
    ):
        # coordinates are signed
        should_be_signed = True

    if burger_type == "byte":
        field_type_rs = "i8" if should_be_signed else "u8"
    elif burger_type == "short":
        field_type_rs = "i16" if should_be_signed else "u16"
    elif burger_type == "int":
        field_type_rs = "i32" if should_be_signed else "u32"
    elif burger_type == "long":
        field_type_rs = "i64" if should_be_signed else "u64"
    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" if should_be_signed else "u32"
    elif burger_type == "varlong":
        is_var = True
        field_type_rs = "i64" if should_be_signed else "u64"

    elif burger_type == "boolean":
        field_type_rs = "bool"
    elif burger_type == "string":
        field_type_rs = "String"

    elif burger_type == "chatcomponent":
        field_type_rs = "FormattedText"
        uses.add("azalea_chat::FormattedText")
    elif burger_type == "identifier":
        field_type_rs = "Identifier"
        uses.add("azalea_core::identifier::Identifier")
    elif burger_type == "uuid":
        field_type_rs = "Uuid"
        uses.add("uuid::Uuid")
    elif burger_type == "position":
        field_type_rs = "BlockPos"
        uses.add("azalea_core::position::BlockPos")
    elif burger_type == "nbtcompound":
        field_type_rs = "simdnbt::owned::NbtCompound"
    elif burger_type == "itemstack":
        field_type_rs = "Slot"
        uses.add("azalea_core::slot::Slot")
    elif burger_type == "metadata":
        field_type_rs = "EntityMetadata"
        uses.add("azalea_entity::EntityMetadata")
    elif burger_type == "bitset":
        if instruction:
            length = instruction["length"]
            field_type_rs = f'todo!("fixed bitset of length {length}")'
        else:
            field_type_rs = 'todo!("fixed bitset")'
    elif burger_type == "abstract":
        field_type_rs = "todo!()"
    elif burger_type == "interface":
        # depends on context
        field_type_rs = "todo!()"
    elif burger_type == "Iterator":
        field_type_rs = "todo!()"
    elif burger_type == "Object":
        # depends on context
        field_type_rs = "todo!()"
    elif burger_type == "enum":
        if not instruction or not class_name:
            field_type_rs = 'todo!("enum")'
        else:
            # generate the whole enum :)
            print(instruction)
            enum_field = instruction["field"]
            # enums with a.b() as the field
            if "." in enum_field:
                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])
                enum_name = enum_field.split("$")[1].split("(")[0]

                print("hm", enum_name)
            else:
                enum_name = enum_field or class_name
            print("enum_name", enum_name)
            enum_obfuscated_name = enum_name
            print("enum_obfuscated_name", enum_obfuscated_name)
            enum_variants = []
            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("")
            extra_code.append("#[derive(AzBuf, Clone, Copy, Debug)]")
            extra_code.append(f"pub enum {field_type_rs} {{")
            for index, variant in enumerate(enum_variants):
                extra_code.append(f"    {to_camel_case(variant.lower())}={index},")
            extra_code.append("}")

    elif burger_type.endswith("[]"):
        field_type_rs, is_var, uses, extra_code = burger_type_to_rust_type(
            burger_type[:-2]
        )
        field_type_rs = f"Vec<{field_type_rs}>"

        # sometimes burger gives us a slightly incorrect type
        if instruction:
            if field_type_rs == "Vec<u8>":
                field = instruction["field"]
                if field.endswith(".copy()"):
                    field = field[:-7]
                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)
        raise Exception(f"Unknown field type: {burger_type} ({class_name})")
    return field_type_rs, is_var, uses, extra_code


def write_packet_file(state, packet_module_name, code):
    with open(
        get_dir_location(
            f"../azalea-protocol/src/packets/{state}/{packet_module_name}.rs"
        ),
        "w",
    ) as f:
        f.write(code)


def fmt():
    os.system(f"cd {get_dir_location('..')} && cargo fmt")