summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesusaves <cpntb1@ymail.com>2022-04-08 22:50:19 -0300
committerJesusaves <cpntb1@ymail.com>2022-04-08 22:50:19 -0300
commitdd323a5f02eb2351be512a6723716756127a8016 (patch)
tree351a0504d5be2a4d7f5417781175f99736084def
parentf29fc21eb8eb7954c03aa80ca60f3a3a6e667eb5 (diff)
downloadrenpy-dd323a5f02eb2351be512a6723716756127a8016.tar.gz
renpy-dd323a5f02eb2351be512a6723716756127a8016.tar.bz2
renpy-dd323a5f02eb2351be512a6723716756127a8016.tar.xz
renpy-dd323a5f02eb2351be512a6723716756127a8016.zip
We'll need to wait for Ren'Py 8 to support the built-in client
-rw-r--r--game/python-extra/tmw/__init__.py119
-rw-r--r--game/python-extra/tmw/char.py139
-rw-r--r--game/python-extra/tmw/login.py174
-rw-r--r--game/python-extra/tmw/map.py11
-rwxr-xr-xgame/python-extra/tmw/packets.py187
-rw-r--r--game/python-extra/tmw/packets_in.py64
-rw-r--r--game/python-extra/tmw/packets_out.py45
7 files changed, 738 insertions, 1 deletions
diff --git a/game/python-extra/tmw/__init__.py b/game/python-extra/tmw/__init__.py
index 252e7c8..7509c43 100644
--- a/game/python-extra/tmw/__init__.py
+++ b/game/python-extra/tmw/__init__.py
@@ -1,6 +1,123 @@
# TMW Plugin, both built-in client as Google Auth workaround
+import socket, traceback
+
+from .login import Login
+from .char import Char
+
+NETLIB_VERSION = 1
+
+running = False
+
+# sockets dict
+sockets = {
+ 'login': socket.socket(socket.AF_INET, socket.SOCK_STREAM),
+ 'char': socket.socket(socket.AF_INET, socket.SOCK_STREAM),
+ 'map': socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+}
def client(OPT, PWD):
print("---- I AM BEING RUN")
- return 0
+ print("++++ OPT %s" % OPT)
+ try:
+ options = OPT.split(" ")
+ bf=""
+ HOST=""
+ PORT=""
+ # "-s %s -y evol2 -p %s -S" % (HOST, PORT)
+ for op in options:
+ # Defines (we don't care for -y or -S)
+ if op == "-s":
+ bf="HOST"
+ elif op == "-p":
+ bf="PORT"
+ # Values
+ elif bf == "HOST":
+ HOST=str(op)
+ bf=""
+ elif bf == "PORT":
+ PORT=str(op)
+ bf=""
+
+ PORT = int(PORT)
+ running = True
+ # FIXME - resolve HOST to an IP?
+ status = loop(running, HOST, PORT)
+ print("---- RESULT %d" % status)
+ return status
+ except:
+ traceback.print_exc()
+ return -1
+
+
+def loop(state, login_ip, login_port):
+ """
+ main network loop
+ """
+ active_server = 1
+ print("Looping while %d" % state)
+
+ while state:
+ try:
+ if((sockets['login'] is not None) and
+ (sockets['char'] is not None) and
+ (sockets['map'] is not None)):
+ if active_server == 1:
+ # login
+ print("LoginServer")
+ sockets['login'].connect((login_ip, login_port))
+ sockets['login'].setblocking(1) # important!
+ login = Login(sockets['login'], True)
+ login.sendServerVersionRequest()
+ login.getUpdateHost()
+ login.sendLoginRegister('testacc', 'testacc')
+ # catch login errors if any?
+ # (may i need to change the packet handling then, caching)
+ _char = login.getCharLoginData()
+ sockets['login'].close()
+ active_server += 1
+ elif active_server == 2:
+ # char server
+ print("CharServer")
+ # TODO: choose server (just first one rn.)
+ sockets['char'].connect(
+ (_char['char_server'][0]['host'], _char['char_server'][0]['port']))
+ sockets['char'].setblocking(1) # important!
+ char = Char(sockets['char'], True)
+ char.sendLogin(_char, 26)
+ char.skipPacket(0x8481)
+ char.skipPacket(0x082d)
+ char.getLoginChars()
+ # TODO: choose character (just first one rn.)
+ char.selectChar(0)
+ char.getMapInfo()
+ active_server += 1
+ elif active_server == 3:
+ # map server
+ print("MapServer")
+ # currently just exit, debugging doesnt work while getting floaded with prints
+ return 0
+ else:
+ # this should not happen, but why not
+ return -1
+
+ else:
+ print("SOCKET ERROR")
+ raise Exception("socket bind error").with_traceback(None)
+ except socket.error:
+ print('ERROR: Bind failed.')
+ return -2
+ except KeyboardInterrupt:
+ # makes 'ctrl + c' work again in a while loop,
+ # so we can kill all sockets when they arnt death allrdy.
+ print("INFO: exited")
+ if sockets['map'] is not None:
+ sockets['map'].close()
+ if sockets['char'] is not None:
+ sockets['char'].close()
+ if sockets['login'] is not None:
+ sockets['login'].close()
+ return 0
+
+ print("INFO: Aborted.")
+ return 0
diff --git a/game/python-extra/tmw/char.py b/game/python-extra/tmw/char.py
new file mode 100644
index 0000000..3e3d19f
--- /dev/null
+++ b/game/python-extra/tmw/char.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python3
+
+import struct
+from .packets_in import in_packets
+from .packets_out import out_packets
+
+
+class Char(object):
+ """
+ Author: jak1
+ License: GPLv2+
+ State: Testing (Draft)
+ Class Char:
+ handles Char Packets (some packets can only get used in the right order!)
+ socket: bound char server socket
+ debugging: enabled if True, disabled otherwise
+
+ NOTE: does the stored data allready have the right type? may i need to convert/cast them.
+ """
+
+ def __init__(self, socket, debugging=False):
+ self.socket = socket
+ self.debugging = debugging
+
+ def sendLogin(self, char_logindata, client_protocol_version):
+ """
+ Function: sendLogin
+ send login data to char server
+ char_logindata: (dict) (of Login.getCharLoginData)
+ returns: None
+ """
+ out_packets[0x0065].send_data(
+ self.socket, [char_logindata['account_id'], char_logindata['session_id_p1'], char_logindata['session_id_p2'], client_protocol_version, char_logindata['gender']], self.debugging)
+ out_packets[0x0065].process_data()
+
+ def getLoginChars(self):
+ """
+ Function: getLoginChars
+ recvs character-slots
+ returns: TODO: desc-> [chars(...)]
+ """
+ # FIXME: can hit the size, better using 2048 (breaking or lagging), while logging in it
+ # should not matter to have lag, so i guess its better save then sorry
+ in_packets[0x006b].recv_data(self.socket, 1024, self.debugging)
+ in_packets[0x006b].process_data()
+
+ char_packet_len = 144
+ chars_raw = in_packets[0x006b].get_list_data()[10]
+ char_count = int(len(chars_raw) / char_packet_len)
+
+ # slot[%] = {char_data}
+ slots = [{}]
+
+ for x in range(char_count):
+
+ GID, exp, money, jobexp, joblevel, bodystate, healthstate, effectstate, virtue, honor, jobpoint, hp, \
+ maxhp, sp, maxsp, speed, job, head, weapon, level, sppoint, accessory, shield, accessory2, accessory3, \
+ headpalette, bodypalette, name, Str, Agi, Vit, Int, Dex, Luk, CharNum, haircolor, bIsChangedCharName, \
+ lastMap, DeleteDate, Robe, SlotAddon, RenameAddon = struct.unpack(
+ "<L I I I I I I I I I H I I H H H H H H H H H H H H H H 24s s s s s s s s s H 16s I I I I", chars_raw[(x * char_packet_len):(x * char_packet_len) + char_packet_len])
+ slots[x] = {
+ "GID": GID,
+ "exp": exp,
+ "money": money,
+ "jobexp": jobexp,
+ "joblevel": joblevel,
+ "bodystate": bodystate,
+ "healthstate": healthstate,
+ "effectstate": effectstate,
+ "virtue": virtue,
+ "honor": honor,
+ "jobpoint": jobpoint,
+ "hp": hp,
+ "maxhp": maxhp,
+ "sp": sp,
+ "maxsp": maxsp,
+ "speed": speed,
+ "job": job,
+ "head": head,
+ "weapon": weapon,
+ "level": level,
+ "sppoint": sppoint,
+ "accessory": accessory,
+ "shield": shield,
+ "accessory2": accessory2,
+ "accessory3": accessory3,
+ "headpalette": headpalette,
+
+ "bodypalette": bodypalette,
+ "name": name.decode('ascii'),
+ "Str": int.from_bytes(Str, 'little'),
+ "Agi": int.from_bytes(Agi, 'little'),
+ "Vit": int.from_bytes(Vit, 'little'),
+ "Int": int.from_bytes(Int, 'little'),
+ "Dex": int.from_bytes(Dex, 'little'),
+ "Luk": int.from_bytes(Luk, 'little'),
+
+ "CharNum": int.from_bytes(CharNum, 'little'),
+ "haircolor": int.from_bytes(haircolor, 'little'),
+ "bIsChangedCharName": bIsChangedCharName,
+
+ "lastMap": lastMap.decode('ascii'),
+ "DeleteDate": DeleteDate,
+ "Robe": Robe,
+ "SlotAddon": SlotAddon,
+ "RenameAddon": RenameAddon
+ }
+ continue
+ # FIXME: adding header values? do we need it? beside the "time created" maybe
+ return (char_count, slots)
+
+ def selectChar(self, slot):
+ """
+ Function: selectChar
+ select a character using the slot id (0 is the first)
+ slot: character slot id (int)
+ returns: None
+ """
+ out_packets[0x0066].send_data(
+ self.socket, [slot], self.debugging)
+ out_packets[0x0066].process_data()
+
+ def getMapInfo(self):
+ """
+ Function: getMapInfo
+ recv information about the map server
+ returns: tuple(char_id(int), map_name(str), host(str), port(int))
+ """
+ in_packets[0x0ac5].recv_data(self.socket, 28, self.debugging)
+ in_packets[0x0ac5].process_data()
+ _, char_id, map_name, host, port = in_packets[0x0ac5].get_list_data()
+ host = "{}.{}.{}.{}".format(*(host))
+ return (char_id, map_name, host, port)
+
+ def skipPacket(self, packet_id, size=1024):
+ if in_packets[packet_id].p_len != -1:
+ size = in_packets[packet_id].p_len
+ in_packets[packet_id].recv_data(self.socket, size, self.debugging)
+ return None
diff --git a/game/python-extra/tmw/login.py b/game/python-extra/tmw/login.py
new file mode 100644
index 0000000..4199f7e
--- /dev/null
+++ b/game/python-extra/tmw/login.py
@@ -0,0 +1,174 @@
+#!/usr/bin/env python3
+
+import struct
+import binascii
+from .packets_in import in_packets
+from .packets_out import out_packets
+
+
+class Login(object):
+ """
+ Author: jak1
+ License: GPLv2+
+ State: Testing (Draft)
+ Class Login:
+ handles Login Packets (some packets can only get used in the right order!)
+ socket: bound login server socket
+ debugging: enabled if True, disabled otherwise
+
+ NOTE: does the stored data allready have the right type? may i need to convert/cast them.
+ """
+
+ def __init__(self, socket, debugging=False):
+ self.socket = socket
+ self.debugging = debugging
+
+ def sendServerVersionRequest(self, version=26):
+ """
+ FIXME: arg? (26 or 28? packet version or client version? [totaly confusing X_X])
+ """
+ out_packets[0x7530].send_data(self.socket, [version], self.debugging)
+
+ def getServerVersion(self):
+ """
+ TODO: WIP
+ """
+ in_packets[0x7531].recv_data(self.socket, 1024, self.debugging)
+ in_packets[0x7531].process_data()
+ #['H', 'H', 'L', 'L', 'L', '*s']
+
+ def getUpdateHost(self):
+ """
+ Function: getUpdateHost
+ getting the updatehost URL
+ returns: updatehost (str)
+ """
+ in_packets[0x0063].recv_data(self.socket, 1024, self.debugging)
+ in_packets[0x0063].process_data()
+ return in_packets[0x0063].p_data_list[2]
+
+ def sendLoginRegister(self, username, password, clientversion=26):
+ """
+ Function: sendLoginRegister
+ auth. a registred Useraccount with username and password
+ username: login username
+ password: login password
+ clientversion: client packet version (default=26)
+ different versions are may not supported rn.
+ returns: None
+ """
+ _flag = 0x03
+ out_packets[0x064].send_data(
+ self.socket, [clientversion, bytes(username, "ascii"), bytes(password, "ascii"), _flag], self.debugging)
+
+ def getCharLoginData(self):
+ """
+ Function: getCharLoginData
+ gets CharServer auth. data, and all char-servers
+ TODO: store session_id_*, account_id, gender, server_name
+ returns: servers (dict)
+ structure:
+ "session_id_p1", "session_id_p2", "account_id",
+ "last_ip", "last_login", "gender",
+ "char_server": [ "host", "port", "server_name", "current_online" ]
+ """
+ in_packets[0x0ac4].recv_data(self.socket, 1024, self.debugging)
+ in_packets[0x0ac4].process_data()
+ server_len = 160
+ s_count = int(in_packets[0x0ac4].p_len / server_len)
+ server = [{}]
+ for x in range(s_count):
+ offset = x * server_len
+ host, port, name, online, twitter, twitter_flag = struct.unpack(
+ "<4s H 20s H H H 128x",
+ in_packets[0x0ac4].p_data_list[10][offset:offset + server_len])
+
+ host = "{}.{}.{}.{}".format(*(host))
+ server[x] = {
+ "host": host,
+ "port": port,
+ "server_name": name,
+ "current_online": online
+ }
+ servers = {
+ "session_id_p1": in_packets[0x0ac4].p_data_list[2],
+ "session_id_p2": in_packets[0x0ac4].p_data_list[4],
+ "account_id": in_packets[0x0ac4].p_data_list[3],
+ "last_ip": in_packets[0x0ac4].p_data_list[5],
+ "last_login": in_packets[0x0ac4].p_data_list[6],
+ "gender": in_packets[0x0ac4].p_data_list[8],
+ "char_server": server
+ }
+ return servers
+
+ def getLoginError(self):
+ """
+ Function: getLoginError
+ NOTE: ONLY CALL FROM LOGIN!
+ traceback error codes for failed Login
+ returns: (
+ 0x00 - The ID is not registered.
+ 0x01 - Incorrect Password.
+ 0x02 - The ID is expired.
+ 0x03 - Rejected from Server.
+ 0x04 - You have been blocked by the GM team.
+ 0x05 - Client version is too low (client out of date).
+ 0x06 - Your are temporarily prohibited from logging in.
+ 0x07 - Server is jammed due to over populated.
+ 0x08 - No MSG (actually, all states after 9 except 99 are No MSG, use only this)
+ 0x63 - This ID has been completely erased.
+ ,
+ Message
+ ) (tuple(int, str))
+ """
+ in_packets[0x006a].recv_data(self.socket, 1024, self.debugging)
+ in_packets[0x006a].process_data()
+ return (in_packets[0x006a].p_data_list[2], in_packets[0x006a].p_data_list[3])
+
+ def getLoginError(self):
+ """
+ Function: getLoginError
+ NOTE: ONLY CALL FROM LOGIN!
+ returns: tuple(id(int), error_msg(str))
+ """
+ in_packets[0x0081].recv_data(self.socket, 3, self.debugging)
+ in_packets[0x0081].process_data()
+ error_code = in_packets[0x0081].get_list_data()[1]
+ errors = {
+ 0: "Authentication failed.",
+ 1: "No servers available.",
+ 2: "Account allready in use.",
+ 3: "Speed hack detected.",
+ 4: "Server full.",
+ 5: "Sorry, you are underaged.",
+ 7: "", # just exit with 7 as exit_code
+ 8: "Duplicated login.",
+ 9: "To many connections from same ip.",
+ 10: "Not paid for this time.",
+ 11: "Pay suspended.",
+ 12: "Pay changed.",
+ 13: "Pay wrong ip.",
+ 14: "Pay game room.",
+ 15: "Disconnect forced by GM.",
+ 16: "Ban japan refuse.",
+ 17: "Ban japan refuse.",
+ 18: "Remained other account.",
+ 100: "Ip unfair.", # translation issue?
+ 101: "Ip count all.", # ?!
+ 102: "Ip count.", # ?!
+ 103: "Memory.",
+ 104: "Memory.",
+ 105: "Han valid.",
+ 106: "Ip limited access.",
+ 107: "Over characters list.",
+ 108: "Ip blocked.",
+ 109: "Invalid password count.",
+ 110: "Not allowed race.",
+ 113: "Access restricted in hours 00:00 to 06:00.",
+ 115: "You were banned."
+ }
+
+ if error_code not in errors:
+ return (error_code, "Unknown connection error.")
+ else:
+ return (error_code, errors[error_code])
diff --git a/game/python-extra/tmw/map.py b/game/python-extra/tmw/map.py
new file mode 100644
index 0000000..5dd2109
--- /dev/null
+++ b/game/python-extra/tmw/map.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python3
+from .packets_in import in_packets
+from .packets_out import out_packets
+
+
+class Map(object):
+ def __init__(self, socket, debugging=False):
+ self.socket = socket
+ self.debugging = debugging
+
+# placeholder
diff --git a/game/python-extra/tmw/packets.py b/game/python-extra/tmw/packets.py
new file mode 100755
index 0000000..9fac95b
--- /dev/null
+++ b/game/python-extra/tmw/packets.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python3
+import struct
+import binascii
+
+"""
+ Struct Module and its pattern
+ https://docs.python.org/3/library/struct.html
+"""
+pattern = {
+ 'x': 1, # padding byte(s)
+ 'c': 1, # char(s)
+ 's': 1, # char[(s)]
+ 'b': 1, # signed char int8
+ 'B': 1, # unsigned char uint8
+ '?': 1, # bool (use b and B insteed)
+ 'h': 2, # signed short int16
+ 'H': 2, # unsigned short uint16 (p_id, p_len)
+ 'i': 4, # signed integer int32
+ 'I': 4, # unsigned integer uint32
+ 'l': 4, # signed long
+ 'L': 4, # unsigned long
+ 'f': 4, # float
+ 'd': 8 # double int64
+}
+
+
+def get_pid(raw_data, _type="int"):
+ """
+ returns parsed p_id from raw_data
+ _type: "int", "str", "hex"
+ """
+ if(_type == "str"):
+ return str(int.from_bytes(raw_data[0:2], 'little'))
+ elif(_type == "int"):
+ return int.from_bytes(raw_data[0:2], 'little')
+ elif(_type == "hex"):
+ return hex(int.from_bytes(raw_data[0:2], 'little'))
+
+# > class Packet
+
+
+class Packet(object):
+ """
+ Author: jak1
+ License: GPLv2+
+ State: Testing
+ class Packet:
+ handles in- and out-packets from hercules based servers (herc.ws)
+ https://github.com/HerculesWS/Hercules/wiki/Packets
+ goal:
+ standalone handling hercules/evol packets as library
+ TODO: struct parsing (with dynamic len on arrayed stuff, like items-in-inventory, or map-servers...)
+
+ p_id: packet id, also known as packet type (int)
+ p_struct: pattern structure (TODO: list, dict, obj, set?)
+ p_len: packet size (default -1) -1 packets send the packet size byte 2:4 otherwise we need to predefine it!
+ p_response: p_id to response (int) (default 0) (XXX: could be always PING packet?)
+ p_skip: skip this whole packet (default False)
+ returns: None
+ """
+ __slots__ = ('p_name', 'p_id', 'p_len', 'p_struct',
+ 'p_data', 'p_data_list', 'p_response', 'p_skip')
+
+ def __init__(self, p_name, p_id, p_struct, p_len=-1, p_response=0, p_skip=False):
+ self.p_name = p_name
+ self.p_id = p_id
+ # it is always!! the len including the packet-id (and length byte in case its given)
+ self.p_len = p_len
+ self.p_struct = p_struct
+ self.p_data = None
+ self.p_data_list = ()
+ self.p_response = p_response
+ self.p_skip = p_skip
+ pass
+
+ def set_data(self, raw_data):
+ """
+ sets p_data to the given bytestream
+ returns: None
+ """
+ self.p_data = raw_data
+ if (self.p_id != get_pid(self.p_data)):
+ #print(
+ # f"WARN: self.p_id({self.p_id}, 0x{self.p_id:04x}) differs raw_data({get_pid(self.p_data)}, {get_pid(self.p_data,'hex')})!")
+ print("WARN: Self PID differs")
+ if (self.p_len < 0):
+ # sets the length in case it is -1 by default
+ self.p_len = int.from_bytes(self.p_data[2:4], 'little')
+
+ ll = 0
+ for a in self.p_struct:
+ if(a[0] != '*'):
+ if(a[0].isnumeric()):
+ if(a[1].isnumeric()):
+ ll += int((str(str(a[0])+str(a[1]))) * pattern[a[-1]])
+ else:
+ ll += int((str(a[0])) * pattern[a[-1]])
+ else:
+ ll += pattern[a[-1]]
+ rep_l = self.p_len - ll
+ c = 0
+ for a in self.p_struct:
+ if(a[0] == '*'):
+ self.p_struct[c] = str(str(rep_l)+str(a[1]))
+ c += 1
+ # this trimms all offset bytes (NOTE: can break, keep an eye on it!)
+ self.p_data = self.p_data[0:self.p_len]
+ pass
+
+ def process_data(self):
+ """
+ NOTE: set_data(raw_data) OR send_data(p_data_struct) needs to be called first!
+ XXX: may rename both methodes to anything similar
+ processes p_data using p_struct and inserts it to p_data_list
+ returns True on success; False otherwise
+ """
+ if(self.p_len < 0):
+ # NOTE: just test cases, idk if i also should just use one methode to set and process (lets see, if we need both seperated)
+ print("you need to set_data before!")
+ return False
+ if(self.p_data == None):
+ print(
+ "p_data for 0x{self.p_id:04x} was never set. can't process a 'None' value!")
+ return False
+ try:
+ print(" - {self.p_name}") # packet name
+ print(" - {self.p_len}") # packet len
+ print(" - {self.p_struct}") # unpack struct
+ up = "<" + (''.join(self.p_struct))
+ self.p_data_list = struct.unpack(up, (self.p_data))
+ return True
+ except:
+ print(
+ "something went wrong while processing data (p_id: {get_pid(self.p_data)}, {get_pid(self.p_data,'hex')})")
+ return False
+
+ def recv_data(self, socket, size, debug_packet=False):
+ ret = socket.recv(size)
+ self.set_data(ret)
+ if debug_packet:
+ print(
+ "INFO: packet {self.p_name} [0x{self.p_id:04x}] has been recv.")
+ pass
+
+ def send_data(self, socket, p_data_struct, debug_packet=False):
+ """
+ prepare data for sending.
+ p_data_struct: like function arguments, str username, str password ...
+ returns: None
+ """
+ p_data = [self.p_id]
+ p_data.extend(p_data_struct)
+ rep_l = self.p_len - 4
+ c = 0
+ for a in self.p_struct:
+ if(a[0] == '*'):
+ self.p_struct[c] = str(str(rep_l)+str(a[1]))
+ c += 1
+ up = "<" + (''.join(self.p_struct))
+ packer = struct.Struct(up)
+ values = tuple(p_data)
+ pack = packer.pack(*values)
+ socket.sendall(pack)
+ if debug_packet:
+ print(
+ "INFO: packet {self.p_name} [0x{self.p_id:04x}] has been send.")
+ self.p_data = bytearray().fromhex(hex(pack))
+ pass
+
+ def get_list_data(self):
+ """
+ returns the processed data as list
+ """
+ return self.p_data_list
+
+ def get_raw_data(self):
+ """
+ returns the raw data as bytestream
+ """
+ return self.p_data
+
+# < class Packet
+
+
+if __name__ == "__main__":
+ print("this is a module, and can't be used as regular main file!")
+ exit(1)
diff --git a/game/python-extra/tmw/packets_in.py b/game/python-extra/tmw/packets_in.py
new file mode 100644
index 0000000..74cee0d
--- /dev/null
+++ b/game/python-extra/tmw/packets_in.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+from .packets import Packet
+
+"""
+ Packet dict
+ p_id also known as packet_type
+ : Packet(
+ p_name: PACKET_NAME
+ p_id: FIXME: can i get th own id inside the packet, so i dont need them twice?
+ p_struct: [byte structure] @see pattern in packets.py
+ p_len: packet size
+ p_response: response p_id
+ p_skip: skips the whole packet
+ )
+ returns: None
+"""
+in_packets = {
+ # SMSG_%
+ # HPM: https://gitlab.com/evol/evol-hercules/-/blob/master/src/elogin/init.c#L49
+ 0x7531: Packet("SERVER_VERSION_RESPONSE", 0x7531, ['H', 'H', 'L', 'L', 'L', '*s'], -1),
+ # HPM: https://gitlab.com/evol/evol-hercules/-/blob/master/src/elogin/init.c#L50
+ 0x0063: Packet("UPDATE_HOST", 0x0063, ['H', 'H', '*s'], -1),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/login/packets_ac_struct.h#L72
+ 0x0ac4: Packet("LOGIN_DATA", 0x0ac4, ['H', 'H', 'I', 'I', 'I', 'I', '24s', 'H', 'b', '17s', '*s'], -1),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/char/char.c#L2229
+ 0x006b: Packet("CHAR_LOGIN", 0x006b, ['H', 'H', 's', 's', 's', '4s', 'L', 'L', 'L', '7s', '*s'], -1),
+ 0x082d: Packet("CHAR_LOGIN2", 0x082d, [], 29),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/char/char.c#L4574 XXX: 28/156 ?!
+ 0x0ac5: Packet("CHAR_MAP_INFO", 0x0ac5, ['H', 'I', '16s', '4s', 'H'], 28),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/clif.c#L10603
+ 0x006a: Packet("LOGIN_ERROR", 0x006a, ['H', 'b', '20s'], 23),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/login/lclif.c#L330
+ 0x0b02: Packet("LOGIN_ERROR2", 0x0b02, ['H', 'L' '20s'], 26),
+ # NOTE: WHY are there 3 packets for error handling on the login server o_O
+ 0x0081: Packet("LOGIN_CONNECTION_ERROR3", 0x0081, ['H', 'b'], 3),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/char/char.c#L2429
+ 0x006c: Packet("CHAR_LOGIN_ERROR", 0x006c, ['H', 'b'], 3),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/clif.c#L723
+ 0x02eb: Packet("MAP_LOGIN_SUCCESS", 0x02eb, ['H', 'L', '3s', 's', 's', 'H'], 13),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/clif.c#L10687 # XXX: ignored packet
+ 0x0283: Packet("MAP_ACCOUNT_ID", 0x0283, ['H', 'L'], 6, p_skip=True),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/clif.c#L2017
+ 0x0091: Packet("PLAYER_WARP", 0x0091, ['H', '16s', 'H', 'H'], 22),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/packets_struct.h#L3275
+ 0x0b1d: Packet("MAP_PING2", 0x0b1d, ['H'], 2),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/clif.c#L11909 FIXME: len 5 or 7?
+ 0x0437: Packet("PLAYER_CHANGE_ACT", 0x0437, [], 7),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/clif.c#L6117
+ 0x043f: Packet("BEING_STATUS_CHANGE", 0x043f, [], 25),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/clif.c#L11248
+ 0x007f: Packet("SERVER_PING", 0x007f, ['H', 'L'], 6),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/clif.c#L886
+ 0x0080: Packet("BEING_REMOVE", 0x0080, ['H', 'L', 'b'], 7),
+ # HERC/M+: 'Dull' ?
+ 0x0894: Packet("IGNORED", 0x0894, [], -1, p_skip=True),
+ 0x8481: Packet("IGNORED", 0x8481, [], 4, p_skip=True),
+ # M+: https://gitlab.com/manaplus/manaplus/-/blob/master/src/net/eathena/mail2recv.cpp#L329 # NOTE: has subpacket ((len(packet)-6?)/43?)
+ 0x0ac2: Packet("MAIL2_MAIL_LIST_PAGE", 0x0ac2, ['H', 'H', 'b', 'b', '43s'], -1)
+
+}
+
+if __name__ == "__main__":
+ print("this is a module, and can't be used as regular main file!")
+ exit(1)
diff --git a/game/python-extra/tmw/packets_out.py b/game/python-extra/tmw/packets_out.py
new file mode 100644
index 0000000..a8385bc
--- /dev/null
+++ b/game/python-extra/tmw/packets_out.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+from .packets import Packet
+
+"""
+ Packet dict
+ p_id also known as packet_type
+ : Packet(
+ p_name: PACKET_NAME
+ p_id: FIXME: can i get th own id inside the packet, so i dont need them twice?
+ p_struct: [byte structure] @see pattern in packets.py
+ p_len: packet size
+ p_response: response p_id
+ p_skip: skips the whole packet
+ )
+ returns: None
+"""
+out_packets = {
+ # CMSG_%
+ # HPM: https://gitlab.com/evol/evol-hercules/-/blob/master/src/elogin/init.c#L45
+ 0x7530: Packet("SERVER_VERSION_REQUEST", 0x7530, ['H', 'b', '19x'], 22),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/login/packets_ca_struct.h#L52 FIXME: encryption, hashing?!
+ 0x0064: Packet("LOGIN_REGISTER", 0x0064, ['H', 'I', '24s', '24s', 'b'], 55),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/char/char.c#L5223
+ 0x0065: Packet("CHAR_SERVER_CONNECT", 0x0065, ['H', 'I', 'I', 'I', 'H', 'b'], 17),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/char/char.c#L5233
+ 0x0066: Packet("CHAR_SELECT", 0x0066, ['H', 'b'], 3),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/packets.h#L1641
+ 0x089c: Packet("MAP_SERVER_CONNECT", 0x089c, ['H', 'I', 'I', 'I', 'I', 'b'], 19),
+ # NOTE: HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/clif.c#L10625
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/packets.h#L40
+ 0x007e: Packet("MAP_PING", 0x007e, ['H'], 2),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/packets.h#L39
+ 0x007d: Packet("MAP_LOADED", 0x007d, ['H'], 2),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/char/char.c#L5274 FIXME: len 6? why do i have a len of 56? (maybe appending packets?)
+ 0x0187: Packet("CHAR_PING", 0x0187, [], 56),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/packets.h#L130
+ 0x018a: Packet("CLIENT_QUIT", 0x018a, [], 4),
+ # HERC: https://gitlab.com/evol/hercules/-/blob/master/src/map/packets.h#L361
+ 0x00f3: Packet("CHAT_MESSAGE", 0x00f3, ['H', 'H', '???'], -1),
+
+}
+
+if __name__ == "__main__":
+ print("this is a module, and can't be used as regular main file!")
+ exit(1)