channel select, character select
This commit is contained in:
parent
2c3f0c65bb
commit
f9a1b4c594
|
@ -1,5 +1,6 @@
|
|||
**/.git
|
||||
**/.vscode
|
||||
**/__pycache__
|
||||
**/.venv
|
||||
|
||||
test*.*
|
||||
test*.*
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
__all__ = "ClientBase", "Client", "Packet", "iPacket", "oPacket"
|
||||
__all__ = "Character", "ClientBase", "Client", "Helpers", "Packet", "iPacket", "oPacket"
|
||||
|
||||
from .character import Character
|
||||
from .client_base import ClientBase
|
||||
from .client import Client
|
||||
from .packet import Packet, iPacket, oPacket
|
||||
from .packet_helper import Helpers
|
|
@ -0,0 +1,41 @@
|
|||
from attrs import define
|
||||
|
||||
|
||||
@define(auto_attribs=True, kw_only=True, init=True)
|
||||
class Character(object):
|
||||
idx: int
|
||||
name: str
|
||||
gender: int
|
||||
skin: int
|
||||
face: int
|
||||
hair: int
|
||||
level: int
|
||||
job: int
|
||||
strn: int
|
||||
dex: int
|
||||
intl: int
|
||||
luk: int
|
||||
hp: int
|
||||
max_hp: int
|
||||
mp: int
|
||||
max_mp: int
|
||||
sp: int
|
||||
exp: int
|
||||
fame: int
|
||||
gach_exp: int
|
||||
map_id: int
|
||||
spawn: int
|
||||
sub_job: int
|
||||
pvp_exp: int
|
||||
pvp_rank: int
|
||||
battle_points: int
|
||||
equips: list[tuple[int, int]]
|
||||
weapon: int = 0
|
||||
rank: int = 0
|
||||
rank_move: int = 0
|
||||
job_rank: int = 0
|
||||
job_rank_move: int = 0
|
||||
|
||||
@classmethod
|
||||
def fill(cls, values: dict):
|
||||
return cls(**values)
|
51
client.py
51
client.py
|
@ -1,15 +1,22 @@
|
|||
from asyncio import sleep
|
||||
|
||||
from rich import print
|
||||
|
||||
from .character import Character
|
||||
from .client_base import ClientBase
|
||||
from .packet import iPacket, packet_handler, oPacket
|
||||
from .opcodes import RecvOps, SendOps
|
||||
from .packet import iPacket, oPacket, packet_handler
|
||||
from .packet_helper import Helpers
|
||||
|
||||
|
||||
class Client(ClientBase):
|
||||
|
||||
def __init__(self, loop=None, username=None, password=None):
|
||||
super().__init__(loop)
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._channels = []
|
||||
self._characters = []
|
||||
|
||||
async def begin(self):
|
||||
while self._loop.is_running() and self._sock.fileno():
|
||||
|
@ -52,8 +59,46 @@ class Client(ClientBase):
|
|||
|
||||
print(f"Account ID: {account_id} | Gender: {gender} | GM 1: {gm_1} | GM 2: {gm_2} | Account Name: {account_name} | New Account: {new_account} | Shadow Ban: {shadow_ban} ")
|
||||
|
||||
opkt = oPacket(SendOps.SERVERLIST_REQUEST)
|
||||
await self.send_packet(opkt)
|
||||
await self.send_packet(oPacket(SendOps.SERVERLIST_REQUEST))
|
||||
|
||||
@packet_handler(RecvOps.SERVERLIST)
|
||||
async def serverlist(self, ipkt: iPacket):
|
||||
if world_id := ipkt.decode_byte() == 0xFF:
|
||||
if not self._channels:
|
||||
return
|
||||
|
||||
await self.send_packet(iPacket(SendOps.CHARLIST_REQUEST))
|
||||
return
|
||||
|
||||
world_name = ipkt.decode_string()
|
||||
world_flag = ipkt.decode_byte()
|
||||
event_message = ipkt.decode_string()
|
||||
|
||||
print(world_id, world_name, world_flag, event_message)
|
||||
|
||||
ipkt.decode_short()
|
||||
ipkt.decode_short()
|
||||
ipkt.decode_byte()
|
||||
|
||||
channel_count = ipkt.decode_byte()
|
||||
|
||||
for _ in range(channel_count):
|
||||
self._channels.append({
|
||||
"name": ipkt.decode_string(),
|
||||
"load": ipkt.decode_int(),
|
||||
"world_id": ipkt.decode_byte(),
|
||||
"channel_id": ipkt.decode_short()
|
||||
})
|
||||
|
||||
ipkt.decode_short()
|
||||
ipkt.decode_int()
|
||||
|
||||
print(self._channels)
|
||||
|
||||
@packet_handler(RecvOps.CHARLIST)
|
||||
async def charlist(self, ipkt: iPacket):
|
||||
self._characters = Helpers.character_entries(ipkt)
|
||||
print(self._characters)
|
||||
|
||||
async def do_login(self):
|
||||
opkt = oPacket(SendOps.LOGIN_PASSWORD)
|
||||
|
|
|
@ -81,6 +81,7 @@ class ClientBase:
|
|||
except ConnectionResetError:
|
||||
print("Connection reset, attempting to reconnect..")
|
||||
self._create_sock()
|
||||
self._buff = bytearray()
|
||||
del self._send_iv
|
||||
del self._recv_iv
|
||||
if self._action_task:
|
||||
|
@ -96,7 +97,6 @@ class ClientBase:
|
|||
self._send_iv = MapleIV(begin_packet.decode_int())
|
||||
self._recv_iv = MapleIV(begin_packet.decode_int())
|
||||
self._locale = begin_packet.decode_byte()
|
||||
# print(begin_packet)
|
||||
self._action_task = self._loop.create_task(
|
||||
getattr(self, "begin")()
|
||||
)
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
from .character import Character
|
||||
from .packet import iPacket
|
||||
|
||||
|
||||
class Helpers:
|
||||
|
||||
@staticmethod
|
||||
def character_entries(ipkt: iPacket, view_all=None):
|
||||
chars = []
|
||||
ipkt.decode_byte()
|
||||
for i in range(ipkt.decode_byte()):
|
||||
chars.append(Character.fill(Helpers.character_entry(ipkt, view_all)))
|
||||
return chars
|
||||
|
||||
@staticmethod
|
||||
def character_entry(ipkt: iPacket, view_all=None):
|
||||
stats = Helpers.character_stats(ipkt)
|
||||
look = Helpers.character_look(ipkt)
|
||||
look_useful = {"equips": look["equips"], "weapon": look["weapon"]}
|
||||
|
||||
if view_all:
|
||||
ipkt.decode_byte()
|
||||
|
||||
rank = {}
|
||||
if ranked := ipkt.decode_byte():
|
||||
rank |= {
|
||||
"rank": ipkt.decode_int(),
|
||||
"rank_move": ipkt.decode_int(),
|
||||
"job_rank": ipkt.decode_int(),
|
||||
"job_rank_move": ipkt.decode_int()
|
||||
}
|
||||
|
||||
return stats | look_useful | rank
|
||||
|
||||
@staticmethod
|
||||
def character_stats(ipkt: iPacket):
|
||||
stats = {
|
||||
"idx": ipkt.decode_int(),
|
||||
"name": ipkt.decode_string(),
|
||||
"gender": ipkt.decode_byte(),
|
||||
"skin": ipkt.decode_byte(),
|
||||
"face": ipkt.decode_int(),
|
||||
"hair": ipkt.decode_int(),
|
||||
"_": ipkt.seek(24),
|
||||
"level": ipkt.decode_byte(),
|
||||
"job": ipkt.decode_short(),
|
||||
"strn": ipkt.decode_short(),
|
||||
"dex": ipkt.decode_short(),
|
||||
"intl": ipkt.decode_short(),
|
||||
"luk": ipkt.decode_short(),
|
||||
"hp": ipkt.decode_int(),
|
||||
"max_hp": ipkt.decode_int(),
|
||||
"mp": ipkt.decode_int(),
|
||||
"max_mp": ipkt.decode_int(),
|
||||
"sp": ipkt.decode_short(),
|
||||
"exp": ipkt.decode_int(),
|
||||
"fame": ipkt.decode_int(),
|
||||
"gach_exp": ipkt.decode_int(),
|
||||
"map_id": ipkt.decode_int(),
|
||||
"spawn": ipkt.decode_byte(),
|
||||
"_": ipkt.decode_int(),
|
||||
"sub_job": ipkt.decode_short(),
|
||||
}
|
||||
|
||||
if stats["job"] in [3001, 3100, 3110, 3111, 3112]:
|
||||
stats["demon_marking"] = ipkt.decode_int()
|
||||
|
||||
stats["fatigue"] = ipkt.decode_byte()
|
||||
ipkt.decode_int() # date
|
||||
|
||||
for i in range(12):
|
||||
if i // 6 == 1:
|
||||
ipkt.decode_short() # today trait xp
|
||||
else:
|
||||
ipkt.decode_int() # total trait xp
|
||||
|
||||
stats |= {
|
||||
"pvp_exp": ipkt.decode_int(),
|
||||
"pvp_rank": ipkt.decode_byte(),
|
||||
"battle_points": ipkt.decode_int(),
|
||||
}
|
||||
ipkt.decode_byte()
|
||||
ipkt.decode_int()
|
||||
ipkt.decode_int()
|
||||
ipkt.decode_int()
|
||||
return stats
|
||||
|
||||
@staticmethod
|
||||
def character_look(ipkt: iPacket):
|
||||
look = {
|
||||
"gender": ipkt.decode_byte(),
|
||||
"skin": ipkt.decode_byte(),
|
||||
"face": ipkt.decode_int(),
|
||||
"job": ipkt.decode_int(),
|
||||
"mega": ipkt.decode_byte(),
|
||||
"hair": ipkt.decode_int(),
|
||||
"equips": [],
|
||||
"cash_equips": []
|
||||
}
|
||||
|
||||
while idx := ipkt.decode_byte() != 0xFF:
|
||||
look["equips"].append((idx, ipkt.decode_int()))
|
||||
|
||||
while idx := ipkt.decode_byte() != 0xFF:
|
||||
look["cash_equips"].append((idx, ipkt.decode_int()))
|
||||
|
||||
look |= {
|
||||
"weapon": ipkt.decode_int(),
|
||||
"mercedes_ears": ipkt.decode_byte()
|
||||
}
|
||||
|
||||
ipkt.seek(12)
|
||||
|
||||
if marking := ipkt.decode_int():
|
||||
look["demon_marking"] = marking
|
||||
|
||||
return look
|
|
@ -0,0 +1,3 @@
|
|||
pycryptodome==3.14.1
|
||||
Pygments==2.11.2
|
||||
rich==12.0.0
|
Loading…
Reference in New Issue