summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--character_information.gd32
-rw-r--r--constants.gd6
-rw-r--r--data/.gitkeep1
-rw-r--r--extractor/grf.gd4
-rw-r--r--login.gd83
-rw-r--r--login.tscn73
-rw-r--r--main.gd94
-rw-r--r--main.tscn6
-rw-r--r--network/character_server.gd54
-rw-r--r--network/login_server.gd31
-rw-r--r--network/map_server.gd35
-rw-r--r--network/network.gd115
-rw-r--r--network/network.tscn6
-rw-r--r--packets/character_information.gd246
-rw-r--r--packets/character_selection_success_packet.gd53
-rw-r--r--packets/character_server_information.gd (renamed from character_server_information.gd)26
-rw-r--r--packets/character_server_login_packet.gd12
-rw-r--r--packets/login_server_login_packet.gd14
-rw-r--r--packets/login_server_login_success_packet.gd25
-rw-r--r--packets/map_server_login_packet.gd13
-rw-r--r--packets/map_server_login_success_packet.gd48
-rw-r--r--packets/packet.gd23
-rw-r--r--packets/packet_chunk.gd10
-rw-r--r--packets/request_character_list_packet.gd7
-rw-r--r--packets/request_character_list_success_packet.gd3
-rw-r--r--packets/select_character_packet.gd18
-rw-r--r--project.godot6
28 files changed, 885 insertions, 161 deletions
diff --git a/.gitignore b/.gitignore
index 0af181c..7850089 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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"