diff --git a/.editorconfig b/.editorconfig index 39baa17..ab7e7f3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -19,5 +19,6 @@ ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^ ij_kotlin_packages_to_use_import_on_demand = java.util.* #Ktlint ktlint_standard_no-wildcard-imports = disabled +ktlint_standard_package-name = disabled ktlint_code_style = official ktlint_ignore_back_ticked_identifier = false \ No newline at end of file diff --git a/.gitignore b/.gitignore index af65d03..a216726 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,5 @@ hs_err_pid* !gradle-wrapper.jar !gradle-wrapper.properties -.DS_Store \ No newline at end of file +.DS_Store +codec.kt.tmp \ No newline at end of file diff --git a/scripts/packet_generate.py b/scripts/packet_generate.py new file mode 100644 index 0000000..e1bd0cd --- /dev/null +++ b/scripts/packet_generate.py @@ -0,0 +1,756 @@ +import re +import os +import requests + +kotlin_types_wrapper = { + "varint": { + "type": "Int", + "deserialize": "readVarInt()", + "serialize": "writeVarInt(%s)", + "comment": "varint" + }, + "optvarint": { + "type": "Int?", + "deserialize": "readVarInt()", + "serialize": "writeVarInt(%s)" + }, + "i8": { + "type": "Byte", + "deserialize": "readByte()", + "serialize": "writeByte(%s)" + }, + "u8": { + "type": "UByte", + "deserialize": "readUByte()", + "serialize": "writeUByte(%s)" + }, + "i16": { + "type": "Short", + "deserialize": "readShort()", + "serialize": "writeShort(%s)" + }, + "u16": { + "type": "UShort", + "deserialize": "readUShort()", + "serialize": "writeUShort(%s)" + }, + "i32": { + "type": "Int", + "deserialize": "readInt()", + "serialize": "writeInt(%s)" + }, + "i64": { + "type": "Long", + "deserialize": "readLong()", + "serialize": "writeLong(%s)" + }, + "bool": { + "type": "Boolean", + "deserialize": "readBoolean()", + "serialize": "writeBoolean(%s)" + }, + "f32": { + "type": "Float", + "deserialize": "readFloat()", + "serialize": "writeFloat(%s)" + }, + "f64": { + "type": "Double", + "deserialize": "readDouble()", + "serialize": "writeDouble(%s)" + }, + "UUID": { + "type": "UUID", + "deserialize": "readUUID()", + "serialize": "writeUUID(%s)", + "import": "import java.util.UUID" + }, + "string": { + "type": "String", + "deserialize": "readString()", + "serialize": "writeString(%s)" + }, + "entityMetadataLoop": "native", + "topBitSetTerminatedArray": "native", + "bitfield": "native", + "void": { + "type": "null", + "deserialize": "null", + "serialize": "null" + }, + "array": "native", + "buffer": { + "type": "ByteArray", + "deserialize": "readVarIntByteArray()", + "serialize": "writeVarIntByteArray(%s)" + }, + "restBuffer": { + "type": "ByteArray", + "deserialize": "readRemainingByteArray()", + "serialize": "writeRemainingByteArray(%s)" + }, + "nbt": { + "type": "ByteArray", + "deserialize": "readNBT()", + "serialize": "writeBytes(%s)" + }, + "optionalNbt": { + "type": "ByteArray", + "deserialize": "readNBT()", + "serialize": "writeBytes(%s)" + }, + "slot": "native", + "position": { + "type": "Position", + "deserialize": "readPosition()", + "serialize": "writePosition(%s)", + "import": "import io.layercraft.packetlib.types.Position" + }, + "entityMetadata": "native", + "previousMessages": "native", + "command_node": "native", + "chunkBlockEntity": "native", +} + +src = "src/main/kotlin" + +wikivg_url = "https://wiki.vg/index.php?title=Protocol" +wikivg_url_end = "&oldid=17873" +wikivg_text = requests.get(wikivg_url + wikivg_url_end).text + +data_url = "https://raw.githubusercontent.com/PrismarineJS/minecraft-data/master/data/pc/1.19.2/protocol.json" + +version = data_url.split("/")[-2] +version_underline = version.replace(".", "_") + +minecraft_codec = [] + +runs = 0 +runswitherror = 0 + + +def camel_case(s): + camel_case_str = re.sub(r"([-_])([a-zA-Z])", + lambda m: m.group(2).upper(), s) + camel_case_str = camel_case_str[0].lower() + camel_case_str[1:] + return camel_case_str + + +def codec_generate(): + handshakingText = "" + loginText = "" + statusText = "" + playText = "" + + for packet in minecraft_codec: + class_name = packet["class"] + package_string = packet["package"] + status = packet["status"] + id = packet["id"] + + direction_string = "registerClientBoundPacket" if packet[ + "direction"] == "clientbound" else "registerServerBoundPacket" + add_text = f""" .{direction_string}({id}, {package_string}.{class_name}::class, {package_string}.{class_name}) \n""" + + if status == "handshaking": + handshakingText += add_text + elif status == "login": + loginText += add_text + elif status == "status": + statusText += add_text + elif status == "play": + playText += add_text + + text = f""" + val V{version_underline}: MinecraftCodec = + MinecraftCodec.create(ProtocolVersion.V{version_underline}) + .registerPacketRegistry( + PacketState.HANDSHAKING, + MinecraftCodecRegistry.create() +{handshakingText}) + .registerPacketRegistry( + PacketState.LOGIN, + MinecraftCodecRegistry.create() +{loginText}) + .registerPacketRegistry( + PacketState.STATUS, + MinecraftCodecRegistry.create() +{statusText}) + .registerPacketRegistry( + PacketState.PLAY, + MinecraftCodecRegistry.create() +{playText}) + """ + + # write to codec.kt.tmp file + with open(f"codec.kt.tmp", "w+") as f: + f.write(text) + + +def transfer_packets(direction: str, status: str, data: dict): + packets_result = [] + + # Packet = data with "packet_" key + packets = {} + for packet in data: + if packet.startswith("packet_"): + packets[packet] = data[packet] + + packet_info = data["packet"][1] + packet_ids = [] + for ids in packet_info: + if ids["name"] == "name": + packet_ids = ids["type"][1]["mappings"] + break + + packet_names = [] + for names in packet_info: + if names["name"] == "params": + packet_names = names["type"][1]["fields"] + break + + # clear packet_info + packet_info = [] + + for id in packet_ids: + name = packet_ids[id] + packet_name = packet_names[name] + + packet_info.append({ + "id": id, + "name": name, + "packet_name": packet_name + }) + + for packet in packet_info: + id = packet["id"] + name = packet["name"] + packet_name = packet["packet_name"] + + packet_fields = packets[packet_name][1] + + packets_result.append({ + "id": id, + "name": name, + "fields": packet_fields, + "direction": direction, + "status": status + }) + + return packets_result + + +def packets(data: dict): + print("Generating packets...") + packets = [] + + for status in data: + if status == "types": + continue + + toClient = data[status]["toClient"]["types"] + toServer = data[status]["toServer"]["types"] + + clientbound = transfer_packets("clientbound", status, toClient) + serverbound = transfer_packets("serverbound", status, toServer) + + packets += clientbound + serverbound + + return packets + + handshakingText = "" + loginText = "" + statusText = "" + playText = "" + + for packet in minecraft_codec: + class_name = packet["class"] + package_string = packet["package"] + status = packet["status"] + id = packet["id"] + + direction_string = "registerClientBoundPacket" if packet[ + "direction"] == "clientbound" else "registerServerBoundPacket" + add_text = f""" .{direction_string}({id}, {package_string}.{class_name}::class, {package_string}.{class_name}) \n""" + + if status == "handshaking": + handshakingText += add_text + elif status == "login": + loginText += add_text + elif status == "status": + statusText += add_text + elif status == "play": + playText += add_text + + text = f""" + val V{version_underline}: MinecraftCodec = + MinecraftCodec.create(ProtocolVersion.V{version_underline}) + .registerPacketRegistry( + PacketState.HANDSHAKING, + MinecraftCodecRegistry.create() +{handshakingText}) + .registerPacketRegistry( + PacketState.LOGIN, + MinecraftCodecRegistry.create() +{loginText}) + .registerPacketRegistry( + PacketState.STATUS, + MinecraftCodecRegistry.create() +{statusText}) + .registerPacketRegistry( + PacketState.PLAY, + MinecraftCodecRegistry.create() +{playText}) + """ + + # write to codec.kt.tmp file + with open(f"codec.kt.tmp", "w+") as f: + f.write(text) + + +def add_run(): + global runs + runs += 1 + + +class PacketGenerator: + def __init__(self, packet_dict: dict): + self.packet = packet_dict + self.id = packet_dict['id'] + self.name = packet_dict['name'] + self.direction = packet_dict['direction'] + self.status = packet_dict["status"] + self.fields = packet_dict['fields'] + self.version = version + self.version_underline = version.replace(".", "_") + self.package = f"io.layercraft.packetlib.packets.v{self.version_underline}.{self.status}.{self.direction}" + self.package_path = f"io/layercraft/packetlib/packets/v{self.version_underline}/{self.status}/{self.direction}" + self.class_name = self.name.replace("_", " ").title().replace(" ", "") + "Packet" + wikivg_data = self.wikivg_data(self.id, self.status, self.direction) + self.wikivg_id = wikivg_data['id'] + self.wikivg_name = wikivg_data['name'] + + self.class_fields = [] + self.class_serialize = [] + self.class_deserialize = [] + self.class_var_list = [] + self.class_docs = [] + self.class_other_imports = [] + self.additional_class = "" + + def direction_interface(self): + if self.direction == "serverbound": + return "ServerBoundPacket" + elif self.direction == "clientbound": + return "ClientBoundPacket" + + def wikivg_data(self, packet_id: str, state: str, direction: str): + body_text = re.search('