start repo
This commit is contained in:
commit
61b623c5d2
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
**/.git
|
||||
**/.vscode
|
||||
**/__pyccache__
|
||||
|
||||
**/test*.*
|
5
__init__.py
Normal file
5
__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
__all__ = "ClientBase", "Client", "Packet", "iPacket", "oPacket"
|
||||
|
||||
from .client_base import ClientBase
|
||||
from .client import Client
|
||||
from .packet import Packet, iPacket, oPacket
|
BIN
__pycache__/__init__.cpython-310.pyc
Normal file
BIN
__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/clb.cpython-310.pyc
Normal file
BIN
__pycache__/clb.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/client.cpython-310.pyc
Normal file
BIN
__pycache__/client.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/client_base.cpython-310.pyc
Normal file
BIN
__pycache__/client_base.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/crypto.cpython-310.pyc
Normal file
BIN
__pycache__/crypto.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/opcodes.cpython-310.pyc
Normal file
BIN
__pycache__/opcodes.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/packet.cpython-310.pyc
Normal file
BIN
__pycache__/packet.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/tools.cpython-310.pyc
Normal file
BIN
__pycache__/tools.cpython-310.pyc
Normal file
Binary file not shown.
16
client.py
Normal file
16
client.py
Normal file
@ -0,0 +1,16 @@
|
||||
from .client_base import ClientBase
|
||||
from .packet import packet_handler, oPacket
|
||||
from .opcodes import RecvOps, SendOps
|
||||
|
||||
|
||||
class Client(ClientBase):
|
||||
|
||||
async def begin(self):
|
||||
# opkt = oPacket(SendOps.LOGIN_PASSWORD)
|
||||
...
|
||||
|
||||
@packet_handler(RecvOps.PING)
|
||||
async def pong(self, ipkt):
|
||||
print("Sending PONG packet")
|
||||
opkt = oPacket(SendOps.PONG)
|
||||
await self.send_packet(opkt)
|
114
client_base.py
Normal file
114
client_base.py
Normal file
@ -0,0 +1,114 @@
|
||||
from asyncio import Event, get_event_loop, get_running_loop, run_coroutine_threadsafe, sleep, Lock
|
||||
from socket import AF_INET, IPPROTO_TCP, SOCK_STREAM, TCP_NODELAY, socket
|
||||
from .packet import PacketHandler, Packet, iPacket, oPacket
|
||||
from .crypto import MapleAes, decrypt_transform, encrypt_transform, MapleIV
|
||||
from rich import print
|
||||
|
||||
VERSION = 111
|
||||
|
||||
|
||||
class ClientBase:
|
||||
|
||||
def __init__(self, loop=None):
|
||||
self._loop = loop or get_event_loop()
|
||||
self._packet_handlers = []
|
||||
self._ready = Event()
|
||||
self._lock = Lock()
|
||||
|
||||
self._sock = socket(AF_INET, SOCK_STREAM)
|
||||
self._sock.setblocking(False)
|
||||
self._sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
|
||||
self._version = None
|
||||
self._sub_version = None
|
||||
self._locale = None
|
||||
self._recv_iv = None
|
||||
self._send_iv = None
|
||||
self._buff = bytearray()
|
||||
self._recv_task = None
|
||||
self.add_packet_handlers()
|
||||
|
||||
async def start(self):
|
||||
self._recv_task = self._loop.create_task(self._sock_recv())
|
||||
self._ready.set()
|
||||
|
||||
async def send_packet(self, opacket, raw=False):
|
||||
opkt = bytearray(opacket.getvalue())
|
||||
length = len(opkt)
|
||||
|
||||
# buf = memoryview(opkt)
|
||||
header = MapleAes.get_header(self._send_iv, length, self._version)
|
||||
encrypt_transform(opkt)
|
||||
opkt = MapleAes.transform(opkt, self._send_iv)
|
||||
|
||||
await self._loop.sock_sendall(self._sock, header + opkt)
|
||||
|
||||
async def _sock_recv(self):
|
||||
await self._loop.sock_connect(self._sock, ("51.222.56.169", 8485))
|
||||
await self._ready.wait()
|
||||
|
||||
nbytes = -1
|
||||
|
||||
while self._loop.is_running() and self._sock.fileno():
|
||||
self._buff.extend((await self._loop.sock_recv(self._sock, nbytes)))
|
||||
|
||||
if not self._send_iv and self._buff:
|
||||
begin_packet = iPacket(self._buff)
|
||||
self._version = begin_packet.decode_short()
|
||||
self._sub_version = begin_packet.decode_string()
|
||||
self._send_iv = MapleIV(begin_packet.decode_int())
|
||||
self._recv_iv = MapleIV(begin_packet.decode_int())
|
||||
self._locale = begin_packet.decode_byte()
|
||||
await getattr(self, "begin")()
|
||||
print(
|
||||
f"Version: {self._version} | Sub Version: {self._sub_version} | Locale: {self._locale} | Send IV: {self._send_iv} | Recv IV: {self._recv_iv}"
|
||||
)
|
||||
self._buff = bytearray()
|
||||
continue
|
||||
|
||||
if self._buff:
|
||||
length = MapleAes.get_length(self._buff[0:4])
|
||||
|
||||
if length != len(self._buff[4:]):
|
||||
nbytes = length - len(self._buff[4:])
|
||||
else:
|
||||
nbytes = -1
|
||||
|
||||
buf = bytearray(self._buff)[4:]
|
||||
|
||||
if length == len(buf):
|
||||
buf = MapleAes.transform(buf, self._recv_iv)
|
||||
buf = decrypt_transform(buf)
|
||||
packet = iPacket(buf)
|
||||
self._buff = bytearray()
|
||||
|
||||
self.handle_packet(packet)
|
||||
|
||||
def handle_packet(self, packet):
|
||||
coro = None
|
||||
|
||||
for packet_handler in self._packet_handlers:
|
||||
if packet_handler.op_code == packet.op_code:
|
||||
coro = packet_handler.callback
|
||||
break
|
||||
else:
|
||||
print(f"Unhandled packet: {packet.op_code}")
|
||||
return
|
||||
|
||||
if not coro:
|
||||
raise AttributeError
|
||||
|
||||
print(f"Queueing packet for consumption: {packet.name}")
|
||||
self._loop.create_task(coro(self, packet))
|
||||
|
||||
def add_packet_handlers(self):
|
||||
import inspect
|
||||
|
||||
members = inspect.getmembers(self)
|
||||
for _, member in members:
|
||||
|
||||
if (isinstance(member, PacketHandler)
|
||||
and member not in self._packet_handlers):
|
||||
self._packet_handlers.append(member)
|
||||
|
||||
async def begin(self):
|
||||
raise NotImplementedError
|
238
crypto.py
Normal file
238
crypto.py
Normal file
@ -0,0 +1,238 @@
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
# from struct import unpack, pack
|
||||
|
||||
|
||||
class MapleAes:
|
||||
_user_key = bytearray([
|
||||
0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||
0xB4, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x33, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def transform(cls, buffer, iv):
|
||||
remaining = len(buffer)
|
||||
length = 0x5B0
|
||||
start = 0
|
||||
|
||||
real_iv = bytearray(16)
|
||||
|
||||
iv_bytes = [
|
||||
iv.value & 255,
|
||||
iv.value >> 8 & 255,
|
||||
iv.value >> 16 & 255,
|
||||
iv.value >> 24 & 255,
|
||||
]
|
||||
|
||||
while remaining > 0:
|
||||
for index in range(len(real_iv)):
|
||||
real_iv[index] = iv_bytes[index % 4] # type: ignore
|
||||
|
||||
if remaining < length:
|
||||
length = remaining
|
||||
|
||||
index = start
|
||||
|
||||
while index < start + length:
|
||||
sub = index - start
|
||||
|
||||
if (sub % 16) == 0:
|
||||
real_iv = AES.new(cls._user_key,
|
||||
AES.MODE_ECB).encrypt(real_iv)
|
||||
|
||||
buffer[index] ^= real_iv[sub % 16]
|
||||
index += 1
|
||||
|
||||
start += length
|
||||
remaining -= length
|
||||
length = 0x5B4
|
||||
|
||||
iv.shuffle()
|
||||
|
||||
return buffer
|
||||
|
||||
@staticmethod
|
||||
def get_header(iv, length, major_ver):
|
||||
first = -(major_ver + 1) ^ iv.hiword
|
||||
second = (first + 2**16) ^ length
|
||||
return bytearray([
|
||||
first & 0xFF, first >> 8 & 0xFF, second & 0xFF, second >> 8 & 0xFF
|
||||
])
|
||||
|
||||
@staticmethod
|
||||
def get_length(data):
|
||||
return ((data[1] << 8) + data[0]) ^ ((data[3] << 8) + data[2])
|
||||
|
||||
|
||||
class MapleIV:
|
||||
_shuffle = bytearray([
|
||||
0xEC, 0x3F, 0x77, 0xA4, 0x45, 0xD0, 0x71, 0xBF, 0xB7, 0x98, 0x20, 0xFC,
|
||||
0x4B, 0xE9, 0xB3, 0xE1, 0x5C, 0x22, 0xF7, 0x0C, 0x44, 0x1B, 0x81, 0xBD,
|
||||
0x63, 0x8D, 0xD4, 0xC3, 0xF2, 0x10, 0x19, 0xE0, 0xFB, 0xA1, 0x6E, 0x66,
|
||||
0xEA, 0xAE, 0xD6, 0xCE, 0x06, 0x18, 0x4E, 0xEB, 0x78, 0x95, 0xDB, 0xBA,
|
||||
0xB6, 0x42, 0x7A, 0x2A, 0x83, 0x0B, 0x54, 0x67, 0x6D, 0xE8, 0x65, 0xE7,
|
||||
0x2F, 0x07, 0xF3, 0xAA, 0x27, 0x7B, 0x85, 0xB0, 0x26, 0xFD, 0x8B, 0xA9,
|
||||
0xFA, 0xBE, 0xA8, 0xD7, 0xCB, 0xCC, 0x92, 0xDA, 0xF9, 0x93, 0x60, 0x2D,
|
||||
0xDD, 0xD2, 0xA2, 0x9B, 0x39, 0x5F, 0x82, 0x21, 0x4C, 0x69, 0xF8, 0x31,
|
||||
0x87, 0xEE, 0x8E, 0xAD, 0x8C, 0x6A, 0xBC, 0xB5, 0x6B, 0x59, 0x13, 0xF1,
|
||||
0x04, 0x00, 0xF6, 0x5A, 0x35, 0x79, 0x48, 0x8F, 0x15, 0xCD, 0x97, 0x57,
|
||||
0x12, 0x3E, 0x37, 0xFF, 0x9D, 0x4F, 0x51, 0xF5, 0xA3, 0x70, 0xBB, 0x14,
|
||||
0x75, 0xC2, 0xB8, 0x72, 0xC0, 0xED, 0x7D, 0x68, 0xC9, 0x2E, 0x0D, 0x62,
|
||||
0x46, 0x17, 0x11, 0x4D, 0x6C, 0xC4, 0x7E, 0x53, 0xC1, 0x25, 0xC7, 0x9A,
|
||||
0x1C, 0x88, 0x58, 0x2C, 0x89, 0xDC, 0x02, 0x64, 0x40, 0x01, 0x5D, 0x38,
|
||||
0xA5, 0xE2, 0xAF, 0x55, 0xD5, 0xEF, 0x1A, 0x7C, 0xA7, 0x5B, 0xA6, 0x6F,
|
||||
0x86, 0x9F, 0x73, 0xE6, 0x0A, 0xDE, 0x2B, 0x99, 0x4A, 0x47, 0x9C, 0xDF,
|
||||
0x09, 0x76, 0x9E, 0x30, 0x0E, 0xE4, 0xB2, 0x94, 0xA0, 0x3B, 0x34, 0x1D,
|
||||
0x28, 0x0F, 0x36, 0xE3, 0x23, 0xB4, 0x03, 0xD8, 0x90, 0xC8, 0x3C, 0xFE,
|
||||
0x5E, 0x32, 0x24, 0x50, 0x1F, 0x3A, 0x43, 0x8A, 0x96, 0x41, 0x74, 0xAC,
|
||||
0x52, 0x33, 0xF0, 0xD9, 0x29, 0x80, 0xB1, 0x16, 0xD3, 0xAB, 0x91, 0xB9,
|
||||
0x84, 0x7F, 0x61, 0x1E, 0xCF, 0xC5, 0xD1, 0x56, 0x3D, 0xCA, 0xF4, 0x05,
|
||||
0xC6, 0xE5, 0x08, 0x49
|
||||
])
|
||||
|
||||
def __init__(self, vector):
|
||||
self.value = vector
|
||||
|
||||
def __int__(self):
|
||||
return self.value
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
@property
|
||||
def hiword(self):
|
||||
return self.value >> 16
|
||||
|
||||
@property
|
||||
def loword(self):
|
||||
return self.value
|
||||
|
||||
def shuffle(self):
|
||||
seed = [0xF2, 0x53, 0x50, 0xC6]
|
||||
p_iv = self.value
|
||||
|
||||
for i in range(4):
|
||||
temp_p_iv = (p_iv >> (8 * i)) & 0xFF
|
||||
|
||||
a = seed[1]
|
||||
b = a
|
||||
b = self._shuffle[b & 0xFF]
|
||||
b -= temp_p_iv
|
||||
seed[0] += b
|
||||
b = seed[2]
|
||||
b ^= self._shuffle[int(temp_p_iv) & 0xFF]
|
||||
a -= int(b) & 0xFF
|
||||
seed[1] = a
|
||||
a = seed[3]
|
||||
b = a
|
||||
a -= seed[0] & 0xFF
|
||||
b = self._shuffle[b & 0xFF]
|
||||
b += temp_p_iv
|
||||
b ^= seed[2]
|
||||
seed[2] = b & 0xFF
|
||||
a += self._shuffle[temp_p_iv & 0xFF] & 0xFF
|
||||
seed[3] = a
|
||||
|
||||
c = seed[0] & 0xFF
|
||||
c |= (seed[1] << 8) & 0xFFFF
|
||||
c |= (seed[2] << 16) & 0xFFFFFF
|
||||
c |= (seed[3] << 24) & 0xFFFFFFFF
|
||||
|
||||
c = (c << 0x03) | (c >> 0x1D)
|
||||
|
||||
seed[0] = c & 0xFF
|
||||
seed[1] = (c >> 8) & 0xFFFF
|
||||
seed[2] = (c >> 16) & 0xFFFFFF
|
||||
seed[3] = (c >> 24) & 0xFFFFFFFF
|
||||
|
||||
c = seed[0] & 0xFF
|
||||
c |= (seed[1] << 8) & 0xFFFF
|
||||
c |= (seed[2] << 16) & 0xFFFFFF
|
||||
c |= (seed[3] << 24) & 0xFFFFFFFF
|
||||
|
||||
self.value = c
|
||||
|
||||
|
||||
def decrypt_transform(data):
|
||||
for j in range(1, 7):
|
||||
remember = 0
|
||||
data_length = len(data) & 0xFF
|
||||
next_remember = 0
|
||||
if j % 2 == 0:
|
||||
for i in range(len(data)):
|
||||
cur = data[i]
|
||||
cur = (cur - 0x48) & 0xFF
|
||||
cur = ~cur & 0xFF
|
||||
cur = roll_left(cur, data_length & 0xFF)
|
||||
next_remember = cur
|
||||
cur ^= remember
|
||||
remember = next_remember
|
||||
cur = (cur - data_length) & 0xFF
|
||||
cur = roll_right(cur, 3)
|
||||
data[i] = cur
|
||||
data_length -= 1
|
||||
else:
|
||||
for i in reversed(range(len(data))):
|
||||
cur = data[i]
|
||||
cur = roll_left(cur, 3)
|
||||
cur ^= 0x13
|
||||
next_remember = cur
|
||||
cur ^= remember
|
||||
remember = next_remember
|
||||
cur = (cur - data_length) & 0xFF
|
||||
cur = roll_right(cur, 4) & 0xFF
|
||||
data[i] = cur
|
||||
data_length -= 1
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def encrypt_transform(data):
|
||||
b = {str(i): 0 for i in range(len(data))}
|
||||
cur = 0
|
||||
|
||||
for _ in range(3):
|
||||
length = len(data) & 0xFF
|
||||
xor_key = 0
|
||||
i = 0
|
||||
while i < len(data):
|
||||
|
||||
cur = roll_left(data[i], 3)
|
||||
cur = cur + length
|
||||
cur = (cur ^ xor_key) & 0xFF
|
||||
xor_key = cur
|
||||
cur = ~roll_right(cur, length & 0xFF) & 0xFF
|
||||
cur = (cur + 0x48) & 0xFF
|
||||
data[i] = cur
|
||||
b[str(i)] = cur
|
||||
length -= 1
|
||||
i += 1
|
||||
|
||||
xor_key = 0
|
||||
length = len(data) & 0xFF
|
||||
i = len(data) - 1
|
||||
|
||||
while i >= 0:
|
||||
cur = roll_left(data[i], 4)
|
||||
cur += length
|
||||
cur = (cur ^ xor_key) & 0xFF
|
||||
xor_key = cur
|
||||
cur ^= 0x13
|
||||
cur = roll_right(cur, 3)
|
||||
data[i] = cur
|
||||
b[str(i)] = cur
|
||||
length -= 1
|
||||
i -= 1
|
||||
|
||||
return bytearray([b[b_] for b_ in b])
|
||||
|
||||
|
||||
def roll_left(value, shift):
|
||||
num = value << (shift % 8)
|
||||
return (num & 0xFF) | (num >> 8)
|
||||
|
||||
|
||||
def roll_right(value, shift):
|
||||
num = (value << 8) >> (shift % 8)
|
||||
return (num & 0xFF) | (num >> 8)
|
725
opcodes.py
Normal file
725
opcodes.py
Normal file
@ -0,0 +1,725 @@
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class RecvOps(IntEnum):
|
||||
LOGIN_STATUS = 0x00
|
||||
SEND_LINK = 0x01
|
||||
LOGIN_SECOND = 0x02
|
||||
SERVERSTATUS = 0x03
|
||||
GENDER_SET = 0x04
|
||||
PIN_OPERATION = 0x06
|
||||
PIN_ASSIGNED = 0x07
|
||||
ALL_CHARLIST = 0x08
|
||||
SERVERLIST = 0x0A
|
||||
CHARLIST = 0x0B
|
||||
SERVER_IP = 0x0C
|
||||
CHAR_NAME_RESPONSE = 0x0D
|
||||
ADD_NEW_CHAR_ENTRY = 0x0E
|
||||
DELETE_CHAR_RESPONSE = 0x0F
|
||||
CHANGE_CHANNEL = 0x10
|
||||
PING = 0x11
|
||||
CS_USE = 0x12
|
||||
CHANNEL_SELECTED = 0x14
|
||||
RELOG_RESPONSE = 0x16
|
||||
ENABLE_RECOMMENDED = 0x1B
|
||||
SEND_RECOMMENDED = 0x1C
|
||||
SPECIAL_CREATION = 0x1E
|
||||
SECONDPW_ERROR = 0x1F
|
||||
|
||||
INVENTORY_OPERATION = 0x20
|
||||
INVENTORY_GROW = 0x21
|
||||
UPDATE_STATS = 0x22
|
||||
GIVE_BUFF = 0x23
|
||||
CANCEL_BUFF = 0x24
|
||||
TEMP_STATS = 0x25
|
||||
TEMP_STATS_RESET = 0x26
|
||||
UPDATE_SKILLS = 0x27
|
||||
SKILL_MEMORY = 0x28
|
||||
UPDATE_SKILL_TICK = 0x29
|
||||
FAME_RESPONSE = 0x2B
|
||||
SHOW_STATUS_INFO = 0x2C
|
||||
GAME_PATCHES = 0x2D
|
||||
SHOW_NOTES = 0x2E
|
||||
TROCK_LOCATIONS = 0x2F
|
||||
LIE_DETECTOR = 0x30
|
||||
BOMB_LIE_DETECTOR = 0x31
|
||||
REPORT_RESPONSE = 0x33
|
||||
REPORT_TIME = 0x34
|
||||
REPORT_STATUS = 0x35
|
||||
UPDATE_MOUNT = 0x36
|
||||
SHOW_QUEST_COMPLETION = 0x37
|
||||
SEND_TITLE_BOX = 0x38
|
||||
USE_SKILL_BOOK = 0x39
|
||||
SP_RESET = 0x3A
|
||||
AP_RESET = 0x3B
|
||||
DISTRIBUTE_ITEM = 0x3D
|
||||
EXPAND_CHARACTER_SLOTS = 0x3E
|
||||
FINISH_SORT = 0x3F
|
||||
FINISH_GATHER = 0x40
|
||||
REPORT_RESULT = 0x42
|
||||
TRADE_LIMIT = 0x44
|
||||
UPDATE_GENDER = 0x45
|
||||
BBS_OPERATION = 0x46
|
||||
CHAR_INFO = 0x49
|
||||
PARTY_OPERATION = 0x4A
|
||||
MEMBER_SEARCH = 0x4B
|
||||
PARTY_SEARCH = 0x4C
|
||||
BOOK_INFO = 0x4D
|
||||
EXPEDITION_OPERATION = 0x4F
|
||||
BUDDYLIST = 0x50
|
||||
GUILD_OPERATION = 0x52
|
||||
ALLIANCE_OPERATION = 0x53
|
||||
SPAWN_PORTAL = 0x54
|
||||
MECH_PORTAL = 0x55
|
||||
ECHO_MESSAGE = 0x56
|
||||
SERVERMESSAGE = 0x58
|
||||
PIGMI_REWARD = 0x59
|
||||
OWL_OF_MINERVA = 0x5A
|
||||
OWL_RESULT = 0x5B
|
||||
ENGAGE_REQUEST = 0x5C
|
||||
ENGAGE_RESULT = 0x5D
|
||||
WEDDING_GIFT = 0x5E
|
||||
WEDDING_MAP_TRANSFER = 0x5F
|
||||
USE_CASH_PET_FOOD = 0x60
|
||||
YELLOW_CHAT = 0x61
|
||||
SHOP_DISCOUNT = 0x62
|
||||
CATCH_MOB = 0x63
|
||||
MAKE_PLAYER_NPC = 0x64
|
||||
PLAYER_NPC = 0x65
|
||||
DISABLE_NPC = 0x66
|
||||
GET_CARD = 0x67
|
||||
CARD_SET = 0x69
|
||||
BOOK_STATS = 0x6A
|
||||
UPDATE_CODEX = 0x6B
|
||||
CARD_DROPS = 0x6C
|
||||
FAMILIAR_INFO = 0x6D
|
||||
CHANGE_HOUR = 0x6F
|
||||
RESET_MINIMAP = 0x70
|
||||
CONSULT_UPDATE = 0x71
|
||||
CLASS_UPDATE = 0x72
|
||||
WEB_BOARD_UPDATE = 0x73
|
||||
SESSION_VALUE = 0x74
|
||||
PARTY_VALUE = 0x75
|
||||
MAP_VALUE = 0x76
|
||||
EXP_BONUS = 0x78
|
||||
POTION_BONUS = 0x79
|
||||
SEND_PEDIGREE = 0x7A
|
||||
OPEN_FAMILY = 0x7B
|
||||
FAMILY_MESSAGE = 0x7C
|
||||
FAMILY_INVITE = 0x7D
|
||||
FAMILY_JUNIOR = 0x7E
|
||||
SENIOR_MESSAGE = 0x7F
|
||||
FAMILY = 0x80
|
||||
REP_INCREASE = 0x81
|
||||
FAMILY_LOGGEDIN = 0x82
|
||||
FAMILY_BUFF = 0x83
|
||||
FAMILY_USE_REQUEST = 0x84
|
||||
LEVEL_UPDATE = 0x85
|
||||
MARRIAGE_UPDATE = 0x86
|
||||
JOB_UPDATE = 0x87
|
||||
MAPLE_TV_MSG = 0x89
|
||||
AVATAR_MEGA_RESULT = 0x8A
|
||||
AVATAR_MEGA = 0x8B
|
||||
AVATAR_MEGA_REMOVE = 0x8C
|
||||
CANCEL_NAME_CHANGE = 0x8D
|
||||
CANCEL_WORLD_TRANSFER = 0x8E
|
||||
CLOSE_HIRED_MERCHANT = 0x8F
|
||||
GM_POLICE = 0x90
|
||||
TREASURE_BOX = 0x91
|
||||
NEW_YEAR_CARD = 0x92
|
||||
RANDOM_MORPH = 0x93
|
||||
CANCEL_NAME_CHANGE_2 = 0x94
|
||||
PENDANT_SLOT = 0x95
|
||||
FOLLOW_REQUEST = 0x96
|
||||
TOP_MSG = 0x97
|
||||
MID_MSG = 0x98
|
||||
CLEAR_MID_MSG = 0x99
|
||||
MAPLE_ADMIN_MSG = 0x9A
|
||||
CAKE_VS_PIE_MSG = 0x9B
|
||||
GM_STORY_BOARD = 0x9C
|
||||
INVENTORY_FULL = 0x9D
|
||||
UPDATE_JAGUAR = 0x9E
|
||||
YOUR_INFORMATION = 0x9F
|
||||
FIND_FRIEND = 0xA0
|
||||
VISITOR = 0xA1
|
||||
PINKBEAN_CHOCO = 0xA2
|
||||
PAM_SONG = 0xA3
|
||||
AUTO_CC_MSG = 0xA4
|
||||
DISALLOW_DELIVERY_QUEST = 0xA5
|
||||
ULTIMATE_EXPLORER = 0xA6
|
||||
PROFESSION_INFO = 0xA8
|
||||
UPDATE_IMP_TIME = 0xA9
|
||||
ITEM_POT = 0xAA
|
||||
GIVE_CHARACTER_SKILL = 0xAE
|
||||
MULUNG_DOJO_RANKING = 0xB1
|
||||
MULUNG_MESSAGE = 0xB4
|
||||
MAGIC_WHEEL_START = 0xB6
|
||||
MAGIC_WHEEL_RECEIVE = 0xB7
|
||||
|
||||
SKILL_MACRO = 0xB8
|
||||
WARP_TO_MAP = 0xB9
|
||||
MTS_OPEN = 0xBA
|
||||
CS_OPEN = 0xBB
|
||||
CHANGE_BACKGROUND = 0xBC
|
||||
LOGIN_WELCOME = 0xBD
|
||||
RESET_SCREEN = 0xBE
|
||||
MAP_BLOCKED = 0xBF
|
||||
SERVER_BLOCKED = 0xC0
|
||||
PARTY_BLOCKED = 0xC1
|
||||
SHOW_EQUIP_EFFECT = 0xC2
|
||||
MULTICHAT = 0xC3
|
||||
WHISPER = 0xC4
|
||||
SPOUSE_CHAT = 0xC5
|
||||
BOSS_ENV = 0xC7
|
||||
MOVE_ENV = 0xC8
|
||||
UPDATE_ENV = 0xC9
|
||||
MAP_EFFECT = 0xCB
|
||||
CASH_SONG = 0xCC
|
||||
GM_EFFECT = 0xCD
|
||||
OX_QUIZ = 0xCE
|
||||
GMEVENT_INSTRUCTIONS = 0xCF
|
||||
CLOCK = 0xD0
|
||||
BOAT_MOVE = 0xD1
|
||||
BOAT_STATE = 0xD2
|
||||
SET_OBJECT_STATE = 0xD6
|
||||
STOP_CLOCK = 0xD7
|
||||
ARIANT_SCOREBOARD = 0xD8
|
||||
PYRAMID_UPDATE = 0xDA
|
||||
PYRAMID_RESULT = 0xDB
|
||||
QUICK_SLOT = 0xDC
|
||||
MOVE_PLATFORM = 0xDD
|
||||
PYRAMID_KILL_COUNT = 0xDF
|
||||
PVP_INFO = 0xE1
|
||||
DIRECTION_STATUS = 0xE2
|
||||
GAIN_FORCE = 0xE3
|
||||
ACHIEVEMENT_RATIO = 0xE4
|
||||
PUBLIC_NPC = 0xE5
|
||||
SPAWN_PLAYER = 0xE6
|
||||
REMOVE_PLAYER_FROM_MAP = 0xE7
|
||||
CHATTEXT = 0xE8
|
||||
CHATTEXT_1 = 0xE9
|
||||
CHALKBOARD = 0xEA
|
||||
UPDATE_CHAR_BOX = 0xEB
|
||||
SHOW_CONSUME_EFFECT = 0xEC
|
||||
SHOW_SCROLL_EFFECT = 0xED
|
||||
SHOW_MAGNIFYING_EFFECT = 0xEF
|
||||
SHOW_POTENTIAL_RESET = 0xF0
|
||||
SHOW_FIREWORKS_EFFECT = 0xF1
|
||||
SHOW_NEBULITE_EFFECT = 0xF2
|
||||
SHOW_FUSION_EFFECT = 0xF3
|
||||
PVP_ATTACK = 0xF4
|
||||
PVP_MIST = 0xF5
|
||||
PVP_COOL = 0xF7
|
||||
TESLA_TRIANGLE = 0xF8
|
||||
FOLLOW_EFFECT = 0xF9
|
||||
SHOW_PQ_REWARD = 0xFA
|
||||
CRAFT_EFFECT = 0xFB
|
||||
CRAFT_COMPLETE = 0xFC
|
||||
HARVESTED = 0xFD
|
||||
PLAYER_DAMAGED = 0xFF
|
||||
NETT_PYRAMID = 0x100
|
||||
SET_PHASE = 0x101
|
||||
PAMS_SONG = 0x103
|
||||
SPAWN_PET = 0x104
|
||||
SPAWN_PET_2 = 0x106
|
||||
MOVE_PET = 0x107
|
||||
PET_CHAT = 0x108
|
||||
PET_NAMECHANGE = 0x109
|
||||
PET_EXCEPTION_LIST = 0x10A
|
||||
PET_COMMAND = 0x10B
|
||||
DRAGON_SPAWN = 0x10C
|
||||
DRAGON_MOVE = 0x10D
|
||||
DRAGON_REMOVE = 0x10E
|
||||
ANDROID_SPAWN = 0x10F
|
||||
ANDROID_MOVE = 0x110
|
||||
ANDROID_EMOTION = 0x111
|
||||
ANDROID_UPDATE = 0x112
|
||||
ANDROID_DEACTIVATED = 0x113
|
||||
SPAWN_FAMILIAR = 0x114
|
||||
MOVE_FAMILIAR = 0x115
|
||||
TOUCH_FAMILIAR = 0x116
|
||||
ATTACK_FAMILIAR = 0x117
|
||||
RENAME_FAMILIAR = 0x118
|
||||
SPAWN_FAMILIAR_2 = 0x119
|
||||
UPDATE_FAMILIAR = 0x11A
|
||||
MOVE_PLAYER = 0x11C
|
||||
CLOSE_RANGE_ATTACK = 0x11E
|
||||
RANGED_ATTACK = 0x11F
|
||||
MAGIC_ATTACK = 0x120
|
||||
ENERGY_ATTACK = 0x121
|
||||
SKILL_EFFECT = 0x122
|
||||
MOVE_ATTACK = 0x123
|
||||
CANCEL_SKILL_EFFECT = 0x124
|
||||
DAMAGE_PLAYER = 0x125
|
||||
FACIAL_EXPRESSION = 0x126
|
||||
SHOW_ITEM_EFFECT = 0x128
|
||||
SHOW_TITLE = 0x12A
|
||||
SHOW_CHAIR = 0x12D
|
||||
UPDATE_CHAR_LOOK = 0x12E
|
||||
SHOW_FOREIGN_EFFECT = 0x12F
|
||||
GIVE_FOREIGN_BUFF = 0x130
|
||||
CANCEL_FOREIGN_BUFF = 0x131
|
||||
UPDATE_PARTYMEMBER_HP = 0x132
|
||||
LOAD_GUILD_NAME = 0x133
|
||||
LOAD_GUILD_ICON = 0x134
|
||||
LOAD_TEAM = 0x135
|
||||
SHOW_HARVEST = 0x137
|
||||
PVP_HP = 0x138
|
||||
CANCEL_CHAIR = 0x13B
|
||||
FACIAL_EXPRESSION_2 = 0x13C
|
||||
SHOW_ITEM_GAIN_INCHAT = 0x13E
|
||||
CURRENT_MAP_WARP = 0x13F
|
||||
MESOBAG_SUCCESS = 0x141
|
||||
MESOBAG_FAILURE = 0x142
|
||||
R_MESOBAG_SUCCESS = 0x143
|
||||
R_MESOBAG_FAILURE = 0x144
|
||||
MAP_FADE = 0x145
|
||||
MAP_FADE_FORCE = 0x146
|
||||
UPDATE_QUEST_INFO = 0x147
|
||||
HP_DECREASE = 0x148
|
||||
PLAYER_HINT = 0x14A
|
||||
PLAY_EVENT_SOUND = 0x14B
|
||||
PLAY_MINIGAME_SOUND = 0x14C
|
||||
MAKER_SKILL = 0x14D
|
||||
OPEN_UI = 0x150
|
||||
OPEN_UI_OPTION = 0x152
|
||||
CYGNUS_INTRO_LOCK = 0x153
|
||||
CYGNUS_INTRO_ENABLE_UI = 0x154
|
||||
CYGNUS_INTRO_DISABLE_UI = 0x155
|
||||
SUMMON_HINT = 0x156
|
||||
SUMMON_HINT_MSG = 0x157
|
||||
ARAN_COMBO = 0x158
|
||||
ARAN_COMBO_RECHARGE = 0x159
|
||||
RANDOM_EMOTION = 0x15A
|
||||
RADIO_SCHEDULE = 0x15D
|
||||
OPEN_SKILL_GUIDE = 0x15E
|
||||
NOTICE_MSG = 0x15F
|
||||
GAME_MESSAGE = 0x160
|
||||
BUFF_ZONE_EFFECT = 0x162
|
||||
GO_CASHSHOP_SN = 0x163
|
||||
DAMAGE_METER = 0x164
|
||||
TIME_BOMB_ATTACK = 0x165
|
||||
FOLLOW_MOVE = 0x166
|
||||
FOLLOW_MSG = 0x167
|
||||
AP_SP_EVENT = 0x169
|
||||
QUEST_GUIDE_NPC = 0x16A
|
||||
REGISTER_FAMILIAR = 0x171
|
||||
FAMILIAR_NAME_ERROR = 0x172
|
||||
CREATE_ULTIMATE = 0x173
|
||||
HARVEST_MESSAGE = 0x174
|
||||
SHOW_MAP_NAME = 0x175
|
||||
OPEN_BAG = 0x176
|
||||
DRAGON_BLINK = 0x177
|
||||
PVP_ICEGAGE = 0x178
|
||||
DIRECTION_INFO = 0x179
|
||||
REISSUE_MEDAL = 0x17A
|
||||
PLAY_MOVIE = 0x17D
|
||||
CAKE_VS_PIE = 0x17F
|
||||
COOLDOWN = 0x181
|
||||
SPAWN_SUMMON = 0x183
|
||||
REMOVE_SUMMON = 0x184
|
||||
MOVE_SUMMON = 0x185
|
||||
SUMMON_ATTACK = 0x186
|
||||
PVP_SUMMON = 0x187
|
||||
SUMMON_SKILL = 0x189
|
||||
SUMMON_SKILL_2 = 0x18A
|
||||
SUMMON_DELAY = 0x18B
|
||||
DAMAGE_SUMMON = 0x18C
|
||||
SPAWN_MONSTER = 0x18D
|
||||
KILL_MONSTER = 0x18E
|
||||
SPAWN_MONSTER_CONTROL = 0x18F
|
||||
MONSTER_CRC_CHANGE = 0x19A
|
||||
REMOVE_TALK_MONSTER = 0x1A3
|
||||
MOVE_MONSTER = 0x190
|
||||
MOVE_MONSTER_RESPONSE = 0x191
|
||||
APPLY_MONSTER_STATUS = 0x193
|
||||
CANCEL_MONSTER_STATUS = 0x194
|
||||
DAMAGE_MONSTER = 0x197
|
||||
SKILL_EFFECT_MOB = 0x198
|
||||
SHOW_MONSTER_HP = 0x19B
|
||||
SHOW_MAGNET = 0x19C
|
||||
ITEM_EFFECT_MOB = 0x19D
|
||||
CATCH_MONSTER = 0x19E
|
||||
MONSTER_PROPERTIES = 0x1A2
|
||||
TALK_MONSTER = 0x1A4
|
||||
CYGNUS_ATTACK = 0x1A8
|
||||
MONSTER_RESIST = 0x1A9
|
||||
MOB_TO_MOB_DAMAGE = 0x1AA
|
||||
SPAWN_NPC = 0x1AC
|
||||
REMOVE_NPC = 0x1AD
|
||||
SPAWN_NPC_REQUEST_CONTROLLER = 0x1AF
|
||||
NPC_ACTION = 0x1B0
|
||||
NPC_SPAWN_EFFECT = 0x1B1
|
||||
NPC_SCRIPTABLE = 0x1B6
|
||||
SPAWN_HIRED_MERCHANT = 0x1B8
|
||||
DESTROY_HIRED_MERCHANT = 0x1B9
|
||||
UPDATE_HIRED_MERCHANT = 0x1BA
|
||||
DROP_ITEM_FROM_MAPOBJECT = 0x1BB
|
||||
REMOVE_ITEM_FROM_MAP = 0x1BD
|
||||
SPAWN_KITE_ERROR = 0x1BE
|
||||
SPAWN_KITE = 0x1BF
|
||||
DESTROY_KITE = 0x1C0
|
||||
SPAWN_MIST = 0x1C1
|
||||
REMOVE_MIST = 0x1C2
|
||||
SPAWN_DOOR = 0x1C3
|
||||
REMOVE_DOOR = 0x1C4
|
||||
MECH_DOOR_SPAWN = 0x1C5
|
||||
MECH_DOOR_REMOVE = 0x1C6
|
||||
REACTOR_HIT = 0x1C7
|
||||
REACTOR_MOVE = 0x1C8
|
||||
REACTOR_SPAWN = 0x1C9
|
||||
REACTOR_DESTROY = 0x1CB
|
||||
SPAWN_EXTRACTOR = 0x1CC
|
||||
REMOVE_EXTRACTOR = 0x1CD
|
||||
ROLL_SNOWBALL = 0x1CE
|
||||
HIT_SNOWBALL = 0x1CF
|
||||
SNOWBALL_MESSAGE = 0x1D0
|
||||
LEFT_KNOCK_BACK = 0x1D1
|
||||
HIT_COCONUT = 0x1D2
|
||||
COCONUT_SCORE = 0x1D3
|
||||
MOVE_HEALER = 0x1D4
|
||||
PULLEY_STATE = 0x1D5
|
||||
MONSTER_CARNIVAL_START = 0x1D6
|
||||
MONSTER_CARNIVAL_OBTAINED_CP = 0x1D7
|
||||
MONSTER_CARNIVAL_STATS = 0x1D8
|
||||
MONSTER_CARNIVAL_SUMMON = 0x1DA
|
||||
MONSTER_CARNIVAL_MESSAGE = 0x1DB
|
||||
MONSTER_CARNIVAL_DIED = 0x1DC
|
||||
MONSTER_CARNIVAL_LEAVE = 0x1DD
|
||||
MONSTER_CARNIVAL_RESULT = 0x1DE
|
||||
MONSTER_CARNIVAL_RANKING = 0x1DF
|
||||
ARIANT_SCORE_UPDATE = 0x1E0
|
||||
SHEEP_RANCH_INFO = 0x1E2
|
||||
SHEEP_RANCH_CLOTHES = 0x1E3
|
||||
WITCH_TOWER = 0x1E4
|
||||
EXPEDITION_CHALLENGE = 0x1E5
|
||||
ZAKUM_SHRINE = 0x1E6
|
||||
CHAOS_ZAKUM_SHRINE = 0x1E7
|
||||
PVP_TYPE = 0x1E8
|
||||
PVP_TRANSFORM = 0x1E9
|
||||
PVP_DETAILS = 0x1EA
|
||||
PVP_ENABLED = 0x1EB
|
||||
PVP_SCORE = 0x1EC
|
||||
PVP_RESULT = 0x1ED
|
||||
PVP_TEAM = 0x1EE
|
||||
PVP_SCOREBOARD = 0x1EF
|
||||
PVP_POINTS = 0x1F1
|
||||
PVP_KILLED = 0x1F2
|
||||
PVP_MODE = 0x1F3
|
||||
PVP_ICEKNIGHT = 0x1F4
|
||||
HORNTAIL_SHRINE = 0x1FA
|
||||
CAPTURE_FLAGS = 0x205
|
||||
CAPTURE_POSITION = 0x206
|
||||
CAPTURE_RESET = 0x207
|
||||
PINK_ZAKUM_SHRINE = 0x208
|
||||
NPC_TALK = 0x209
|
||||
OPEN_NPC_SHOP = 0x20A
|
||||
CONFIRM_SHOP_TRANSACTION = 0x20B
|
||||
OPEN_STORAGE = 0x20E
|
||||
MERCH_ITEM_MSG = 0x20F
|
||||
MERCH_ITEM_STORE = 0x210
|
||||
RPS_GAME = 0x211
|
||||
MESSENGER = 0x212
|
||||
PLAYER_INTERACTION = 0x213
|
||||
TOURNAMENT = 0x214
|
||||
TOURNAMENT_MATCH_TABLE = 0x215
|
||||
TOURNAMENT_SET_PRIZE = 0x216
|
||||
TOURNAMENT_UEW = 0x217
|
||||
TOURNAMENT_CHARACTERS = 0x218
|
||||
WEDDING_PROGRESS = 0x219
|
||||
WEDDING_CEREMONY_END = 0x21A
|
||||
DUEY = 0x21B
|
||||
KEYMAP = 0x22E
|
||||
PET_AUTO_HP = 0x230
|
||||
PET_AUTO_MP = 0x231
|
||||
START_TV = 0x237
|
||||
REMOVE_TV = 0x238
|
||||
ENABLE_TV = 0x239
|
||||
|
||||
CS_CHARGE_CASH = 0x21C
|
||||
CS_UPDATE = 0x21D
|
||||
CS_OPERATION = 0x21E
|
||||
CS_EXP_PURCHASE = 0x21F
|
||||
GIFT_RESULT = 0x220
|
||||
CHANGE_NAME_CHECK = 0x221
|
||||
CHANGE_NAME_RESPONSE = 0x222
|
||||
CHAR_TRANSFER_WORLD = 0x224
|
||||
GACHAPON_STAMPS = 0x225
|
||||
FREE_CASH_ITEM = 0x226
|
||||
CS_SURPRISE = 0x227
|
||||
XMAS_SURPRISE = 0x228
|
||||
ONE_A_DAY = 0x22A
|
||||
NX_SPEND_GIFT = 0x22C
|
||||
|
||||
ALIEN_SOCKET_CREATOR = 0x247
|
||||
BATTLE_RECORD_DAMAGE_INFO = 0x24A
|
||||
CALCULATE_REQUEST_RESULT = 0x24B
|
||||
VICIOUS_HAMMER = 0x24F
|
||||
LUCKY_LOGOUT_GIFT = 0x256
|
||||
|
||||
BOOSTER_PACK = 0x999
|
||||
GET_MTS_TOKENS = 0x999
|
||||
MTS_OPERATION = 0x999
|
||||
BOOSTER_FAMILIAR = 0x999
|
||||
BLOCK_PORTAL = 0x7FFE
|
||||
NPC_CONFIRM = 0x7FFE
|
||||
# PIN_ASSIGNED = 0x7FFE
|
||||
RSA_KEY = 0x7FFE
|
||||
LOGIN_AUTH = 0x7FFE
|
||||
PET_FLAG_CHANGE = 0x7FFE
|
||||
BUFF_BAR = 0x7FFE
|
||||
GAME_POLL_REPLY = 0x7FFE
|
||||
GAME_POLL_QUESTION = 0x7FFE
|
||||
ENGLISH_QUIZ = 0x7FFE
|
||||
FISHING_BOARD_UPDATE = 0x7FFE
|
||||
BOAT_EFFECT = 0x7FFE
|
||||
FISHING_CAUGHT = 0xFF
|
||||
SIDEKICK_OPERATION = 0x7FFE
|
||||
|
||||
|
||||
class SendOps(IntEnum):
|
||||
PONG = 0x2E
|
||||
|
||||
CLIENT_HELLO = 0x14
|
||||
LOGIN_PASSWORD = 0x15
|
||||
GUEST_LOGIN = 0x16
|
||||
CHARLIST_REQUEST = 0x19
|
||||
SERVERSTATUS_REQUEST = 0x1A
|
||||
TOS = 0x1B #NOT NEEDED
|
||||
SERVERLIST_REQUEST = 0x1F
|
||||
REDISPLAY_SERVERLIST = 0x20
|
||||
VIEW_ALL_CHAR = 0x21
|
||||
PICK_ALL_CHAR = 0x22
|
||||
VIEW_SERVERLIST = 0x23
|
||||
CHAR_SELECT_NO_PIC = 0x27
|
||||
CHECK_CHAR_NAME = 0x29
|
||||
CREATE_CHAR = 0x2A
|
||||
CREATE_ULTIMATE = 0x2C
|
||||
DELETE_CHAR = 0x2D
|
||||
CHAR_SELECT = 0x32
|
||||
AUTH_SECOND_PASSWORD = 0x33
|
||||
VIEW_REGISTER_PIC = 0x34
|
||||
VIEW_SELECT_PIC = 0x35
|
||||
CLIENT_START = 0x38
|
||||
CLIENT_FAILED = 0x39
|
||||
CLIENT_ERROR = 0x3B
|
||||
ENABLE_LV50_CHAR = 0x3C
|
||||
CREATE_LV50_CHAR = 0x3D
|
||||
ENABLE_SPECIAL_CREATION = 0x3E
|
||||
CREATE_SPECIAL_CHAR = 0x3F
|
||||
|
||||
PLAYER_LOGGEDIN = 0x28
|
||||
CHANGE_MAP = 0x42
|
||||
CHANGE_CHANNEL = 0x43
|
||||
ENTER_CASH_SHOP = 0x44
|
||||
ENTER_PVP = 0x45
|
||||
ENTER_PVP_PARTY = 0x46
|
||||
LEAVE_PVP = 0x48
|
||||
MOVE_PLAYER = 0x49
|
||||
CANCEL_CHAIR = 0x4B
|
||||
USE_CHAIR = 0x4C
|
||||
CLOSE_RANGE_ATTACK = 0x4D
|
||||
RANGED_ATTACK = 0x4E
|
||||
MAGIC_ATTACK = 0x4F
|
||||
PASSIVE_ENERGY = 0x50
|
||||
TAKE_DAMAGE = 0x52
|
||||
PVP_ATTACK = 0x53
|
||||
GENERAL_CHAT = 0x54
|
||||
CLOSE_CHALKBOARD = 0x55
|
||||
FACE_EXPRESSION = 0x56
|
||||
FACE_ANDROID = 0x57
|
||||
USE_ITEMEFFECT = 0x58
|
||||
WHEEL_OF_FORTUNE = 0x59
|
||||
USE_TITLE = 0x5A
|
||||
CHANGE_SET = 0x61
|
||||
MONSTER_BOOK_DROPS = 0x64
|
||||
NPC_TALK = 0x66
|
||||
NPC_TALK_MORE = 0x68
|
||||
NPC_SHOP = 0x69
|
||||
STORAGE = 0x6A
|
||||
USE_HIRED_MERCHANT = 0x6B
|
||||
MERCH_ITEM_STORE = 0x6C
|
||||
DUEY_ACTION = 0x6D
|
||||
MECH_CANCEL = 0x6E
|
||||
OWL = 0x70
|
||||
OWL_WARP = 0x71
|
||||
ITEM_SORT = 0x73
|
||||
ITEM_GATHER = 0x74
|
||||
ITEM_MOVE = 0x75
|
||||
MOVE_BAG = 0x76
|
||||
SWITCH_BAG = 0x77
|
||||
USE_ITEM = 0x79
|
||||
CANCEL_ITEM_EFFECT = 0x7A
|
||||
USE_SUMMON_BAG = 0x7C
|
||||
PET_FOOD = 0x7D
|
||||
USE_MOUNT_FOOD = 0x7E
|
||||
USE_SCRIPTED_NPC_ITEM = 0x7F
|
||||
USE_RECIPE = 0x80
|
||||
USE_NEBULITE = 0x81
|
||||
USE_ALIEN_SOCKET = 0x82
|
||||
USE_ALIEN_SOCKET_RESPONSE = 0x83
|
||||
USE_NEBULITE_FUSION = 0x84
|
||||
USE_CASH_ITEM = 0x85
|
||||
USE_CATCH_ITEM = 0x999
|
||||
USE_SKILL_BOOK = 0x88
|
||||
USE_OWL_MINERVA = 0x8E
|
||||
USE_TELE_ROCK = 0x8F
|
||||
USE_RETURN_SCROLL = 0x90
|
||||
USE_UPGRADE_SCROLL = 0x91
|
||||
USE_FLAG_SCROLL = 0x92
|
||||
USE_EQUIP_SCROLL = 0x93
|
||||
USE_POTENTIAL_SCROLL = 0x95
|
||||
USE_BAG = 0x98
|
||||
USE_MAGNIFY_GLASS = 0x99
|
||||
DISTRIBUTE_AP = 0x9A
|
||||
AUTO_ASSIGN_AP = 0x9B
|
||||
HEAL_OVER_TIME = 0x9C
|
||||
DISTRIBUTE_SP = 0x9F
|
||||
SPECIAL_MOVE = 0xA0
|
||||
CANCEL_BUFF = 0xA1
|
||||
SKILL_EFFECT = 0xA2
|
||||
MESO_DROP = 0xA3
|
||||
GIVE_FAME = 0xA4
|
||||
CHAR_INFO_REQUEST = 0xA6
|
||||
SPAWN_PET = 0xA7
|
||||
GET_BOOK_INFO = 0xA8
|
||||
USE_FAMILIAR = 0xAA
|
||||
SPAWN_FAMILIAR = 0xAB
|
||||
RENAME_FAMILIAR = 0xAC
|
||||
CANCEL_DEBUFF = 0xAE
|
||||
CHANGE_MAP_SPECIAL = 0xAF
|
||||
USE_INNER_PORTAL = 0xB1
|
||||
TROCK_ADD_MAP = 0xB2
|
||||
REPORT = 0x999
|
||||
QUEST_ACTION = 0xB8
|
||||
REISSUE_MEDAL = 0xB9
|
||||
SKILL_MACRO = 0xBC
|
||||
REWARD_ITEM = 0xBE
|
||||
ITEM_MAKER = 0x999
|
||||
REPAIR_ALL = 0xC4
|
||||
REPAIR = 0xC5
|
||||
SOLOMON = 0xC6
|
||||
GACH_EXP = 0xC7
|
||||
FOLLOW_REQUEST = 0xC8
|
||||
PQ_REWARD = 0xCA
|
||||
FOLLOW_REPLY = 0xCC
|
||||
AUTO_FOLLOW_REPLY = 0x999
|
||||
USE_TREASUER_CHEST = 0x999
|
||||
PROFESSION_INFO = 0xD2
|
||||
USE_POT = 0xD3
|
||||
CLEAR_POT = 0xD4
|
||||
FEED_POT = 0xD5
|
||||
CURE_POT = 0xD6
|
||||
REWARD_POT = 0xD7
|
||||
USE_COSMETIC = 0x999
|
||||
PVP_RESPAWN = 0xD8
|
||||
GAIN_FORCE = 0xDA
|
||||
PARTYCHAT = 0xDD
|
||||
WHISPER = 0xDE
|
||||
SPOUSE_CHAT = 0xDF
|
||||
MESSENGER = 0xE0
|
||||
PLAYER_INTERACTION = 0xE1
|
||||
PARTY_OPERATION = 0xE2
|
||||
DENY_PARTY_REQUEST = 0xE3
|
||||
ALLOW_PARTY_INVITE = 0xE4
|
||||
EXPEDITION_OPERATION = 0xE5
|
||||
EXPEDITION_LISTING = 0xE6
|
||||
GUILD_OPERATION = 0xE7
|
||||
DENY_GUILD_REQUEST = 0xE8
|
||||
ADMIN_COMMAND = 0xE9 #TODO ADD TO SOURCE
|
||||
ADMIN_LOG = 0xEA #TODO ADD TO SOURCE
|
||||
BUDDYLIST_MODIFY = 0xEB
|
||||
NOTE_ACTION = 0xEC
|
||||
USE_DOOR = 0xEE
|
||||
USE_MECH_DOOR = 0xEF
|
||||
CHANGE_KEYMAP = 0xF1
|
||||
RPS_GAME = 0xF2
|
||||
RING_ACTION = 0xF3
|
||||
WEDDING_ACTION = 0xF4
|
||||
ALLIANCE_OPERATION = 0xF8
|
||||
DENY_ALLIANCE_REQUEST = 0xF9
|
||||
REQUEST_FAMILY = 0xFA
|
||||
OPEN_FAMILY = 0xFB
|
||||
FAMILY_OPERATION = 0xFC
|
||||
DELETE_JUNIOR = 0xFD
|
||||
DELETE_SENIOR = 0xFE
|
||||
ACCEPT_FAMILY = 0xFF
|
||||
USE_FAMILY = 0x100
|
||||
FAMILY_PRECEPT = 0x101
|
||||
FAMILY_SUMMON = 0x102
|
||||
BBS_OPERATION = 0x104
|
||||
ENTER_MTS = 0x105
|
||||
NEW_YEAR_CARD = 0x108
|
||||
XMAS_SURPRISE = 0x10A
|
||||
TWIN_DRAGON_EGG = 0x10B
|
||||
ARAN_COMBO = 0x10E
|
||||
TRANSFORM_PLAYER = 0x999
|
||||
CYGNUS_SUMMON = 0x999
|
||||
CRAFT_DONE = 0x110
|
||||
CRAFT_EFFECT = 0x111
|
||||
CRAFT_MAKE = 0x112
|
||||
CHANGE_ROOM_CHANNEL = 0x116
|
||||
EVENT_CARD = 0x117
|
||||
YOUR_INFORMATION = 0x11B
|
||||
FIND_FRIEND = 0x11C
|
||||
PINKBEAN_CHOCO_OPEN = 0x11E
|
||||
PINKBEAN_CHOCO_SUMMON = 0x11F
|
||||
MOVE_PET = 0x123
|
||||
PET_CHAT = 0x124
|
||||
PET_COMMAND = 0x125
|
||||
PET_LOOT = 0x126
|
||||
PET_AUTO_POT = 0x127
|
||||
PET_IGNORE = 0x128
|
||||
MOVE_SUMMON = 0x12B
|
||||
SUMMON_ATTACK = 0x12C
|
||||
DAMAGE_SUMMON = 0x12D
|
||||
SUB_SUMMON = 0x12E
|
||||
REMOVE_SUMMON = 0x12F
|
||||
PVP_SUMMON = 0x130
|
||||
MOVE_DRAGON = 0x133
|
||||
MOVE_ANDROID = 0x137
|
||||
MOVE_FAMILIAR = 0x13B
|
||||
TOUCH_FAMILIAR = 0x13C
|
||||
ATTACK_FAMILIAR = 0x13D
|
||||
REVEAL_FAMILIAR = 0x13E
|
||||
QUICK_SLOT = 0x140
|
||||
#SKILL_MEMORY = 0x141 INT
|
||||
PAM_SONG = 0x145
|
||||
MOVE_LIFE = 0x14F
|
||||
AUTO_AGGRO = 0x150
|
||||
FRIENDLY_DAMAGE = 0x153
|
||||
MONSTER_BOMB = 0x154
|
||||
HYPNOTIZE_DMG = 0x155
|
||||
MOB_BOMB = 0x157
|
||||
MOB_NODE = 0x158
|
||||
DISPLAY_NODE = 0x159
|
||||
NPC_ACTION = 0x15F
|
||||
ITEM_PICKUP = 0x164
|
||||
DAMAGE_REACTOR = 0x167
|
||||
CLICK_REACTOR = 0x168
|
||||
TOUCH_REACTOR = 0x169
|
||||
MAKE_EXTRACTOR = 0x999
|
||||
UPDATE_ENV = 0x16B
|
||||
SNOWBALL = 0x999
|
||||
LEFT_KNOCK_BACK = 0x999
|
||||
COCONUT = 0x999
|
||||
MONSTER_CARNIVAL = 0x17B
|
||||
SHIP_OBJECT = 0x999
|
||||
PARTY_SEARCH_START = 0x184
|
||||
PARTY_SEARCH_STOP = 0x185
|
||||
START_HARVEST = 0x189
|
||||
STOP_HARVEST = 0x18A
|
||||
PUBLIC_NPC = 0x18C
|
||||
CS_UPDATE = 0x192
|
||||
BUY_CS_ITEM = 0x193
|
||||
COUPON_CODE = 0x194
|
||||
UPDATE_QUEST = 0x999
|
||||
QUEST_ITEM = 0x999
|
||||
USE_ITEM_QUEST = 0x999
|
||||
VICIOUS_HAMMER = 0x1AB
|
||||
TOUCHING_MTS = 0x999
|
||||
MTS_TAB = 0x999
|
||||
|
||||
PYRAMID_BUY_ITEM = 0x17E
|
||||
CLASS_COMPETITION = 0x1A6
|
||||
MAGIC_WHEEL = 0x1BD
|
176
packet.py
Normal file
176
packet.py
Normal file
@ -0,0 +1,176 @@
|
||||
from enum import Enum, IntEnum
|
||||
from io import BytesIO
|
||||
from struct import pack, unpack
|
||||
|
||||
from .opcodes import RecvOps, SendOps
|
||||
from .tools import to_string
|
||||
|
||||
|
||||
class ByteBuffer(BytesIO):
|
||||
"""Base class for packet write and read operations"""
|
||||
|
||||
def encode(self, _bytes):
|
||||
self.write(_bytes)
|
||||
return self
|
||||
|
||||
def encode_byte(self, value):
|
||||
if isinstance(value, Enum):
|
||||
value = value.value
|
||||
|
||||
self.write(bytes([value]))
|
||||
return self
|
||||
|
||||
def encode_short(self, value):
|
||||
self.write(pack("H", value))
|
||||
return self
|
||||
|
||||
def encode_int(self, value):
|
||||
self.write(pack("I", value))
|
||||
return self
|
||||
|
||||
def encode_long(self, value):
|
||||
self.write(pack("Q", value))
|
||||
return self
|
||||
|
||||
def encode_buffer(self, buffer):
|
||||
self.write(buffer)
|
||||
return self
|
||||
|
||||
def skip(self, count):
|
||||
self.write(bytes(count))
|
||||
return self
|
||||
|
||||
def encode_string(self, string):
|
||||
self.write(pack("H", len(string)))
|
||||
|
||||
for ch in string:
|
||||
self.write(ch.encode())
|
||||
|
||||
return self
|
||||
|
||||
def encode_fixed_string(self, string, length=13):
|
||||
for i in range(length):
|
||||
if i < len(string):
|
||||
self.write(string[i].encode())
|
||||
continue
|
||||
|
||||
self.encode_byte(0)
|
||||
|
||||
return self
|
||||
|
||||
def encode_hex_string(self, string):
|
||||
string = string.strip(" -")
|
||||
self.write(bytes.fromhex(string))
|
||||
return self
|
||||
|
||||
def decode_byte(self):
|
||||
return self.read(1)[0]
|
||||
|
||||
def decode_bool(self):
|
||||
return bool(self.decode_byte())
|
||||
|
||||
def decode_short(self):
|
||||
return unpack("H", self.read(2))[0]
|
||||
|
||||
def decode_int(self):
|
||||
return unpack("I", self.read(4))[0]
|
||||
|
||||
def decode_long(self):
|
||||
return unpack("Q", self.read(8))[0]
|
||||
|
||||
def decode_buffer(self, size):
|
||||
return self.read(size)
|
||||
|
||||
def decode_string(self):
|
||||
length = self.decode_short()
|
||||
string = ""
|
||||
|
||||
for _ in range(length):
|
||||
string += self.read(1).decode()
|
||||
|
||||
return string
|
||||
|
||||
|
||||
class Packet(ByteBuffer):
|
||||
"""Packet class use in all send / recv opertions
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data: bytes
|
||||
The initial data to load into the packet
|
||||
op_code: :class:`OpCodes`
|
||||
OpCode used to encode the first short onto the packet
|
||||
op_codes: :class:`OpCodes`
|
||||
Which enum to try to get the op_code from
|
||||
|
||||
"""
|
||||
|
||||
_op_codes: type[IntEnum]
|
||||
|
||||
def __init__(self, data=None, op_code=None, raw=False):
|
||||
self.op_code: IntEnum | int
|
||||
|
||||
if not data:
|
||||
data = bytearray()
|
||||
|
||||
if isinstance(data, IntEnum):
|
||||
if op_code and isinstance(op_code, bytearray):
|
||||
op_code, data = data, op_code
|
||||
else:
|
||||
op_code, data = data, bytearray()
|
||||
|
||||
super().__init__(data)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
if isinstance(self.op_code, IntEnum):
|
||||
return self.op_code.name
|
||||
|
||||
return self.op_code
|
||||
|
||||
def to_array(self):
|
||||
return self.getvalue()
|
||||
|
||||
def to_string(self):
|
||||
return to_string(self.getvalue())
|
||||
|
||||
def __len__(self):
|
||||
return len(self.getvalue())
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
return len(self.getvalue())
|
||||
|
||||
|
||||
class iPacket(Packet):
|
||||
_op_codes = RecvOps
|
||||
|
||||
def __init__(self, data=None):
|
||||
super().__init__(data=data)
|
||||
self.op_code = self._op_codes(self.decode_short())
|
||||
|
||||
|
||||
class oPacket(Packet):
|
||||
_op_codes = SendOps
|
||||
|
||||
def __init__(self, op_code=None):
|
||||
super().__init__(op_code=op_code)
|
||||
self.op_code = op_code.value if hasattr(op_code, "value") else op_code
|
||||
if self.op_code:
|
||||
self.encode_short(self.op_code)
|
||||
|
||||
|
||||
class PacketHandler:
|
||||
|
||||
def __init__(self, name, callback, op_code=None, **kwargs):
|
||||
self.name = name
|
||||
self.callback = callback
|
||||
self.op_code = op_code
|
||||
|
||||
|
||||
def packet_handler(op_code=None):
|
||||
|
||||
def wrap(func):
|
||||
return PacketHandler(func.__name__, func, op_code=op_code)
|
||||
|
||||
return wrap
|
125
tools.py
Normal file
125
tools.py
Normal file
@ -0,0 +1,125 @@
|
||||
from asyncio import sleep
|
||||
|
||||
from dataclasses import dataclass, is_dataclass
|
||||
from random import randint
|
||||
|
||||
|
||||
class TagPoint:
|
||||
|
||||
def __init__(self, x=0, y=0):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.x},{self.y}"
|
||||
|
||||
|
||||
class Random:
|
||||
|
||||
def __init__(self):
|
||||
self.seed_1 = randint(1, 2**31 - 1)
|
||||
self.seed_2 = randint(1, 2**31 - 1)
|
||||
self.seed_3 = randint(1, 2**31 - 1)
|
||||
|
||||
def encode(self, packet):
|
||||
packet.encode_int(self.seed_1)
|
||||
packet.encode_int(self.seed_2)
|
||||
packet.encode_int(self.seed_3)
|
||||
|
||||
|
||||
def find(predicate, seq):
|
||||
for element in seq:
|
||||
if predicate(element):
|
||||
return element
|
||||
return None
|
||||
|
||||
|
||||
def get(iterable, **attrs):
|
||||
|
||||
def predicate(elem):
|
||||
for attr, val in attrs.items():
|
||||
nested = attr.split("__")
|
||||
obj = elem
|
||||
for attribute in nested:
|
||||
obj = getattr(obj, attribute)
|
||||
|
||||
if obj != val:
|
||||
return False
|
||||
return True
|
||||
|
||||
return find(predicate, iterable)
|
||||
|
||||
|
||||
def filter_out_to(func, iters, out):
|
||||
new = []
|
||||
|
||||
for item in iters:
|
||||
if func(item):
|
||||
new.append(item)
|
||||
else:
|
||||
out.append(item)
|
||||
|
||||
return new
|
||||
|
||||
|
||||
def first_or_default(list_, f):
|
||||
return next((val for val in list_ if f(val)), None)
|
||||
|
||||
|
||||
def fix_dict_keys(dict_):
|
||||
copy = dict(dict_)
|
||||
|
||||
for key, value in copy.items():
|
||||
if key.isdigit():
|
||||
value = dict_.pop(key)
|
||||
key = int(key)
|
||||
|
||||
if isinstance(value, dict):
|
||||
dict_[key] = fix_dict_keys(value)
|
||||
|
||||
else:
|
||||
dict_[key] = value
|
||||
|
||||
return dict_
|
||||
|
||||
|
||||
def to_string(bytes_):
|
||||
return " ".join([
|
||||
bytes_.hex()[i:i + 2].upper() for i in range(0, len(bytes_.hex()), 2)
|
||||
])
|
||||
|
||||
|
||||
async def wakeup():
|
||||
while True:
|
||||
await sleep(0.01)
|
||||
|
||||
|
||||
def nested_dataclass(*args, **kwargs):
|
||||
|
||||
def wrapper(cls):
|
||||
cls = dataclass(cls, **kwargs)
|
||||
original_init = cls.__init__
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
for name, value in kwargs.items():
|
||||
field_type = cls.__annotations__.get(name, None)
|
||||
|
||||
if is_dataclass(field_type) and isinstance(value, dict):
|
||||
new_obj = field_type(**value)
|
||||
kwargs[name] = new_obj
|
||||
|
||||
original_init(self, *args, **kwargs)
|
||||
|
||||
cls.__init__ = __init__
|
||||
return cls
|
||||
|
||||
return wrapper(args[0]) if args else wrapper
|
||||
|
||||
|
||||
class Manager(list):
|
||||
|
||||
def get(self, search):
|
||||
return first_or_default(self, search)
|
||||
|
||||
def first_or_default(self, func):
|
||||
return next((val for val in self if func(val)), None)
|
Loading…
Reference in New Issue
Block a user