summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rwxr-xr-xtools/colorize11
-rwxr-xr-xtools/config.py684
-rwxr-xr-xtools/debug-debug-scripts107
-rw-r--r--tools/debug-debug.gdb5
-rwxr-xr-xtools/nightly111
-rwxr-xr-xtools/protocol.py3733
6 files changed, 3997 insertions, 654 deletions
diff --git a/tools/colorize b/tools/colorize
index ae4cb56..ce6f410 100755
--- a/tools/colorize
+++ b/tools/colorize
@@ -12,6 +12,10 @@ def color(i):
return '\x1b[%dm' % (90 + (i - 8))
def main(argv):
+ # can't change buffering on sys.stdout after creation using python APIs
+ # so do our own buffering
+ buffer = []
+
colors = {}
while argv:
arg0 = argv[0]
@@ -26,12 +30,13 @@ def main(argv):
arg = argv[0]
c = colors.get('', '')
e = c and '\x1b[m'
- print(c, arg, e, sep='', end=''),
+ buffer.extend([c, arg, e])
for arg in argv[1:]:
c = colors.get(arg, '')
e = c and '\x1b[m'
- print(' ', c, arg, e, sep='', end=''),
- print()
+ buffer.extend([' ', c, arg, e])
+ buffer.append('\n')
+ sys.stdout.write(''.join(buffer))
sys.stdout.flush()
os.execvp(argv[0], argv)
diff --git a/tools/config.py b/tools/config.py
new file mode 100755
index 0000000..aeb4e99
--- /dev/null
+++ b/tools/config.py
@@ -0,0 +1,684 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+# config.py - generator for config file parsers
+#
+# Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+#
+# This file is part of The Mana World (Athena server)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import print_function
+
+import glob
+import os
+
+from protocol import OpenWrite
+
+
+generated = '// This is a generated file, edit %s instead\n' % __file__
+
+copyright = '''// {filename} - {description}
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+'''
+
+
+class AnyHeader(object):
+ __slots__ = ('name')
+
+ def __init__(self, name):
+ self.name = name
+
+class SystemHeader(AnyHeader):
+ __slots__ = ()
+ meta = 0
+
+ def relative_to(self, path):
+ return '<%s>' % self.name
+
+class Header(AnyHeader):
+ __slots__ = ()
+ meta = 1
+
+ def relative_to(self, path):
+ return '"%s"' % os.path.relpath(self.name, path)
+
+
+class ConfigType(object):
+ __slots__ = ()
+
+class SimpleType(ConfigType):
+ __slots__ = ('name', 'headers')
+
+ def __init__(self, name, headers):
+ self.name = name
+ self.headers = frozenset(headers)
+
+ def __repr__(self):
+ return 'SimpleType(%r, %r)' % (self.name, self.headers)
+
+ def type_name(self):
+ return self.name
+
+ def dump_extract(self, cpp, var):
+ cpp.write(
+'''
+ if (!extract(value.data, &{var}))
+ {{
+ value.span.error("Failed to extract value"_s);
+ return false;
+ }}
+'''.lstrip('\n').format(var=var))
+
+class PhonyType(ConfigType):
+ __slots__ = ('type', 'name', 'call', 'headers')
+
+ def __init__(self, type, name, call, extra_headers):
+ self.type = type
+ self.name = name
+ self.call = call
+ self.headers = type.headers | extra_headers
+
+ def __repr__(self):
+ return 'PhonyType(%r, %r, %r, %r)' % (self.type, self.name, self.call, self.headers)
+
+ def type_name(self):
+ return '// special %s' % self.type.type_name()
+
+ def dump_extract(self, cpp, var):
+ cpp.write(' %s %s;\n' % (self.type.type_name(), self.name))
+ self.type.dump_extract(cpp, self.name)
+ cpp.write(' %s\n' % self.call)
+
+class TransformedType(ConfigType):
+ __slots__ = ('type', 'transform', 'headers')
+
+ def __init__(self, type, transform, extra_headers=set()):
+ self.type = type
+ self.transform = transform
+ self.headers = type.headers | extra_headers
+
+ def __repr__(self):
+ return 'TransformedType(%r, %r)' % (self.type, self.transform)
+
+ def type_name(self):
+ return self.type.type_name()
+
+ def dump_extract(self, cpp, var):
+ self.type.dump_extract(cpp, var)
+ cpp.write(' %s;\n' % self.transform)
+
+class BoundedType(ConfigType):
+ __slots__ = ('type', 'low', 'high', 'headers')
+
+ def __init__(self, type, low, high, extra_headers=set()):
+ assert isinstance(type, ConfigType)
+ self.type = type
+ self.low = low
+ self.high = high
+ self.headers = type.headers | extra_headers
+
+ def __repr__(self):
+ return 'BoundedType(%r, %r, %r, %r)' % (self.type, self.low, self.high, self.headers)
+
+ def type_name(self):
+ return self.type.type_name()
+
+ def dump_extract(self, cpp, var):
+ self.type.dump_extract(cpp, var)
+ cpp.write(
+'''
+ if (!({low} <= {var} && {var} <= {high}))
+ {{
+ value.span.error("Value of {name} not in range [{low}, {high}]"_s);
+ return false;
+ }}
+'''.format(low=self.low, high=self.high, var=var, name=var.split('.')[-1]))
+
+class MinBoundedType(ConfigType):
+ __slots__ = ('type', 'low', 'headers')
+
+ def __init__(self, type, low, extra_headers=set()):
+ assert isinstance(type, ConfigType)
+ self.type = type
+ self.low = low
+ self.headers = type.headers | extra_headers
+
+ def __repr__(self):
+ return 'MinBoundedType(%r, %r, %r, %r)' % (self.type, self.low, self.headers)
+
+ def type_name(self):
+ return self.type.type_name()
+
+ def dump_extract(self, cpp, var):
+ self.type.dump_extract(cpp, var)
+ cpp.write(
+'''
+ if (!({low} <= {var}))
+ {{
+ value.span.error("Value of {name} not at least {low}"_s);
+ return false;
+ }}
+'''.format(low=self.low, var=var, name=var.split('.')[-1]))
+
+class Option(object):
+ __slots__ = ('name', 'type', 'default', 'headers')
+
+ def __init__(self, name, type, default, extra_headers=set()):
+ self.name = name
+ self.type = type
+ self.default = default
+ self.headers = type.headers | extra_headers
+
+ def dump1(self, hpp):
+ hpp.write(' %s %s = %s;\n' % (self.type.type_name(), self.name, self.default))
+ def dump2(self, cpp, x):
+ # NOTE about hashing
+ # dead simple hash: pack 6 bits of first 5 letters into an int
+ y = self.name[:5]
+ if x != y:
+ if x is not None:
+ cpp.write(' break;\n')
+ c0 = y[0] if len(y) > 0 else '\\0'
+ c1 = y[1] if len(y) > 1 else '\\0'
+ c2 = y[2] if len(y) > 2 else '\\0'
+ c3 = y[3] if len(y) > 3 else '\\0'
+ c4 = y[4] if len(y) > 4 else '\\0'
+ assert len(y) >= 3, '<-- change this number in the source file for: %r' % self.name
+ cpp.write(" case (('%s' << 24) | ('%s' << 18) | ('%s' << 12) | ('%s' << 6) | ('%s' << 0)):\n" % (c0, c1, c2, c3, c4))
+ cpp.write(' if (key.data == "{name}"_s)\n'.format(name=self.name))
+ cpp.write(' {\n')
+ self.type.dump_extract(cpp, 'conf.%s' % self.name)
+ cpp.write(' return true;\n')
+ cpp.write(' }\n')
+ return y
+
+class Group(object):
+ __slots__ = ('name', 'options', 'extra_headers')
+
+ def __init__(self, name):
+ self.name = name
+ self.options = {}
+ self.extra_headers = []
+
+ def extra(self, h):
+ self.extra_headers.append(h)
+
+ def opt(self, name, type, default, extra_headers=set(), pre=None, post=None, min=None, max=None):
+ assert name not in self.options, 'Duplicate option name: %s' % name
+ assert isinstance(default, str)
+ if pre is not None:
+ type = TransformedType(type, pre)
+ if min is not None:
+ if max is not None:
+ type = BoundedType(type, min, max)
+ else:
+ type = MinBoundedType(type, min)
+ else:
+ assert max is None
+ if post is not None:
+ type = TransformedType(type, post)
+ self.options[name] = rv = Option(name, type, default, extra_headers)
+ return rv
+
+ def dump_in(self, path, namespace_name):
+ if namespace_name == 'char':
+ namespace_name += '_'
+ var_name = '%s_conf' % self.name
+ class_name = var_name.replace('_', ' ').title().replace(' ', '')
+ short_hpp_name = '%s.hpp' % var_name
+ hpp_name = os.path.join(path, short_hpp_name)
+ short_cpp_name = '%s.cpp' % var_name
+ cpp_name = os.path.join(path, short_cpp_name)
+
+ values = sorted(self.options.values(), key=lambda o: o.name)
+
+ desc = 'Config for %s::%s' % (namespace_name, self.name)
+ with OpenWrite(hpp_name) as hpp, \
+ OpenWrite(cpp_name) as cpp:
+ hpp.write('#pragma once\n')
+ hpp.write(copyright.format(filename=short_hpp_name, description=desc))
+ hpp.write('\n')
+ hpp.write(generated)
+ cpp.write('#include "%s"\n' % short_hpp_name)
+ cpp.write(copyright.format(filename=short_cpp_name, description=desc))
+ cpp.write('\n')
+ cpp.write(generated)
+ headers = {
+ Header('src/io/fwd.hpp'),
+ Header('src/strings/fwd.hpp')
+ }
+ for o in values:
+ headers |= o.headers
+
+ hpp.write('\n')
+ hpp.write('#include "fwd.hpp"\n')
+ for h in sorted(headers, key=lambda h: (h.meta, h.name)):
+ hpp.write('#include %s\n' % h.relative_to(path))
+ hpp.write('\n')
+ cpp.write('\n')
+ for h in [
+ SystemHeader('bitset'),
+ Header('src/io/extract.hpp'),
+ Header('src/io/span.hpp'),
+ Header('src/mmo/extract_enums.hpp'),
+ Header('src/high/extract_mmo.hpp'),
+ ] + self.extra_headers:
+ cpp.write('#include %s\n' % h.relative_to(path))
+ cpp.write('\n')
+ cpp.write('#include "../poison.hpp"\n')
+ cpp.write('\n')
+
+ hpp.write('namespace tmwa\n{\n')
+ cpp.write('namespace tmwa\n{\n')
+ cpp.write('''
+static __attribute__((unused))
+bool extract(XString str, bool *v)
+{
+ if (str == "true"_s || str == "on"_s || str == "yes"_s
+ || str == "oui"_s || str == "ja"_s
+ || str == "si"_s || str == "1"_s)
+ {
+ *v = 1;
+ return true;
+ }
+ if (str == "false"_s || str == "off"_s || str == "no"_s
+ || str == "non"_s || str == "nein"_s || str == "0"_s)
+ {
+ *v = 0;
+ return true;
+ }
+ return false;
+}
+
+static __attribute__((unused))
+bool extract(XString str, std::bitset<256> *v)
+{
+ if (!str)
+ {
+ v->reset();
+ return true;
+ }
+ for (uint8_t c : str)
+ {
+ (*v)[c] = true;
+ }
+ return true;
+}
+''')
+ hpp.write('namespace %s\n{\n' % namespace_name)
+ cpp.write('namespace %s\n{\n' % namespace_name)
+ hpp.write('struct %s\n{\n' % class_name)
+ for o in values:
+ o.dump1(hpp)
+ hpp.write('}; // struct %s\n' % class_name)
+ hpp.write('bool parse_%s(%s& conf, io::Spanned<XString> key, io::Spanned<ZString> value);\n' % (var_name, class_name))
+ hpp.write('} // namespace %s\n' % namespace_name)
+ hpp.write('} // namespace tmwa\n')
+ cpp.write('bool parse_%s(%s& conf, io::Spanned<XString> key, io::Spanned<ZString> value)\n{\n' % (var_name, class_name))
+ # see NOTE about hashing in Option.dump2
+ cpp.write(' int key_hash = 0;\n')
+ cpp.write(' if (key.data.size() > 0)\n')
+ cpp.write(' key_hash |= key.data[0] << 24;\n')
+ cpp.write(' if (key.data.size() > 1)\n')
+ cpp.write(' key_hash |= key.data[1] << 18;\n')
+ cpp.write(' if (key.data.size() > 2)\n')
+ cpp.write(' key_hash |= key.data[2] << 12;\n')
+ cpp.write(' if (key.data.size() > 3)\n')
+ cpp.write(' key_hash |= key.data[3] << 6;\n')
+ cpp.write(' if (key.data.size() > 4)\n')
+ cpp.write(' key_hash |= key.data[4] << 0;\n')
+ cpp.write(' switch (key_hash)\n')
+ cpp.write(' {\n')
+ x = None
+ for o in values:
+ x = o.dump2(cpp, x)
+ cpp.write(' break;\n')
+ cpp.write(' } // switch\n')
+ cpp.write(' key.span.error("Unknown config key"_s);\n')
+ cpp.write(' return false;\n')
+ cpp.write('} // fn parse_%s_conf()\n' % var_name)
+ cpp.write('} // namespace %s\n' % namespace_name)
+ cpp.write('} // namespace tmwa\n')
+
+class Realm(object):
+ __slots__ = ('path', 'groups')
+
+ def __init__(self, path):
+ self.path = path
+ self.groups = {}
+
+ def conf(self, name=None):
+ if not name:
+ name = self.path.split('/')[-1]
+ assert name not in self.groups, 'Duplicate group name: %s' % name
+ self.groups[name] = rv = Group(name)
+ return rv
+
+ def dump(self):
+ for g in self.groups.values():
+ g.dump_in(self.path, self.path.split('/')[-1])
+
+class Everything(object):
+ __slots__ = ('realms')
+
+ def __init__(self):
+ self.realms = {}
+
+ def realm(self, path):
+ assert path not in self.realms, 'Duplicate realm path: %s' % path
+ self.realms[path] = rv = Realm(path)
+ return rv
+
+ def dump(self):
+ for g in glob.glob('src/*/*_conf.[ch]pp'):
+ os.rename(g, g + '.old')
+ for v in self.realms.values():
+ v.dump()
+ for g in glob.glob('src/*/*_conf.[ch]pp.old'):
+ print('Obsolete: %s' % g)
+ os.remove(g)
+
+
+def lit(s):
+ return '"%s"_s' % s.replace('\\', '\\\\').replace('"', '\\"')
+
+def build_config():
+ rv = Everything()
+
+ # realms
+ login_realm = rv.realm('src/login')
+ admin_realm = rv.realm('src/admin')
+ char_realm = rv.realm('src/char')
+ map_realm = rv.realm('src/map')
+
+ # confs
+ login_conf = login_realm.conf()
+ login_lan_conf = login_realm.conf('login_lan')
+
+ admin_conf = admin_realm.conf()
+
+ char_conf = char_realm.conf()
+ char_lan_conf = char_realm.conf('char_lan')
+ inter_conf = char_realm.conf('inter')
+
+ map_conf = map_realm.conf()
+ battle_conf = map_realm.conf('battle')
+
+ # headers
+ cstdint_sys = SystemHeader('cstdint')
+ vector_sys = SystemHeader('vector')
+ bitset_sys = SystemHeader('bitset')
+
+ ip_h = Header('src/net/ip.hpp')
+ rstring_h = Header('src/strings/rstring.hpp')
+ literal_h = Header('src/strings/literal.hpp')
+ ids_h = Header('src/mmo/ids.hpp')
+ strs_h = Header('src/mmo/strs.hpp')
+ timer_th = Header('src/net/timer.t.hpp')
+ login_th = Header('src/login/login.t.hpp')
+ udl_h = Header('src/ints/udl.hpp')
+ net_point_h = Header('src/proto2/net-Point.hpp')
+ char_h = Header('src/char/char.hpp')
+ map_h = Header('src/map/map.hpp')
+ map_th = Header('src/map/map.t.hpp')
+ npc_h = Header('src/map/npc.hpp')
+ npc_parse_h = Header('src/map/npc-parse.hpp')
+
+ map_conf.extra(npc_parse_h)
+
+ # types
+ bool = SimpleType('bool', set())
+ #double = SimpleType('double', set())
+ u8 = SimpleType('uint8_t', {cstdint_sys})
+ u16 = SimpleType('uint16_t', {cstdint_sys})
+ u32 = SimpleType('uint32_t', {cstdint_sys})
+ u64 = SimpleType('uint64_t', {cstdint_sys})
+ i8 = SimpleType('int8_t', {cstdint_sys})
+ i16 = SimpleType('int16_t', {cstdint_sys})
+ i32 = SimpleType('int32_t', {cstdint_sys})
+ i64 = SimpleType('int64_t', {cstdint_sys})
+
+ percent = i32
+ perk = i32
+ per10k = i32
+ #per10kd = double
+ per10kd = i32
+
+ IP4Address = SimpleType('IP4Address', {ip_h})
+ IP4Mask = SimpleType('IP4Mask', {ip_h})
+ IpSet = SimpleType('std::vector<IP4Mask>', {vector_sys, ip_h})
+ RString = SimpleType('RString', {rstring_h, literal_h})
+ GmLevel = SimpleType('GmLevel', {ids_h})
+ hours = SimpleType('std::chrono::hours', {timer_th})
+ seconds = SimpleType('std::chrono::seconds', {timer_th})
+ milliseconds = SimpleType('std::chrono::milliseconds', {timer_th})
+ ACO = SimpleType('ACO', {login_th})
+ ServerName = SimpleType('ServerName', {strs_h})
+ AccountName = SimpleType('AccountName', {strs_h})
+ AccountPass = SimpleType('AccountPass', {strs_h})
+ Point = SimpleType('Point', {net_point_h})
+ CharName = SimpleType('CharName', {strs_h})
+ CharBitset = SimpleType('std::bitset<256>', {bitset_sys})
+ MapName = SimpleType('MapName', {strs_h})
+ ATK = SimpleType('ATK', {map_th})
+
+ addmap = PhonyType(MapName, 'name', 'map_addmap(name);', {map_h})
+ delmap = PhonyType(MapName, 'name', 'map_delmap(name);', {map_h})
+ addnpc = PhonyType(RString, 'npc', 'npc_addsrcfile(npc);', {npc_h})
+ delnpc = PhonyType(RString, 'npc', 'npc_delsrcfile(npc);', {npc_h})
+
+
+ # options
+ login_lan_conf.opt('lan_char_ip', IP4Address, 'IP4_LOCALHOST')
+ login_lan_conf.opt('lan_subnet', IP4Mask, 'IP4Mask(IP4_LOCALHOST, IP4_BROADCAST)')
+
+ login_conf.opt('admin_state', bool, 'false')
+ login_conf.opt('admin_pass', AccountPass, '{}')
+ login_conf.opt('ladminallowip', IpSet, '{}')
+ login_conf.opt('gm_pass', RString, '{}')
+ login_conf.opt('level_new_gm', GmLevel, 'GmLevel::from(60_u32)', {udl_h})
+ login_conf.opt('new_account', bool, 'false')
+ login_conf.opt('login_port', u16, '6901', min='1024')
+ login_conf.opt('account_filename', RString, lit('save/account.txt'))
+ login_conf.opt('gm_account_filename', RString, lit('save/gm_account.txt'))
+ login_conf.opt('gm_account_filename_check_timer', seconds, '15_s')
+ login_conf.opt('login_log_filename', RString, lit('log/login.log'))
+ login_conf.opt('display_parse_login', bool, 'false')
+ login_conf.opt('display_parse_admin', bool, 'false')
+ login_conf.opt('display_parse_fromchar', i32, '0', min='0', max='2')
+ login_conf.opt('min_level_to_connect', GmLevel, 'GmLevel::from(0_u32)', {udl_h})
+ login_conf.opt('order', ACO, 'ACO::DENY_ALLOW')
+ login_conf.opt('allow', IpSet, '{}')
+ login_conf.opt('deny', IpSet, '{}')
+ login_conf.opt('anti_freeze_enable', bool, 'false')
+ login_conf.opt('anti_freeze_interval', seconds, '15_s')
+ login_conf.opt('update_host', RString, '{}')
+ login_conf.opt('main_server', ServerName, '{}')
+ login_conf.opt('userid', AccountName, '{}')
+ login_conf.opt('passwd', AccountPass, '{}')
+
+
+ admin_conf.opt('login_ip', IP4Address, 'IP4_LOCALHOST')
+ admin_conf.opt('login_port', u16, '6901', min='1024')
+ admin_conf.opt('admin_pass', AccountPass, 'stringish<AccountPass>("admin"_s)')
+ admin_conf.opt('ladmin_log_filename', RString, lit('log/ladmin.log'))
+
+
+ char_lan_conf.opt('lan_map_ip', IP4Address, 'IP4_LOCALHOST')
+ char_lan_conf.opt('lan_subnet', IP4Mask, 'IP4Mask(IP4_LOCALHOST, IP4_BROADCAST)')
+
+ char_conf.opt('userid', AccountName, '{}')
+ char_conf.opt('passwd', AccountPass, '{}')
+ char_conf.opt('server_name', ServerName, '{}')
+ char_conf.opt('login_ip', IP4Address, '{}')
+ char_conf.opt('login_port', u16, '6901', min='1024')
+ char_conf.opt('char_ip', IP4Address, '{}')
+ char_conf.opt('char_port', u16, '6121', min='1024')
+ char_conf.opt('char_txt', RString, '{}')
+ char_conf.opt('max_connect_user', u32, '0')
+ char_conf.opt('autosave_time', seconds, 'DEFAULT_AUTOSAVE_INTERVAL', {char_h}, min='1_s')
+ char_conf.opt('start_point', Point, '{ {"001-1.gat"_s}, 273, 354 }')
+ char_conf.opt('unknown_char_name', CharName, 'stringish<CharName>("Unknown"_s)')
+ char_conf.opt('char_log_filename', RString, lit('log/char.log'))
+ char_conf.opt('char_name_letters', CharBitset, '{}')
+ char_conf.opt('online_txt_filename', RString, lit('online.txt'))
+ char_conf.opt('online_html_filename', RString, lit('online.html'))
+ char_conf.opt('online_gm_display_min_level', GmLevel, 'GmLevel::from(20_u32)', {udl_h})
+ char_conf.opt('online_refresh_html', u32, '20', min=1)
+ char_conf.opt('anti_freeze_enable', bool, 'false')
+ char_conf.opt('anti_freeze_interval', seconds, '6_s', min='5_s')
+
+ inter_conf.opt('storage_txt', RString, lit('save/storage.txt'))
+ inter_conf.opt('party_txt', RString, lit('save/party.txt'))
+ inter_conf.opt('accreg_txt', RString, lit('save/accreg.txt'))
+ inter_conf.opt('party_share_level', u32, '10')
+
+ map_conf.opt('userid', AccountName, '{}')
+ map_conf.opt('passwd', AccountPass, '{}')
+ map_conf.opt('char_ip', IP4Address, '{}')
+ map_conf.opt('char_port', u16, '6121', min='1024')
+ map_conf.opt('map_ip', IP4Address, '{}')
+ map_conf.opt('map_port', u16, '5121', min='1024')
+ map_conf.opt('map', addmap, '{}')
+ map_conf.opt('delmap', delmap, '{}')
+ map_conf.opt('npc', addnpc, '{}')
+ map_conf.opt('delnpc', delnpc, '{}')
+ map_conf.opt('autosave_time', seconds, 'DEFAULT_AUTOSAVE_INTERVAL', {map_h})
+ map_conf.opt('motd_txt', RString, lit('conf/motd.txt'))
+ map_conf.opt('mapreg_txt', RString, lit('save/mapreg.txt'))
+ map_conf.opt('gm_log', RString, '{}')
+ map_conf.opt('log_file', RString, '{}')
+
+ battle_conf.opt('enemy_critical', bool, 'false')
+ battle_conf.opt('enemy_critical_rate', percent, '100')
+ battle_conf.opt('enemy_str', bool, 'true')
+ battle_conf.opt('enemy_perfect_flee', bool, 'false')
+ battle_conf.opt('casting_rate', percent, '100')
+ battle_conf.opt('delay_rate', percent, '100')
+ battle_conf.opt('delay_dependon_dex', bool, 'false')
+ battle_conf.opt('skill_delay_attack_enable', bool, 'false')
+ battle_conf.opt('monster_skill_add_range', i32, '0')
+ battle_conf.opt('player_damage_delay', bool, '1')
+ battle_conf.opt('flooritem_lifetime', milliseconds, 'LIFETIME_FLOORITEM', min='1_s')
+ battle_conf.opt('item_auto_get', bool, 'false')
+ battle_conf.opt('item_first_get_time', milliseconds, '3_s')
+ battle_conf.opt('item_second_get_time', milliseconds, '1_s')
+ battle_conf.opt('item_third_get_time', milliseconds, '1_s')
+ battle_conf.opt('base_exp_rate', percent, '100')
+ battle_conf.opt('job_exp_rate', percent, '100')
+ battle_conf.opt('death_penalty_type', i32, '0', min='0', max='2')
+ battle_conf.opt('death_penalty_base', per10kd, '0')
+ battle_conf.opt('death_penalty_job', per10kd, '0')
+ battle_conf.opt('restart_hp_rate', percent, '0', min='0', max='100')
+ battle_conf.opt('restart_sp_rate', percent, '0', min='0', max='100')
+ battle_conf.opt('monster_hp_rate', percent, '0')
+ battle_conf.opt('monster_max_aspd', milliseconds, '199_ms', pre='conf.monster_max_aspd = 2000_ms - conf.monster_max_aspd * 10;', min='10_ms', max='1000_ms')
+ battle_conf.opt('atcommand_gm_only', bool, '0')
+ battle_conf.opt('atcommand_spawn_quantity_limit', i32, '{}')
+ battle_conf.opt('gm_all_equipment', GmLevel, 'GmLevel::from(0_u32)', {udl_h})
+ battle_conf.opt('monster_active_enable', bool, 'true')
+ battle_conf.opt('mob_skill_use', bool, 'true')
+ battle_conf.opt('mob_count_rate', percent, '100')
+ battle_conf.opt('basic_skill_check', bool, 'true')
+ battle_conf.opt('player_invincible_time', milliseconds, '5_s')
+ battle_conf.opt('player_pvp_time', milliseconds, '5_s')
+ battle_conf.opt('skill_min_damage', bool, 'false')
+ battle_conf.opt('natural_healhp_interval', milliseconds, '6_s', {map_h}, min='NATURAL_HEAL_INTERVAL')
+ battle_conf.opt('natural_healsp_interval', milliseconds, '8_s', {map_h}, min='NATURAL_HEAL_INTERVAL')
+ battle_conf.opt('natural_heal_weight_rate', percent, '50', min='50', max='101')
+ battle_conf.opt('arrow_decrement', bool, 'true')
+ battle_conf.opt('max_aspd', milliseconds, '199_ms', pre='conf.max_aspd = 2000_ms - conf.max_aspd * 10;', min='10_ms', max='1000_ms')
+ battle_conf.opt('max_hp', i32, '32500', min='100', max='1000000')
+ battle_conf.opt('max_sp', i32, '32500', min='100', max='1000000')
+ battle_conf.opt('max_lv', i32, '99')
+ battle_conf.opt('max_parameter', i32, '99', min='10', max='10000')
+ battle_conf.opt('monster_skill_log', bool, 'false')
+ battle_conf.opt('battle_log', bool, 'false')
+ battle_conf.opt('save_log', bool, 'false')
+ battle_conf.opt('error_log', bool, 'true')
+ battle_conf.opt('etc_log', bool, 'true')
+ battle_conf.opt('save_clothcolor', bool, 'false')
+ battle_conf.opt('undead_detect_type', i32, '0', min='0', max='2')
+ battle_conf.opt('agi_penaly_type', i32, '0', min='0', max='2')
+ battle_conf.opt('agi_penaly_count', i32, '3', min='2')
+ battle_conf.opt('agi_penaly_num', i32, '0')
+ battle_conf.opt('vit_penaly_type', i32, '0', min='0', max='2')
+ battle_conf.opt('vit_penaly_count', i32, '3', min='2')
+ battle_conf.opt('vit_penaly_num', i32, '0')
+ battle_conf.opt('mob_changetarget_byskill', bool, 'false')
+ battle_conf.opt('player_attack_direction_change', bool, 'true')
+ battle_conf.opt('monster_attack_direction_change', bool, 'true')
+ battle_conf.opt('display_delay_skill_fail', bool, 'true')
+ battle_conf.opt('prevent_logout', bool, 'true')
+ battle_conf.opt('alchemist_summon_reward', bool, '{}')
+ battle_conf.opt('maximum_level', i32, '255')
+ battle_conf.opt('drops_by_luk', percent, '0')
+ battle_conf.opt('monsters_ignore_gm', bool, '{}')
+ battle_conf.opt('multi_level_up', bool, 'false')
+ battle_conf.opt('pk_mode', bool, 'false')
+ battle_conf.opt('agi_penaly_count_lv', ATK, 'ATK::FLEE')
+ battle_conf.opt('vit_penaly_count_lv', ATK, 'ATK::DEF')
+ battle_conf.opt('hide_GM_session', bool, 'false')
+ battle_conf.opt('invite_request_check', bool, 'true')
+ battle_conf.opt('disp_experience', bool, '0')
+ battle_conf.opt('hack_info_GM_level', GmLevel, 'GmLevel::from(60_u32)', {udl_h})
+ battle_conf.opt('any_warp_GM_min_level', GmLevel, 'GmLevel::from(20_u32)', {udl_h})
+ battle_conf.opt('min_hair_style', i32, '0')
+ battle_conf.opt('max_hair_style', i32, '20')
+ battle_conf.opt('min_hair_color', i32, '0')
+ battle_conf.opt('max_hair_color', i32, '9')
+ battle_conf.opt('min_cloth_color', i32, '0')
+ battle_conf.opt('max_cloth_color', i32, '4')
+ battle_conf.opt('castrate_dex_scale', i32, '150')
+ battle_conf.opt('area_size', i32, '14')
+ battle_conf.opt('chat_lame_penalty', i32, '2')
+ battle_conf.opt('chat_spam_threshold', seconds, '10_s', min='0_s', max='32767_s')
+ battle_conf.opt('chat_spam_flood', i32, '10', min='0', max='32767')
+ battle_conf.opt('chat_spam_ban', hours, '1_h', min='0_h', max='32767_h')
+ battle_conf.opt('chat_spam_warn', i32, '8', min='0', max='32767')
+ battle_conf.opt('chat_maxline', i32, '255', min='1', max='512')
+ battle_conf.opt('packet_spam_threshold', seconds, '2_s', min='0_s', max='32767_s')
+ battle_conf.opt('packet_spam_flood', i32, '30', min='0', max='32767')
+ battle_conf.opt('packet_spam_kick', bool, 'true')
+ battle_conf.opt('mask_ip_gms', bool, 'true')
+ battle_conf.opt('drop_pickup_safety_zone', i32, '20')
+ battle_conf.opt('itemheal_regeneration_factor', i32, '1')
+ battle_conf.opt('mob_splash_radius', i32, '-1', min='-1')
+
+ return rv
+
+def main():
+ cfg = build_config()
+ cfg.dump()
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/debug-debug-scripts b/tools/debug-debug-scripts
index e5eeb6c..2112a6e 100755
--- a/tools/debug-debug-scripts
+++ b/tools/debug-debug-scripts
@@ -21,12 +21,15 @@ copyright = '''
// along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
+import glob
import itertools
import os
import subprocess
import sys
import tempfile
+import protocol
+
error = False
def eprint(s):
@@ -63,58 +66,74 @@ def get_classes_from_file(a):
def c_quote(s):
s = s.replace('\\', '\\\\')
+ s = s.replace('\n', '\\n')
s = s.replace('"', '\\"')
return '"' + s + '"'
-def gen_test(name, expr, expected):
- print('static')
- print('void %s()' % name)
- print('{')
- print(' auto value = %s;' % expr)
- print(' const char *expected = %s;' % c_quote(expected))
- print(' do_breakpoint(value, expected);')
- print('}')
+def gen_test(name, expr, expected, w):
+ print('static', file=w)
+ print('void %s()' % name, file=w)
+ print('{', file=w)
+ print(' auto&& value = %s;' % expr, file=w)
+ print(' const char *expected = %s;' % c_quote(expected), file=w)
+ print(' do_breakpoint(value, expected);', file=w)
+ print('}', file=w)
def main(args):
- print('// test.cpp - generated by', __file__)
- print(copyright)
- print('#include <cstdio>')
- print('// just mention "fwd.hpp" and "../poison.hpp" to make formatter happy')
- print('namespace tmwa')
- print('{')
- print('} // namespace tmwa')
- print()
- print('template<class T>')
- print('__attribute__((noinline))')
- print('void do_breakpoint(const T& value, const char *expected)')
- print('{')
- print(' (void)value;')
- print(' (void)expected;')
- print(' if (!expected) printf("printer test: %p = %s\\n", &value, expected);')
- print('}')
-
- names = []
+ outdir = args[0]
+ args = args[1:]
+
+ for g in glob.glob(os.path.join(outdir, '*.[ch]pp')):
+ os.rename(g, g + '.old')
+
for a in args:
+ names = []
basename, ext = os.path.splitext(a)
assert ext == '.py'
- print()
- print('// Tests from', a)
- header = basename + '.hpp'
- print('#include "%s"' % header)
-
- for (k, tests, extra) in get_classes_from_file(a):
- print()
- print('// Tests for', k)
- print(extra)
- for (i, (expr, expected)) in enumerate(tests):
- name = 'testset_%s_subtest_%d' % (k, i)
- gen_test(name, expr, expected)
- names.append(name)
- print('int main()')
- print('{')
- for n in names:
- print(' %s();' % n)
- print('}')
+ newbase = basename.split('src/')[1].replace('/', '-')
+ out = os.path.join(outdir, newbase + '.cpp')
+ with protocol.OpenWrite(out) as w:
+ print('// %s.cpp - generated by %s from %s' % (newbase, __file__, a), file=w)
+ print(copyright, file=w)
+ print('#include <cstdio>', file=w)
+ print('// just mention "fwd.hpp" and "../poison.hpp" to make formatter happy', file=w)
+ print('namespace tmwa', file=w)
+ print('{', file=w)
+ print(' void do_nothing_asan_is_funny_global_constructor();', file=w)
+ print(' void do_nothing_asan_is_funny_global_constructor() {}', file=w)
+ print('} // namespace tmwa', file=w)
+ print(file=w)
+ print('template<class T>', file=w)
+ print('__attribute__((noinline))', file=w)
+ print('void do_breakpoint(const T& value, const char *expected)', file=w)
+ print('{', file=w)
+ print(' (void)value;', file=w)
+ print(' (void)expected;', file=w)
+ print(' if (!expected) printf("printer test: %p = %s\\n", &value, expected);', file=w)
+ print('}', file=w)
+ print(file=w)
+ print('// Tests from', a, file=w)
+ header = basename + '.hpp'
+ print('#include "%s"' % header, file=w)
+
+ for (k, tests, extra) in get_classes_from_file(a):
+ print(file=w)
+ print('// Tests for', k, file=w)
+ print(extra, file=w)
+ for (i, (expr, expected)) in enumerate(tests):
+ name = 'testset_%s_subtest_%d' % (k, i)
+ gen_test(name, expr, expected, w)
+ names.append(name)
+ print('int main()', file=w)
+ print('{', file=w)
+ for n in names:
+ print(' %s();' % n, file=w)
+ print('}', file=w)
+
+ for g in glob.glob(os.path.join(outdir, '*.old')):
+ print('Obsolete: %s' % g)
+ os.remove(g)
+
if error and not os.getenv('TMWA_FORCE_GENERATE'):
sys.exit(1)
diff --git a/tools/debug-debug.gdb b/tools/debug-debug.gdb
index 72a45e1..79046c6 100644
--- a/tools/debug-debug.gdb
+++ b/tools/debug-debug.gdb
@@ -5,8 +5,8 @@ try:
gdb.execute('set auto-load safe-path /')
except:
pass
+gdb.execute('file %s' % file_to_load)
end
-file bin/test-debug-debug
set logging file /dev/null
set logging redirect on
set logging off
@@ -19,7 +19,7 @@ def hit_breakpoint():
sys.stdout.write('.')
value = str(gdb.parse_and_eval('*&value'))
expected = gdb.parse_and_eval('expected').string()
- if expected.startswith('regex:'):
+ if False and expected.startswith('regex:'):
def compare(value, expected):
m = re.match(expected[6:], value)
return m and m.end() == m.endpos
@@ -39,6 +39,7 @@ end
set print static-members off
set print elements 9999
set print frame-arguments none
+set python print-stack full
set logging on
rbreak do_breakpoint
diff --git a/tools/nightly b/tools/nightly
index e20cdd5..1f7ecc7 100755
--- a/tools/nightly
+++ b/tools/nightly
@@ -1,61 +1,126 @@
#!/bin/bash -e
+#
+# The buildbot script is only intended to run on Debian amd64
+#
WWW=${HOME}/www/
-ALL_CONFIGS=(
- config64
- config32
+BUILD=x86_64-linux-gnu
+
+ALL_HOSTS=(
+ x86_64-linux-gnu
+ i586-linux-gnu
+# x32 requires a recent kernel with appropriate flags.
+# On Debian, boot with 'syscall.x32=y' on the kernel command line
+# (see GRUB_CMDLINE_LINUX in /etc/default/grub)
+ #x86_64-linux-gnux32
+# Cross arches can be built by enabling multiarch, then installing
+# the g++-$ARCH package.
+# Executables can be run via qemu-user, but gdb tests must be disabled.
+# Bug 762073 notes impossible coinstallation of mips, mipsel, and powerpc.
+# dpkg-divert can only fix *one* of them.
+ #aarch64-linux-gnu
+ #arm-linux-gnueabi
+ #arm-linux-gnueabihf
+ #mips-linux-gnu
+ #mipsel-linux-gnu
+ #powerpc-linux-gnu
)
-# need a special kernel for configx32
+config---help() {
+ set +x
+ echo 'Usage: tools/nightly <list of arches>'
+ echo 'Arches are:'
+ echo x86_64-linux-gnu
+ echo i586-linux-gnu
+ echo x86_64-linux-gnux32
+ echo aarch64-linux-gnu
+ echo arm-linux-gnueabi
+ echo arm-linux-gnueabihf
+ echo mips-linux-gnu
+ echo mipsel-linux-gnu
+ echo powerpc-linux-gnu
+ exit
+}
+
+common-config() {
+ # HOST is set by the calling function
+ CXX=$HOST-g++
+ EXTRA_LIBS=(
+ /lib/$HOST/libc.so.6
+ /lib/$HOST/libm.so.6
+ /lib/$HOST/libgcc_s.so.1
+ /usr/lib/$HOST/libstdc++.so.6
+ )
+ GDB=/bin/true
+}
-config32() {
+
+config-x86_64-linux-gnu () {
+ common-config
+ GDB=gdb
+}
+
+config-i586-linux-gnu () {
# No one knows what number this is supposed to be:
# - the lib directory is called i386-linux-gnu
# - the 32-bit files are called i486-linux-gnu
# - the cross-32 configury says i586
- HOST=i586-linux-gnu
- CXX='g++ -m32'
+ CXX=$BUILD-'g++ -m32'
EXTRA_LIBS=(
/lib32/libc.so.6
/lib32/libm.so.6
/usr/lib32/libgcc_s.so.1
/usr/lib32/libstdc++.so.6
)
+ GDB=gdb
}
-configx32() {
- HOST=x86_64-linux-gnux32
- CXX='g++ -mx32'
+config-x86_64-linux-gnux32 () {
+ CXX=$BUILD-'g++ -mx32'
EXTRA_LIBS=(
/libx32/libc.so.6
/libx32/libm.so.6
/usr/libx32/libgcc_s.so.1
/usr/libx32/libstdc++.so.6
)
+ GDB=gdb
}
-config64() {
- HOST=x86_64-linux-gnu
- CXX='g++ -m64'
- EXTRA_LIBS=(
- /lib/x86_64-linux-gnu/libc.so.6
- /lib/x86_64-linux-gnu/libm.so.6
- /lib/x86_64-linux-gnu/libgcc_s.so.1
- /usr/lib/x86_64-linux-gnu/libstdc++.so.6
- )
+config-aarch64-linux-gnu () {
+ common-config
+}
+
+config-arm-linux-gnueabi () {
+ common-config
+}
+
+config-arm-linux-gnueabihf () {
+ common-config
+}
+
+config-mips-linux-gnu () {
+ common-config
+}
+
+config-mipsel-linux-gnu () {
+ common-config
+}
+
+config-powerpc-linux-gnu () {
+ common-config
}
first=true
set -x
-for config in ${ALL_CONFIGS[@]}
+for HOST in ${@:-${ALL_HOSTS[@]}}
do
- $config
+ config-$HOST
mkdir -p build-$HOST
cd build-$HOST
- ../configure --host=$HOST CXX="$CXX -Wno-deprecated-declarations" --prefix=/. --enable-rpath=relative
- rm -rf bin lib
+ ../configure --build=$BUILD --host=$HOST CXX="$CXX -Wno-deprecated-declarations -DQUIET" --prefix=/. --enable-rpath=relative GDB=$GDB
+ rm -rf bin lib stamp
make -j3 all
make -j3 test
if $first
diff --git a/tools/protocol.py b/tools/protocol.py
index 3927b0c..7547bc6 100755
--- a/tools/protocol.py
+++ b/tools/protocol.py
@@ -20,11 +20,40 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import print_function
+
import glob
import itertools
import os
from pipes import quote
from posixpath import relpath
+from weakref import ref as wr
+
+try:
+ unicode
+except NameError:
+ unicode = str
+
+## For various reasons this is all one file, so let's navigate with a
+##
+## Table of Contents
+##
+## TOC_HEAD
+## TOC_TYPES
+## TOC_EVENT
+## TOC_CHAN
+## TOC_DATA
+## TOC_GENTYPE
+## TOC_CLIENT
+## TOC_LOGINCHAR
+## TOC_CHARMAP
+## TOC_INTERMAP
+## TOC_MISC
+## TOC_LOGINADMIN
+## TOC_NEW
+## TOC_DRAWING
+## TOC_MAIN
+##
# The following code should be relatively easy to understand, but please
# keep your sanity fastened and your arms and legs inside at all times.
@@ -32,6 +61,9 @@ from posixpath import relpath
# important note: all numbers in this file will make a lot more sense in
# decimal, but they're written in hex.
+
+# TOC_HEAD
+
generated = '// This is a generated file, edit %s instead\n' % __file__
copyright = '''// {filename} - {description}
@@ -67,9 +99,9 @@ class OpenWrite(object):
if ty is not None:
return
frag = '''
- if cmp {0}.tmp {0}.old
+ if cmp -s {0}.tmp {0}.old
then
- echo Unchanged: {0}
+ : echo Unchanged: {0}
rm {0}.tmp
mv {0}.old {0}
else
@@ -80,14 +112,210 @@ class OpenWrite(object):
'''.format(quote(self.filename))
os.system(frag)
+
+# TOC_
+
+def gvq(s):
+ return u'"%s"' % s.replace(u'"', u'\\"')
+
+def gva(d):
+ if d:
+ return u' [%s]' % u', '.join(u'%s=%s' % (ak, gvq(av)) for (ak, av) in sorted(d.items()))
+ return u''
+
+class Attributes(object):
+ __slots__ = (u'_attributes')
+
+ def __init__(self):
+ self._attributes = {}
+
+ def __getitem__(self, k):
+ assert isinstance(k, unicode)
+ return self._attributes[k]
+
+ def __setitem__(self, k, v):
+ assert isinstance(k, unicode)
+ assert isinstance(v, unicode)
+ self._attributes[k] = v
+
+ def __delitem__(self, k):
+ assert isinstance(k, unicode)
+ del self._attributes[k]
+
+ def merge(self, *others):
+ for other in others:
+ assert self.__class__ == other.__class__
+ # if an attribute is present on multiple inputs, prefer
+ # the last one. This is not necessarily what you want, though.
+ self._attributes.update(other._attributes)
+
+class Graph(Attributes):
+ __slots__ = (u'default_vertex', u'default_edge', u'_vertices', u'_edges', u'_vertex_lookup')
+
+ def __init__(self):
+ Attributes.__init__(self)
+ self.default_vertex = Attributes()
+ self.default_edge = Attributes()
+ self._vertices = set()
+ self._edges = {}
+ self._vertex_lookup = {}
+
+ def vertex(self, name, insert=True):
+ assert isinstance(name, unicode)
+ vert = self._vertex_lookup.get(name)
+ if insert and vert is None:
+ vert = Vertex(name)
+ self._vertex_lookup[name] = vert
+ self._vertices.add(vert)
+ return vert
+
+ def _fix_vertex(self, v, insert=True):
+ if isinstance(v, Vertex):
+ return v
+ return self.vertex(v, insert)
+
+ def edge(self, v1, v2, insert=True):
+ v1 = self._fix_vertex(v1, insert)
+ if v1 is None:
+ return None
+ v2 = self._fix_vertex(v2, insert)
+ if v2 is None:
+ return None
+ ek = (v1, v2)
+ edge = self._edges.get(ek)
+ if insert and edge is None:
+ edge = Edge(v1, v2)
+ v1._post.add(wr(v2))
+ v2._pre.add(wr(v1))
+ self._edges[ek] = edge
+ return edge
+
+ def del_edge(self, v1, v2):
+ edge = self.edge(v1, v2, False)
+ if edge is None:
+ return
+ v1 = edge._from
+ v2 = edge._to
+ ek = (v1, v2)
+ v1._post.remove(wr(v2))
+ v2._pre.remove(wr(v1))
+ del self._edges[ek]
+
+ def del_vertex(self, v):
+ v = self._fix_vertex(v, False)
+ if v is None:
+ return
+ pre = list(v._pre)
+ for vp in pre:
+ vp = vp()
+ self.del_edge(vp, v)
+ post = list(v._post)
+ for vp in post:
+ vp = vp()
+ self.del_edge(v, vp)
+ del self._vertex_lookup[v._key]
+ self._vertices.remove(v)
+
+ def splice_out_vertex(self, v):
+ v = self._fix_vertex(v, False)
+ if v is None:
+ return
+ self.del_edge(v, v)
+ for vp in v._pre:
+ vp = vp()
+ ep = self.edge(vp, v, False)
+ for vn in v._post:
+ vn = vn()
+ en = self.edge(v, vn, False)
+ self.edge(vp, vn).merge(ep, en)
+ self.del_vertex(v)
+
+ def dot(self, out, close):
+ if close:
+ with out:
+ self.dot(out, False)
+ return
+
+ def p(*args):
+ for x in args:
+ out.write(unicode(x))
+ out.write(u'\n')
+ p(u'digraph')
+ p(u'{')
+ for ak, av in sorted(self._attributes.items()):
+ p(u' ', ak, u'=', gvq(av), u';')
+ for ak, av in sorted(self.default_vertex._attributes.items()):
+ p(u' node [', ak, u'=', gvq(av), u'];')
+ for ak, av in sorted(self.default_edge._attributes.items()):
+ p(u' edge [', ak, u'=', gvq(av), u'];')
+ for n in sorted(self._vertices):
+ p(u' ', n)
+ for _, e in sorted(self._edges.items()):
+ p(u' ', e)
+ p(u'}')
+
+ def dot_str(self):
+ from io import StringIO
+ out = StringIO()
+ self.dot(out, False)
+ return out.getvalue()
+
+ def dot_file(self, name):
+ with open(name, u'w') as f:
+ self.dot(f, False)
+
+ def preview(self, block):
+ from subprocess import Popen, PIPE
+ proc = Popen([u'dot', u'-Txlib', u'/dev/stdin'], stdin=PIPE, universal_newlines=True)
+ self.dot(proc.stdin, True)
+ if block:
+ proc.wait()
+
+class Vertex(Attributes):
+ __slots__ = (u'_key', u'_post', u'_pre', u'__weakref__')
+
+ def __init__(self, key):
+ Attributes.__init__(self)
+ self._key = key
+ self._post = set()
+ self._pre = set()
+
+ def __str__(self):
+ return u'%s%s;' % (gvq(self._key), gva(self._attributes))
+
+ def __lt__(self, other):
+ return self._key < other._key
+
+class Edge(Attributes):
+ __slots__ = (u'_from', u'_to')
+
+ def __init__(self, f, t):
+ Attributes.__init__(self)
+ self._from = f
+ self._to = t
+
+ def __str__(self):
+ return u'%s -> %s%s;' % (gvq(self._from._key), gvq(self._to._key), gva(self._attributes))
+
+
+# TOC_TYPES
+
class LowType(object):
- __slots__ = ()
+ __slots__ = ('includes')
+
+ def __repr__(self):
+ return '%s(%s)' % (type(self).__name__, self.name)
class NativeType(LowType):
__slots__ = ('name')
- def __init__(self, name):
+ def __init__(self, name, include):
self.name = name
+ self.includes = frozenset({include}) if include else frozenset()
+
+ def __repr__(self):
+ return 'NativeType(%r)' % (self.name)
+
def a_tag(self):
return self.name
@@ -95,21 +323,41 @@ class NativeType(LowType):
class NetworkType(LowType):
__slots__ = ('name')
- def __init__(self, name):
+ def __init__(self, name, include):
self.name = name
+ self.includes = frozenset({include}) if include else frozenset()
+
+ def __repr__(self):
+ return 'NetworkType(%r)' % (self.name)
+
def e_tag(self):
return self.name
class Type(object):
- __slots__ = ()
+ __slots__ = ('includes', 'do_dump')
+
+ def __new__(cls, *args):
+ rv = object.__new__(cls)
+ rv.do_dump = True
+ return rv
class NeutralType(Type):
__slots__ = ('name')
- def __init__(self, name):
+ def __init__(self, name, include):
self.name = name
+ identity = '#include "../proto-base/net-neutral.hpp"\n'
+ self.includes = frozenset({include, identity}) if include else frozenset({identity})
+
+ def __repr__(self):
+ return 'NeutralType(%r)' % (self.name)
+
+
+ def __repr__(self):
+ return 'NeutralType(%r)' % (self.name)
+
def native_tag(self):
return self.name
@@ -119,11 +367,52 @@ class NeutralType(Type):
e_tag = network_tag
+class PolyFakeType(object):
+ __slots__ = ('already', 'do_dump', 'nat_tag')
+
+ def __init__(self, already):
+ self.already = already = tuple(already)
+ self.do_dump = True
+
+ assert len(already) >= 2
+ self.nat_tag = nat_tag = already[0].native_tag()
+ for a in self.already:
+ a.do_dump = False
+ assert nat_tag == a.native_tag()
+
+ def __repr__(self):
+ return 'PolyFakeType(*%r)' % (self.already,)
+
+
+ def native_tag(self):
+ return self.nat_tag
+
+ def add_headers_to(self, headers):
+ for a in self.already:
+ a.add_headers_to(headers)
+
+ def dump_fwd(self, f):
+ for a in self.already:
+ a.dump_fwd(f)
+
+ def dump(self, f):
+ for a in self.already:
+ a.dump(f)
+
class StringType(Type):
__slots__ = ('native')
def __init__(self, native):
self.native = native
+ self.includes = native.includes | {'#include "../proto-base/net-string.hpp"\n'}
+
+ def __repr__(self):
+ return 'StringType(%r)' % (self.native)
+
+
+ def __repr__(self):
+ return 'StringType(%r)' % self.native
+
def native_tag(self):
return self.native.a_tag()
@@ -131,9 +420,9 @@ class StringType(Type):
def network_tag(self):
return 'NetString<sizeof(%s)>' % self.native.a_tag()
- def dump(self, f):
- # not implemented properly, uses a template in the meta instead
- pass
+ # not implemented properly, uses a template in net-string.hpp instead
+ #do_dump = False
+ # in Context.string() instead because reasons
class ProvidedType(Type):
__slots__ = ('native', 'network')
@@ -141,6 +430,15 @@ class ProvidedType(Type):
def __init__(self, native, network):
self.native = native
self.network = network
+ self.includes = native.includes | network.includes
+
+ def __repr__(self):
+ return 'ProvidedType(%r, %r)' % (self.native, self.network)
+
+
+ def __repr__(self):
+ return 'ProvidedType(native=%r, network=%r)' % (self.native, self.network)
+
def native_tag(self):
return self.native.a_tag()
@@ -154,6 +452,16 @@ class EnumType(Type):
def __init__(self, native, under):
self.native = native
self.under = under
+ name = native.a_tag()
+ self.includes = frozenset({'#include "net-%s.hpp"\n' % name})
+
+ def __repr__(self):
+ return 'EnumType(%r, %r)' % (self.native, self.under)
+
+
+ def __repr__(self):
+ return 'EnumType(%r, %r)' % (self.native, self.under)
+
def native_tag(self):
return self.native.a_tag()
@@ -161,6 +469,14 @@ class EnumType(Type):
def network_tag(self):
return self.under.network_tag()
+ def add_headers_to(self, headers):
+ headers.update(self.native.includes)
+ headers.update(self.under.includes)
+
+ def dump_fwd(self, f):
+ # nothing to see here
+ pass
+
def dump(self, f):
native = self.native_tag()
under = self.under.native_tag()
@@ -189,6 +505,16 @@ class WrappedType(Type):
def __init__(self, native, under):
self.native = native
self.under = under
+ name = self.native.a_tag()
+ self.includes = native.includes | under.includes | {'#include "net-%s.hpp"\n' % name}
+
+ def __repr__(self):
+ return 'WrappedType(%r, %r)' % (self.native, self.under)
+
+
+ def __repr__(self):
+ return 'WrappedType(%r, %r)' % (self.native, self.under)
+
def native_tag(self):
return self.native.a_tag()
@@ -196,6 +522,14 @@ class WrappedType(Type):
def network_tag(self):
return self.under.network_tag()
+ def add_headers_to(self, headers):
+ headers.update(self.native.includes)
+ headers.update(self.under.includes)
+
+ def dump_fwd(self, f):
+ # nothing to see here
+ pass
+
def dump(self, f):
native = self.native_tag()
under = self.under.native_tag()
@@ -223,6 +557,11 @@ class SkewLengthType(Type):
def __init__(self, ty, skew):
self.type = ty
self.skew = skew
+ self.includes = ty.includes | {'#include "../proto-base/net-skewed-length.hpp"\n'}
+
+ def __repr__(self):
+ return 'SkewLengthType(%r, %r)' % (self.ty, self.skew)
+
def native_tag(self):
return self.type.native_tag()
@@ -230,6 +569,10 @@ class SkewLengthType(Type):
def network_tag(self):
return 'SkewedLength<%s, %d>' % (self.type.network_tag(), self.skew)
+ def dumpx_fwd(self, f):
+ # nothing to see here
+ pass
+
def dumpx(self):
# not registered properly, so the method name is wrong
# TODO remind myself to kill this code with fire before release
@@ -246,6 +589,17 @@ class StructType(Type):
self.fields = fields
self.size = size
self.ctor = ctor
+ # only used if id is None
+ # internally uses add_headers_to()
+ self.includes = frozenset({'#include "net-%s.hpp"\n' % name})
+
+ def __repr__(self):
+ return '<StructType(%r) with %d fields>' % (self.name, len(self.fields))
+
+
+ def __repr__(self):
+ return '<StructType(id=%r, name=%r, size=%r, ctor=%r) with %d fields>' % (self.id, self.name, self.size, self.ctor, len(self.fields))
+
def native_tag(self):
return self.name
@@ -253,6 +607,13 @@ class StructType(Type):
def network_tag(self):
return 'Net' + self.name
+ def add_headers_to(self, headers):
+ # only used directly if id is not None
+ # used indirectly if id is None
+ for (o, l, n) in self.fields:
+ headers.update(l.includes)
+ headers.add('#include <cstddef>\n') # for offsetof
+
def dump(self, f):
self.dump_native(f)
self.dump_network(f)
@@ -262,7 +623,9 @@ class StructType(Type):
def dump_fwd(self, fwd):
if self.id is not None:
fwd.write('template<>\n')
+ assert False
fwd.write('struct %s;\n' % self.name)
+ return
if self.id is not None:
fwd.write('template<>\n')
fwd.write('struct Net%s;\n' % self.name)
@@ -331,6 +694,16 @@ class PartialStructType(Type):
def __init__(self, native, body):
self.native = native
self.body = body
+ name = native.a_tag()
+ self.includes = frozenset({'#include "net-%s.hpp"\n' % name})
+
+ def __repr__(self):
+ return '<PartialStructType(%r) with %d fields>' % (self.native, len(self.body))
+
+
+ def __repr__(self):
+ return '<PartialStructType(native=%r) with %d pieces of a body>' % (self.native, len(self.body))
+
def native_tag(self):
return self.native.a_tag()
@@ -338,6 +711,15 @@ class PartialStructType(Type):
def network_tag(self):
return 'Net%s' % self.native_tag()
+ def add_headers_to(self, headers):
+ headers.update(self.native.includes)
+ for n, t in self.body:
+ headers.update(t.includes)
+
+ def dump_fwd(self, f):
+ # nothing to see here
+ pass
+
def dump(self, f):
f.write('struct %s\n{\n' % self.network_tag())
for n, t in self.body:
@@ -371,6 +753,11 @@ class ArrayType(Type):
def __init__(self, element, count):
self.element = element
self.count = count
+ self.includes = element.includes | {'#include "../proto-base/net-array.hpp"\n'}
+
+ def __repr__(self):
+ return 'ArrayType(%r, %r)' % (self.element, self.count)
+
def native_tag(self):
return 'Array<%s, %s>' % (self.element.native_tag(), self.count)
@@ -385,6 +772,11 @@ class EArrayType(Type):
self.element = element
self.index = index
self.count = count
+ self.includes = element.includes | {'#include "../proto-base/net-array.hpp"\n'}
+
+ def __repr__(self):
+ return 'EArrayType(%r, %r)' % (self.element, self.index, self.count)
+
def native_tag(self):
return 'earray<%s, %s, %s>' % (self.element.native_tag(), self.index, self.count)
@@ -399,6 +791,15 @@ class InvArrayType(Type):
self.element = element
self.index = index
self.count = count
+ self.includes = element.includes | {
+ '#include "../proto-base/net-array.hpp"\n',
+ '#include "../mmo/clif.t.hpp"\n',
+ '#include "../mmo/consts.hpp"\n',
+ }
+
+ def __repr__(self):
+ return 'InvArrayType(%r, %r)' % (self.element, self.index, self.count)
+
def native_tag(self):
return 'GenericArray<%s, InventoryIndexing<%s, %s>>' % (self.element.native_tag(), self.index, self.count)
@@ -407,6 +808,47 @@ class InvArrayType(Type):
return 'NetArray<%s, %s>' % (self.element.network_tag(), self.count)
+# TOC_EVENT
+# special event origins
+class SpecialEventOrigin(object):
+ __slots__ = ('name')
+
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return self.name
+
+ADMIN = SpecialEventOrigin('ADMIN')
+BOOT = SpecialEventOrigin('BOOT')
+FINISH = SpecialEventOrigin('FINISH')
+GM = SpecialEventOrigin('GM')
+HUMAN = SpecialEventOrigin('HUMAN')
+IDLE = SpecialEventOrigin('IDLE')
+MAGIC = SpecialEventOrigin('MAGIC')
+NOTHING = SpecialEventOrigin('NOTHING')
+OTHER = SpecialEventOrigin('OTHER')
+PRETTY = SpecialEventOrigin('PRETTY')
+SCRIPT = SpecialEventOrigin('SCRIPT')
+TIMER = SpecialEventOrigin('TIMER')
+
+def event_name(p):
+ if isinstance(p, SpecialEventOrigin):
+ return p.name
+ if isinstance(p, int):
+ return 'packet 0x%04x' % p
+ assert False, 'Unknown event: %r' % p
+
+def event_link(p):
+ if isinstance(p, SpecialEventOrigin):
+ return p.name
+ if isinstance(p, int):
+ return '[[Packet 0x%04x]]' % p
+ assert False, 'Unknown event: %r' % p
+
+
+# TOC_CHAN
+
class Include(object):
__slots__ = ('path', '_types')
@@ -414,6 +856,10 @@ class Include(object):
self.path = path
self._types = []
+ def __repr__(self):
+ return '<Include(%r) with %d types>' % (self.path, len(self._types))
+
+
def testcase(self, outdir):
basename = os.path.basename(self.path.strip('<">'))
root = os.path.splitext(basename)[0]
@@ -426,41 +872,133 @@ class Include(object):
f.write('\n')
f.write('#include "%s"\n\nnamespace tmwa\n{\n' % poison)
- for t in self._types:
- f.write('using %s = %s;\n' % ('Test_' + ident(t.name), t.name))
- f.write('} // namespace tmwa\n')
def pp(self, n):
return '#%*sinclude %s\n' % (n, '', self.path)
-
def native(self, name):
- ty = NativeType(name)
+ ty = NativeType(name, self.pp(0))
self._types.append(ty)
return ty
def network(self, name):
- ty = NetworkType(name)
+ ty = NetworkType(name, self.pp(0))
self._types.append(ty)
return ty
def neutral(self, name):
- ty = NeutralType(name)
+ ty = NeutralType(name, self.pp(0))
self._types.append(ty)
return ty
+class BasePacket(object):
+ __slots__ = ('id', 'name', 'define', 'pre', 'post', 'xpost', 'desc')
+
+ def __init__(self, **kwargs):
+ for s in BasePacket.__slots__:
+ setattr(self, s, kwargs[s])
+ del kwargs[s]
+ assert not kwargs, 'Unknown kwargs: %s' % repr(kwargs)
+
+ def base_repr_fragment(self):
+ return ', '.join('%s=%r' % (s, getattr(self, s)) for s in BasePacket.__slots__)
-class FixedPacket(object):
+ def comment_doc(self):
+ id = self.id
+ name = self.name
+ define = self.define
+ desc = self.desc
+ pre = self.pre
+ post = self.post
+
+ comment = 'Packet 0x%04x: "%s"\n' % (id, name)
+ if define:
+ comment += 'define: ' + define + '\n'
+ if True:
+ prestr = ', '.join(event_name(x) for x in pre) or 'none'
+ poststr = ', '.join(event_name(x) for x in post) or 'none'
+ comment += 'pre: ' + prestr + '\n'
+ comment += 'post: ' + poststr + '\n'
+ comment += desc
+ comment = ''.join('// ' + c + '\n' if c else '//\n' for c in comment.split('\n'))
+ return comment
+
+ def wiki_doc(self):
+ # TODO do markdown magic
+ id = self.id
+ name = self.name
+ desc = self.desc
+ pre = self.pre
+ post = self.post
+ xpost = self.xpost
+
+ wiki = 'Packet 0x%04x: "%s"\n\n' % (id, name)
+ if True:
+ prestr = ', '.join(event_link(x) for x in pre) or 'none'
+ poststr = ', '.join(event_link(x) for x in fix_sort(post + xpost)) or 'none'
+ wiki += 'pre: ' + prestr + '\n\n'
+ wiki += 'post: ' + poststr + '\n\n'
+ wiki += desc
+ wiki += '\n\n'
+ wiki += '![](packets-around-0x%04x.png)\n' % id
+ return wiki
+
+ def pre_set(self, d, n=float('inf'), accum=None):
+ if accum is None:
+ accum = set()
+ if self.id in accum:
+ return accum
+ accum.add(self.id)
+ if not n:
+ return accum
+ for p in self.pre:
+ # ignore specials
+ if isinstance(p, SpecialEventOrigin):
+ continue
+ # ignore weak links
+ if self.id not in d[p].post:
+ continue
+ d[p].pre_set(d, n - 1 + (len([z for z in self.pre if isinstance(z, SpecialEventOrigin) or self.id in d[z].post]) == 1), accum)
+ return accum
+
+ def post_set(self, d, n=float('inf'), accum=None):
+ if accum is None:
+ accum = set()
+ if self.id in accum:
+ return accum
+ accum.add(self.id)
+ if not n:
+ return accum
+ for p in self.post:
+ # ignore specials
+ if isinstance(p, SpecialEventOrigin):
+ continue
+ # weak links are in xpost
+
+ d[p].post_set(d, n - 1 + (len(self.post) == 1), accum)
+ return accum
+
+
+class FixedPacket(BasePacket):
__slots__ = ('fixed_struct')
- def __init__(self, fixed_struct):
+ def __init__(self, fixed_struct, **kwargs):
+ BasePacket.__init__(self, **kwargs)
self.fixed_struct = fixed_struct
+ def __repr__(self):
+ return 'FixedPacket(%r, %s)' % (self.fixed_struct, self.base_repr_fragment())
+
+
+ def add_headers_to(self, headers):
+ self.fixed_struct.add_headers_to(headers)
+
def dump_fwd(self, fwd):
self.fixed_struct.dump_fwd(fwd)
fwd.write('\n')
def dump_native(self, f):
+ f.write(self.comment_doc())
self.fixed_struct.dump_native(f)
f.write('\n')
@@ -472,19 +1010,29 @@ class FixedPacket(object):
self.fixed_struct.dump_convert(f)
f.write('\n')
-class VarPacket(object):
+class VarPacket(BasePacket):
__slots__ = ('head_struct', 'repeat_struct')
- def __init__(self, head_struct, repeat_struct):
+ def __init__(self, head_struct, repeat_struct, **kwargs):
+ BasePacket.__init__(self, **kwargs)
self.head_struct = head_struct
self.repeat_struct = repeat_struct
+ def __repr__(self):
+ return 'VarPacket(%r, %r, %s)' % (self.head_struct, self.repeat_struct, self.base_repr_fragment())
+
+
+ def add_headers_to(self, headers):
+ self.head_struct.add_headers_to(headers)
+ self.repeat_struct.add_headers_to(headers)
+
def dump_fwd(self, fwd):
self.head_struct.dump_fwd(fwd)
self.repeat_struct.dump_fwd(fwd)
fwd.write('\n')
def dump_native(self, f):
+ f.write(self.comment_doc())
self.head_struct.dump_native(f)
self.repeat_struct.dump_native(f)
f.write('\n')
@@ -499,40 +1047,76 @@ class VarPacket(object):
self.repeat_struct.dump_convert(f)
f.write('\n')
-def packet(id, name,
+def sanitize_line(line, n):
+ if not line:
+ return line
+ m = len(line) - len(line.lstrip(' '))
+ assert m >= n, 'not %d: %r' % (n, line)
+ return line[n:]
+
+def sanitize_multiline(text):
+ text = text.strip('\n').rstrip(' ')
+ assert '\r' not in text
+ assert '\t' not in text
+ n = len(text) - len(text.lstrip(' '))
+ return '\n'.join(sanitize_line(l, n) for l in text.split('\n'))
+
+def packet(id, name, define=None,
fixed=None, fixed_size=None,
payload=None, payload_size=None,
head=None, head_size=None,
repeat=None, repeat_size=None,
option=None, option_size=None,
+ pre=None, post=None, xpost=None, desc=None,
):
assert (fixed is None) <= (fixed_size is None)
assert (payload is None) <= (payload_size is None)
assert (head is None) <= (head_size is None)
assert (repeat is None) <= (repeat_size is None)
assert (option is None) <= (option_size is None)
+ assert (pre is not None) and (post is not None) and desc.strip()
+ if xpost is None:
+ xpost = []
+
+ desc = sanitize_multiline(desc)
if fixed is not None:
assert not head and not repeat and not option and not payload
return FixedPacket(
- StructType(id, 'Packet_Fixed<0x%04x>' % id, fixed, fixed_size))
+ StructType(id, 'Packet_Fixed<0x%04x>' % id, fixed, fixed_size),
+ id=id, name=name, define=define, pre=pre, post=post, xpost=xpost, desc=desc,
+ )
elif payload is not None:
assert not head and not repeat and not option
return FixedPacket(
- StructType(id, 'Packet_Payload<0x%04x>' % id, payload, payload_size))
+ StructType(id, 'Packet_Payload<0x%04x>' % id, payload, payload_size),
+ id=id, name=name, define=define, pre=pre, post=post, xpost=xpost, desc=desc,
+ )
else:
assert head
if option:
+ assert not repeat
return VarPacket(
StructType(id, 'Packet_Head<0x%04x>' % id, head, head_size),
- StructType(id, 'Packet_Option<0x%04x>' % id, option, option_size))
+ StructType(id, 'Packet_Option<0x%04x>' % id, option, option_size),
+ id=id, name=name, define=define, pre=pre, post=post, xpost=xpost, desc=desc,
+ )
else:
assert repeat
return VarPacket(
StructType(id, 'Packet_Head<0x%04x>' % id, head, head_size),
- StructType(id, 'Packet_Repeat<0x%04x>' % id, repeat, repeat_size))
-
-
+ StructType(id, 'Packet_Repeat<0x%04x>' % id, repeat, repeat_size),
+ id=id, name=name, define=define, pre=pre, post=post, xpost=xpost, desc=desc,
+ )
+
+
+# TODO this whole idea is wrong
+# in particular, 'any'.
+# instead, have just one channel for all packets, but store send/recv/type
+# with each packet.
+# Then during codegen, emit:
+# #if PACKET_LOGIN || PACKET_CHAR || PACKET_MAP || PACKET_ADMIN || PACKET_USER
+# (for an 'all' packet) around the send/recv portions separately
class Channel(object):
__slots__ = ('server', 'client', 'packets')
@@ -541,18 +1125,29 @@ class Channel(object):
self.client = client
self.packets = []
+ def __repr__(self):
+ return '<Channel(%r, %r) with %d packets>' % (
+ self.server,
+ self.client,
+ len(self.packets),
+ )
+
+
def x(self, id, name, **kwargs):
self.packets.append(packet(id, name, **kwargs))
r = x
s = x
- def dump(self, outdir, fwd):
+ def dump(self, outdir):
server = self.server
client = self.client
header = '%s-%s.hpp' % (server, client)
- test = '%s-%s_test.cpp' % (server, client)
desc = 'TMWA network protocol: %s/%s' % (server, client)
with OpenWrite(os.path.join(outdir, header)) as f:
+ type_headers = set()
+ for p in self.packets:
+ p.add_headers_to(type_headers)
+ type_headers = sorted(type_headers)
proto2 = relpath(outdir, 'src')
f.write('#pragma once\n')
f.write(copyright.format(filename=header, description=desc))
@@ -560,7 +1155,9 @@ class Channel(object):
f.write(generated)
f.write('\n')
f.write('#include "fwd.hpp"\n\n')
- f.write('#include "types.hpp"\n')
+ f.write('// sorry these are not ordered by hierarchy\n')
+ for h in type_headers:
+ f.write(h)
f.write('\n')
f.write('namespace tmwa\n{\n')
if client == 'user':
@@ -569,9 +1166,6 @@ class Channel(object):
f.write('// This is an internal protocol, and can be changed without notice\n')
f.write('\n')
for p in self.packets:
- p.dump_fwd(fwd)
- fwd.write('\n')
- for p in self.packets:
p.dump_native(f)
f.write('\n')
for p in self.packets:
@@ -581,16 +1175,6 @@ class Channel(object):
p.dump_convert(f)
f.write('} // namespace tmwa\n')
- with OpenWrite(os.path.join(outdir, test)) as f:
- poison = relpath('src/poison.hpp', outdir)
- f.write('#include "%s"\n' % header)
- f.write(copyright.format(filename=test, description=desc))
- f.write('\n')
- f.write(generated)
- f.write('\n')
- f.write('#include "%s"\n\nnamespace tmwa\n{\n' % poison)
- f.write('} // namespace tmwa\n')
-
ident_translation = ''.join(chr(c) if chr(c).isalnum() else '_' for c in range(256))
@@ -615,6 +1199,14 @@ class Context(object):
self._channels = []
self._types = []
+ def __repr__(self):
+ return '<Context(%r) with %d _includes, %d _channels, %d _types>' % (
+ self.outdir,
+ len(self._includes),
+ len(self._channels),
+ len(self._types),
+ )
+
def sysinclude(self, name):
rv = Include('<%s>' % name)
@@ -638,13 +1230,25 @@ class Context(object):
proto2 = relpath(outdir, 'src')
with OpenWrite(os.path.join(outdir, 'fwd.hpp')) as f:
header = '%s/fwd.hpp' % proto2
- desc = 'Forward declarations of network packets'
+ desc = 'Forward declarations of network packet body structs'
sanity = relpath('src/sanity.hpp', outdir)
+
f.write('#pragma once\n')
f.write(copyright.format(filename=header, description=desc))
f.write('\n')
+
f.write('#include "%s"\n\n' % sanity)
f.write('#include <cstdint>\n\n')
+
+ f.write('#include "../ints/fwd.hpp" // rank 1\n')
+ f.write('#include "../strings/fwd.hpp" // rank 1\n')
+ f.write('#include "../compat/fwd.hpp" // rank 2\n')
+ f.write('#include "../net/fwd.hpp" // rank 5\n')
+ f.write('#include "../mmo/fwd.hpp" // rank 6\n')
+ f.write('#include "../proto-base/fwd.hpp" // rank 7\n')
+ f.write('// proto2/fwd.hpp is rank 8\n')
+ f.write('\n\n')
+
f.write('namespace tmwa\n{\n')
for b in ['Fixed', 'Payload', 'Head', 'Repeat', 'Option']:
c = 'Packet_' + b
@@ -652,143 +1256,57 @@ class Context(object):
f.write('template<uint16_t PACKET_ID> class Net%s;\n' % c)
f.write('\n')
+ for ty in self._types:
+ if not ty.do_dump:
+ continue
+ ty.dump_fwd(f)
for ch in self._channels:
- ch.dump(outdir, f)
+ ch.dump(outdir)
f.write('} // namespace tmwa\n')
- with OpenWrite(os.path.join(outdir, 'types.hpp')) as f:
- header = '%s/types.hpp' % proto2
- desc = 'Forward declarations of packet component types'
- f.write('#pragma once\n')
- f.write(copyright.format(filename=header, description=desc))
- f.write('\n')
- f.write('#include "fwd.hpp"\n\n')
- f.write('#include "../generic/array.hpp"\n')
- f.write('#include "../mmo/consts.hpp"\n')
-
- f.write('\n//TODO split the includes\n')
- for inc in self._includes:
- f.write(inc.pp(0))
- # this is writing another file
- inc.testcase(outdir)
- f.write('\n')
- f.write('namespace tmwa\n{\n')
-
- f.write('template<class T>\n')
- f.write('bool native_to_network(T *network, T native)\n{\n')
- f.write(' *network = native;\n')
- f.write(' return true;\n')
- f.write('}\n')
- f.write('template<class T>\n')
- f.write('bool network_to_native(T *native, T network)\n{\n')
- f.write(' *native = network;\n')
- f.write(' return true;\n')
- f.write('}\n')
-
- f.write('template<class T, size_t N>\n')
- f.write('struct NetArray\n{\n')
- f.write(' T data[N];\n')
- f.write('};\n')
- f.write('template<class T, class U, class I>\n')
- f.write('bool native_to_network(NetArray<T, I::alloc_size> *network, GenericArray<U, I> native)\n{\n')
- f.write(' for (size_t i = 0; i < I::alloc_size; ++i)\n')
- f.write(' {\n')
- f.write(' if (!native_to_network(&(*network).data[i], native[I::offset_to_index(i)]))\n')
- f.write(' return false;\n')
- f.write(' }\n')
- f.write(' return true;\n')
- f.write('}\n')
- f.write('template<class T, class U, class I>\n')
- f.write('bool network_to_native(GenericArray<U, I> *native, NetArray<T, I::alloc_size> network)\n{\n')
- f.write(' for (size_t i = 0; i < I::alloc_size; ++i)\n')
- f.write(' {\n')
- f.write(' if (!network_to_native(&(*native)[I::offset_to_index(i)], network.data[i]))\n')
- f.write(' return false;\n')
- f.write(' }\n')
- f.write(' return true;\n')
- f.write('}\n')
- f.write('\n')
-
- f.write('template<size_t N>\n')
- f.write('struct NetString\n{\n')
- f.write(' char data[N];\n')
- f.write('};\n')
- f.write('template<size_t N>\n')
- f.write('bool native_to_network(NetString<N> *network, VString<N-1> native)\n{\n')
- f.write(' // basically WBUF_STRING\n')
- f.write(' char *const begin = network->data;\n')
- f.write(' char *const end = begin + N;\n')
- f.write(' char *const mid = std::copy(native.begin(), native.end(), begin);\n')
- f.write(' std::fill(mid, end, \'\\0\');\n')
- f.write(' return true;\n')
- f.write('}\n')
- f.write('template<size_t N>\n')
- f.write('bool network_to_native(VString<N-1> *native, NetString<N> network)\n{\n')
- f.write(' // basically RBUF_STRING\n')
- f.write(' const char *const begin = network.data;\n')
- f.write(' const char *const end = begin + N;\n')
- f.write(' const char *const mid = std::find(begin, end, \'\\0\');\n')
- f.write(' *native = XString(begin, mid, nullptr);\n')
- f.write(' return true;\n')
- f.write('}\n')
- f.write('\n')
- f.write('inline\n')
- f.write('bool native_to_network(NetString<24> *network, CharName native)\n{\n')
- f.write(' VString<23> tmp = native.to__actual();\n')
- f.write(' bool rv = native_to_network(network, tmp);\n')
- f.write(' return rv;\n')
- f.write('}\n')
- f.write('inline\n')
- f.write('bool network_to_native(CharName *native, NetString<24> network)\n{\n')
- f.write(' VString<23> tmp;\n')
- f.write(' bool rv = network_to_native(&tmp, network);\n')
- f.write(' *native = stringish<CharName>(tmp);\n')
- f.write(' return rv;\n')
- f.write('}\n')
- f.write('\n')
- f.write('inline\n')
- f.write('bool native_to_network(NetString<16> *network, MapName native)\n{\n')
- f.write(' XString tmp = native;\n')
- f.write(' bool rv = native_to_network(network, VString<15>(tmp));\n')
- f.write(' return rv;\n')
- f.write('}\n')
- f.write('inline\n')
- f.write('bool network_to_native(MapName *native, NetString<16> network)\n{\n')
- f.write(' VString<15> tmp;\n')
- f.write(' bool rv = network_to_native(&tmp, network);\n')
- f.write(' *native = stringish<MapName>(tmp);\n')
- f.write(' return rv;\n')
- f.write('}\n')
- f.write('\n')
-
- f.write('template<class T, size_t N>\n')
- f.write('struct SkewedLength\n{\n')
- f.write(' T data;\n')
- f.write('};\n')
- f.write('template<class T, size_t N, class U>\n')
- f.write('bool native_to_network(SkewedLength<T, N> *network, U native)\n{\n')
- f.write(' native -= N;\n')
- f.write(' return native_to_network(&network->data, native);\n')
- f.write('}\n')
- f.write('template<class T, size_t N, class U>\n')
- f.write('bool network_to_native(U *native, SkewedLength<T, N> network)\n{\n')
- f.write(' bool rv = network_to_native(native, network.data);\n')
- f.write(' *native += N;\n')
- f.write(' return rv;\n')
- f.write('}\n')
- f.write('\n')
-
- for ty in self._types:
+ for ty in self._types:
+ header = 'net-%s.hpp' % ty.native_tag()
+ if not ty.do_dump:
+ continue
+ type_headers = set()
+ ty.add_headers_to(type_headers)
+ type_headers = sorted(type_headers)
+ with OpenWrite(os.path.join(outdir, header)) as f:
+ header = '%s/%s' % (proto2, header)
+ desc = 'Packet structures and conversions'
+ f.write('#pragma once\n')
+ f.write(copyright.format(filename=header, description=desc))
+ f.write('\n')
+ f.write('#include "fwd.hpp"\n\n')
+
+ for inc in type_headers:
+ f.write(inc)
+ f.write('\n')
+ f.write('namespace tmwa\n{\n')
ty.dump(f)
- f.write('} // namespace tmwa\n')
+ f.write('} // namespace tmwa\n')
for g in glob.glob(os.path.join(outdir, '*.old')):
print('Obsolete: %s' % g)
os.remove(g)
+ def finish_types(self):
+ s = set()
+ for t in self._types:
+ if not t.do_dump:
+ continue
+ n = t.native_tag()
+ assert n not in s, n
+ s.add(n)
+
# types
+ def poly(self, *already):
+ rv = PolyFakeType(already)
+ self._types.append(rv)
+ return rv
+
def provided(self, native, network):
# the whole point of 'provided' is to not be implemented
return ProvidedType(native, network)
@@ -805,6 +1323,7 @@ class Context(object):
def string(self, native):
rv = StringType(native)
+ rv.do_dump = False
self._types.append(rv)
return rv
@@ -833,7 +1352,9 @@ class Context(object):
return rv
-def main():
+# TOC_DATA
+
+def build_context():
## setup
@@ -848,6 +1369,8 @@ def main():
vstring_h = ctx.include('src/strings/vstring.hpp')
+ timet_h = ctx.include('src/compat/time_t.hpp')
+
ip_h = ctx.include('src/net/ip.hpp')
timer_th = ctx.include('src/net/timer.t.hpp')
@@ -856,17 +1379,17 @@ def main():
human_time_diff_h = ctx.include('src/mmo/human_time_diff.hpp')
ids_h = ctx.include('src/mmo/ids.hpp')
strs_h = ctx.include('src/mmo/strs.hpp')
- utils_h = ctx.include('src/mmo/utils.hpp')
+ utils_h = ctx.include('src/net/timestamp-utils.hpp')
version_h = ctx.include('src/mmo/version.hpp')
- login_th = ctx.include('src/login/login.t.hpp')
+ login_th = ctx.include('src/mmo/login.t.hpp')
- clif_th = ctx.include('src/map/clif.t.hpp')
- skill_th = ctx.include('src/map/skill.t.hpp')
+ clif_th = ctx.include('src/mmo/clif.t.hpp')
+ skill_th = ctx.include('src/mmo/skill.t.hpp')
## primitive types
- char = NeutralType('char')
- bit = NeutralType('bool')
+ char = NeutralType('char', None)
+ bit = NeutralType('bool', None)
## included types
@@ -885,7 +1408,7 @@ def main():
Little64 = endians_h.network('Little64')
SEX = enums_h.native('SEX')
- Option = enums_h.native('Option')
+ Opt0 = enums_h.native('Opt0')
EPOS = enums_h.native('EPOS')
ItemLook = enums_h.native('ItemLook')
@@ -905,7 +1428,7 @@ def main():
ip4 = ip_h.neutral('IP4Address')
- TimeT = utils_h.native('TimeT')
+ TimeT = timet_h.native('TimeT')
VString16 = vstring_h.native('VString<15>')
VString20 = vstring_h.native('VString<19>')
@@ -961,6 +1484,7 @@ def main():
interval_t = timer_th.native('interval_t')
## generated types
+ # TOC_GENTYPE
u8 = ctx.provided(uint8_t, Byte)
u16 = ctx.provided(uint16_t, Little16)
@@ -997,7 +1521,7 @@ def main():
interval16 = ctx.provided(interval_t, Little16)
sex = ctx.enum(SEX, u8)
- option = ctx.enum(Option, u16)
+ option = ctx.enum(Opt0, u16)
epos = ctx.enum(EPOS, u16)
item_look = ctx.enum(ItemLook, u16)
@@ -1007,14 +1531,19 @@ def main():
party_id = ctx.wrap(PartyId, u32)
item_name_id = ctx.wrap(ItemNameId, u16)
item_name_id4 = ctx.wrap(ItemNameId, u32)
+ ctx.poly(item_name_id, item_name_id4)
block_id = ctx.wrap(BlockId, u32)
time32 = ctx.provided(TimeT, Little32)
time64 = ctx.provided(TimeT, Little64)
+ #ctx.poly(time32, time32)
gm1 = ctx.provided(GmLevel, Byte)
gm2 = ctx.provided(GmLevel, Little16)
gm = ctx.provided(GmLevel, Little32)
+ #ctx.poly(gm1, gm2, gm)
+
+ # poly is not currently needed for ProvidedType
str16 = ctx.string(VString16)
str20 = ctx.string(VString20)
@@ -1251,6 +1780,7 @@ def main():
size=None,
)
+ # TODO move 'account id' out somehow.
storage = ctx.struct(
'Storage',
[
@@ -1263,6 +1793,8 @@ def main():
size=None,
)
+ ctx.finish_types()
+
## packet channels
@@ -1301,18 +1833,60 @@ def main():
# sized if all of its members are fixed sized.
#
# The element type of an array shall not be of implicit size.
- # The element type of an array *may* be of explicit size, in which
+ # The element type of an array *may* be of explicit size, in which case
+ # the layout is like option 1 below (see below for discussion).
#
# A string is just an array of characters, except that it may be padded
# with '\0' bytes even when it is sized.
# A map is just an array of two-element structs (key, value)
# However, strings and maps have custom classes used to represent them
- # on the sender and receiver (earray also has this).
+ # on the sender and receiver (earray also has this (but maybe shouldn't?)).
+ # Ytf is tmwa still using fixed-size arrays for this crap anyways?
+ # It probably should be a map in this case.
#
# It would probably be a good idea if everybody parsed network input as
# padded with '\0's if it is too short, and ignored the extra if it is
# too long. However, there are efficiency concerns with this, since we
- # don't want to branch everywhere.
+ # don't want to branch everywhere. But parsing in reverse might work ...
+
+ # possible array layouts:
+ # 1:
+ # | array1 size | array1 data | array2 size | array2 data |
+ # Advantage: linear writing (except final size?).
+ # Disadvantage: linear reading.
+ # This is what I originally imagined, but ...
+ # 2:
+ # | array1 size/offset | array2 size/offset | array1 data | array2 data |
+ # Advantage: random-access reading.
+ # Disadvantage: slightly larger packets, more complicated writing?.
+ # TODO verify whether you really need offset or not?
+ # TODO think about array-of-array case, it's complicated.
+ # | array0 size | array1 size | array 0.0 size | array 0.1 size | array 1.0 size | array 1.1 size | element 0.0.0 | element 0.0.1 | element 0.1.0 | element 0.1.1 | element 1.0.0 | element 1.0.1 | element 1.1.0 | element 1.1.1 |
+ # data a=(b=[d=[h=0.0.0, i=0.0.1], e=[j=0.1.0, k=0.1.1]], c=[f=[l=1.0.0, m=1.0.1], g=[n=1.1.0, o=1.1.1]])
+ # I'm not sure how this can work. If I wrote:
+ # def write_a():
+ # write_head_b() # size
+ # write_head_c() # size
+ # write_data_b()
+ # write_data_c()
+ # def write_data_b():
+ # write_head_d()
+ # write_head_e()
+ # write_data_d()
+ # write_data_e()
+ # def write_data_c():
+ # write_head_f()
+ # write_head_g()
+ # write_data_f()
+ # write_data_g()
+ # ... that would yield |s0|s1|s0.0|s0.1|d0.0.0|d0.0.1|d0.1.0|d0.1.1|s1.0|s1.1|d1.0.0|d1.0.1|d1.1.0|d1.1.1|
+ # Can't do random-access writes because you don't know the offset yet.
+ # I would have to do a bunch of 'write depth n' functions.
+ # Maybe this was a bad idea.
+ # And what exactly is the use of random-access reads? We're doing full
+ # translation for all data anyway. It's probably a bad idea for any
+ # packet to ever contain information that you don't look at.
+
login_char = ctx.chan('login', 'char')
login_admin = ctx.chan('login', 'admin')
@@ -1333,23 +1907,43 @@ def main():
## legacy packets
+ # TOC_CLIENT
# * user
- char_user.r(0x0061, 'change password request',
+ char_user.r(0x0061, 'change password',
+ define='CMSG_CHAR_PASSWORD_CHANGE',
fixed=[
at(0, u16, 'packet id'),
at(2, account_pass, 'old pass'),
at(26, account_pass, 'new pass'),
],
fixed_size=50,
+ pre=[HUMAN],
+ post=[0x2740],
+ desc='''
+ Sent by a client to the character server to request a password change.
+ ''',
)
- char_user.s(0x0062, 'change password response',
+ char_user.s(0x0062, 'change password result',
+ define='SMSG_CHAR_PASSWORD_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'status'),
],
fixed_size=3,
- )
- login_user.r(0x0063, 'update host',
+ pre=[0x2741],
+ post=[PRETTY],
+ desc='''
+ Sent by the character server with the response of a password change request.
+
+ Status:
+ 0: The account was not found.
+ 1: Success.
+ 2: The old password was incorrect.
+ 3: The new password was too short.
+ ''',
+ )
+ login_user.s(0x0063, 'update host notify',
+ define='SMSG_UPDATE_HOST',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -1357,8 +1951,16 @@ def main():
head_size=4,
repeat=[at(0, u8, 'c')],
repeat_size=1,
+ pre=[0x0064],
+ post=[IDLE],
+ desc='''
+ This packet gives the client the location of the update server URL, such as http://tmwdata.org/updates/
+
+ It is only sent if an update host is specified for the server (there is one in the default configuration) and the client identifies as accepting an update host (which all supported clients do).
+ ''',
)
- login_user.r(0x0064, 'login request',
+ login_user.r(0x0064, 'account login',
+ define='CMSG_LOGIN_REGISTER',
fixed=[
at(0, u16, 'packet id'),
at(2, u32, 'unknown'),
@@ -1367,8 +1969,16 @@ def main():
at(54, version_2, 'version 2 flags'),
],
fixed_size=55,
+ pre=[HUMAN, 0x7531],
+ post=[0x0063, 0x0069, 0x006a, 0x0081],
+ desc='''
+ Authenticate a client by user/password.
+
+ All clients must now set both defined version 2 flags.
+ ''',
)
- char_user.r(0x0065, 'char-server connection request',
+ char_user.r(0x0065, 'connect char',
+ define='CMSG_CHAR_SERVER_CONNECT',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -1378,15 +1988,28 @@ def main():
at(16, sex, 'sex'),
],
fixed_size=17,
+ pre=[0x0069, 0x0092, 0x00b3],
+ post=[0x006b, 0x006c, 0x2712, 0x2716],
+ desc='''
+ Begin connection to the char server, based on keys the login
+ server gave us.
+ ''',
)
- char_user.r(0x0066, 'select character request',
+ char_user.r(0x0066, 'select character',
+ define='CMSG_CHAR_SELECT',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'code'),
],
fixed_size=3,
+ pre=[0x006b],
+ post=[FINISH, 0x0071, 0x0081],
+ desc='''
+ Choose a character to enter the map.
+ ''',
)
- char_user.r(0x0067, 'create character request',
+ char_user.r(0x0067, 'create character',
+ define='CMSG_CHAR_CREATE',
fixed=[
at(0, u16, 'packet id'),
at(2, char_name, 'char name'),
@@ -1396,16 +2019,32 @@ def main():
at(35, u16, 'hair style'),
],
fixed_size=37,
+ pre=[0x006b],
+ post=[0x006d, 0x006e],
+ desc='''
+ Create a new character.
+ ''',
)
- char_user.r(0x0068, 'delete character request',
+ char_user.r(0x0068, 'delete character',
+ define='CMSG_CHAR_DELETE',
fixed=[
at(0, u16, 'packet id'),
at(2, char_id, 'char id'),
- at(6, account_email, 'email'),
+ at(6, account_email, 'unused email'),
],
fixed_size=46,
- )
- login_user.r(0x0069, 'login data',
+ pre=[0x006b],
+ post=[0x006f, 0x0070, 0x2afe, 0x2b12, 0x3821, 0x3824, 0x3826],
+ desc='''
+ Delete an existing character.
+
+ There is no authentication on the server besides what has
+ already been performed to create the connection. "Are you sure?"
+ is solely the client's job.
+ ''',
+ )
+ login_user.s(0x0069, 'account login success',
+ define='SMSG_LOGIN_DATA',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -1427,16 +2066,44 @@ def main():
at(30, u16, 'is new'),
],
repeat_size=32,
+ pre=[0x0064],
+ post=[0x0065],
+ desc='''
+ Big blob of information available once when you authenticate:
+
+ * dumb session keys
+ * sex and last login
+ * list of char server
+ ''',
)
- login_user.s(0x006a, 'login error',
+ login_user.s(0x006a, 'account login error',
+ define='SMSG_LOGIN_ERROR',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'error code'),
at(3, seconds, 'error message'),
],
fixed_size=23,
- )
- char_user.s(0x006b, 'update character list',
+ pre=[0x0064],
+ post=[PRETTY],
+ desc='''
+ Failure to log in.
+
+ Error codes:
+ * 0: unregistered id
+ * 1: incorrect password
+ * 2: expired id (unused?)
+ * 3: rejected from server (unused?)
+ * 4: permanently blocked
+ * 5: client too old (unused?)
+ * 6: temporary ban (date in 'error message' field)
+ * 7: server full
+ * 8: no message
+ * 99: id erased
+ ''',
+ )
+ char_user.s(0x006b, 'connect char success',
+ define='SMSG_CHAR_LOGIN',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -1447,42 +2114,83 @@ def main():
at(0, char_select, 'char select'),
],
repeat_size=106,
+ pre=[0x0065, 0x2713],
+ post=[PRETTY],
+ xpost=[0x0066, 0x0067, 0x0068],
+ desc='''
+ List account's characters on this server.
+ ''',
)
- char_user.s(0x006c, 'login error',
+ char_user.s(0x006c, 'connect char error',
+ define='SMSG_CHAR_LOGIN_ERROR',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'code'),
],
fixed_size=3,
+ pre=[0x0065, 0x2713],
+ post=[PRETTY],
+ desc='''
+ Refuse connection.
+
+ Status:
+ 0: Overpopulated
+ 0x42: Auth failed
+ ''',
)
- char_user.s(0x006d, 'create character succeeded',
+ char_user.s(0x006d, 'create character success',
+ define='SMSG_CHAR_CREATE_SUCCEEDED',
fixed=[
at(0, u16, 'packet id'),
at(2, char_select, 'char select'),
],
fixed_size=108,
+ pre=[0x0067],
+ post=[PRETTY],
+ desc='''
+ Give information about newly-created character.
+ ''',
)
- char_user.s(0x006e, 'create character failed',
+ char_user.s(0x006e, 'create character error',
+ define='SMSG_CHAR_CREATE_FAILED',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'code'),
],
fixed_size=3,
+ pre=[0x0067],
+ post=[PRETTY],
+ desc='''
+ Failure to create a new character.
+ ''',
)
- char_user.s(0x006f, 'delete character succeeded',
+ char_user.s(0x006f, 'delete character success',
+ define='SMSG_CHAR_DELETE_SUCCEEDED',
fixed=[
at(0, u16, 'packet id'),
],
fixed_size=2,
+ pre=[0x0068],
+ post=[PRETTY],
+ desc='''
+ Character successfully deleted.
+ ''',
)
- char_user.s(0x0070, 'delete character failed',
+ char_user.s(0x0070, 'delete character error',
+ define='SMSG_CHAR_DELETE_FAILED',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'code'),
],
fixed_size=3,
+ pre=[0x0068],
+ post=[PRETTY],
+ desc='''
+ Failure to delete character.
+ ''',
)
- char_user.s(0x0071, 'char-map info',
+ char_user.s(0x0071, 'select character success',
+ define='SMSG_CHAR_MAP_INFO',
fixed=[
at(0, u16, 'packet id'),
at(2, char_id, 'char id'),
@@ -1491,8 +2199,14 @@ def main():
at(26, u16, 'port'),
],
fixed_size=28,
+ pre=[0x0066],
+ post=[0x0072],
+ desc='''
+ Return character location and map server IP.
+ ''',
)
- map_user.r(0x0072, 'map server connect',
+ map_user.r(0x0072, 'connect map',
+ define='CMSG_MAP_SERVER_CONNECT',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -1502,8 +2216,15 @@ def main():
at(18, sex, 'sex'),
],
fixed_size=19,
+ pre=[0x0071, 0x0092],
+ post=[0x0081, 0x2afc],
+ desc='''
+ Begin connection to the map server, based on keys the login
+ server gave us.
+ ''',
)
- map_user.s(0x0073, 'map login succeeded',
+ map_user.s(0x0073, 'connect map success',
+ define='SMSG_MAP_LOGIN_SUCCESS',
fixed=[
at(0, u16, 'packet id'),
at(2, tick32, 'tick'),
@@ -1512,8 +2233,14 @@ def main():
at(10, u8, 'five2'),
],
fixed_size=11,
+ pre=[0x2afd],
+ post=[PRETTY, 0x007e],
+ desc='''
+ Successfully auth'd to the map server
+ ''',
)
- map_user.s(0x0078, 'being visibility',
+ map_user.s(0x0078, 'being appear notify',
+ define='SMSG_BEING_VISIBLE',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
@@ -1545,8 +2272,14 @@ def main():
at(52, u16, 'level'),
],
fixed_size=54,
+ pre=[BOOT, FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00b8, 0x00b9, 0x00e6, 0x00f7, 0x0143, 0x0146, 0x01d5],
+ post=[0x0094],
+ desc='''
+ A being is stationary.
+ ''',
)
- map_user.s(0x007b, 'being move',
+ map_user.s(0x007b, 'being move notify',
+ define='SMSG_BEING_MOVE',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
@@ -1577,8 +2310,14 @@ def main():
at(58, u16, 'level'),
],
fixed_size=60,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f],
+ post=[0x0094],
+ desc='''
+ A being is moving.
+ ''',
)
- map_user.s(0x007c, 'being spawn',
+ map_user.s(0x007c, 'being spawn notify',
+ define='SMSG_BEING_SPAWN',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
@@ -1601,50 +2340,95 @@ def main():
at(39, u16, 'unknown 11'),
],
fixed_size=41,
+ pre=[BOOT, FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0089, 0x008c, 0x00b8, 0x00b9, 0x00e6, 0x00f7, 0x0143, 0x0146, 0x01d5],
+ post=[PRETTY],
+ desc='''
+ A being is created.
+ ''',
)
map_user.r(0x007d, 'map loaded',
+ define='CMSG_MAP_LOADED',
fixed=[
at(0, u16, 'packet id'),
],
fixed_size=2,
+ pre=[0x0091],
+ post=[SCRIPT, 0x0078, 0x007b, 0x009d, 0x00a4, 0x00b0, 0x00b1, 0x00bd, 0x00fb, 0x0101, 0x010f, 0x0119, 0x013a, 0x0141, 0x019b, 0x01d7, 0x01d8, 0x01d9, 0x01da, 0x01ee, 0x3025, 0x3028],
+ xpost=[0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x013c, 0x0196, 0x01b1, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Changed map; need info.
+ ''',
)
- map_user.r(0x007e, 'client ping',
+ map_user.r(0x007e, 'ping',
+ define='CMSG_MAP_PING',
fixed=[
at(0, u16, 'packet id'),
at(2, u32, 'client tick'),
],
fixed_size=6,
+ pre=[TIMER, 0x0073],
+ post=[0x007f],
+ desc='''
+ Request ping.
+ ''',
)
- map_user.s(0x007f, 'server ping',
+ map_user.s(0x007f, 'pong',
+ define='SMSG_SERVER_PING',
fixed=[
at(0, u16, 'packet id'),
at(2, tick32, 'tick'),
],
fixed_size=6,
+ pre=[0x007e],
+ post=[NOTHING],
+ desc='''
+ Provide ping.
+ ''',
)
- map_user.s(0x0080, 'remove being',
+ map_user.s(0x0080, 'remove being notify',
+ define='SMSG_BEING_REMOVE',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
at(6, being_remove_why, 'type'),
],
fixed_size=7,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x0090, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ A being disappeared.
+ ''',
)
- any_user.s(0x0081, 'connection problem',
+ any_user.s(0x0081, 'connect foo error',
+ define='SMSG_CONNECTION_PROBLEM',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'error code'),
],
fixed_size=3,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0064, 0x0066, 0x0072, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2afe, 0x2b06, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ Failed to connect to some server (multiple meanings).
+ ''',
)
- map_user.r(0x0085, 'change player destination',
+ map_user.r(0x0085, 'walk',
+ define='CMSG_PLAYER_CHANGE_DEST',
fixed=[
at(0, u16, 'packet id'),
at(2, pos1, 'pos'),
],
fixed_size=5,
+ pre=[HUMAN],
+ post=[0x0080, 0x0087, 0x01d7, 0x01da],
+ xpost=[SCRIPT, 0x0078, 0x007b, 0x0081, 0x0088, 0x0091, 0x009d, 0x00a0, 0x00a1, 0x00ac, 0x00b0, 0x00b1, 0x00b4, 0x00b6, 0x00be, 0x00c0, 0x00c4, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Start walking somewhere.
+ ''',
)
- map_user.s(0x0087, 'walk response',
+ # 0x0086 define='SMSG_BEING_MOVE2',
+ map_user.s(0x0087, 'walk success',
+ define='SMSG_WALK_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, tick32, 'tick'),
@@ -1652,8 +2436,16 @@ def main():
at(11, u8, 'zero'),
],
fixed_size=12,
+ pre=[0x0085, 0x0089],
+ post=[IDLE],
+ desc='''
+ Confirm that you did walk somewhere.
+
+ No corresponding error!
+ ''',
)
- map_user.s(0x0088, 'player stop',
+ map_user.s(0x0088, 'stop walking notify',
+ define='SMSG_PLAYER_STOP',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
@@ -1661,16 +2453,36 @@ def main():
at(8, u16, 'y'),
],
fixed_size=10,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ Being stopped walking.
+ ''',
)
map_user.r(0x0089, 'player action',
+ define='CMSG_PLAYER_CHANGE_ACT',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'target id'),
at(6, damage_type, 'action'),
],
fixed_size=7,
+ pre=[HUMAN],
+ post=[MAGIC, 0x0080, 0x008a, 0x0110, 0x0139],
+ xpost=[SCRIPT, 0x0078, 0x007b, 0x007c, 0x0081, 0x0087, 0x0088, 0x0091, 0x009e, 0x00a0, 0x00a1, 0x00ac, 0x00af, 0x00b0, 0x00b1, 0x00b4, 0x00b6, 0x00be, 0x00c0, 0x00c4, 0x00e9, 0x00ee, 0x00fb, 0x00fd, 0x0101, 0x0106, 0x010f, 0x0119, 0x013a, 0x013b, 0x0141, 0x0196, 0x019b, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x01de, 0x2b01, 0x2b05, 0x3011, 0x3022, 0x3025, 0x3028],
+ desc='''
+ Perform an action.
+
+ Action:
+ * single attack
+ * continuous attack
+ * sit
+ * stand
+ * (other actions on return only - special calls)
+ ''',
)
- map_user.s(0x008a, 'being action',
+ map_user.s(0x008a, 'being action notify',
+ define='SMSG_BEING_ACTION',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'src id'),
@@ -1684,8 +2496,14 @@ def main():
at(27, u16, 'damage2'),
],
fixed_size=29,
- )
- map_user.r(0x008c, 'character chat',
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0089, 0x009f],
+ post=[PRETTY],
+ desc='''
+ Somebody performed an action on something/somebody.
+ ''',
+ )
+ map_user.r(0x008c, 'global chat',
+ define='CMSG_CHAT_MESSAGE',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -1695,8 +2513,15 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
- )
- map_user.s(0x008d, 'being chat',
+ pre=[HUMAN],
+ post=[GM, MAGIC, 0x008d, 0x008e],
+ xpost=[SCRIPT, 0x0078, 0x007b, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00af, 0x00b0, 0x00b1, 0x00b4, 0x00b6, 0x00be, 0x00c0, 0x00c4, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x2b0e, 0x3003, 0x3011, 0x3022],
+ desc='''
+ Talk to everyone nearby.
+ ''',
+ )
+ map_user.s(0x008d, 'global chat notify',
+ define='SMSG_BEING_CHAT',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -1707,8 +2532,14 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
- )
- map_user.s(0x008e, 'player chat',
+ pre=[GM, SCRIPT, 0x008c],
+ post=[PRETTY],
+ desc='''
+ Somebody is talking (not just a player).
+ ''',
+ )
+ map_user.s(0x008e, 'global chat result',
+ define='SMSG_PLAYER_CHAT',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -1718,16 +2549,33 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[OTHER, 0x008c],
+ post=[PRETTY],
+ desc='''
+ You talked.
+ ''',
)
- map_user.r(0x0090, 'chat to npc',
+ map_user.r(0x0090, 'npc click',
+ define='CMSG_NPC_TALK',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
at(6, u8, 'unused'),
],
fixed_size=7,
+ pre=[HUMAN],
+ post=[SCRIPT, 0x0080, 0x00b4, 0x00b6, 0x00c4],
+ xpost=[0x00c0],
+ desc='''
+ Click on an NPC, to invoke its script/shop/message.
+
+ Error if already talking to an NPC or too far.
+
+ No error if clicking on a warp.
+ ''',
)
- map_user.s(0x0091, 'warp player',
+ map_user.s(0x0091, 'change map notify',
+ define='SMSG_PLAYER_WARP',
fixed=[
at(0, u16, 'packet id'),
at(2, map_name, 'map name'),
@@ -1735,8 +2583,15 @@ def main():
at(20, u16, 'y'),
],
fixed_size=22,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[0x007d],
+ desc='''
+ You teleported to a new map (possible the same map), but on the
+ same server.
+ ''',
)
- map_user.s(0x0092, 'change map server',
+ map_user.s(0x0092, 'change map server notify',
+ define='SMSG_CHANGE_MAP_SERVER',
fixed=[
at(0, u16, 'packet id'),
at(2, map_name, 'map name'),
@@ -1746,23 +2601,43 @@ def main():
at(26, u16, 'port'),
],
fixed_size=28,
+ pre=[0x2b06],
+ post=[0x0065, 0x0072],
+ desc='''
+ You teleported to another server.
+ ''',
)
- map_user.r(0x0094, 'request being name',
+ map_user.r(0x0094, 'get being name',
+ define='CMSG_NAME_REQUEST',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
],
fixed_size=6,
+ pre=[TIMER, 0x0078, 0x007b, 0x01d8, 0x01d9, 0x01da],
+ post=[0x0095, 0x0195, 0x020c],
+ desc='''
+ Request a beings name. No reply if wrong type.
+
+ Also send misc other info about players.
+ ''',
)
- map_user.s(0x0095, 'being name response',
+ map_user.s(0x0095, 'get being name result',
+ define='SMSG_BEING_NAME_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
at(6, char_name, 'char name'),
],
fixed_size=30,
- )
- map_user.r(0x0096, 'send whisper',
+ pre=[0x0094],
+ post=[PRETTY],
+ desc='''
+ Somebody has a name.
+ ''',
+ )
+ map_user.r(0x0096, 'whisper',
+ define='CMSG_CHAT_WHISPER',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -1773,8 +2648,15 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[HUMAN],
+ post=[GM, 0x0097, 0x0098, 0x3001],
+ xpost=[0x2b0e, 0x3003],
+ desc='''
+ Talk to someone privately.
+ ''',
)
map_user.s(0x0097, 'receive whisper',
+ define='SMSG_WHISPER',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -1785,15 +2667,28 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[0x0096, 0x3801, 0x3803],
+ post=[PRETTY],
+ desc='''
+ Somebody is talking to you privately.
+ '''
)
- map_user.s(0x0098, 'whisper status',
+ map_user.s(0x0098, 'whisper result',
+ define='SMSG_WHISPER_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'flag'),
],
fixed_size=3,
- )
- map_user.s(0x009a, 'gm announcement',
+ pre=[0x0096, 0x3802],
+ post=[PRETTY],
+ desc='''
+ Did you successfully talk to someone?
+ ''',
+ )
+ # 0x0099 define='CMSG_ADMIN_ANNOUNCE',
+ map_user.s(0x009a, 'gm announcement notify',
+ define='SMSG_GM_CHAT',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -1803,16 +2698,28 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[GM, SCRIPT, 0x3800],
+ post=[PRETTY],
+ desc='''
+ The GMs are shouting in red.
+ ''',
)
- map_user.r(0x009b, 'change player direction',
+ map_user.r(0x009b, 'face direction',
+ define='CMSG_PLAYER_CHANGE_DIR',
fixed=[
at(0, u16, 'packet id'),
at(2, u16, 'unused'),
at(4, u8, 'client dir'),
],
fixed_size=5,
+ pre=[HUMAN],
+ post=[0x009c],
+ desc='''
+ Look in a different direction, without moving.
+ ''',
)
- map_user.s(0x009c, 'being changed direction',
+ map_user.s(0x009c, 'face direction notify',
+ define='SMSG_BEING_CHANGE_DIRECTION',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
@@ -1820,8 +2727,14 @@ def main():
at(8, u8, 'client dir'),
],
fixed_size=9,
+ pre=[0x009b],
+ post=[PRETTY],
+ desc='''
+ Somebody looked in a different direction, without moving.
+ ''',
)
- map_user.s(0x009d, 'visible item',
+ map_user.s(0x009d, 'item visible notify',
+ define='SMSG_ITEM_VISIBLE',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
@@ -1834,8 +2747,14 @@ def main():
at(16, u8, 'suby'),
],
fixed_size=17,
+ pre=[0x007d, 0x0085],
+ post=[PRETTY],
+ desc='''
+ An item appeared on the ground.
+ ''',
)
- map_user.s(0x009e, 'dropped item',
+ map_user.s(0x009e, 'item dropped notify',
+ define='SMSG_ITEM_DROPPED',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
@@ -1848,15 +2767,28 @@ def main():
at(15, u16, 'amount'),
],
fixed_size=17,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0089, 0x00a2],
+ post=[PRETTY],
+ desc='''
+ An item was dropped on the ground.
+ ''',
)
- map_user.r(0x009f, 'pickup item',
+ map_user.r(0x009f, 'item pickup',
+ define='CMSG_ITEM_PICKUP',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'object id'),
],
fixed_size=6,
+ pre=[HUMAN],
+ post=[SCRIPT, 0x0080, 0x008a, 0x0091, 0x00a0, 0x00a1, 0x00b0],
+ xpost=[0x0078, 0x007b, 0x0081, 0x0088, 0x00ac, 0x00b1, 0x00b4, 0x00b6, 0x00be, 0x00c0, 0x00c4, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Hey, it's just lying around on the floor, I want it.
+ ''',
)
- map_user.s(0x00a0, 'add item to inventory',
+ map_user.s(0x00a0, 'inventory add notify',
+ define='SMSG_PLAYER_INVENTORY_ADD',
fixed=[
at(0, u16, 'packet id'),
at(2, ioff2, 'ioff2'),
@@ -1874,23 +2806,44 @@ def main():
at(22, pickup_fail, 'pickup fail'),
],
fixed_size=23,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ Item appeared in your inventory.
+ ''',
)
- map_user.s(0x00a1, 'item removed',
+ map_user.s(0x00a1, 'flooritem delete notify',
+ define='SMSG_ITEM_REMOVE',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
],
fixed_size=6,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0085, 0x0089, 0x009f, 0x00a2],
+ post=[PRETTY],
+ desc='''
+ Item disappeared from the floor.
+ ''',
)
- map_user.r(0x00a2, 'drop an item',
+ map_user.r(0x00a2, 'drop item',
+ define='CMSG_PLAYER_INVENTORY_DROP',
fixed=[
at(0, u16, 'packet id'),
at(2, ioff2, 'ioff2'),
at(4, u16, 'amount'),
],
fixed_size=6,
+ pre=[HUMAN],
+ post=[0x0080, 0x009e, 0x00ac, 0x00af, 0x01d7],
+ xpost=[SCRIPT, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00a1, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ This is worthless, just leave it lying on the ground.
+
+ Also used by people who don't understand trades.
+ ''',
)
- map_user.s(0x00a4, 'player equipment',
+ map_user.s(0x00a4, 'inventory equipment notify',
+ define='SMSG_PLAYER_EQUIPMENT',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -1911,8 +2864,14 @@ def main():
at(18, u16, 'card3'),
],
repeat_size=20,
- )
- map_user.s(0x00a6, 'storage equipment',
+ pre=[0x007d],
+ post=[PRETTY],
+ desc='''
+ Complete list of equipment in inventory.
+ ''',
+ )
+ map_user.s(0x00a6, 'storage equipment notify',
+ define='SMSG_PLAYER_STORAGE_EQUIP',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -1933,16 +2892,29 @@ def main():
at(18, u16, 'card3'),
],
repeat_size=20,
+ pre=[GM, SCRIPT, 0x3810],
+ post=[PRETTY],
+ desc='''
+ Complete list of equipment in storage.
+ ''',
)
- map_user.r(0x00a7, 'use inventory item',
+ map_user.r(0x00a7, 'use item',
+ define='CMSG_PLAYER_INVENTORY_USE',
fixed=[
at(0, u16, 'packet id'),
at(2, ioff2, 'ioff2'),
at(4, u32, 'unused id'),
],
fixed_size=8,
+ pre=[HUMAN],
+ post=[SCRIPT, 0x0080, 0x00a8, 0x00ac, 0x00af, 0x00b0, 0x01c8, 0x01d7],
+ xpost=[0x0081, 0x0088, 0x0091, 0x00a0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Use a consumable item in your inventory.
+ ''',
)
- map_user.s(0x00a8, 'item usage response',
+ map_user.s(0x00a8, 'use item result',
+ define='SMSG_ITEM_USE_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, ioff2, 'ioff2'),
@@ -1950,16 +2922,31 @@ def main():
at(6, u8, 'ok'),
],
fixed_size=7,
+ pre=[0x00a7],
+ post=[PRETTY],
+ desc='''
+ You used (or tried to use) an item in your inventory.
+
+ Currently only used in the failure case.
+ ''',
)
- map_user.r(0x00a9, 'equip an item request',
+ map_user.r(0x00a9, 'equip item',
+ define='CMSG_PLAYER_EQUIP',
fixed=[
at(0, u16, 'packet id'),
at(2, ioff2, 'ioff2'),
at(4, epos, 'epos ignored'),
],
fixed_size=6,
+ pre=[HUMAN],
+ post=[SCRIPT, 0x0080, 0x00aa, 0x00ac, 0x013b, 0x013c, 0x01d7],
+ xpost=[0x0081, 0x0088, 0x0091, 0x00a0, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Put on armor or something.
+ ''',
)
- map_user.s(0x00aa, 'item equip ack',
+ map_user.s(0x00aa, 'equip item result',
+ define='SMSG_PLAYER_EQUIP',
fixed=[
at(0, u16, 'packet id'),
at(2, ioff2, 'ioff2'),
@@ -1967,15 +2954,28 @@ def main():
at(6, u8, 'ok'),
],
fixed_size=7,
+ pre=[0x00a9],
+ post=[PRETTY],
+ desc='''
+ Result of trying to wear something.
+ ''',
)
- map_user.r(0x00ab, 'unequip an item',
+ map_user.r(0x00ab, 'unequip item',
+ define='CMSG_PLAYER_UNEQUIP',
fixed=[
at(0, u16, 'packet id'),
at(2, ioff2, 'ioff2'),
],
fixed_size=4,
+ pre=[HUMAN],
+ post=[SCRIPT, 0x0080, 0x00ac, 0x01d7],
+ xpost=[0x0081, 0x0088, 0x0091, 0x00a0, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Take off armor or something.
+ ''',
)
- map_user.s(0x00ac, 'unequip item ack',
+ map_user.s(0x00ac, 'unequip item result',
+ define='SMSG_PLAYER_UNEQUIP',
fixed=[
at(0, u16, 'packet id'),
at(2, ioff2, 'ioff2'),
@@ -1983,46 +2983,83 @@ def main():
at(6, u8, 'ok'),
],
fixed_size=7,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ Result of trying to unwear something.
+ ''',
)
- map_user.s(0x00af, 'remove item from inventory',
+ map_user.s(0x00af, 'inventory delete notify',
+ define='SMSG_PLAYER_INVENTORY_REMOVE',
fixed=[
at(0, u16, 'packet id'),
at(2, ioff2, 'ioff2'),
at(4, u16, 'amount'),
],
fixed_size=6,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0089, 0x008c, 0x00a2, 0x00a7, 0x00c9, 0x00ef, 0x00f3],
+ post=[PRETTY],
+ desc='''
+ An item has been deleted from your inventory.
+ ''',
)
- map_user.s(0x00b0, 'player stat update 1',
+ map_user.s(0x00b0, 'player stat update 1 notify',
+ define='SMSG_PLAYER_STAT_UPDATE_1',
fixed=[
at(0, u16, 'packet id'),
at(2, sp, 'sp type'),
at(4, u32, 'value'),
],
fixed_size=8,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ One of many player stat packets with no real difference.
+ ''',
)
- map_user.s(0x00b1, 'player stat update 2',
+ map_user.s(0x00b1, 'player stat update 2 notify',
+ define='SMSG_PLAYER_STAT_UPDATE_2',
fixed=[
at(0, u16, 'packet id'),
at(2, sp, 'sp type'),
at(4, u32, 'value'),
],
fixed_size=8,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ One of many player stat packets with no real difference.
+ ''',
)
- map_user.r(0x00b2, 'switch or respawn the character',
+ map_user.r(0x00b2, 'respawn or switch character',
+ define='CMSG_PLAYER_REBOOT',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'flag'),
],
fixed_size=3,
+ pre=[HUMAN, 0x01d8],
+ post=[0x0080, 0x0091, 0x00b0, 0x018b, 0x2b02, 0x2b05],
+ xpost=[SCRIPT, 0x0081, 0x0088, 0x00a0, 0x00ac, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x3011, 0x3022],
+ desc='''
+ If flag is 0, respawn after dying. If flag is 1, try to switch characters.
+ ''',
)
- map_user.s(0x00b3, 'character switch response',
+ map_user.s(0x00b3, 'character switch success',
+ define='SMSG_CHAR_SWITCH_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'one'),
],
fixed_size=3,
- )
- map_user.s(0x00b4, 'npc message',
+ pre=[0x2b03],
+ post=[0x0065],
+ desc='''
+ Permission granted to switch characters.
+ ''',
+ )
+ map_user.s(0x00b4, 'script message notify',
+ define='SMSG_NPC_MESSAGE',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -2033,22 +3070,40 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[MAGIC, SCRIPT, 0x0085, 0x0089, 0x008c, 0x0090, 0x009f],
+ post=[PRETTY],
+ desc='''
+ An npc is droning on.
+ ''',
)
- map_user.s(0x00b5, 'npc message continues',
+ map_user.s(0x00b5, '(reverse) script next',
+ define='SMSG_NPC_NEXT',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
],
fixed_size=6,
+ pre=[SCRIPT],
+ post=[0x00b9],
+ desc='''
+ An npc paused briefly to catch its breath.
+ ''',
)
- map_user.s(0x00b6, 'npc message ends',
+ map_user.s(0x00b6, '(reverse) script close',
+ define='SMSG_NPC_CLOSE',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
],
fixed_size=6,
- )
- map_user.s(0x00b7, 'npc prompts a choice',
+ pre=[GM, MAGIC, SCRIPT, 0x0085, 0x0089, 0x008c, 0x0090, 0x009f, 0x00b8, 0x00b9, 0x00e6, 0x00f7, 0x0143, 0x0146, 0x01d5],
+ post=[0x0146],
+ desc='''
+ An npc finally shut up.
+ ''',
+ )
+ map_user.s(0x00b7, '(reverse) script menu',
+ define='SMSG_NPC_CHOICE',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -2059,31 +3114,58 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[SCRIPT],
+ post=[0x00b8],
+ desc='''
+ An npc let you choose how it will drone on.
+ ''',
)
- map_user.r(0x00b8, 'send npc response',
+ map_user.r(0x00b8, '(reverse) script menu result',
+ define='CMSG_NPC_LIST_CHOICE',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'npc id'),
at(6, u8, 'menu entry'),
],
fixed_size=7,
+ pre=[0x00b7],
+ post=[MAGIC, SCRIPT],
+ xpost=[0x0078, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00b6, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Choose an entry from the menu (1-based), 0xff to run away.
+ ''',
)
- map_user.r(0x00b9, 'request next npc message',
+ map_user.r(0x00b9, '(reverse) script next result',
+ define='CMSG_NPC_NEXT_REQUEST',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'npc id'),
],
fixed_size=6,
+ pre=[0x00b5],
+ post=[MAGIC, SCRIPT],
+ xpost=[0x0078, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00b6, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Let the npc keep breathing.
+ ''',
)
- map_user.r(0x00bb, 'request a stat update',
+ map_user.r(0x00bb, 'stat increase',
+ define='CMSG_STAT_UPDATE_REQUEST',
fixed=[
at(0, u16, 'packet id'),
at(2, sp, 'asp'),
at(4, u8, 'unused'),
],
fixed_size=5,
+ pre=[HUMAN],
+ post=[0x00b0, 0x00bc, 0x00be, 0x0141],
+ xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b1, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Spend stat points.
+ ''',
)
- map_user.s(0x00bc, 'player stat update 4',
+ map_user.s(0x00bc, 'stat increase result',
+ define='SMSG_PLAYER_STAT_UPDATE_4',
fixed=[
at(0, u16, 'packet id'),
at(2, sp, 'sp type'),
@@ -2091,8 +3173,14 @@ def main():
at(5, u8, 'val'),
],
fixed_size=6,
+ pre=[SCRIPT, 0x00bb],
+ post=[PRETTY],
+ desc='''
+ Spent stat points?
+ ''',
)
- map_user.s(0x00bd, 'player stat update 5',
+ map_user.s(0x00bd, 'player stat update 5 notify',
+ define='SMSG_PLAYER_STAT_UPDATE_5',
fixed=[
at(0, u16, 'packet id'),
at(2, u16, 'status point'),
@@ -2124,59 +3212,89 @@ def main():
at(42, u16, 'manner'),
],
fixed_size=44,
+ pre=[0x007d],
+ post=[PRETTY],
+ desc='''
+ Bulk notify of stats.
+ ''',
)
- map_user.s(0x00be, 'player stat update 6',
+ map_user.s(0x00be, 'stat price notify',
+ define='SMSG_PLAYER_STAT_UPDATE_6',
fixed=[
at(0, u16, 'packet id'),
at(2, sp, 'sp type'),
at(4, u8, 'value'),
],
fixed_size=5,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ Cost of spending stat points.
+ ''',
)
- map_user.r(0x00bf, 'show an emote',
+ map_user.r(0x00bf, 'emote',
+ define='CMSG_PLAYER_EMOTE',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'emote'),
],
fixed_size=3,
+ pre=[HUMAN, TIMER],
+ post=[0x00c0, 0x110],
+ desc='''
+ Don't act like a faceless robot.
+ ''',
)
- map_user.s(0x00c0, 'show the emote of a being',
+ map_user.s(0x00c0, 'emote notify',
+ define='SMSG_BEING_EMOTION',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
at(6, u8, 'type'),
],
fixed_size=7,
+ pre=[BOOT, FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x0090, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00bf, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d, 0x3800, 0x3821, 0x3823, 0x3824, 0x3825, 0x3826, 0x3827],
+ post=[PRETTY],
+ desc='''
+ Somebody is claiming to not be a faceless robot.
+ ''',
)
- map_user.r(0x00c1, 'request online users (unused)',
- fixed=[
- at(0, u16, 'packet id'),
- ],
- fixed_size=2,
- )
- map_user.s(0x00c2, 'online users response (unused)',
- fixed=[
- at(0, u16, 'packet id'),
- at(2, u32, 'users'),
- ],
- fixed_size=6,
- )
- map_user.s(0x00c4, 'npc shop choice',
+ # 0x00c1 define='CMSG_CHAT_WHO', (unused by this name)
+ # 0x00c1 define='CMSG_WHO_REQUEST',
+ # 0x00c2 define='SMSG_WHO_ANSWER',
+ # 0x00c3 define='SMSG_BEING_CHANGE_LOOKS',
+ map_user.s(0x00c4, 'npc click result shop',
+ define='SMSG_NPC_BUY_SELL_CHOICE',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
],
fixed_size=6,
+ pre=[MAGIC, SCRIPT, 0x0085, 0x0089, 0x008c, 0x0090, 0x009f],
+ post=[0x00c5],
+ desc='''
+ That npc you clicked on was a shop.
+ ''',
)
- map_user.r(0x00c5, 'npc shop request',
+ map_user.r(0x00c5, 'npc shop buy/sell select',
+ define='CMSG_NPC_BUY_SELL_REQUEST',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
at(6, u8, 'type'),
],
fixed_size=7,
+ pre=[0x00c4],
+ post=[0x00c6, 0x00c7],
+ desc='''
+ Choose whether to buy or sell in a shop.
+
+ type 0: buy
+ type 1: sell
+ ''',
)
- map_user.s(0x00c6, 'npc buy prompt',
+ map_user.s(0x00c6, 'npc shop buy select result',
+ define='SMSG_NPC_BUY',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -2189,8 +3307,14 @@ def main():
at(9, item_name_id, 'name id'),
],
repeat_size=11,
- )
- map_user.s(0x00c7, 'npc sell prompt',
+ pre=[0x00c5],
+ post=[0x00c8],
+ desc='''
+ List of items you can buy from the shop.
+ ''',
+ )
+ map_user.s(0x00c7, 'npc shop sell select result',
+ define='SMSG_NPC_SELL',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -2202,8 +3326,17 @@ def main():
at(6, u32, 'actual price'),
],
repeat_size=10,
+ pre=[0x00c5],
+ post=[0x00c9],
+ desc='''
+ List of items you can sell to the shop.
+
+ Currently the server doesn't support customizing this list,
+ but in theory it could.
+ ''',
)
- map_user.r(0x00c8, 'npc buy request',
+ map_user.r(0x00c8, 'npc shop buy',
+ define='CMSG_NPC_BUY_REQUEST',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -2214,8 +3347,15 @@ def main():
at(2, item_name_id, 'name id'),
],
repeat_size=4,
- )
- map_user.r(0x00c9, 'npc sell request',
+ pre=[0x00c6],
+ post=[0x00a0, 0x00b0, 0x00b1, 0x00ca],
+ xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00ac, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Purchase a bunch of items from a shop.
+ ''',
+ )
+ map_user.r(0x00c9, 'npc shop sell',
+ define='CMSG_NPC_SELL_REQUEST',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -2226,65 +3366,127 @@ def main():
at(2, u16, 'count'),
],
repeat_size=4,
+ pre=[0x00c7],
+ post=[0x00ac, 0x00af, 0x00b0, 0x00b1, 0x00cb, 0x01d7],
+ xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Sell a bunch of items to a shop.
+ ''',
)
- map_user.s(0x00ca, 'npc buy response',
+ map_user.s(0x00ca, 'npc shop buy result',
+ define='SMSG_NPC_BUY_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'fail'),
],
fixed_size=3,
+ pre=[0x00c8],
+ post=[PRETTY],
+ desc='''
+ Result of purchasing items.
+ ''',
)
- map_user.s(0x00cb, 'npc sell response',
+ map_user.s(0x00cb, 'npc shop sell result',
+ define='SMSG_NPC_SELL_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'fail'),
],
fixed_size=3,
+ pre=[0x00c9],
+ post=[PRETTY],
+ desc='''
+ Result of purchasing items.
+ ''',
)
- map_user.s(0x00cd, 'kick status',
+ # 0x00cc define='CMSG_ADMIN_KICK',
+ map_user.s(0x00cd, 'kick result',
+ define='SMSG_ADMIN_KICK_ACK',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
],
fixed_size=6,
+ pre=[GM],
+ post=[PRETTY],
+ desc='''
+ Successfully used @kick.
+ ''',
)
- map_user.r(0x00e4, 'trade request request',
+ # 0x00cf define='CMSG_IGNORE_NICK',
+ # 0x00d0 define='CMSG_IGNORE_ALL',
+ # 0x00d2 define='SMSG_IGNORE_ALL_RESPONSE',
+ map_user.r(0x00e4, 'trade please',
+ define='CMSG_TRADE_REQUEST',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
],
fixed_size=6,
+ pre=[HUMAN],
+ post=[0x00e5, 0x00e7, 0x00ee, 0x0110],
+ xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Ask someone to trade.
+ ''',
)
map_user.s(0x00e5, 'incoming trade request',
+ define='SMSG_TRADE_REQUEST',
fixed=[
at(0, u16, 'packet id'),
at(2, char_name, 'char name'),
],
fixed_size=26,
+ pre=[GM, 0x00e4],
+ post=[0x00e6],
+ desc='''
+ Somebody wants to trade with you.
+ ''',
)
- map_user.r(0x00e6, 'incoming trade request response',
+ map_user.r(0x00e6, 'incoming trade request result',
+ define='CMSG_TRADE_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'type'),
],
fixed_size=3,
+ pre=[0x00e5],
+ post=[0x00e7],
+ xpost=[MAGIC, SCRIPT, 0x0078, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00b6, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00f8, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ You agree/disagree to begin a trade with someone.
+ ''',
)
- map_user.s(0x00e7, 'trade request response',
+ map_user.s(0x00e7, 'trade please result',
+ define='SMSG_TRADE_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'type'),
],
fixed_size=3,
+ pre=[GM, 0x00e4, 0x00e6],
+ post=[PRETTY],
+ desc='''
+ The original result of asking for a trade.
+ ''',
)
- map_user.r(0x00e8, 'trade item add request',
+ map_user.r(0x00e8, 'trade add',
+ define='CMSG_TRADE_ITEM_ADD_REQUEST',
fixed=[
at(0, u16, 'packet id'),
at(2, ioff2, 'zeny or ioff2'),
at(4, u32, 'amount'),
],
fixed_size=8,
+ pre=[HUMAN],
+ post=[0x00ac, 0x00e9, 0x01b1, 0x01d7],
+ xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Add item/zeny to your trade offer.
+ ''',
)
- map_user.s(0x00e9, 'trade item add',
+ map_user.s(0x00e9, 'trade item added notify',
+ define='SMSG_TRADE_ITEM_ADD',
fixed=[
at(0, u16, 'packet id'),
at(2, u32, 'amount'),
@@ -2298,62 +3500,125 @@ def main():
at(17, u16, 'card3'),
],
fixed_size=19,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ Item successfully added.
+ ''',
)
- map_user.r(0x00eb, 'trade add complete',
+ map_user.r(0x00eb, 'trade lock',
+ define='CMSG_TRADE_ADD_COMPLETE',
fixed=[
at(0, u16, 'packet id'),
],
fixed_size=2,
+ pre=[HUMAN],
+ post=[0x00ec, 0x1b1],
+ xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Indicate readiness to end trade.
+
+ Do this when the other person has added all the items you want.
+ ''',
)
- map_user.s(0x00ec, 'trade ok',
+ map_user.s(0x00ec, 'trade lock notify',
+ define='SMSG_TRADE_OK',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'fail'),
],
fixed_size=3,
+ pre=[0x00eb],
+ post=[PRETTY],
+ desc='''
+ Somebody locked their half of the trade (0=self, 1=other).
+ ''',
)
- map_user.r(0x00ed, 'trace cancel request',
+ map_user.r(0x00ed, 'trade cancel',
+ define='CMSG_TRADE_CANCEL_REQUEST',
fixed=[
at(0, u16, 'packet id'),
],
fixed_size=2,
+ pre=[HUMAN],
+ post=[0x00a0, 0x00b0, 0x00ee],
+ xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00ac, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Cancel an ongoing trade.
+ ''',
)
- map_user.s(0x00ee, 'trade cancel',
+ map_user.s(0x00ee, 'trade cancel notify',
+ define='SMSG_TRADE_CANCEL',
fixed=[
at(0, u16, 'packet id'),
],
fixed_size=2,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[],
+ desc='''
+ Somebody cancelled your trade.
+ ''',
)
- map_user.r(0x00ef, 'trade ok request',
+ map_user.r(0x00ef, 'trade commit',
+ define='CMSG_TRADE_OK',
fixed=[
at(0, u16, 'packet id'),
],
fixed_size=2,
+ pre=[HUMAN],
+ post=[0x00a0, 0x00af, 0x00b0, 0x00f0],
+ xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00ac, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Actually perform the trade. Requires half-lock from both sides.
+ ''',
)
- map_user.s(0x00f0, 'trade complete',
+ map_user.s(0x00f0, 'trade complete notify',
+ define='SMSG_TRADE_COMPLETE',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'fail'),
],
fixed_size=3,
+ pre=[0x00ef],
+ post=[PRETTY],
+ desc='''
+ Trade is finally over.
+ ''',
)
- map_user.s(0x00f2, 'storage status',
+ map_user.s(0x00f2, 'storage size notify',
+ define='SMSG_PLAYER_STORAGE_STATUS',
fixed=[
at(0, u16, 'packet id'),
at(2, u16, 'current slots'),
at(4, u16, 'max slots'),
],
fixed_size=6,
+ pre=[GM, SCRIPT, 0x00f3, 0x00f5, 0x3810],
+ post=[PRETTY],
+ desc='''
+ Update the current/max storage size.
+
+ It's easy to change the max storage on any basis, pity about
+ the inventory limit ...
+ ''',
)
- map_user.r(0x00f3, 'move item to storage request',
+ map_user.r(0x00f3, 'storage put',
+ define='CMSG_MOVE_TO_STORAGE',
fixed=[
at(0, u16, 'packet id'),
at(2, ioff2, 'ioff2'),
at(4, u32, 'amount'),
],
fixed_size=8,
+ pre=[HUMAN],
+ post=[0x00ac, 0x00af, 0x00b0, 0x00f2, 0x00f4, 0x01d7],
+ xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Move item from inventory to storage.
+ ''',
)
- map_user.s(0x00f4, 'move item to storage',
+ map_user.s(0x00f4, 'storage added notify',
+ define='SMSG_PLAYER_STORAGE_ADD',
fixed=[
at(0, u16, 'packet id'),
at(2, soff1, 'soff1'),
@@ -2368,50 +3633,98 @@ def main():
at(19, u16, 'card3'),
],
fixed_size=21,
+ pre=[GM, 0x00f3],
+ post=[PRETTY],
+ desc='''
+ Item was added to your storage.
+ ''',
)
- map_user.r(0x00f5, 'move item from storage request',
+ map_user.r(0x00f5, 'storage take',
+ define='CSMG_MOVE_FROM_STORAGE',
fixed=[
at(0, u16, 'packet id'),
at(2, soff1, 'soff1'),
at(4, u32, 'amount'),
],
fixed_size=8,
+ pre=[HUMAN],
+ post=[0x00a0, 0x00b0, 0x00f2, 0x00f6],
+ xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00ac, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Move item from storage to inventory.
+ ''',
)
- map_user.s(0x00f6, 'remove item from storage',
+ map_user.s(0x00f6, 'storage removed notify',
+ define='SMSG_PLAYER_STORAGE_REMOVE',
fixed=[
at(0, u16, 'packet id'),
at(2, soff1, 'soff1'),
at(4, u32, 'amount'),
],
fixed_size=8,
+ pre=[0x00f5],
+ post=[PRETTY],
+ desc='''
+ Item was removed from your storage.
+ ''',
)
- map_user.r(0x00f7, 'storage close request',
+ map_user.r(0x00f7, 'storage close',
+ define='CMSG_CLOSE_STORAGE',
fixed=[
at(0, u16, 'packet id'),
],
fixed_size=2,
+ pre=[HUMAN],
+ post=[MAGIC, SCRIPT, 0x00f8, 0x2b01, 0x3011],
+ xpost=[0x0078, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00b6, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b05, 0x3022],
+ desc='''
+ Close your storage.
+ ''',
)
- map_user.s(0x00f8, 'storage closed',
+ map_user.s(0x00f8, 'storage closed notify',
+ define='SMSG_PLAYER_STORAGE_CLOSE',
fixed=[
at(0, u16, 'packet id'),
],
fixed_size=2,
+ pre=[GM, 0x00e6, 0x00f7],
+ post=[PRETTY],
+ desc='''
+ Your storage was closed.
+ ''',
)
- map_user.r(0x00f9, 'create party request',
+ map_user.r(0x00f9, 'party create',
+ define='CMSG_PARTY_CREATE',
fixed=[
at(0, u16, 'packet id'),
at(2, party_name, 'party name'),
],
fixed_size=26,
+ pre=[HUMAN],
+ post=[0x00fa, 0x110, 0x3020],
+ desc='''
+ Create a new party with yourself.
+ ''',
)
- map_user.s(0x00fa, 'create party',
+ map_user.s(0x00fa, 'party create result',
+ define='SMSG_PARTY_CREATE',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'flag'),
],
fixed_size=3,
- )
- map_user.s(0x00fb, 'party info',
+ pre=[GM, 0x00f9, 0x3820],
+ post=[PRETTY],
+ desc='''
+ Result of creating a new party.
+
+ flag 0: success
+ flag 1: bad name
+ flag 2: already in party
+ ''',
+ )
+ map_user.s(0x00fb, 'party info notify',
+ define='SMSG_PARTY_INFO',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -2426,69 +3739,130 @@ def main():
at(45, u8, 'online'),
],
repeat_size=46,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0089, 0x3821, 0x3825],
+ post=[PRETTY],
+ desc='''
+ Full info about your party.
+ ''',
)
- map_user.r(0x00fc, 'party invite request',
+ map_user.r(0x00fc, 'party invite',
+ define='CMSG_PARTY_INVITE',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
],
fixed_size=6,
+ pre=[HUMAN],
+ post=[0x00fd, 0x00fe],
+ desc='''
+ Invite player to your party.
+ ''',
)
- map_user.s(0x00fd, 'party invite response',
+ map_user.s(0x00fd, 'party invite result',
+ define='SMSG_PARTY_INVITE_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, char_name, 'char name'),
at(26, u8, 'flag'),
],
fixed_size=27,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x00fc, 0x00ff, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d, 0x3822],
+ post=[PRETTY],
+ desc='''
+ Party invitation response.
+
+ flag 0: already in party
+ flag 1: reject
+ flag 2: accept
+ flag 3: party full
+ flag 4: same party
+ ''',
)
- map_user.s(0x00fe, 'party invite succeeded',
+ map_user.s(0x00fe, '(reverse) party invitation',
+ define='SMSG_PARTY_INVITED',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
at(6, party_name, 'party name'),
],
fixed_size=30,
+ pre=[0x00fc],
+ post=[0x00ff],
+ desc='''
+ You're invited to join someone's party.
+ ''',
)
- map_user.r(0x00ff, 'party join request',
+ map_user.r(0x00ff, '(reverse) party invitation result',
+ define='CMSG_PARTY_INVITED',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
at(6, u32, 'flag'),
],
fixed_size=10,
+ pre=[0x00fe],
+ post=[0x00fd, 0x0110, 0x3022],
+ desc='''
+ Reply to party invitation.
+ ''',
)
- map_user.r(0x0100, 'party leave request',
+ map_user.r(0x0100, 'party leave',
+ define='CMSG_PARTY_LEAVE',
fixed=[
at(0, u16, 'packet id'),
],
fixed_size=2,
+ pre=[HUMAN],
+ post=[0x3024],
+ desc='''
+ I'm sick of your lame party.
+ ''',
)
- map_user.s(0x0101, 'party settings',
+ map_user.s(0x0101, 'party option notify',
+ define='SMSG_PARTY_SETTINGS',
fixed=[
at(0, u16, 'packet id'),
at(2, u16, 'exp'),
at(4, u16, 'item'),
],
fixed_size=6,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0089, 0x3821, 0x3823],
+ post=[PRETTY],
+ desc='''
+ Party settings changed.
+ ''',
)
- map_user.r(0x0102, 'party settings request',
+ map_user.r(0x0102, 'party option',
+ define='CMSG_PARTY_SETTINGS',
fixed=[
at(0, u16, 'packet id'),
at(2, u16, 'exp'),
at(4, u16, 'item'),
],
fixed_size=6,
+ pre=[HUMAN],
+ post=[0x3023],
+ desc='''
+ Please change party settings.
+ ''',
)
- map_user.r(0x0103, 'party kick request',
+ map_user.r(0x0103, 'party kick',
+ define='CMSG_PARTY_KICK',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
at(6, char_name, 'unused char name'),
],
fixed_size=30,
+ pre=[HUMAN],
+ post=[0x3024],
+ desc='''
+ Forcibly remove party member.
+ ''',
)
- map_user.s(0x0105, 'party leave',
+ # 0x0104 define='SMSG_PARTY_MOVE',
+ map_user.s(0x0105, 'party left notify',
+ define='SMSG_PARTY_LEAVE',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -2496,8 +3870,14 @@ def main():
at(30, u8, 'flag'),
],
fixed_size=31,
+ pre=[0x3824, 0x3826],
+ post=[PRETTY],
+ desc='''
+ Somebody got tired of your party.
+ ''',
)
- map_user.s(0x0106, 'update party member hp',
+ map_user.s(0x0106, 'party hp notify',
+ define='SMSG_PARTY_UPDATE_HP',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -2505,8 +3885,14 @@ def main():
at(8, u16, 'max hp'),
],
fixed_size=10,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ Party member hp update.
+ ''',
)
- map_user.s(0x0107, 'update party member coords',
+ map_user.s(0x0107, 'party xy notify',
+ define='SMSG_PARTY_UPDATE_COORDS',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -2514,8 +3900,14 @@ def main():
at(8, u16, 'y'),
],
fixed_size=10,
- )
- map_user.r(0x0108, 'party message request',
+ pre=[TIMER],
+ post=[PRETTY],
+ desc='''
+ Party member location update.
+ ''',
+ )
+ map_user.r(0x0108, 'party message',
+ define='CMSG_PARTY_MESSAGE',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -2525,8 +3917,14 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
- )
- map_user.s(0x0109, 'party message',
+ pre=[HUMAN],
+ post=[0x2b0e, 0x3003, 0x3027],
+ desc='''
+ Talk privately to your party.
+ ''',
+ )
+ map_user.s(0x0109, 'party message notify',
+ define='SMSG_PARTY_MESSAGE',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -2536,16 +3934,17 @@ def main():
repeat=[
at(0, u8, 'c'),
],
- repeat_size=1
- )
- map_user.s(0x010c, 'MVP (unused)',
- fixed=[
- at(0, u16, 'packet id'),
- at(2, block_id, 'block id'),
- ],
- fixed_size=6,
+ repeat_size=1,
+ pre=[0x3827],
+ post=[PRETTY],
+ desc='''
+ Somebody in your party is talking to you.
+ ''',
)
- map_user.s(0x010e, 'raise a skill',
+ # 0x010c define='SMSG_MVP',
+ map_user.s(0x010e, 'skill raise result',
+ # define='SMSG_GUILD_SKILL_UP',
+ define='SMSG_PLAYER_SKILL_UP',
fixed=[
at(0, u16, 'packet id'),
at(2, skill_id, 'skill id'),
@@ -2555,8 +3954,14 @@ def main():
at(10, u8, 'can raise'),
],
fixed_size=11,
- )
- map_user.s(0x010f, 'player skills',
+ pre=[0x0112],
+ post=[PRETTY],
+ desc='''
+ Successfully raised a skill.
+ ''',
+ )
+ map_user.s(0x010f, 'skill info notify',
+ define='SMSG_PLAYER_SKILLS',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -2566,8 +3971,14 @@ def main():
at(0, skill_info, 'info'),
],
repeat_size=37,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ Enumeration of your skills.
+ ''',
)
map_user.s(0x0110, 'skill failed',
+ define='SMSG_SKILL_FAILED',
fixed=[
at(0, u16, 'packet id'),
at(2, skill_id, 'skill id'),
@@ -2577,21 +3988,42 @@ def main():
at(9, u8, 'type'),
],
fixed_size=10,
+ pre=[0x0089, 0x00bf, 0x00e4, 0x00f9, 0x00ff],
+ post=[PRETTY],
+ desc='''
+ Failed to perform a skill.
+ ''',
)
- map_user.r(0x0112, 'request a skill lvl up',
+ map_user.r(0x0112, 'skill raise',
+ define='CMSG_SKILL_LEVELUP_REQUEST',
fixed=[
at(0, u16, 'packet id'),
at(2, skill_id, 'skill id'),
],
fixed_size=4,
+ pre=[HUMAN],
+ post=[0x00b0, 0x010e, 0x010f],
+ xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Spend skill points to raise your skills.
+ ''',
)
- map_user.r(0x0118, 'stop attack request',
+ # 0x0113 define='CMSG_SKILL_USE_BEING',
+ # 0x0116 define='CMSG_SKILL_USE_POSITION',
+ map_user.r(0x0118, 'attack stop',
+ define='CMSG_PLAYER_STOP_ATTACK',
fixed=[
at(0, u16, 'packet id'),
],
fixed_size=2,
+ pre=[HUMAN],
+ post=[IDLE],
+ desc='''
+ Cancel a continuous attack.
+ ''',
)
- map_user.s(0x0119, 'change player status',
+ map_user.s(0x0119, 'player option notify',
+ define='SMSG_PLAYER_STATUS_CHANGE',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
@@ -2601,8 +4033,16 @@ def main():
at(12, u8, 'zero'),
],
fixed_size=13,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ Update 3/4ths of the option fields.
+ ''',
)
- map_user.s(0x0139, 'move player to within attack range',
+ # 0x011a define='SMSG_SKILL_NO_DAMAGE',
+ # 0x011b define='CMSG_SKILL_USE_MAP',
+ map_user.s(0x0139, 'player move attack range notify',
+ define='SMSG_PLAYER_MOVE_TO_ATTACK',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
@@ -2613,29 +4053,54 @@ def main():
at(14, u16, 'range'),
],
fixed_size=16,
+ pre=[0x0089],
+ post=[PRETTY],
+ desc='''
+ You are moving to enter attack range.
+ ''',
)
- map_user.s(0x013a, 'player attack range',
+ map_user.s(0x013a, 'player attack range notify',
+ define='SMSG_PLAYER_ATTACK_RANGE',
fixed=[
at(0, u16, 'packet id'),
at(2, u16, 'attack range'),
],
fixed_size=4,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ Update attack range field.
+ ''',
)
- map_user.s(0x013b, 'player arrow message',
+ map_user.s(0x013b, 'player arrow fail notify',
+ define='SMSG_PLAYER_ARROW_MESSAGE',
fixed=[
at(0, u16, 'packet id'),
at(2, u16, 'type'),
],
fixed_size=4,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0089, 0x00a9],
+ post=[PRETTY],
+ desc='''
+ Arrow status: failed (0) or equipped (3, ignored).
+ ''',
)
- map_user.s(0x013c, 'player arrow equip',
+ map_user.s(0x013c, 'player arrow equip notify',
+ define='SMSG_PLAYER_ARROW_EQUIP',
fixed=[
at(0, u16, 'packet id'),
at(2, ioff2, 'ioff2'),
],
fixed_size=4,
+ pre=[0x007d, 0x00a9],
+ post=[PRETTY],
+ desc='''
+ Arrow equip inventory slot.
+ ''',
)
+ # 0x013e define='SMSG_SKILL_CASTING',
map_user.s(0x0141, 'player stat update 3',
+ define='SMSG_PLAYER_STAT_UPDATE_3',
fixed=[
at(0, u16, 'packet id'),
at(2, sp, 'sp type'),
@@ -2644,65 +4109,142 @@ def main():
at(10, u32, 'value b e'),
],
fixed_size=14,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ Update base and cumulative (?) stat points.
+ ''',
)
- map_user.s(0x0142, 'npc integer input',
+ map_user.s(0x0142, '(reverse) script input integer',
+ define='SMSG_NPC_INT_INPUT',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
],
fixed_size=6,
+ pre=[SCRIPT],
+ post=[0x0143],
+ desc='''
+ Npc wants an integer.
+ ''',
)
- map_user.r(0x0143, 'npc integer response',
+ map_user.r(0x0143, '(reverse) script input integer result',
+ define='CMSG_NPC_INT_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
at(6, u32, 'input int value'),
],
fixed_size=10,
+ pre=[0x0142],
+ post=[MAGIC, SCRIPT],
+ xpost=[0x0078, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00b6, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Npc can have an integer.
+ ''',
)
- map_user.r(0x0146, 'npc close request',
+ map_user.r(0x0146, '(reverse) script close response',
+ define='CMSG_NPC_CLOSE',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
],
fixed_size=6,
+ pre=[0x00b6],
+ post=[MAGIC, SCRIPT],
+ xpost=[0x0078, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00b6, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Interactive npc chat closed, maybe finish noninteractively now.
+ ''',
)
- map_user.s(0x0147, 'single skill info (unused)',
- fixed=[
- at(0, u16, 'packet id'),
- at(2, skill_info, 'info'),
- ],
- fixed_size=39,
- )
- map_user.s(0x0148, 'being resurrected',
+ map_user.s(0x0148, 'being resurrected notify',
+ define='SMSG_BEING_RESURRECT',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
at(6, u16, 'type'),
],
fixed_size=8,
- )
- map_user.r(0x014d, 'guild check master (unused)',
- fixed=[
- at(0, u16, 'packet id'),
- ],
- fixed_size=2,
- )
+ pre=[GM],
+ post=[PRETTY],
+ desc='''
+ It's alive!
+ ''',
+ )
+ # 0x0149 define='CMSG_ADMIN_MUTE',
+ # 0x014c define='SMSG_GUILD_ALIANCE_INFO',
+ # 0x014d define='CMSG_GUILD_CHECK_MASTER',
+ # 0x014e define='SMSG_GUILD_MASTER_OR_MEMBER',
+ # 0x014f define='CMSG_GUILD_REQUEST_INFO',
+ # 0x0151 define='CMSG_GUILD_REQUEST_EMBLEM',
+ # 0x0152 define='SMSG_GUILD_EMBLEM',
+ # 0x0153 define='CMSG_GUILD_CHANGE_EMBLEM',
+ # 0x0154 define='SMSG_GUILD_MEMBER_LIST',
+ # 0x0155 define='CMSG_GUILD_CHANGE_MEMBER_POS',
+ # 0x0156 define='SMSG_GUILD_MEMBER_POS_CHANGE',
+ # 0x0159 define='CMSG_GUILD_LEAVE',
+ # 0x015a define='SMSG_GUILD_LEAVE',
+ # 0x015b define='CMSG_GUILD_EXPULSION',
+ # 0x015c define='SMSG_GUILD_EXPULSION',
+ # 0x015d define='CMSG_GUILD_BREAK',
+ # 0x015e define='SMSG_GUILD_BROKEN',
+ # 0x0160 define='SMSG_GUILD_POS_INFO_LIST',
+ # 0x0161 define='CMSG_GUILD_CHANGE_POS_INFO',
+ # 0x0162 define='SMSG_GUILD_SKILL_INFO',
+ # 0x0163 define='SMSG_GUILD_EXPULSION_LIST',
+ # 0x0165 define='CMSG_GUILD_CREATE',
+ # 0x0166 define='SMSG_GUILD_POS_NAME_LIST',
+ # 0x0167 define='SMSG_GUILD_CREATE_RESPONSE',
+ # 0x0168 define='CMSG_GUILD_INVITE',
+ # 0x0169 define='SMSG_GUILD_INVITE_ACK',
+ # 0x016a define='SMSG_GUILD_INVITE',
+ # 0x016b define='CMSG_GUILD_INVITE_REPLY',
+ # 0x016c define='SMSG_GUILD_POSITION_INFO',
+ # 0x016d define='SMSG_GUILD_MEMBER_LOGIN',
+ # 0x016e define='CMSG_GUILD_CHANGE_NOTICE',
+ # 0x016f define='SMSG_GUILD_NOTICE',
+ # 0x0170 define='CMSG_GUILD_ALLIANCE_REQUEST',
+ # 0x0171 define='SMSG_GUILD_REQ_ALLIANCE',
+ # 0x0172 define='CMSG_GUILD_ALLIANCE_REPLY',
+ # 0x0173 define='SMSG_GUILD_REQ_ALLIANCE_ACK',
+ # 0x0174 define='SMSG_GUILD_POSITION_CHANGED',
+ # 0x017e define='CMSG_GUILD_MESSAGE',
+ # 0x017f define='SMSG_GUILD_MESSAGE',
+ # 0x0180 define='CMSG_GUILD_OPPOSITION',
+ # 0x0181 define='SMSG_GUILD_OPPOSITION_ACK',
+ # 0x0183 define='CMSG_GUILD_ALLIANCE_DELETE',
+ # 0x0184 define='SMSG_GUILD_DEL_ALLIANCE',
map_user.r(0x018a, 'client quit',
+ define='CMSG_CLIENT_QUIT',
fixed=[
at(0, u16, 'packet id'),
at(2, u16, 'unused'),
],
fixed_size=4,
+ pre=[NOTHING],
+ post=[0x018b],
+ desc='''
+ Request explicit end of connection.
+ ''',
)
- map_user.s(0x018b, 'map quit response',
+ map_user.s(0x018b, 'client quit result',
+ define='SMSG_MAP_QUIT_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, u16, 'okay'),
],
fixed_size=4,
+ pre=[GM, 0x00b2, 0x018a],
+ post=[PRETTY],
+ desc='''
+ Result of explicit end of connection.
+ ''',
)
- map_user.s(0x0195, 'guild party info (unused)',
+ # 0x0190 define='CMSG_SKILL_USE_POSITION_MORE',
+ # 0x0193 define='CMSG_SOLVE_CHAR_NAME',
+ # 0x0194 define='SMSG_SOLVE_CHAR_NAME',
+ map_user.s(0x0195, 'guild party info notify',
+ define='SMSG_PLAYER_GUILD_PARTY_INFO',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
@@ -2712,8 +4254,14 @@ def main():
at(78, str24, 'guild pos again'),
],
fixed_size=102,
+ pre=[0x0094],
+ post=[PRETTY],
+ desc='''
+ Name of player's party and guild.
+ ''',
)
- map_user.s(0x0196, 'being status change',
+ map_user.s(0x0196, 'being status change notify',
+ define='SMSG_BEING_STATUS_CHANGE',
fixed=[
at(0, u16, 'packet id'),
at(2, status_change, 'sc type'),
@@ -2721,16 +4269,32 @@ def main():
at(8, u8, 'flag'),
],
fixed_size=9,
- )
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ Being adds/removes a persistent status effect.
+ ''',
+ )
+ # 0x0199 define='SMSG_PVP_MAP_MODE',
+ # 0x019a define='SMSG_PVP_SET',
map_user.s(0x019b, 'being effect',
+ define='SMSG_BEING_SELFEFFECT',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
at(6, u32, 'type'),
],
fixed_size=10,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0089],
+ post=[PRETTY],
+ desc='''
+ Being runs a one-shot (?) effect.
+ ''',
)
- map_user.s(0x01b1, 'trade item add response',
+ # 0x019C define='CMSG_ADMIN_LOCAL_ANNOUNCE',
+ # 0x019D define='CMSG_ADMIN_HIDE',
+ map_user.s(0x01b1, 'trade add result',
+ define='SMSG_TRADE_ITEM_ADD_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, ioff2, 'ioff2'),
@@ -2738,8 +4302,16 @@ def main():
at(6, u8, 'fail'),
],
fixed_size=7,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ Result of trying to add an item/zeny to your trade offer.
+ ''',
)
- map_user.s(0x01c8, 'use inventory item succeeded',
+ # 0x01b6 define='SMSG_GUILD_BASIC_INFO',
+ # 0x01b9 define='SMSG_SKILL_CAST_CANCEL',
+ map_user.s(0x01c8, 'use item result',
+ define='SMSG_PLAYER_INVENTORY_USE',
fixed=[
at(0, u16, 'packet id'),
at(2, ioff2, 'ioff2'),
@@ -2749,15 +4321,29 @@ def main():
at(12, u8, 'ok'),
],
fixed_size=13,
+ pre=[0x00a7],
+ post=[PRETTY],
+ desc='''
+ You used (or tried to use) an item in your inventory.
+
+ Currently used only in the success case.
+ ''',
)
- map_user.s(0x01d4, 'npc string input',
+ map_user.s(0x01d4, '(reverse) script input string',
+ define='SMSG_NPC_STR_INPUT',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
],
fixed_size=6,
- )
- map_user.r(0x01d5, 'npc string response',
+ pre=[SCRIPT],
+ post=[0x01d5],
+ desc='''
+ Npc wants a string.
+ ''',
+ )
+ map_user.r(0x01d5, '(reverse) script input string result',
+ define='CMSG_NPC_STR_RESPONSE',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -2768,8 +4354,15 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[0x01d4],
+ post=[MAGIC, SCRIPT],
+ xpost=[0x0078, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00b6, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Npc can have an integer.
+ ''',
)
- map_user.s(0x01d7, 'change being appearance (unused)',
+ map_user.s(0x01d7, 'being change look',
+ define='SMSG_BEING_CHANGE_LOOKS2',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
@@ -2778,9 +4371,18 @@ def main():
at(9, item_name_id, 'shield'),
],
fixed_size=11,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[PRETTY],
+ desc='''
+ Being change appearance.
+
+ This may be a weapon type, an item nameid, or just a value -
+ it all depends on which look type.
+ ''',
)
# very similar to, but not compatible with, 0x01d9 and 0x01da
- map_user.s(0x01d8, 'player update 1',
+ map_user.s(0x01d8, 'player appear notify',
+ define='SMSG_PLAYER_UPDATE_1',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
@@ -2811,9 +4413,17 @@ def main():
at(52, u16, 'unused'),
],
fixed_size=54,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[0x0094, 0x00b2],
+ desc='''
+ A player appeared (unmoving).
+
+ Like 0x0078 but for players, and only in the "enter area" case.
+ ''',
)
# very similar to, but not compatible with, 0x01d8 and 0x01da
- map_user.s(0x01d9, 'player update 2',
+ map_user.s(0x01d9, 'player appear notify',
+ define='SMSG_PLAYER_UPDATE_2',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
@@ -2843,9 +4453,17 @@ def main():
at(51, u16, 'unused'),
],
fixed_size=53,
+ pre=[0x007d],
+ post=[0x0094],
+ desc='''
+ A wild player appeared!
+
+ Like 0x0078 but for players, but only in the "spawn on map" case.
+ ''',
)
# very similar to, but not compatible with, 0x01d8 and 0x01d9
- map_user.s(0x01da, 'player move',
+ map_user.s(0x01da, 'player move notify',
+ define='SMSG_PLAYER_MOVE',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
@@ -2877,8 +4495,17 @@ def main():
at(58, u16, 'unused'),
],
fixed_size=60,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[0x0094],
+ desc='''
+ A player appeared, moving.
+
+ Like 0x007b but for players.
+ ''',
)
map_user.s(0x01de, 'deal skill damage',
+ # define='CMSG_LOGIN_REGISTER2', with a different body ...
+ define='SMSG_SKILL_DAMAGE',
fixed=[
at(0, u16, 'packet id'),
at(2, skill_id, 'skill id'),
@@ -2893,8 +4520,14 @@ def main():
at(32, u8, 'type or hit'),
],
fixed_size=33,
- )
- map_user.s(0x01ee, 'player inventory',
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0089],
+ post=[PRETTY],
+ desc='''
+ Took damage from a skill.
+ ''',
+ )
+ map_user.s(0x01ee, 'inventory list notify',
+ define='SMSG_PLAYER_INVENTORY',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -2913,8 +4546,14 @@ def main():
at(16, u16, 'card3'),
],
repeat_size=18,
- )
- map_user.s(0x01f0, 'storage item list',
+ pre=[0x007d],
+ post=[PRETTY],
+ desc='''
+ List of all items in inventory.
+ ''',
+ )
+ map_user.s(0x01f0, 'storage list notify',
+ define='SMSG_PLAYER_STORAGE_ITEMS',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -2933,16 +4572,32 @@ def main():
at(16, u16, 'card3'),
],
repeat_size=18,
+ pre=[GM, SCRIPT, 0x3810],
+ post=[PRETTY],
+ desc='''
+ List of all items in storage.
+ ''',
)
- map_user.s(0x020c, 'set being ip',
+ map_user.s(0x020c, 'player ip notify',
+ define='SMSG_BEING_IP_RESPONSE',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'block id'),
at(6, ip4, 'ip'),
],
fixed_size=10,
+ pre=[0x0094],
+ post=[PRETTY],
+ desc='''
+ Show a player's (hashed) IP, for detecting alts.
+
+ The hash is rerandomized every restart.
+ ''',
)
+ # 0x0210 define='CMSG_ONLINE_LIST',
+ # 0x0211 define='SMSG_ONLINE_LIST',
map_user.s(0x0212, 'npc command',
+ define='SMSG_NPC_COMMAND',
fixed=[
at(0, u16, 'packet id'),
at(2, block_id, 'npc id'),
@@ -2952,16 +4607,60 @@ def main():
at(14, u16, 'y'),
],
fixed_size=16,
+ pre=[NOTHING],
+ post=[PRETTY],
+ desc='''
+ Make an npc do fancy things (for Manaplus).
+ ''',
)
-
- # login char
- login_char.r(0x2709, 'reload gm accounts request',
+ # 0x0213 define='CMSG_SET_STATUS',
+ # 0x0214 define='SMSG_QUEST_SET_VAR',
+ map_user.s(0x0214, 'send quest',
+ define='SMSG_QUEST_SET_VAR',
fixed=[
at(0, u16, 'packet id'),
+ at(2, u16, 'variable'),
+ at(4, u32, 'value'),
],
- fixed_size=2,
- )
- login_char.r(0x2710, 'add char server request',
+ fixed_size=8,
+ pre=[NOTHING],
+ post=[PRETTY],
+ desc='''
+ Set Quest Log Variable to Value.
+ ''',
+ )
+ # 0x0215 define='SMSG_QUEST_PLAYER_VARS',
+ map_user.s(0x0215, 'send all quest',
+ define='SMSG_QUEST_PLAYER_VARS',
+ head=[
+ at(0, u16, 'packet id'),
+ at(2, u16, 'packet length'),
+ ],
+ head_size=4,
+ repeat=[
+ at(0, u16, 'variable'),
+ at(2, u32, 'value'),
+ ],
+ repeat_size=6,
+ pre=[NOTHING],
+ post=[PRETTY],
+ desc='''
+ Set All Quest Log Variable to Value.
+ ''',
+ )
+ # 0x0220 define='SMSG_BEING_NAME_RESPONSE2',
+ # 0x0221 define='SMSG_CHAR_CREATE_SUCCEEDED2',
+ # 0x0222 define='CMSG_CHAT_MESSAGE2',
+ # 0x0223 define='SMSG_BEING_CHAT2',
+ # 0x0224 define='SMSG_PLAYER_CHAT2',
+ # 0x0225 define='SMSG_BEING_MOVE3',
+ # 0x0226 define='SMSG_MAP_MASK',
+ # 0x0227 define='SMSG_MAP_MUSIC',
+ # 0x0228 define='SMSG_NPC_CHANGETITLE',
+
+ # TOC_LOGINCHAR
+ # login char
+ login_char.r(0x2710, 'server connect char',
fixed=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
@@ -2975,15 +4674,25 @@ def main():
at(84, u16, 'is new'),
],
fixed_size=86,
+ pre=[BOOT],
+ post=[0x2711, 0x2732],
+ desc='''
+ Become an authenticated char server.
+ ''',
)
- login_char.s(0x2711, 'add char server result',
+ login_char.s(0x2711, 'server connect char result',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'code'),
],
fixed_size=3,
+ pre=[0x2710],
+ post=[FINISH, IDLE],
+ desc='''
+ Result of auth'ing as a char server.
+ ''',
)
- login_char.r(0x2712, 'account auth request',
+ login_char.r(0x2712, 'account auth',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -2993,6 +4702,11 @@ def main():
at(15, ip4, 'ip'),
],
fixed_size=19,
+ pre=[0x0065],
+ post=[0x2713, 0x2729],
+ desc='''
+ Check whether client's cookies are okay.
+ ''',
)
login_char.s(0x2713, 'account auth result',
fixed=[
@@ -3000,35 +4714,60 @@ def main():
at(2, account_id, 'account id'),
at(6, u8, 'invalid'),
at(7, account_email, 'email'),
- at(47, time32, 'connect until'),
+ at(47, time32, 'unused connect until'),
],
fixed_size=51,
+ pre=[0x2712],
+ post=[0x006b, 0x006c],
+ desc='''
+ Send account auth status to tmwa-char.
+
+ Status:
+ 0: good
+ 1: bad
+ ''',
)
- login_char.r(0x2714, 'online count',
+ login_char.r(0x2714, 'online count notify',
fixed=[
at(0, u16, 'packet id'),
at(2, u32, 'users'),
],
fixed_size=6,
+ pre=[TIMER],
+ post=[IDLE],
+ desc='''
+ Update count of online users.
+
+ This occurs every few seconds, so is also used for antifreeze if enabled.
+ ''',
)
- login_char.r(0x2716, 'email limit request',
+ login_char.r(0x2716, 'email limit',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
],
fixed_size=6,
+ pre=[0x0065],
+ post=[0x2717],
+ desc='''
+ Fetch current email (and previously, validity limit) for an account.
+ ''',
)
login_char.s(0x2717, 'email limit result',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
at(6, account_email, 'email'),
- at(46, time32, 'connect until'),
+ at(46, time32, 'unused connect until'),
],
fixed_size=50,
+ pre=[0x2716],
+ post=[IDLE],
+ desc='''
+ Yield current email (and previously, validity limit) for an account.
+ ''',
)
- # 0x2b0a
- login_char.r(0x2720, 'become gm request',
+ login_char.r(0x2720, 'become gm account',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3037,17 +4776,26 @@ def main():
head_size=8,
repeat=[at(0, u8, 'c')],
repeat_size=1,
+ pre=[0x2b0a],
+ post=[0x2721, 0x2732],
+ desc='''
+ Grant privileges by password.
+ ''',
)
- login_char.s(0x2721, 'become gm reply',
+ login_char.s(0x2721, 'become gm account reply',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
at(6, gm, 'gm level'),
],
fixed_size=10,
+ pre=[0x2720],
+ post=[0x2b0b],
+ desc='''
+ Maybe granted privileges?
+ ''',
)
- # 0x2b0c
- login_char.r(0x2722, 'account email change request',
+ login_char.r(0x2722, 'account email change',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -3055,14 +4803,24 @@ def main():
at(46, account_email, 'new email'),
],
fixed_size=86,
+ pre=[0x2b0c],
+ post=[IDLE],
+ desc='''
+ Change account email.
+ ''',
)
- login_char.s(0x2723, 'changesex reply',
+ login_char.s(0x2723, 'changesex result/notify',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
at(6, sex, 'sex'),
],
fixed_size=7,
+ pre=[0x2727, 0x793c],
+ post=[0x2b0d],
+ desc='''
+ Set was flipped (if from char server) or set to an absolute value (if set from admin).
+ ''',
)
login_char.r(0x2724, 'block status',
fixed=[
@@ -3071,6 +4829,15 @@ def main():
at(6, u32, 'status'),
],
fixed_size=10,
+ pre=[0x2b0e],
+ post=[0x2731],
+ desc='''
+ Set an account's blocked status, see 0x006a but add one.
+
+ The only really useful values here are 0 ("unblock") and
+ 5 ("block"). Don't use this for state 7 ("ban") because it
+ requires a timestamp argument.
+ ''',
)
login_char.r(0x2725, 'ban add',
fixed=[
@@ -3079,6 +4846,12 @@ def main():
at(6, human_time_diff, 'ban add'),
],
fixed_size=18,
+ pre=[0x2b0e],
+ post=[0x2731],
+ desc='''
+ Adjust the time of an account's temporary ban. If the account
+ is not already banned, first set the ban to the current time.
+ ''',
)
# evil packet, see also 0x794e
login_admin.s(0x2726, 'broadcast',
@@ -3092,47 +4865,94 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[0x794e],
+ post=[0x3800],
+ desc='''
+ This packet is evil.
+ ''',
)
- login_char.r(0x2727, 'change sex request',
+ login_char.r(0x2727, 'change sex',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
],
fixed_size=6,
+ pre=[0x2b0e],
+ post=[0x2723],
+ desc='''
+ Flip account's gender.
+ ''',
)
- # 0x2b10, 0x2b11
- for (id, cat) in [
- (0x2728, login_char.r),
- (0x2729, login_char.s),
- ]:
- cat(id, 'update account reg2',
- head=[
- at(0, u16, 'packet id'),
- at(2, u16, 'packet length'),
- at(4, account_id, 'account id'),
- ],
- head_size=8,
- repeat=[
- at(0, var_name, 'name'),
- at(32, u32, 'value'),
- ],
- repeat_size=36,
- )
- login_char.r(0x272a, 'unban request',
+ login_char.r(0x2728, 'update account reg2',
+ head=[
+ at(0, u16, 'packet id'),
+ at(2, u16, 'packet length'),
+ at(4, account_id, 'account id'),
+ ],
+ head_size=8,
+ repeat=[
+ at(0, var_name, 'name'),
+ at(32, u32, 'value'),
+ ],
+ repeat_size=36,
+ pre=[0x2b10],
+ post=[0x2729],
+ desc='''
+ Update account's login-stored variables.
+
+ These start with ## and are unused because:
+
+ 1. They didn't work at all before I started working on the server.
+ 2. We only run one char-server so we might as well store vars there.
+ 3. The meaning would be weird anyway.
+ ''',
+ )
+ login_char.s(0x2729, 'update account reg2 notify',
+ head=[
+ at(0, u16, 'packet id'),
+ at(2, u16, 'packet length'),
+ at(4, account_id, 'account id'),
+ ],
+ head_size=8,
+ repeat=[
+ at(0, var_name, 'name'),
+ at(32, u32, 'value'),
+ ],
+ repeat_size=36,
+ pre=[0x2712, 0x2728],
+ post=[0x2b11],
+ desc='''
+ Updated account's ##variables.
+
+ This is sent both in response to 0x2728 and in response to
+ 0x2712 cookie check.
+ '''
+ )
+ login_char.r(0x272a, 'unban',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
],
fixed_size=6,
+ pre=[0x2b0e],
+ post=[IDLE],
+ desc='''
+ Clear an account's temporary ban date.
+ ''',
)
- login_char.s(0x2730, 'account deleted',
+ login_char.s(0x2730, 'account deleted notify',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
],
fixed_size=6,
+ pre=[0x7932],
+ post=[0x2afe, 0x2b12, 0x2b13, 0x3821, 0x3824, 0x3826],
+ desc='''
+ Account just doesn't exist anymore.
+ ''',
)
- login_char.s(0x2731, 'status or ban changed',
+ login_char.s(0x2731, 'status or ban changed notify',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -3140,8 +4960,13 @@ def main():
at(7, time32, 'status or ban until'),
],
fixed_size=11,
+ pre=[0x2724, 0x2725, 0x7936, 0x794a, 0x794c],
+ post=[0x2b14],
+ desc='''
+ Account has a new ban date or block status.
+ ''',
)
- login_char.s(0x2732, 'gm account list',
+ login_char.s(0x2732, 'gm account list notify',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3152,8 +4977,13 @@ def main():
at(4, gm1, 'gm level'),
],
repeat_size=5,
+ pre=[TIMER, 0x2710, 0x2720, 0x793e, 0x7955],
+ post=[0x2b15],
+ desc='''
+ Send GM accounts to all character servers.
+ ''',
)
- login_char.r(0x2740, 'change password request',
+ login_char.r(0x2740, 'change password',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -3161,24 +4991,29 @@ def main():
at(30, account_pass, 'new pass'),
],
fixed_size=54,
+ pre=[0x0061],
+ post=[0x2741],
+ desc='''
+ Change password of an account.
+ ''',
)
- login_char.s(0x2741, 'change password reply',
+ login_char.s(0x2741, 'change password result',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
at(6, u8, 'status'),
],
fixed_size=7,
+ pre=[0x2740],
+ post=[0x0062],
+ desc='''
+ Password changed or not.
+ ''',
)
+ # TOC_CHARMAP
# char map
- char_map.r(0x2af7, 'reload gm db',
- fixed=[
- at(0, u16, 'packet id'),
- ],
- fixed_size=2,
- )
- char_map.r(0x2af8, 'add map server request',
+ char_map.r(0x2af8, 'server connect map',
fixed=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
@@ -3188,15 +5023,27 @@ def main():
at(58, u16, 'port'),
],
fixed_size=60,
+ pre=[TIMER],
+ post=[0x2af9, 0x2b15],
+ desc='''
+ Become an authenticated map server.
+ ''',
)
- char_map.s(0x2af9, 'add map server result',
+ char_map.s(0x2af9, 'server connect map result',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'code'),
],
fixed_size=3,
+ pre=[0x2af8],
+ post=[FINISH, SCRIPT, 0x2afa],
+ desc='''
+ Did I auth?
+ ''',
)
# wtf duplicate v
+ # formerly 0x2afa, now fixed, but now sorted wrong
+ # (should go below, but don't want to break diff)
char_map.r(0x2afa, 'map list',
head=[
at(0, u16, 'packet id'),
@@ -3207,25 +5054,34 @@ def main():
at(0, map_name, 'map name'),
],
repeat_size=16,
+ pre=[0x2af9],
+ post=[0x2afb, 0x2b04],
+ desc='''
+ Tell the char server what maps I have.
+ ''',
)
# wtf duplicate ^
- char_map.s(0x2afa, 'itemfrob',
- fixed=[
- at(0, u16, 'packet id'),
- at(2, item_name_id4, 'source item id'),
- at(6, item_name_id4, 'dest item id'),
- ],
- fixed_size=10,
- )
- char_map.s(0x2afb, 'map list ack',
+ # formerly 0x2afa, now fixed, but now sorted wrong
+ # (should go below, but don't want to break diff)
+ #
+ # (now erased)
+
+ char_map.s(0x2afb, 'map list result',
fixed=[
at(0, u16, 'packet id'),
at(2, u8, 'unknown'),
- at(3, char_name, 'whisper name'),
+ at(3, char_name, 'unused whisper name'),
],
fixed_size=27,
+ pre=[0x2afa],
+ post=[FINISH, IDLE],
+ desc='''
+ I got your maps.
+
+ (having pronoun consistency problems here)
+ ''',
)
- char_map.r(0x2afc, 'character auth request',
+ char_map.r(0x2afc, 'character auth',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -3235,19 +5091,30 @@ def main():
at(18, ip4, 'ip'),
],
fixed_size=22,
+ pre=[0x0072],
+ post=[0x2afd, 0x2afe],
+ desc='''
+ Check whether client's cookies are okay.
+ ''',
)
- char_map.s(0x2afd, 'character auth and data',
+ char_map.s(0x2afd, 'character auth success',
payload=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
at(4, account_id, 'account id'),
at(8, u32, 'login id2'),
- at(12, time32, 'connect until'),
+ at(12, time32, 'unused connect until'),
at(16, u16, 'packet tmw version'),
at(18, char_key, 'char key'),
at(None, char_data, 'char data'),
],
payload_size=None,
+ pre=[0x2afc],
+ post=[0x0073, 0x0081, 0x0091, 0x3005, 0x3021],
+ xpost=[GM, SCRIPT, 0x0080, 0x0088, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022],
+ desc='''
+ Account authentication succeeded.
+ ''',
)
char_map.s(0x2afe, 'character auth error',
fixed=[
@@ -3255,6 +5122,11 @@ def main():
at(2, account_id, 'account id'),
],
fixed_size=6,
+ pre=[0x0068, 0x2730, 0x2afc],
+ post=[0x0081],
+ desc='''
+ Send account id to tmwa-map for disconnection.
+ ''',
)
char_map.r(0x2aff, 'user list',
head=[
@@ -3267,6 +5139,11 @@ def main():
at(0, char_id, 'char id'),
],
repeat_size=4,
+ pre=[TIMER],
+ post=[IDLE],
+ desc='''
+ Where can everybody be found?
+ ''',
)
char_map.s(0x2b00, 'total users',
fixed=[
@@ -3274,6 +5151,11 @@ def main():
at(2, u32, 'users'),
],
fixed_size=6,
+ pre=[TIMER],
+ post=[IDLE],
+ desc='''
+ How many people are there in total?
+ ''',
)
char_map.r(0x2b01, 'character save',
payload=[
@@ -3285,8 +5167,13 @@ def main():
at(None, char_data, 'char data'),
],
payload_size=None,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[IDLE],
+ desc='''
+ Full character save.
+ ''',
)
- char_map.r(0x2b02, 'char select req',
+ char_map.r(0x2b02, 'char select',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -3295,16 +5182,26 @@ def main():
at(14, ip4, 'ip'),
],
fixed_size=18,
+ pre=[0x00b2],
+ post=[0x2b03],
+ desc='''
+ Client is going back to select character again.
+ ''',
)
- char_map.s(0x2b03, 'char select res',
+ char_map.s(0x2b03, 'char select result',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
at(6, u8, 'unknown'),
],
fixed_size=7,
+ pre=[0x2b02],
+ post=[0x00b3],
+ desc='''
+ Client can go back to select character again.
+ ''',
)
- char_map.s(0x2b04, 'map list broadcast',
+ char_map.s(0x2b04, 'map list notify',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3316,8 +5213,13 @@ def main():
at(0, map_name, 'map name'),
],
repeat_size=16,
+ pre=[0x2afa],
+ post=[IDLE],
+ desc='''
+ Send remote map information to everyone.
+ ''',
)
- char_map.r(0x2b05, 'change map server request',
+ char_map.r(0x2b05, 'change map server',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -3333,6 +5235,11 @@ def main():
at(45, ip4, 'client ip'),
],
fixed_size=49,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[0x2b06],
+ desc='''
+ Client wants to use a remote map.
+ ''',
)
char_map.s(0x2b06, 'change map server ack',
fixed=[
@@ -3348,9 +5255,13 @@ def main():
at(42, u16, 'map port'),
],
fixed_size=44,
+ pre=[0x2b05],
+ post=[0x0081, 0x0092],
+ desc='''
+ Client can use a remote map.
+ ''',
)
- # 0x2720
- char_map.r(0x2b0a, 'become gm request',
+ char_map.r(0x2b0a, 'become gm',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3359,6 +5270,11 @@ def main():
head_size=8,
repeat=[at(0, u8, 'c')],
repeat_size=1,
+ pre=[GM],
+ post=[0x2720, 0x2b0b],
+ desc='''
+ Get a GM level from a password.
+ ''',
)
char_map.s(0x2b0b, 'become gm result',
fixed=[
@@ -3367,9 +5283,13 @@ def main():
at(6, gm, 'gm level'),
],
fixed_size=10,
+ pre=[0x2721, 0x2b0a],
+ post=[IDLE],
+ desc='''
+ Got a GM level from a password, or not.
+ ''',
)
- # 0x2722
- char_map.r(0x2b0c, 'change email request',
+ char_map.r(0x2b0c, 'change email',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -3377,6 +5297,11 @@ def main():
at(46, account_email, 'new email'),
],
fixed_size=86,
+ pre=[GM],
+ post=[0x2722],
+ desc='''
+ Player used @email.
+ ''',
)
char_map.s(0x2b0d, 'sex changed notify',
fixed=[
@@ -3385,8 +5310,14 @@ def main():
at(6, sex, 'sex'),
],
fixed_size=7,
+ pre=[0x2723],
+ post=[0x00ac, 0x01d7, 0x2b01, 0x3011],
+ xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b05, 0x3022],
+ desc='''
+ Kick someone who had a sex-change operation.
+ ''',
)
- char_map.r(0x2b0e, 'named char operation request',
+ char_map.r(0x2b0e, 'named char operation',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -3395,8 +5326,13 @@ def main():
at(32, human_time_diff, 'ban add'),
],
fixed_size=44,
+ pre=[GM, SCRIPT, 0x008c, 0x0096, 0x0108],
+ post=[0x2724, 0x2725, 0x2727, 0x272a, 0x2b0f],
+ desc='''
+ Perform block/ban/unblock/unban/sexchange by name.
+ ''',
)
- char_map.r(0x2b0f, 'named char operation answer',
+ char_map.r(0x2b0f, 'named char operation result',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -3405,25 +5341,48 @@ def main():
at(32, u16, 'error'),
],
fixed_size=34,
+ pre=[0x2b0e],
+ post=[IDLE],
+ desc='''
+ Maybe performed block/ban/unblock/unban/sexchange by name.
+ ''',
+ )
+ char_map.r(0x2b10, 'account reg2',
+ head=[
+ at(0, u16, 'packet id'),
+ at(2, u16, 'packet length'),
+ at(4, account_id, 'account id'),
+ ],
+ head_size=8,
+ repeat=[
+ at(0, var_name, 'name'),
+ at(32, u32, 'value'),
+ ],
+ repeat_size=36,
+ pre=[SCRIPT],
+ post=[0x2728],
+ desc='''
+ Update login-stored ##registers.
+ ''',
+ )
+ char_map.s(0x2b11, 'account reg2 notify',
+ head=[
+ at(0, u16, 'packet id'),
+ at(2, u16, 'packet length'),
+ at(4, account_id, 'account id'),
+ ],
+ head_size=8,
+ repeat=[
+ at(0, var_name, 'name'),
+ at(32, u32, 'value'),
+ ],
+ repeat_size=36,
+ pre=[0x2729],
+ post=[IDLE],
+ desc='''
+ Broadcast login-stored ##registers.
+ ''',
)
- # 0x2728, 0x2729
- for (id, cat) in [
- (0x2b10, char_map.r),
- (0x2b11, char_map.s),
- ]:
- cat(id, 'account reg2 update',
- head=[
- at(0, u16, 'packet id'),
- at(2, u16, 'packet length'),
- at(4, account_id, 'account id'),
- ],
- head_size=8,
- repeat=[
- at(0, var_name, 'name'),
- at(32, u32, 'value'),
- ],
- repeat_size=36,
- )
char_map.s(0x2b12, 'divorce notify',
fixed=[
at(0, u16, 'packet id'),
@@ -3431,6 +5390,11 @@ def main():
at(6, char_id, 'partner id'),
],
fixed_size=10,
+ pre=[0x0068, 0x2730, 0x2b16],
+ post=[IDLE],
+ desc='''
+ Somebody either performed a divorce or had it done to them.
+ ''',
)
char_map.s(0x2b13, 'account delete notify',
fixed=[
@@ -3438,6 +5402,11 @@ def main():
at(2, account_id, 'account id'),
],
fixed_size=6,
+ pre=[0x2730],
+ post=[IDLE],
+ desc='''
+ Disconnect player who was deleted.
+ ''',
)
char_map.s(0x2b14, 'status or ban notify',
fixed=[
@@ -3447,6 +5416,11 @@ def main():
at(7, time32, 'status or ban until'),
],
fixed_size=11,
+ pre=[0x2731],
+ post=[IDLE],
+ desc='''
+ Somebody was banned or otherwise had their status changed.
+ ''',
)
char_map.s(0x2b15, 'gm account list notify',
head=[
@@ -3459,16 +5433,29 @@ def main():
at(4, gm1, 'gm level'),
],
repeat_size=5,
+ pre=[0x2732, 0x2af8],
+ post=[IDLE],
+ desc='''
+ Update full list of GM accounts.
+ ''',
)
- char_map.r(0x2b16, 'divorce request',
+ char_map.r(0x2b16, 'divorce',
fixed=[
at(0, u16, 'packet id'),
at(2, char_id, 'char id'),
],
fixed_size=6,
+ pre=[SCRIPT],
+ post=[0x2b12],
+ desc='''
+ Live the single life again.
+ ''',
)
+ # 2bfa/2bfb are injected above
+
+ # TOC_INTERMAP
- char_map.r(0x3000, 'gm broadcast',
+ char_map.r(0x3000, 'gm broadcast begin',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3478,8 +5465,13 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[GM, SCRIPT],
+ post=[0x3800],
+ desc='''
+ Yell at everybody.
+ ''',
)
- char_map.r(0x3001, 'whisper forward',
+ char_map.r(0x3001, 'whisper remote begin',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3491,17 +5483,26 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[0x0096],
+ post=[0x3801, 0x3802],
+ desc='''
+ Begin forwarding a private message to a nonlocal map server.
+ ''',
)
- char_map.r(0x3002, 'whisper forward result',
+ char_map.r(0x3002, 'whisper remote forward result',
fixed=[
at(0, u16, 'packet id'),
at(2, char_id, 'char id'),
at(6, u8, 'flag'),
],
fixed_size=7,
+ pre=[0x3801],
+ post=[0x3802],
+ desc='''
+ Intermediate result of forwarding a private message to a nonlocal map server.
+ ''',
)
- # 0x3803
- char_map.r(0x3003, 'wgm forward',
+ char_map.r(0x3003, 'wgm remote begin',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3513,8 +5514,12 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[GM, 0x008c, 0x0096, 0x0108],
+ post=[0x3803],
+ desc='''
+ Begin GM whisper to all map servers.
+ ''',
)
- # 0x3804
char_map.r(0x3004, 'save account reg',
head=[
at(0, u16, 'packet id'),
@@ -3527,6 +5532,11 @@ def main():
at(32, u32, 'value'),
],
repeat_size=36,
+ pre=[MAGIC, SCRIPT],
+ post=[0x3804],
+ desc='''
+ Save all char-server-stored account registers.
+ ''',
)
char_map.r(0x3005, 'want account reg',
fixed=[
@@ -3534,15 +5544,25 @@ def main():
at(2, account_id, 'account id'),
],
fixed_size=6,
+ pre=[0x2afd],
+ post=[0x3804],
+ desc='''
+ Explicitly request values of char-server-stored account registers.
+ ''',
)
- char_map.r(0x3010, 'want storage',
+ char_map.r(0x3010, 'load storage',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
],
fixed_size=6,
+ pre=[GM, SCRIPT],
+ post=[0x3810],
+ desc='''
+ Request access to storage.
+ ''',
)
- char_map.r(0x3011, 'got storage',
+ char_map.r(0x3011, 'save storage',
payload=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3550,8 +5570,13 @@ def main():
at(8, storage, 'storage'),
],
payload_size=None,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[0x3811],
+ desc='''
+ Transmission of changed storage.
+ ''',
)
- char_map.r(0x3020, 'create party',
+ char_map.r(0x3020, 'party create',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -3561,13 +5586,23 @@ def main():
at(70, u16, 'level'),
],
fixed_size=72,
+ pre=[GM, 0x00f9],
+ post=[0x3820, 0x3821],
+ desc='''
+ Request creation of a party.
+ ''',
)
- char_map.r(0x3021, 'request party info',
+ char_map.r(0x3021, 'party needinfo',
fixed=[
at(0, u16, 'packet id'),
at(2, party_id, 'party id'),
],
fixed_size=6,
+ pre=[0x2afd],
+ post=[0x3821],
+ desc='''
+ Request full info about a party.
+ ''',
)
char_map.r(0x3022, 'party add member',
fixed=[
@@ -3579,6 +5614,11 @@ def main():
at(50, u16, 'level'),
],
fixed_size=52,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x00ff, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d],
+ post=[0x3821, 0x3822, 0x3823],
+ desc='''
+ Request to add a party member in a slot somewhere.
+ ''',
)
char_map.r(0x3023, 'party change option',
fixed=[
@@ -3589,6 +5629,11 @@ def main():
at(12, u16, 'item'),
],
fixed_size=14,
+ pre=[0x0102],
+ post=[0x3823],
+ desc='''
+ Explicitly request a change of party settings.
+ ''',
)
char_map.r(0x3024, 'party leave',
fixed=[
@@ -3597,6 +5642,11 @@ def main():
at(6, account_id, 'account id'),
],
fixed_size=10,
+ pre=[0x0100, 0x0103, 0x3822],
+ post=[0x3821, 0x3824, 0x3826],
+ desc='''
+ Remove a member from party.
+ ''',
)
char_map.r(0x3025, 'party change map',
fixed=[
@@ -3608,8 +5658,17 @@ def main():
at(27, u16, 'level'),
],
fixed_size=29,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0089],
+ post=[0x3823, 0x3825],
+ desc='''
+ A party member has:
+
+ 1. loaded a new map
+ 2. levelled up
+ 3. logged out
+ ''',
)
- char_map.r(0x3027, 'party message',
+ char_map.r(0x3027, 'party message remote begin',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3621,6 +5680,11 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[0x0108],
+ post=[0x3827],
+ desc='''
+ Begin to send a party message to other map servers.
+ ''',
)
char_map.r(0x3028, 'party check conflict',
fixed=[
@@ -3630,9 +5694,16 @@ def main():
at(10, char_name, 'char name'),
],
fixed_size=34,
+ pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0089, 0x3822],
+ post=[0x3821, 0x3824, 0x3826],
+ desc='''
+ Explicitly request a conflict check.
+
+ (I am not sure this is actually meaningful).
+ ''',
)
- char_map.s(0x3800, 'gm broadcast',
+ char_map.s(0x3800, 'gm broadcast forward',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3642,8 +5713,13 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[0x2726, 0x3000],
+ post=[0x009a, 0x00c0],
+ desc='''
+ Yell, reaching everybody.
+ ''',
)
- char_map.s(0x3801, 'whisper forward',
+ char_map.s(0x3801, 'whisper remote forward',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3656,17 +5732,26 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[0x3001],
+ post=[0x0097, 0x3002],
+ desc='''
+ Intermediate forwarding a private message to a nonlocal map server.
+ ''',
)
- char_map.s(0x3802, 'whisper result',
+ char_map.s(0x3802, 'whisper remote begin result',
fixed=[
at(0, u16, 'packet id'),
at(2, char_name, 'sender char name'),
at(26, u8, 'flag'),
],
fixed_size=27,
+ pre=[0x3001, 0x3002],
+ post=[0x0098],
+ desc='''
+ Final result of forwarding a private message to a nonlocal map server.
+ ''',
)
- # 0x3003
- char_map.s(0x3803, 'whisper gm',
+ char_map.s(0x3803, 'wgm remote forward',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3678,9 +5763,13 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[0x3003],
+ post=[0x0097],
+ desc='''
+ Forward of GM whisper.
+ ''',
)
- # 0x3004
- char_map.s(0x3804, 'broadcast account reg',
+ char_map.s(0x3804, 'account reg notify',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3692,8 +5781,15 @@ def main():
at(32, u32, 'value'),
],
repeat_size=36,
+ pre=[0x3004, 0x3005],
+ post=[IDLE],
+ desc='''
+ Char-server-stored account regs have changed.
+
+ Also sent on explicit request.
+ ''',
)
- char_map.s(0x3810, 'load storage',
+ char_map.s(0x3810, 'load storage result',
payload=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3701,16 +5797,26 @@ def main():
at(8, storage, 'storage'),
],
payload_size=None,
+ pre=[0x3010],
+ post=[0x00a6, 0x00f2, 0x01f0],
+ desc='''
+ Transmission of requested storage.
+ ''',
)
- char_map.s(0x3811, 'save storage ack',
+ char_map.s(0x3811, 'save storage result',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
at(6, u8, 'unknown'),
],
fixed_size=7,
+ pre=[0x3011],
+ post=[IDLE],
+ desc='''
+ Acknowledgement of saved storage.
+ ''',
)
- char_map.s(0x3820, 'party created',
+ char_map.s(0x3820, 'party create result',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
@@ -3719,8 +5825,13 @@ def main():
at(11, party_name, 'party name'),
],
fixed_size=35,
+ pre=[0x3020],
+ post=[0x00fa],
+ desc='''
+ Result of requesting party creation.
+ ''',
)
- char_map.s(0x3821, 'party info maybe',
+ char_map.s(0x3821, 'party needinfo result',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3731,8 +5842,16 @@ def main():
at(0, party_most, 'party most'),
],
option_size=None,
+ pre=[0x0068, 0x2730, 0x3020, 0x3021, 0x3022, 0x3024, 0x3028],
+ post=[0x00c0, 0x00fb, 0x0101],
+ desc='''
+ Full info about a party, it present.
+
+ Payload may be missing if no such party (*should* not
+ happen, but ...).
+ ''',
)
- char_map.s(0x3822, 'party member added',
+ char_map.s(0x3822, 'party add member result',
fixed=[
at(0, u16, 'packet id'),
at(2, party_id, 'party id'),
@@ -3740,8 +5859,15 @@ def main():
at(10, u8, 'flag'),
],
fixed_size=11,
+ pre=[0x3022],
+ post=[0x00fd, 0x3024, 0x3028],
+ desc='''
+ Result of trying to add a party member.
+
+ Flag is 1 if party is no slots or something.
+ ''',
)
- char_map.s(0x3823, 'party option changed',
+ char_map.s(0x3823, 'party change option notify',
fixed=[
at(0, u16, 'packet id'),
at(2, party_id, 'party id'),
@@ -3751,8 +5877,15 @@ def main():
at(14, u8, 'flag'),
],
fixed_size=15,
+ pre=[0x3022, 0x3023, 0x3025],
+ post=[0x00c0, 0x0101],
+ desc='''
+ Party options were changed.
+
+ Often generated implicitly.
+ ''',
)
- char_map.s(0x3824, 'party member left',
+ char_map.s(0x3824, 'party leave notify',
fixed=[
at(0, u16, 'packet id'),
at(2, party_id, 'party id'),
@@ -3760,8 +5893,13 @@ def main():
at(10, char_name, 'char name'),
],
fixed_size=34,
+ pre=[0x0068, 0x2730, 0x3024, 0x3028],
+ post=[0x00c0, 0x0105],
+ desc='''
+ Member is no longer in party.
+ ''',
)
- char_map.s(0x3825, 'party member moved',
+ char_map.s(0x3825, 'party change map notify',
fixed=[
at(0, u16, 'packet id'),
at(2, party_id, 'party id'),
@@ -3771,16 +5909,26 @@ def main():
at(27, u16, 'level'),
],
fixed_size=29,
+ pre=[0x3025],
+ post=[0x00c0, 0x00fb],
+ desc='''
+ Party member location/level/online status has changed.
+ ''',
)
- char_map.s(0x3826, 'party broken',
+ char_map.s(0x3826, 'party broken notify',
fixed=[
at(0, u16, 'packet id'),
at(2, party_id, 'party id'),
at(6, u8, 'flag'),
],
fixed_size=7,
+ pre=[BOOT, 0x0068, 0x2730, 0x3024, 0x3028],
+ post=[0x00c0, 0x0105],
+ desc='''
+ A party just isn't there anymore.
+ ''',
)
- char_map.s(0x3827, 'party message',
+ char_map.s(0x3827, 'party message remote forward',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3792,37 +5940,67 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[0x3027],
+ post=[0x00c0, 0x0109],
+ desc='''
+ Actually send a party message to other map servers.
+ ''',
)
+ # TOC_MISC
# any client
- any_user.r(0x7530, 'version request',
+ any_user.r(0x7530, 'version',
fixed=[
at(0, u16, 'packet id'),
],
fixed_size=2,
+ pre=[ADMIN, BOOT, 0x7919],
+ post=[0x7531],
+ desc='''
+ Request from client or ladmin for server version.
+ ''',
)
- any_user.s(0x7531, 'version reply',
+ any_user.s(0x7531, 'version result',
fixed=[
at(0, u16, 'packet id'),
at(2, version, 'version'),
],
fixed_size=10,
+ pre=[0x7530],
+ post=[0x0064],
+ desc='''
+ Response to client's request for server version.
+ ''',
)
- any_user.r(0x7532, 'shutdown please',
+ any_user.r(0x7532, 'disconnect',
fixed=[
at(0, u16, 'packet id'),
],
fixed_size=2,
+ pre=[HUMAN],
+ post=[FINISH],
+ desc='''
+ Request from client or ladmin to disconnect.
+ ''',
)
+ # 0x7530 define='CMSG_SERVER_VERSION_REQUEST',
+ # 0x7531 define='SMSG_SERVER_VERSION_RESPONSE',
+ # 0x7532 define='CMSG_CLIENT_DISCONNECT',
+ # TOC_LOGINADMIN
# login admin
- login_admin.r(0x7918, 'admin auth request',
+ login_admin.r(0x7918, 'admin auth',
fixed=[
at(0, u16, 'packet id'),
at(2, u16, 'encryption zero'),
at(4, account_pass, 'account pass'),
],
fixed_size=28,
+ pre=[BOOT],
+ post=[0x7919],
+ desc='''
+ Authenticate as an admin.
+ ''',
)
login_admin.s(0x7919, 'admin auth result',
fixed=[
@@ -3830,16 +6008,26 @@ def main():
at(2, u8, 'error'),
],
fixed_size=3,
+ pre=[0x7918],
+ post=[0x7530],
+ desc='''
+ Status of admin authentication.
+ ''',
)
- login_admin.r(0x7920, 'account list request',
+ login_admin.r(0x7920, 'account list',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'start account id'),
at(6, account_id, 'end account id'),
],
fixed_size=10,
+ pre=[ADMIN, 0x7921],
+ post=[0x7921],
+ desc='''
+ Request list of all accounts (between bounds if given).
+ ''',
)
- login_admin.s(0x7921, 'account list reply',
+ login_admin.s(0x7921, 'account list result',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'packet length'),
@@ -3854,22 +6042,13 @@ def main():
at(34, u32, 'status'),
],
repeat_size=38,
+ pre=[0x7920],
+ post=[IDLE, 0x7920],
+ desc='''
+ May be truncated, if so should retry with new lower bound.
+ ''',
)
- login_admin.r(0x7924, 'itemfrob',
- fixed=[
- at(0, u16, 'packet id'),
- at(2, item_name_id4, 'source item id'),
- at(6, item_name_id4, 'dest item id'),
- ],
- fixed_size=10,
- )
- login_admin.s(0x7925, 'itemfrob ok',
- fixed=[
- at(0, u16, 'packet id'),
- ],
- fixed_size=2,
- )
- login_admin.r(0x7930, 'account create request',
+ login_admin.r(0x7930, 'account create',
fixed=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
@@ -3878,6 +6057,11 @@ def main():
at(51, account_email, 'email'),
],
fixed_size=91,
+ pre=[ADMIN],
+ post=[0x7931],
+ desc='''
+ Account creation request.
+ ''',
)
login_admin.s(0x7931, 'account create result',
fixed=[
@@ -3886,29 +6070,49 @@ def main():
at(6, account_name, 'account name'),
],
fixed_size=30,
+ pre=[0x7930],
+ post=[IDLE],
+ desc='''
+ Account creation response.
+ ''',
)
- login_admin.r(0x7932, 'account delete request',
+ login_admin.r(0x7932, 'account delete',
fixed=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
],
fixed_size=26,
+ pre=[ADMIN],
+ post=[0x2730, 0x7933],
+ desc='''
+ Account deletion request.
+ ''',
)
- login_admin.s(0x7933, 'account delete reply',
+ login_admin.s(0x7933, 'account delete result',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
at(6, account_name, 'account name'),
],
fixed_size=30,
+ pre=[0x7932],
+ post=[IDLE],
+ desc='''
+ Account deletion response.
+ ''',
)
- login_admin.r(0x7934, 'password change request',
+ login_admin.r(0x7934, 'password change',
fixed=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
at(26, account_pass, 'password'),
],
fixed_size=50,
+ pre=[ADMIN],
+ post=[0x7935],
+ desc='''
+ Change password request.
+ ''',
)
login_admin.s(0x7935, 'password change result',
fixed=[
@@ -3917,8 +6121,13 @@ def main():
at(6, account_name, 'account name'),
],
fixed_size=30,
+ pre=[0x7934],
+ post=[IDLE],
+ desc='''
+ Change password response.
+ ''',
)
- login_admin.r(0x7936, 'account state change request',
+ login_admin.r(0x7936, 'account state change',
fixed=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
@@ -3926,6 +6135,11 @@ def main():
at(30, seconds, 'error message'),
],
fixed_size=50,
+ pre=[ADMIN],
+ post=[0x2731, 0x7937],
+ desc='''
+ Account state change request.
+ ''',
)
login_admin.s(0x7937, 'account state change result',
fixed=[
@@ -3935,12 +6149,22 @@ def main():
at(30, u32, 'status'),
],
fixed_size=34,
+ pre=[0x7936],
+ post=[IDLE],
+ desc='''
+ Account state change response.
+ ''',
)
- login_admin.r(0x7938, 'server list request',
+ login_admin.r(0x7938, 'server list',
fixed=[
at(0, u16, 'packet id'),
],
fixed_size=2,
+ pre=[ADMIN],
+ post=[0x7939],
+ desc='''
+ Server list and player count request.
+ ''',
)
login_admin.s(0x7939, 'server list result',
head=[
@@ -3957,14 +6181,24 @@ def main():
at(30, u16, 'is new'),
],
repeat_size=32,
+ pre=[0x7938],
+ post=[IDLE],
+ desc='''
+ Server list and player count response.
+ ''',
)
- login_admin.r(0x793a, 'password check request',
+ login_admin.r(0x793a, 'password check',
fixed=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
at(26, account_pass, 'password'),
],
fixed_size=50,
+ pre=[ADMIN],
+ post=[0x793b],
+ desc='''
+ Password check request.
+ ''',
)
login_admin.s(0x793b, 'password check result',
fixed=[
@@ -3973,14 +6207,24 @@ def main():
at(6, account_name, 'account name'),
],
fixed_size=30,
+ pre=[0x793a],
+ post=[IDLE],
+ desc='''
+ Password check response.
+ ''',
)
- login_admin.r(0x793c, 'change sex request',
+ login_admin.r(0x793c, 'change sex',
fixed=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
at(26, sex_char, 'sex'),
],
fixed_size=27,
+ pre=[ADMIN],
+ post=[0x2723, 0x793d],
+ desc='''
+ Modify sex request.
+ ''',
)
login_admin.s(0x793d, 'change sex result',
fixed=[
@@ -3989,14 +6233,24 @@ def main():
at(6, account_name, 'account name'),
],
fixed_size=30,
+ pre=[0x793c],
+ post=[IDLE],
+ desc='''
+ Modify sex response.
+ ''',
)
- login_admin.r(0x793e, 'adjust gm level request',
+ login_admin.r(0x793e, 'adjust gm level',
fixed=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
at(26, gm1, 'gm level'),
],
fixed_size=27,
+ pre=[ADMIN],
+ post=[0x2732, 0x793f],
+ desc='''
+ Modify GM level request.
+ ''',
)
login_admin.s(0x793f, 'adjust gm level result',
fixed=[
@@ -4005,14 +6259,24 @@ def main():
at(6, account_name, 'account name'),
],
fixed_size=30,
+ pre=[0x793e],
+ post=[IDLE],
+ desc='''
+ Modify GM level response.
+ ''',
)
- login_admin.r(0x7940, 'change email request',
+ login_admin.r(0x7940, 'change email',
fixed=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
at(26, account_email, 'email'),
],
fixed_size=66,
+ pre=[ADMIN],
+ post=[0x7941],
+ desc='''
+ Modify e-mail request.
+ ''',
)
login_admin.s(0x7941, 'change email result',
fixed=[
@@ -4021,9 +6285,14 @@ def main():
at(6, account_name, 'account name'),
],
fixed_size=30,
+ pre=[0x7940],
+ post=[IDLE],
+ desc='''
+ Modify e-mail response.
+ ''',
)
# this packet is insane
- login_admin.r(0x7942, 'change memo request',
+ login_admin.r(0x7942, 'change memo',
head=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
@@ -4034,6 +6303,11 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[ADMIN],
+ post=[0x7943],
+ desc='''
+ Modify memo request.
+ ''',
)
login_admin.s(0x7943, 'change memo result',
fixed=[
@@ -4042,13 +6316,23 @@ def main():
at(6, account_name, 'account name'),
],
fixed_size=30,
+ pre=[0x7942],
+ post=[IDLE],
+ desc='''
+ Modify memo response.
+ ''',
)
- login_admin.r(0x7944, 'account id lookup request',
+ login_admin.r(0x7944, 'account id lookup',
fixed=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
],
fixed_size=26,
+ pre=[ADMIN],
+ post=[0x7945],
+ desc='''
+ Find account id request.
+ ''',
)
login_admin.s(0x7945, 'account id lookup result',
fixed=[
@@ -4057,13 +6341,23 @@ def main():
at(6, account_name, 'account name'),
],
fixed_size=30,
+ pre=[0x7944],
+ post=[IDLE],
+ desc='''
+ Find account id response.
+ ''',
)
- login_admin.r(0x7946, 'account name lookup request',
+ login_admin.r(0x7946, 'account name lookup',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
],
fixed_size=6,
+ pre=[ADMIN],
+ post=[0x7947],
+ desc='''
+ Find account name request.
+ ''',
)
login_admin.s(0x7947, 'account name lookup result',
fixed=[
@@ -4072,31 +6366,24 @@ def main():
at(6, account_name, 'account name'),
],
fixed_size=30,
+ pre=[0x7946],
+ post=[IDLE],
+ desc='''
+ Find account name response.
+ ''',
)
- login_admin.r(0x7948, 'validity absolute request',
- fixed=[
- at(0, u16, 'packet id'),
- at(2, account_name, 'account name'),
- at(26, time32, 'valid until'),
- ],
- fixed_size=30,
- )
- login_admin.s(0x7949, 'validity absolute result',
- fixed=[
- at(0, u16, 'packet id'),
- at(2, account_id, 'account id'),
- at(6, account_name, 'account name'),
- at(30, time32, 'valid until'),
- ],
- fixed_size=34,
- )
- login_admin.r(0x794a, 'ban absolute request',
+ login_admin.r(0x794a, 'ban absolute',
fixed=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
at(26, time32, 'ban until'),
],
fixed_size=30,
+ pre=[ADMIN],
+ post=[0x2731, 0x794b],
+ desc='''
+ Ban date end change request.
+ ''',
)
login_admin.s(0x794b, 'ban absolute result',
fixed=[
@@ -4106,14 +6393,24 @@ def main():
at(30, time32, 'ban until'),
],
fixed_size=34,
+ pre=[0x794a],
+ post=[IDLE],
+ desc='''
+ Ban date end change response.
+ ''',
)
- login_admin.r(0x794c, 'ban relative request',
+ login_admin.r(0x794c, 'ban relative',
fixed=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
at(26, human_time_diff, 'ban add'),
],
fixed_size=38,
+ pre=[ADMIN],
+ post=[0x2731, 0x794d],
+ desc='''
+ Ban date end change request (2).
+ ''',
)
login_admin.s(0x794d, 'ban relative result',
fixed=[
@@ -4123,9 +6420,14 @@ def main():
at(30, time32, 'ban until'),
],
fixed_size=34,
+ pre=[0x794c],
+ post=[IDLE],
+ desc='''
+ Ban date end change response (2).
+ ''',
)
# evil packet (see also 0x2726)
- login_admin.r(0x794e, 'broadcast message request',
+ login_admin.r(0x794e, 'broadcast message',
head=[
at(0, u16, 'packet id'),
at(2, u16, 'unused'),
@@ -4136,6 +6438,11 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[ADMIN],
+ post=[0x2726, 0x794f],
+ desc='''
+ Send broadcast message request.
+ ''',
)
login_admin.s(0x794f, 'broadcast message result',
fixed=[
@@ -4143,30 +6450,23 @@ def main():
at(2, u16, 'error'),
],
fixed_size=4,
+ pre=[0x794e],
+ post=[IDLE],
+ desc='''
+ Send broadcast message response.
+ ''',
)
- login_admin.r(0x7950, 'validity relative request',
- fixed=[
- at(0, u16, 'packet id'),
- at(2, account_name, 'account name'),
- at(26, human_time_diff, 'valid add'),
- ],
- fixed_size=38,
- )
- login_admin.s(0x7951, 'validity relative result',
- fixed=[
- at(0, u16, 'packet id'),
- at(2, account_id, 'account id'),
- at(6, account_name, 'account name'),
- at(30, time32, 'valid until'),
- ],
- fixed_size=34,
- )
- login_admin.r(0x7952, 'account name info request',
+ login_admin.r(0x7952, 'account name info',
fixed=[
at(0, u16, 'packet id'),
at(2, account_name, 'account name'),
],
fixed_size=26,
+ pre=[ADMIN],
+ post=[0x7953],
+ desc='''
+ Account information by name request.
+ ''',
)
# this packet is insane
login_admin.s(0x7953, 'account info result',
@@ -4182,7 +6482,7 @@ def main():
at(60, millis, 'last login string'),
at(84, str16, 'ip string'),
at(100, account_email, 'email'),
- at(140, time32, 'connect until'),
+ at(140, time32, 'unused connect until'),
at(144, time32, 'ban until'),
at(148, SkewLengthType(u16, 150), 'magic packet length'),
],
@@ -4191,33 +6491,302 @@ def main():
at(0, u8, 'c'),
],
repeat_size=1,
+ pre=[0x7952, 0x7954],
+ post=[IDLE],
+ desc='''
+ Account information by name or id response.
+ ''',
)
- login_admin.r(0x7954, 'account id info request',
+ login_admin.r(0x7954, 'account id info',
fixed=[
at(0, u16, 'packet id'),
at(2, account_id, 'account id'),
],
fixed_size=6,
+ pre=[ADMIN],
+ post=[0x7953],
+ desc='''
+ Account information by id request.
+ ''',
)
- login_admin.r(0x7955, 'reload gm signal',
+ login_admin.r(0x7955, 'reload gm',
fixed=[
at(0, u16, 'packet id'),
],
fixed_size=2,
+ pre=[ADMIN],
+ post=[0x2732],
+ desc='''
+ Reload GM file request.
+ ''',
)
+ # TOC_NEW
## new-style packets
- # general packets
- any_user.x(0x8000, 'special hold',
+ # notify packets, standalone, can occur at any time; always 'payload'
+ any_user.s(0x8000, 'special hold notify',
payload=[
at(0, u16, 'packet id'),
# packet 0x8000 is handled specially
at(2, u16, 'packet length'),
],
payload_size=4,
- )
+ pre=[OTHER],
+ post=[OTHER],
+ desc='''
+ A special packet that is handled specially.
+
+ The "is the packet complete" logic will read its given length,
+ but the "skip packet" will only skip 4 bytes, thus allowing
+ transactions. I'm still not entirely convinced that this is a
+ good idea - perhaps explicit 'being transaction buffer' and
+ 'end transaction buffer' packets? Allow nesting?
+ ''',
+ )
+ # invoke/return/error packets, threaded; always 'payload'
+ # invoke: 0x9000
+ # return: 0xA000
+ # error: 0xB000
+ # invoke: 0x9001
+ # return: 0xA001
+ # error: 0xB001
+ # ... or should it be implicit via a wrapper?
+ #
+ # add_call_packet(from=ANY, to=ANY, id='0x?001', name='version info'
+ # invoke_type=struct(), return_type=struct(), error_type=struct())
+ # for that matter, should these things be read from separate files?
+ # still a lot of ambiguous stuff here ...
+ #
+ # no, merging is a bad idea at the low level
+ # ... but for doc it is ...
+
+ return ctx
+
+# TOC_DRAWING
+
+def fix_sort(idlst):
+ idlst = set(idlst)
+ return (sorted([i for i in idlst if isinstance(i, SpecialEventOrigin)], key=lambda e: e.name)
+ + sorted([i for i in idlst if not isinstance(i, SpecialEventOrigin)]))
+
+def check(ctx):
+ d = {}
+
+ for ch in ctx._channels:
+ for p in ch.packets:
+ id = p.id
+ if id in d:
+ print('packet 0x%04x duplicated (old=%r, new=%r)' % (id, d[id], p))
+ d[id] = p
+
+ for (id, packet) in sorted(d.items()):
+ if packet.pre != fix_sort(packet.pre):
+ print('packet 0x%04x pre is not sorted' % id)
+ for pre in packet.pre:
+ if isinstance(pre, SpecialEventOrigin):
+ continue
+ if pre not in d:
+ print('packet 0x%04x pre 0x%04x does not exist' % (id, pre))
+ elif id not in d[pre].post and id not in d[pre].xpost:
+ print('packet 0x%04x pre 0x%04x does not have me in post or xpost' % (id, pre))
+ if packet.post != fix_sort(packet.post):
+ print('packet 0x%04x post is not sorted' % id)
+ if packet.xpost != fix_sort(packet.xpost):
+ print('packet 0x%04x xpost is not sorted' % id)
+ if len(set(packet.post) | set(packet.xpost)) != len(packet.post) + len(packet.xpost):
+ print('packet 0x%04x post intersects xpost' % id)
+ for post in packet.post:
+ if isinstance(post, SpecialEventOrigin):
+ continue
+ if post not in d:
+ print('packet 0x%04x post 0x%04x does not exist' % (id, post))
+ elif id not in d[post].pre:
+ print('packet 0x%04x post 0x%04x does not have me in pre' % (id, post))
+
+ return d
+
+def partition(d):
+ ''' given a directed graph in the form
+ {1: [2, 3], 2: [3, 4], 3: [], 4: [], 5: [6], 6: []}
+ return a list of sets of connected keys, in the form
+ [{1, 2, 3, 4}, {5, 6}]
+ '''
+ leaders = {k: k for k in d}
+
+ # this code looks nothing like what I was intending to write
+ changed = True
+ while changed:
+ changed = False
+ for k, vlist in d.items():
+ if vlist:
+ m = min(leaders[v] for v in vlist)
+ if m < leaders[k]:
+ changed = True
+ leaders[k] = m
+ else:
+ m = leaders[k]
+ else:
+ m = leaders[k]
+ for v in vlist:
+ if m < leaders[v]:
+ changed = True
+ leaders[v] = m
+
+ followers = {}
+ for k, v in leaders.items():
+ followers.setdefault(v, []).append(k)
+ return [set(v) for v in followers.values()]
+
+def ids_only(vpost):
+ rv = [e for e in vpost if not isinstance(e, SpecialEventOrigin)]
+ if len(rv) == len(vpost):
+ rv = vpost
+ return rv
+
+def make_dots(ctx):
+ d = check(ctx)
+ # automatically excludes xpost and does not touch pre
+ # disabled because that's still just too many packets for the 0x0063
+ #p = partition({k: ids_only(v.post) for (k, v) in d.items()})
+
+ if not os.path.exists('doc-gen'):
+ # generate.make will succeed if missing the wiki repo
+ # but 'make doc' will fail
+ return
+ for g in glob.glob('doc-gen/*.gv'):
+ os.rename(g, g + '.old')
+ for g in glob.glob('doc-gen/Packet-*.md'):
+ os.rename(g, g + '.old')
+
+ for (id, p) in d.items():
+ md = 'doc-gen/Packet-0x%04x.md' % id
+ dot = 'doc-gen/packets-around-0x%04x.gv' % id
+ with OpenWrite(md) as f:
+ f.write(p.wiki_doc())
+ with OpenWrite(dot) as f:
+ # The goal looks sort of like this:
+ #
+ # O O O O
+ # \ / \ /
+ # O O
+ # \ /
+ # \ /
+ # O
+ # / \
+ # / \
+ # O O
+ # / \ / \
+ # O O O O
+ #
+ # except that we also connect any extra connections between those
+ # in particular, this will DTRT for the common skip-case:
+ #
+ # |
+ # O
+ # |\
+ # | O
+ # | |
+ # | O
+ # |/
+ # O
+ # |
+ #
+ # regardless of which node you're drawing from.
+ #
+ # however, note that we do *not* list siblings.
+ #
+ # The above comment is wrong.
+ # Also, remember that none of this would be necessary without
+ # the overbroad dependencies!
+ #
+ # ... except that I got rid of the overbroad dependencies but
+ # had to kept this still. It's arguably nicer anyway, so I
+ # also made it the only way.
+ covered_nodes = sorted(p.pre_set(d, 2) | p.post_set(d, 2))
+ covered_edges = [(a, b) for a in covered_nodes for b in covered_nodes if b in d[a].post]
+ g = Graph()
+ g.default_vertex[u'shape'] = u'box'
+ # g[u'layout'] = u'twopi'
+ # g[u'root'] = u'0x%04x' % id
+ for n in covered_nodes:
+ v = g.vertex(u'0x%04x' % n)
+ v[u'label'] = u'Packet \\N: %s' % d[n].name
+ if n == id:
+ v[u'style'] = u'filled'
+ for (a, b) in covered_edges:
+ g.edge(u'0x%04x' % a, u'0x%04x' % b)
+ for n in covered_nodes:
+ # the center node will be covered specially below
+ if n == id:
+ continue
+ # backward links (both strong and weak)
+ count = 0
+ for p in d[n].pre:
+ # pre specials are always strong links
+ if isinstance(p, SpecialEventOrigin):
+ count += 1
+ elif n in d[p].xpost:
+ # weak backward links
+ pass
+ elif p not in covered_nodes:
+ # don't show mere siblings unless also ancestor/descendent
+ count += 1
+ if count:
+ v = g.vertex(u'0x%04x...pre' % n)
+ v[u'label'] = u'%d more' % count
+ v[u'style'] = u'dashed'
+ g.edge(v, u'0x%04x' % n)
+ # strong forward links
+ count = 0
+ for p in d[n].post:
+ if isinstance(p, SpecialEventOrigin):
+ count += 1
+ elif p not in covered_nodes:
+ count += 1
+ if count:
+ v = g.vertex(u'0x%04x...post' % n)
+ v[u'label'] = u'%d more' % count
+ v[u'style'] = u'dashed'
+ g.edge(u'0x%04x' % n, v)
+ # for the immediate node, also cover specials and weaks
+ for p in d[id].pre:
+ # (there are no weak backward specials)
+ # strong backward specials
+ if isinstance(p, SpecialEventOrigin):
+ g.edge(unicode(p.name), u'0x%04x' % id)
+ # weak backward nodes
+ elif id in d[p].xpost:
+ e = g.edge(u'0x%04x' % p, u'0x%04x' % id)
+ e[u'style']=u'dashed'
+ e[u'weight'] = u'0'
+ for p in d[id].post:
+ # strong forward specials
+ if isinstance(p, SpecialEventOrigin):
+ g.edge(u'0x%04x' % id, unicode(p.name))
+ for p in d[id].xpost:
+ # weak forward specials
+ if isinstance(p, SpecialEventOrigin):
+ e = g.edge(u'0x%04x' % id, unicode(p.name))
+ e[u'style'] = u'dashed'
+ e[u'weight'] = u'0'
+ # weak forward nodes
+ elif p not in covered_nodes:
+ e = g.edge(u'0x%04x' % id, u'0x%04x' % p)
+ e[u'style'] = u'dashed'
+ e[u'weight'] = u'0'
+ g.dot(f, False)
+
+
+ for g in glob.glob('doc-gen/*.old'):
+ print('Obsolete: %s' % g)
+ os.remove(g)
+
+# TOC_MAIN
+def main():
+ ctx = build_context()
## teardown
+ make_dots(ctx)
ctx.dump()
if __name__ == '__main__':