diff options
28 files changed, 885 insertions, 161 deletions
@@ -1,3 +1,5 @@ # Godot 4+ specific ignores .godot/ /android/ + +/data/*.grf diff --git a/character_information.gd b/character_information.gd deleted file mode 100644 index 101f74f..0000000 --- a/character_information.gd +++ /dev/null @@ -1,32 +0,0 @@ -class_name CharacterInformation - - -## Byte Length: 4 -var character_id: int - -## Byte Length: 8 -var experience: int - -## Byte Length: 4 -var money: int - - -static func from_bytes(bytes: PackedByteArray): - var info = CharacterInformation.new() - - info.character_id = bytes.decode_u32(0) - info.experience = bytes.decode_s64(4) - info.money = bytes.decode_s32(12) - - return info - - -static func array_from_bytes(bytes: PackedByteArray) -> Array: - var array = [] - - var offset = 0 - while offset < bytes.size(): - array.append(from_bytes(bytes.slice(offset))) - offset += 175 - - return array diff --git a/constants.gd b/constants.gd index 7e1a37d..a275dc5 100644 --- a/constants.gd +++ b/constants.gd @@ -1,8 +1,14 @@ class_name Constants + enum Gender { Female, Male, Both, Server, } + +static var Packet_DB = { + LoginServerLoginPacket.header: LoginServerLoginPacket, + LoginServerLoginSuccessPacket.new().header: LoginServerLoginSuccessPacket, +} diff --git a/data/.gitkeep b/data/.gitkeep new file mode 100644 index 0000000..c059196 --- /dev/null +++ b/data/.gitkeep @@ -0,0 +1 @@ +put your grf files in this directory before building and running :) diff --git a/extractor/grf.gd b/extractor/grf.gd new file mode 100644 index 0000000..8cb03c1 --- /dev/null +++ b/extractor/grf.gd @@ -0,0 +1,4 @@ +class_name GRF + +class Header: + var signature: int diff --git a/login.gd b/login.gd new file mode 100644 index 0000000..97daf51 --- /dev/null +++ b/login.gd @@ -0,0 +1,83 @@ +extends Control + + +var account_information: LoginServerLoginSuccessPacket +var character_server_information: Array + +var current_character_information: CharacterInformation + + +func _ready() -> void: + switch_screen(%Login) + + +func switch_screen(screen: Node): + for node in get_children(): + node.visible = false + + screen.visible = true + + +func _on_login_pressed() -> void: + account_information = Network.login_server.login(%Username.text, %Password.text) + character_server_information = account_information.character_server_information + + for node in %CharacterServerList.get_children(): + node.queue_free() + + for info: CharacterServerInformation in character_server_information: + var select_character_server = Button.new() + select_character_server.text = info.server_name + select_character_server.pressed.connect(func(): + _on_character_server_login_pressed(info) + ) + %CharacterServerList.add_child(select_character_server) + + switch_screen(%CharacterServer) + + +func _on_character_server_login_pressed(character_server_info: CharacterServerInformation) -> void: + Network.character_server = CharacterServer.new( + character_server_info.get_server_ip(), + character_server_info.server_port + ) + + var _response = Network.character_server.login( + account_information.account_id, + account_information.login_id1, + account_information.login_id2, + account_information.gender + ) + print(inst_to_dict(_response)) + + var response = Network.character_server.request_character_list() + + for slot_idx in response.character_information.size(): + var info: CharacterInformation = response.character_information[slot_idx] + var character = Button.new() + character.text = info.name + character.pressed.connect(func(): + current_character_information = info + _on_character_selected_pressed(slot_idx) + ) + %CharacterList.add_child(character) + + switch_screen(%CharacterSelection) + + +func _on_character_selected_pressed(slot_idx: int): + var success = Network.character_server.select_character(slot_idx) + + Network.map_server = MapServer.new( + success.get_map_server_ip(), + success.map_server_port + ) + + var _response = Network.map_server.login( + account_information.account_id, + current_character_information.character_id, + account_information.login_id1, + account_information.gender + ) + + # TODO: switch to game :) diff --git a/login.tscn b/login.tscn new file mode 100644 index 0000000..25b8a6d --- /dev/null +++ b/login.tscn @@ -0,0 +1,73 @@ +[gd_scene load_steps=2 format=3 uid="uid://dser74lcd3a4g"] + +[ext_resource type="Script" path="res://login.gd" id="1_1m5cv"] + +[node name="Login" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_1m5cv") + +[node name="Login" type="CenterContainer" parent="."] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="Login"] +layout_mode = 2 + +[node name="Username" type="LineEdit" parent="Login/VBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +text = "dweipert" +placeholder_text = "Username" + +[node name="Password" type="LineEdit" parent="Login/VBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +text = "ragnarok" +placeholder_text = "Password" + +[node name="Login" type="Button" parent="Login/VBoxContainer"] +layout_mode = 2 +text = "Login" + +[node name="CharacterServer" type="CenterContainer" parent="."] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="CharacterServerList" type="VBoxContainer" parent="CharacterServer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="CharacterSelection" type="CenterContainer" parent="."] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="CharacterList" type="GridContainer" parent="CharacterSelection"] +unique_name_in_owner = true +layout_mode = 2 +columns = 5 + +[connection signal="pressed" from="Login/VBoxContainer/Login" to="." method="_on_login_pressed"] diff --git a/main.gd b/main.gd deleted file mode 100644 index 0396244..0000000 --- a/main.gd +++ /dev/null @@ -1,94 +0,0 @@ -extends Node2D - - -class LoginServer: - var host: String - var port: int = 6900 - -class CharacterServer: - var host: String - var port: int = 6121 - -class MapServer: - var host: String - var port: int = 5121 - -var stream: PacketPeerStream -var peer: StreamPeerTCP - -var stream2: PacketPeerStream -var peer2: StreamPeerTCP - -var stream3: PacketPeerStream -var peer3: StreamPeerTCP - - -func _ready() -> void: - stream = PacketPeerStream.new() - peer = StreamPeerTCP.new() - stream.stream_peer = peer - - peer.connect_to_host("127.0.0.1", 6900) - while peer.get_status() != StreamPeerTCP.STATUS_CONNECTED: - print("status: ", peer.get_status(), " polling: ", peer.poll()) - print("working status: ", peer.get_status()) - - var login_server_login_packet = LoginServerLoginPacket.new() - login_server_login_packet.username = "dweipert" - login_server_login_packet.password = "ragnarok" - - peer.put_data(login_server_login_packet.to_bytes()) - - var p = LoginServerLoginSuccessPacket.from_bytes_via_peer(peer) - var character_server_information: CharacterServerInformation = p.character_server_information[0] - print(inst_to_dict(p), inst_to_dict(character_server_information)) - - - stream2 = PacketPeerStream.new() - peer2 = StreamPeerTCP.new() - stream2.stream_peer = peer2 - - peer2.connect_to_host(character_server_information.get_server_ip(), character_server_information.server_port) - while peer2.get_status() != StreamPeerTCP.STATUS_CONNECTED: - print("status: ", peer2.get_status(), " polling: ", peer2.poll()) - print("working status: ", peer2.get_status()) - - var character_server_login_packet = CharacterServerLoginPacket.new() - character_server_login_packet.account_id = p.account_id - character_server_login_packet.login_id1 = p.login_id1 - character_server_login_packet.login_id2 = p.login_id2 - character_server_login_packet.gender = p.gender - - print(inst_to_dict(character_server_login_packet)) - peer2.put_data(character_server_login_packet.to_bytes()) - - var c = CharacterServerLoginSuccessPacket.from_bytes_via_peer(peer2) - print(inst_to_dict(c)) - - # get character list first - var request_character_list_packet = RequestCharacterListPacket.new() - peer2.put_data(request_character_list_packet.to_bytes()) - - var rcl = RequestCharacterListSuccessPacket.from_bytes_via_peer(peer2) - var character_information: CharacterInformation = rcl.character_information[0] - print(inst_to_dict(rcl), inst_to_dict(character_information)) - - - #stream3 = PacketPeerStream.new() - #peer3 = StreamPeerTCP.new() - #stream3.stream_peer = peer3 - # - #peer3.connect_to_host(character_server_information.get_server_ip(), character_server_information.server_port) - #while peer3.get_status() != StreamPeerTCP.STATUS_CONNECTED: - #print("status: ", peer3.get_status(), " polling: ", peer3.poll()) - #print("working status: ", peer3.get_status()) - # - #var map_server_login_packet = MapServerLoginPacket.new() - #map_server_login_packet.account_id = p.account_id - #map_server_login_packet.character_id = character_information.character_id - #map_server_login_packet.login_id1 = p.login_id1 - #map_server_login_packet.gender = p.gender - # - #peer3.put_data(map_server_login_packet.to_bytes()) - #peer3.get_data(1) - #peer3.get_data(peer3.get_available_bytes()) diff --git a/main.tscn b/main.tscn deleted file mode 100644 index 8f545ff..0000000 --- a/main.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://t8bawt4nmflc"] - -[ext_resource type="Script" path="res://main.gd" id="1_py1bq"] - -[node name="Main" type="Node2D"] -script = ExtResource("1_py1bq") diff --git a/network/character_server.gd b/network/character_server.gd new file mode 100644 index 0000000..e710639 --- /dev/null +++ b/network/character_server.gd @@ -0,0 +1,54 @@ +class_name CharacterServer + + +var host: String +var port: int = 6121 +var stream: PacketPeerStream = PacketPeerStream.new() +var peer: StreamPeerTCP = StreamPeerTCP.new() + + +@warning_ignore("shadowed_variable") +func _init(host: String, port: int = 6121) -> void: + self.host = host + self.port = port + + stream.stream_peer = peer + peer.connect_to_host(host, port) + peer.poll() + + +func login(account_id: int, login_id1: int, login_id2: int, gender: Constants.Gender) -> CharacterServerLoginSuccessPacket: + var character_server_login_packet = CharacterServerLoginPacket.new() + character_server_login_packet.account_id = account_id + character_server_login_packet.login_id1 = login_id1 + character_server_login_packet.login_id2 = login_id2 + character_server_login_packet.gender = gender + + peer.put_data(character_server_login_packet.to_bytes()) + + var success = CharacterServerLoginSuccessPacket.from_bytes_via_peer(peer) + print(inst_to_dict(success)) + + return success + + +func request_character_list() -> RequestCharacterListSuccessPacket: + var request_character_list_packet = RequestCharacterListPacket.new() + peer.put_data(request_character_list_packet.to_bytes()) + + var success = RequestCharacterListSuccessPacket.from_bytes_via_peer(peer) + print(inst_to_dict(success)) + + return success + + +func select_character(slot: int) -> CharacterSelectionSuccessPacket: + var select_character_packet = SelectCharacterPacket.new() + select_character_packet.selected_slot = slot + + peer.put_data(select_character_packet.to_bytes()) + + var success = CharacterSelectionSuccessPacket.from_bytes_via_peer(peer) + print(inst_to_dict(success)) + + return success diff --git a/network/login_server.gd b/network/login_server.gd new file mode 100644 index 0000000..bb7c093 --- /dev/null +++ b/network/login_server.gd @@ -0,0 +1,31 @@ +class_name LoginServer + + +var host: String +var port: int = 6900 +var stream: PacketPeerStream = PacketPeerStream.new() +var peer: StreamPeerTCP = StreamPeerTCP.new() + + +@warning_ignore("shadowed_variable") +func _init(host: String, port: int = 6900) -> void: + self.host = host + self.port = port + + stream.stream_peer = peer + peer.connect_to_host(host, port) + peer.poll() + + +func login(username: String, password: String) -> LoginServerLoginSuccessPacket: + var login_server_login_packet = LoginServerLoginPacket.new() + login_server_login_packet.username = username + login_server_login_packet.password = password + + peer.put_data(login_server_login_packet.to_bytes()) + + var success = LoginServerLoginSuccessPacket.from_bytes_via_peer(peer) + var character_server_information: CharacterServerInformation = success.character_server_information[0] + print(inst_to_dict(success), inst_to_dict(character_server_information)) + + return success diff --git a/network/map_server.gd b/network/map_server.gd new file mode 100644 index 0000000..af46c6c --- /dev/null +++ b/network/map_server.gd @@ -0,0 +1,35 @@ +class_name MapServer + + +var host: String +var port: int = 5121 +var stream: PacketPeerStream = PacketPeerStream.new() +var peer: StreamPeerTCP = StreamPeerTCP.new() + + +@warning_ignore("shadowed_variable") +func _init(host: String, port: int = 5121) -> void: + self.host = host + self.port = port + + stream.stream_peer = peer + peer.connect_to_host(host, port) + peer.poll() + + +func login(account_id: int, character_id: int, login_id1: int, gender: Constants.Gender) -> MapServerLoginSuccessPacket: + var map_server_login_packet = MapServerLoginPacket.new() + map_server_login_packet.account_id = account_id + map_server_login_packet.character_id = character_id + map_server_login_packet.login_id1 = login_id1 + map_server_login_packet.gender = gender + + peer.put_data(map_server_login_packet.to_bytes()) + + peer.get_data(6) # in-between packet + peer.get_data(4) # in-between packet + + var success = MapServerLoginSuccessPacket.from_bytes_via_peer(peer) + print(inst_to_dict(success)) + + return success diff --git a/network/network.gd b/network/network.gd new file mode 100644 index 0000000..25607a2 --- /dev/null +++ b/network/network.gd @@ -0,0 +1,115 @@ +extends Node + + +#class LoginServer: + #var host: String + #var port: int = 6900 + #var stream: PacketPeerStream = PacketPeerStream.new() + #var peer: StreamPeerTCP = StreamPeerTCP.new() + # + #func _init(host: String, port: int = 6900) -> void: + #self.host = host + #self.port = port + # + #stream.stream_peer = peer + #peer.connect_to_host(host, port) + #peer.poll() + +#class CharacterServer: + #var host: String + #var port: int = 6121 + #var stream: PacketPeerStream + #var peer: StreamPeerTCP + +#class MapServer: + #var host: String + #var port: int = 5121 + #var stream: PacketPeerStream + #var peer: StreamPeerTCP + +static var login_server: LoginServer +static var character_server: CharacterServer +static var map_server: MapServer + + +func _ready() -> void: + login_server = LoginServer.new("127.0.0.1") + + #var login_server_login_packet = LoginServerLoginPacket.new() + #login_server_login_packet.username = "dweipert" + #login_server_login_packet.password = "ragnarok" + # + #peer.put_data(login_server_login_packet.to_bytes()) + # + #var p = LoginServerLoginSuccessPacket.from_bytes_via_peer(peer) + #var character_server_information: CharacterServerInformation = p.character_server_information[0] + #print(inst_to_dict(p), inst_to_dict(character_server_information)) + # + # + #stream2 = PacketPeerStream.new() + #peer2 = StreamPeerTCP.new() + #stream2.stream_peer = peer2 + # + #peer2.connect_to_host(character_server_information.get_server_ip(), character_server_information.server_port) + #while peer2.get_status() != StreamPeerTCP.STATUS_CONNECTED: + #print("status: ", peer2.get_status(), " polling: ", peer2.poll()) + #print("working status: ", peer2.get_status()) + # + #var character_server_login_packet = CharacterServerLoginPacket.new() + #character_server_login_packet.account_id = p.account_id + #character_server_login_packet.login_id1 = p.login_id1 + #character_server_login_packet.login_id2 = p.login_id2 + #character_server_login_packet.gender = p.gender + # + #print(inst_to_dict(character_server_login_packet)) + #peer2.put_data(character_server_login_packet.to_bytes()) + # + #var c = CharacterServerLoginSuccessPacket.from_bytes_via_peer(peer2) + #print(inst_to_dict(c)) + # + ## get character list + #var request_character_list_packet = RequestCharacterListPacket.new() + #peer2.put_data(request_character_list_packet.to_bytes()) + # + #var rcl = RequestCharacterListSuccessPacket.from_bytes_via_peer(peer2) + #var character_information: CharacterInformation = rcl.character_information[0] + #print(inst_to_dict(rcl), inst_to_dict(character_information)) + # + ## select character + #var select_character_packet = SelectCharacterPacket.new() + #select_character_packet.selected_slot = 0 + #peer2.put_data(select_character_packet.to_bytes()) + # + #var css = CharacterSelectionSuccessPacket.from_bytes_via_peer(peer2) + #print(inst_to_dict(css)) + # + # + #stream3 = PacketPeerStream.new() + #peer3 = StreamPeerTCP.new() + #stream3.stream_peer = peer3 + # + #peer3.connect_to_host(css.get_map_server_ip(), css.map_server_port) + #while peer3.get_status() != StreamPeerTCP.STATUS_CONNECTED: + #print("status: ", peer3.get_status(), " polling: ", peer3.poll()) + #print("working status: ", peer3.get_status()) + # + #var map_server_login_packet = MapServerLoginPacket.new() + #map_server_login_packet.account_id = p.account_id + #map_server_login_packet.character_id = css.character_id + #map_server_login_packet.login_id1 = p.login_id1 + #map_server_login_packet.gender = p.gender + # + #peer3.put_data(map_server_login_packet.to_bytes()) + # + #print(peer3.get_data(6))#in-between packet + #print(peer3.get_data(4))#in-between packet + # + #var msls = MapServerLoginSuccessPacket.from_bytes_via_peer(peer3) + #print(inst_to_dict(msls)) + #print(msls.get_position()) + + var file = FileAccess.open("res://data/data.grf", FileAccess.READ) + var signature = file.get_buffer("Master of Magic".length()) + print(signature.get_string_from_utf8()) + print(file.get_16()) + print(file.get_32()) diff --git a/network/network.tscn b/network/network.tscn new file mode 100644 index 0000000..46463de --- /dev/null +++ b/network/network.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://t8bawt4nmflc"] + +[ext_resource type="Script" path="res://network/network.gd" id="1_iyygl"] + +[node name="Network" type="Node"] +script = ExtResource("1_iyygl") diff --git a/packets/character_information.gd b/packets/character_information.gd new file mode 100644 index 0000000..20e40cd --- /dev/null +++ b/packets/character_information.gd @@ -0,0 +1,246 @@ +class_name CharacterInformation +extends PacketChunk + + +## Byte Type: u32 +## Byte Length: 4 +var character_id: int + +## Byte Type: i64 +## Byte Length: 8 +var experience: int + +## Byte Type: i32 +## Byte Length: 4 +var money: int + +## Byte Type: i64 +## Byte Length: 8 +var job_experience: int + +## Byte Type: i32 +## Byte Length: 4 +var job_level: int + +## Byte Type: i32 +## Byte Length: 4 +var body_state: int + +## Byte Type: i32 +## Byte Length: 4 +var health_state: int + +## Byte Type: i32 +## Byte Length: 4 +var effect_state: int + +## Byte Type: i32 +## Byte Length: 4 +var virtue: int + +## Byte Type: i32 +## Byte Length: 4 +var honor: int + +## Byte Type: i16 +## Byte Length: 2 +var jobpoint: int + +## Byte Type: i64 +## Byte Length: 8 +var health_points: int + +## Byte Type: i64 +## Byte Length: 8 +var maximum_health_points: int + +## Byte Type: i64 +## Byte Length: 8 +var spell_points: int + +## Byte Type: i64 +## Byte Length: 8 +var maximum_spell_points: int + +## Byte Type: i16 +## Byte Length: 2 +var movement_speed: int + +## Byte Type: i16 +## Byte Length: 2 +var job: int + +## Byte Type: i16 +## Byte Length: 2 +var head: int + +## Byte Type: i16 +## Byte Length: 2 +var body: int + +## Byte Type: i16 +## Byte Length: 2 +var weapon: int + +## Byte Type: i16 +## Byte Length: 2 +var level: int + +## Byte Type: i16 +## Byte Length: 2 +var skill_point: int + +## Byte Type: i16 +## Byte Length: 2 +var accessory: int + +## Byte Type: i16 +## Byte Length: 2 +var shield: int + +## Byte Type: i16 +## Byte Length: 2 +var accessory2: int + +## Byte Type: i16 +## Byte Length: 2 +var accessory3: int + +## Byte Type: i16 +## Byte Length: 2 +var head_palette: int + +## Byte Type: i16 +## Byte Length: 2 +var body_palette: int + +## Byte Type: u8 +## Byte Length: 24 +var name: String + +## Byte Type: u8 +## Byte Length: 1 +var strength: int + +## Byte Type: u8 +## Byte Length: 1 +var agility: int + +## Byte Type: u8 +## Byte Length: 1 +var vitality: int + +## Byte Type: u8 +## Byte Length: 1 +var intelligence: int + +## Byte Type: u8 +## Byte Length: 1 +var dexterity: int + +## Byte Type: u8 +## Byte Length: 1 +var luck: int + +## Byte Type: u8 +## Byte Length: 1 +var character_number: int + +## Byte Type: u8 +## Byte Length: 1 +var hair_color: int + +## Byte Type: i16 +## Byte Length: 2 +var b_is_changed_char: int + +## Byte Type: u8 +## Byte Length: 16 +var map_name: String + +## Byte Type: i32 +## Byte Length: 4 +var deletion_reverse_date: int + +## Byte Type: i32 +## Byte Length: 4 +var robe_palette: int + +## Byte Type: i32 +## Byte Length: 4 +var character_slot_change: int + +## Byte Type: i32 +## Byte Length: 4 +var character_name_change: int + +## Byte Type: u8 +## Byte Length: 1 +var gender: Constants.Gender + + +func _init() -> void: + byte_length = 175 + + +static func from_bytes(bytes: PackedByteArray): + var info = CharacterInformation.new() + + info.character_id = bytes.decode_u32(0) + info.experience = bytes.decode_s64(4) + info.money = bytes.decode_s32(12) + info.job_experience = bytes.decode_s64(16) + info.job_level = bytes.decode_s32(24) + info.body_state = bytes.decode_s32(28) + info.health_state = bytes.decode_s32(32) + info.effect_state = bytes.decode_s32(36) + info.virtue = bytes.decode_s32(40) + info.honor = bytes.decode_s32(44) + info.jobpoint = bytes.decode_s16(48) + info.health_points = bytes.decode_s64(50) + info.maximum_health_points = bytes.decode_s64(58) + info.spell_points = bytes.decode_s64(66) + info.maximum_spell_points = bytes.decode_s64(74) + info.movement_speed = bytes.decode_s16(82) + info.job = bytes.decode_s16(84) + info.head = bytes.decode_s16(86) + info.body = bytes.decode_s16(88) + info.weapon = bytes.decode_s16(90) + info.level = bytes.decode_s16(92) + info.skill_point = bytes.decode_s16(94) + info.accessory = bytes.decode_s16(96) + info.shield = bytes.decode_s16(98) + info.accessory2 = bytes.decode_s16(100) + info.accessory3 = bytes.decode_s16(102) + info.head_palette = bytes.decode_s16(104) + info.body_palette = bytes.decode_s16(106) + info.name = bytes.slice(108, 108 + 24).get_string_from_utf8() + info.strength = bytes.decode_u8(132) + info.agility = bytes.decode_u8(133) + info.vitality = bytes.decode_u8(134) + info.intelligence = bytes.decode_u8(135) + info.dexterity = bytes.decode_u8(136) + info.luck = bytes.decode_u8(137) + info.character_number = bytes.decode_u8(138) + info.hair_color = bytes.decode_u8(139) + info.b_is_changed_char = bytes.decode_s16(140) + info.map_name = bytes.slice(142, 142 + 16).get_string_from_utf8() + info.deletion_reverse_date = bytes.decode_s32(158) + info.robe_palette = bytes.decode_s32(162) + info.character_slot_change = bytes.decode_s32(166) + info.character_name_change = bytes.decode_s32(170) + info.gender = bytes.decode_u8(174) + + return info + + +static func array_from_bytes(bytes: PackedByteArray) -> Array: + var array = [] + + var offset = 0 + while offset < bytes.size(): + var chunk = from_bytes(bytes.slice(offset)) + array.append(chunk) + offset += chunk.byte_length + + return array diff --git a/packets/character_selection_success_packet.gd b/packets/character_selection_success_packet.gd new file mode 100644 index 0000000..f483d9e --- /dev/null +++ b/packets/character_selection_success_packet.gd @@ -0,0 +1,53 @@ +class_name CharacterSelectionSuccessPacket +extends Packet + + +static var header := 0x0ac5 + + +## Byte Type: u32 +## Byte Length: 4 +var character_id: int + +## Byte Type: u8 +## Byte Length: 16 +var map_name: String + +## Byte Type: u32 +## Byte Length: 4 +var map_server_ip: PackedByteArray + +## Byte Type: u16 +## Byte Length: 2 +var map_server_port: int + +## Byte Type: u8 +## Byte Length: 128 +var unknown: PackedByteArray + + +func get_map_server_ip(): + return "%s.%s.%s.%s" % [ + map_server_ip.decode_u8(0), + map_server_ip.decode_u8(1), + map_server_ip.decode_u8(2), + map_server_ip.decode_u8(3) + ] + + +static func from_bytes(bytes: PackedByteArray): + var packet = CharacterSelectionSuccessPacket.new() + + packet.character_id = bytes.decode_u32(0) + packet.map_name = bytes.slice(4, 4 + 16).get_string_from_utf8() + packet.map_server_ip = bytes.slice(20, 20 + 4) + packet.map_server_port = bytes.decode_u16(24) + packet.unknown = bytes.slice(26) + + return packet + +static func from_bytes_via_peer(peer: StreamPeer): + var _header = peer.get_data(2) + var remaining_bytes = peer.get_data(peer.get_available_bytes()) + + return from_bytes(remaining_bytes[1]) diff --git a/character_server_information.gd b/packets/character_server_information.gd index 9ede3b6..f67b065 100644 --- a/character_server_information.gd +++ b/packets/character_server_information.gd @@ -1,30 +1,47 @@ class_name CharacterServerInformation +extends PacketChunk +## Byte Type: u32 ## Byte Length: 4 var server_ip: PackedByteArray +## Byte Type: u16 ## Byte Length: 2 var server_port: int +## Byte Type: u8 ## Byte Length: 20 var server_name: String +## Byte Type: u16 ## Byte Length: 2 var user_count: int +## Byte Type: u16 ## Byte Length: 2 var server_type: int +## Byte Type: u16 ## Byte Length: 2 var display_new: int +## Byte Type: u8 ## Byte Length: 128 var unknown: PackedByteArray +func _init() -> void: + byte_length = 160 + + func get_server_ip(): - return "%s.%s.%s.%s" % [server_ip.decode_u8(0), server_ip.decode_u8(1), server_ip.decode_u8(2), server_ip.decode_u8(3)] + return "%s.%s.%s.%s" % [ + server_ip.decode_u8(0), + server_ip.decode_u8(1), + server_ip.decode_u8(2), + server_ip.decode_u8(3) + ] static func from_bytes(bytes: PackedByteArray): @@ -32,7 +49,7 @@ static func from_bytes(bytes: PackedByteArray): info.server_ip = bytes.slice(0, 4) info.server_port = bytes.decode_u16(4) - info.server_name = bytes.slice(6, 6 + 20 - 1).get_string_from_utf8() + info.server_name = bytes.slice(6, 6 + 20).get_string_from_utf8() info.user_count = bytes.decode_u16(26) info.server_type = bytes.decode_u16(28) info.display_new = bytes.decode_u16(30) @@ -46,7 +63,8 @@ static func array_from_bytes(bytes: PackedByteArray) -> Array: var offset = 0 while offset < bytes.size(): - array.append(from_bytes(bytes.slice(offset))) - offset += 160 + var chunk = from_bytes(bytes.slice(offset)) + array.append(chunk) + offset += chunk.byte_length return array diff --git a/packets/character_server_login_packet.gd b/packets/character_server_login_packet.gd index e6ba07b..0e3116f 100644 --- a/packets/character_server_login_packet.gd +++ b/packets/character_server_login_packet.gd @@ -2,25 +2,31 @@ class_name CharacterServerLoginPacket extends Packet +static var header := 0x0065 + + +## Byte Type: u32 ## Byte Length: 4 var account_id: int +## Byte Type: u32 ## Byte Length: 4 var login_id1: int +## Byte Type: u32 ## Byte Length: 4 var login_id2: int +## Byte Type: u16 ## Byte Length: 2 var unknown: int +## Byte Type: u8 ## Byte Length: 1 var gender: Constants.Gender func to_bytes(): - var header = PackedByteArray([101, 0]) - var payload = PackedByteArray([]) payload.resize(15) payload.encode_u32(0, account_id) @@ -29,4 +35,4 @@ func to_bytes(): payload.encode_u16(12, unknown) payload.encode_u8(14, gender) - return header + payload + return get_header() + payload diff --git a/packets/login_server_login_packet.gd b/packets/login_server_login_packet.gd index 29f23cd..5b4c1f3 100644 --- a/packets/login_server_login_packet.gd +++ b/packets/login_server_login_packet.gd @@ -2,6 +2,10 @@ class_name LoginServerLoginPacket extends Packet +static var header := 0x064 + + +## Byte Type: u32 ## Byte Length: 4 var version: int = 0 @@ -11,13 +15,12 @@ var username: String ## Byte Length: 24 var password: String +## Byte Type: u8 ## Byte Length: 1 var client_type: int = 0 func to_bytes(): - var header = PackedByteArray([100, 0]) - var username_bytes = username.to_utf8_buffer() username_bytes.resize(24) @@ -27,8 +30,11 @@ func to_bytes(): var payload = PackedByteArray([]) payload.resize(4) payload.encode_u32(0, version) + payload.append_array(username_bytes) payload.append_array(password_bytes) - payload.append(client_type) - return header + payload + payload.resize(53) + payload.encode_u8(52, client_type) + + return get_header() + payload diff --git a/packets/login_server_login_success_packet.gd b/packets/login_server_login_success_packet.gd index 3d74289..fd7dbf1 100644 --- a/packets/login_server_login_success_packet.gd +++ b/packets/login_server_login_success_packet.gd @@ -2,30 +2,36 @@ class_name LoginServerLoginSuccessPacket extends Packet +static var header := 0x0ac4 + + +## Byte Type: u32 ## Byte Length: 4 var login_id1: int +## Byte Type: u32 ## Byte Length: 4 var account_id: int +## Byte Type: u32 ## Byte Length: 4 var login_id2: int ## Deprecated and always 0 on rAthena +## Byte Type: u32 ## Byte Length: 4 var ip_address: PackedByteArray ## Deprecated and always 0 on rAthena -## Byte Length: 24 -var name: String - -## Always 0 on rAthena -## Byte Length: 2 -var unknown: int +## Byte Type: u8 +## Byte Length: 26 +var last_login: PackedByteArray +## Byte Type: u8 ## Byte Length: 1 var gender: Constants.Gender +## Byte Type: u8 ## Byte Length: 17 var auth_token: String @@ -39,16 +45,15 @@ static func from_bytes(bytes: PackedByteArray): packet.account_id = bytes.decode_u32(4) packet.login_id2 = bytes.decode_u32(8) packet.ip_address = bytes.slice(12, 16) - packet.name = bytes.slice(16, 16 + 24).get_string_from_utf8() - packet.unknown = bytes.decode_u16(40) + packet.last_login = bytes.slice(16, 16 + 26) packet.gender = bytes[42] - packet.auth_token = bytes.slice(43, 43 + 17 - 1).get_string_from_utf8() + packet.auth_token = bytes.slice(43, 43 + 17).get_string_from_utf8() packet.character_server_information = CharacterServerInformation.array_from_bytes(bytes.slice(60)) return packet static func from_bytes_via_peer(peer: StreamPeer): - var _header = peer.get_data(4) + var _header = peer.get_data(4) # 2 = header, 2 = packet_size var remaining_bytes = peer.get_data(peer.get_available_bytes()) return from_bytes(remaining_bytes[1]) diff --git a/packets/map_server_login_packet.gd b/packets/map_server_login_packet.gd index 62af086..89d0108 100644 --- a/packets/map_server_login_packet.gd +++ b/packets/map_server_login_packet.gd @@ -2,28 +2,35 @@ class_name MapServerLoginPacket extends Packet +static var header := 0x0436 + + +## Byte Type: u32 ## Byte Length: 4 var account_id: int +## Byte Type: u32 ## Byte Length: 4 var character_id: int +## Byte Type: u32 ## Byte Length: 4 var login_id1: int +## Byte Type: u32 ## Byte Length: 4 var client_tick: int = 100 +## Byte Type: u8 ## Byte Length: 1 var gender: Constants.Gender +## Byte Type: u32 ## Byte Length: 4 var unknown: int = 0 func to_bytes(): - var header = PackedByteArray([54, 4]) - var payload = PackedByteArray([]) payload.resize(21) payload.encode_u32(0, account_id) @@ -33,4 +40,4 @@ func to_bytes(): payload.encode_u8(16, gender) payload.encode_u32(17, unknown) - return header + payload + return get_header() + payload diff --git a/packets/map_server_login_success_packet.gd b/packets/map_server_login_success_packet.gd new file mode 100644 index 0000000..9eecc65 --- /dev/null +++ b/packets/map_server_login_success_packet.gd @@ -0,0 +1,48 @@ +class_name MapServerLoginSuccessPacket +extends Packet + + +static var header := 0x02eb + + +## Byte Type: u32 +## Byte Length: 4 +var client_tick: int + +## Byte Type: u8 +## Byte Length: 3 +var position: PackedByteArray + +## Byte Type: u8 +## Byte Length: 2 +## Always [5, 5] on rAthena +var ignored: PackedByteArray + +## Byte Type: u16 +## Byte Length: 2 +var font: int + + +func get_position(): + return Vector2( + position[1] >> 6 | position[0] << 2, + position[2] >> 4 | (position[1] & 0b111111) << 4 + ) + + +static func from_bytes(bytes: PackedByteArray): + var packet = MapServerLoginSuccessPacket.new() + + packet.client_tick = bytes.decode_u32(0) + packet.position = bytes.slice(4, 4 + 3) + packet.ignored = bytes.slice(7, 7 + 2) + packet.font = bytes.decode_u32(9) + + return packet + + +static func from_bytes_via_peer(peer: StreamPeer): + var _header = peer.get_data(2) + var remaining_bytes = peer.get_data(peer.get_available_bytes()) + + return from_bytes(remaining_bytes[1]) diff --git a/packets/packet.gd b/packets/packet.gd index ea1621c..9f82782 100644 --- a/packets/packet.gd +++ b/packets/packet.gd @@ -1 +1,24 @@ class_name Packet + + +#static var header: int = 0 + +var byte_length: int = 0 + + +## Override if packet has variable length +func get_byte_length() -> int: + return byte_length + + +## Get header to prepend to the packet. [br] +## [param packet_length] is only needed when the packet has a variable length. +func get_header(packet_length: int = 0) -> PackedByteArray: + var bytes = PackedByteArray([0,0]) + bytes.encode_u16(0, self.header) + + if packet_length > 0: + bytes.resize(4) + bytes.encode_u16(2, packet_length) + + return bytes diff --git a/packets/packet_chunk.gd b/packets/packet_chunk.gd new file mode 100644 index 0000000..2bd8bc6 --- /dev/null +++ b/packets/packet_chunk.gd @@ -0,0 +1,10 @@ +class_name PacketChunk + + +var byte_length: int: + get = get_byte_length + + +## to be overriden +func get_byte_length() -> int: + return byte_length diff --git a/packets/request_character_list_packet.gd b/packets/request_character_list_packet.gd index eca61ad..e347abd 100644 --- a/packets/request_character_list_packet.gd +++ b/packets/request_character_list_packet.gd @@ -2,7 +2,8 @@ class_name RequestCharacterListPacket extends Packet +static var header := 0x09a1 + + func to_bytes(): - var header = PackedByteArray([161, 9]) - - return header + return get_header() diff --git a/packets/request_character_list_success_packet.gd b/packets/request_character_list_success_packet.gd index 0b6cea8..431e8ca 100644 --- a/packets/request_character_list_success_packet.gd +++ b/packets/request_character_list_success_packet.gd @@ -12,8 +12,9 @@ static func from_bytes(bytes: PackedByteArray): return packet + static func from_bytes_via_peer(peer: StreamPeer): - var _header = peer.get_data(4) + var _header = peer.get_data(4) # 2 = header, 2 = length var remaining_bytes = peer.get_data(peer.get_available_bytes()) return from_bytes(remaining_bytes[1]) diff --git a/packets/select_character_packet.gd b/packets/select_character_packet.gd new file mode 100644 index 0000000..18843cc --- /dev/null +++ b/packets/select_character_packet.gd @@ -0,0 +1,18 @@ +class_name SelectCharacterPacket +extends Packet + + +static var header := 0x0066 + + +## Byte Type: u8 +## Byte Length: 1 +var selected_slot: int + + +func to_bytes(): + var payload = PackedByteArray([]) + payload.resize(1) + payload.encode_u8(0, selected_slot) + + return get_header() + payload diff --git a/project.godot b/project.godot index b2c4014..6157940 100644 --- a/project.godot +++ b/project.godot @@ -11,6 +11,10 @@ config_version=5 [application] config/name="Minerva" -run/main_scene="res://main.tscn" +run/main_scene="res://login.tscn" config/features=PackedStringArray("4.3", "Forward Plus") config/icon="res://icon.svg" + +[autoload] + +Network="*res://network/network.tscn" |