summaryrefslogtreecommitdiff
path: root/net/mapserv.py
diff options
context:
space:
mode:
Diffstat (limited to 'net/mapserv.py')
-rw-r--r--net/mapserv.py1216
1 files changed, 1216 insertions, 0 deletions
diff --git a/net/mapserv.py b/net/mapserv.py
new file mode 100644
index 0000000..121c3a9
--- /dev/null
+++ b/net/mapserv.py
@@ -0,0 +1,1216 @@
+
+import atexit
+import time
+from construct import *
+from construct.protocols.layer3.ipv4 import IpAddress
+from protocol import *
+from common import *
+from utils import *
+from being import BeingsCache
+from item import FloorItem
+from inventory import (add_to_inventory, remove_from_inventory,
+ add_to_storage, remove_from_storage)
+from trade import reset_trade_state
+from loggers import netlog
+
+server = None
+beings_cache = None
+party_info = []
+party_members = {}
+player_pos = {'map': 'unknown', 'x': 0, 'y': 0, 'dir': 0}
+tick = 0
+last_whisper = {'to': '', 'msg': ''}
+player_inventory = {}
+player_storage = {}
+player_stats = {}
+player_skills = {}
+player_money = 0
+player_attack_range = 0
+trade_state = {'items_give': [], 'items_get': [],
+ 'zeny_give': 0, 'zeny_get': 0}
+floor_items = {}
+
+for s in range(255):
+ player_stats[s] = 0
+
+
+# --------------------------------------------------------------------
+def smsg_ignore(data):
+ pass
+
+
+@extendable
+def smsg_being_chat(data):
+ cached_name = beings_cache.findName(data.id)
+ real_name, _ = data.message.split(' : ', 1)
+ if real_name != cached_name:
+ cmsg_name_request(data.id)
+ netlog.info("SMSG_BEING_CHAT id={} msg={}".format(data.id, data.message))
+
+
+@extendable
+def smsg_being_emotion(data):
+ beings_cache.findName(data.id)
+ netlog.info("SMSG_BEING_EMOTION {} : {}".format(data.id, data.emote))
+
+
+@extendable
+def smsg_being_move(data):
+ global tick
+ tick = data.tick
+ beings_cache.findName(data.id, data.job)
+ beings_cache[data.id].x = data.coor_pair.dst_x
+ beings_cache[data.id].y = data.coor_pair.dst_y
+ netlog.info("SMSG_BEING_MOVE {}".format(data))
+
+
+@extendable
+def smsg_being_action(data):
+ global tick
+ tick = data.tick
+ netlog.info("SMSG_BEING_ACTION {}".format(data))
+
+
+@extendable
+def smsg_being_change_direction(data):
+ netlog.info("SMSG_BEING_CHANGE_DIRECTION {}".format(data))
+
+
+@extendable
+def smsg_being_name_response(data):
+ try:
+ beings_cache[data.id].name = data.name
+ except KeyError:
+ pass
+ netlog.info("SMSG_BEING_NAME_RESPONSE id={} name={}".format(
+ data.id, data.name))
+
+
+@extendable
+def smsg_being_remove(data):
+ try:
+ del beings_cache[data.id]
+ except KeyError:
+ pass
+ netlog.info("SMSG_BEING_REMOVE (id={}, deadflag={})".format(
+ data.id, data.deadflag))
+
+
+@extendable
+def smsg_being_visible(data):
+ beings_cache.findName(data.id, data.job)
+ beings_cache[data.id].speed = data.speed
+ beings_cache[data.id].x = data.coor.x
+ beings_cache[data.id].y = data.coor.y
+ netlog.info("SMSG_BEING_VISIBLE {}".format(data))
+
+
+@extendable
+def smsg_player_chat(data):
+ netlog.info("SMSG_PLAYER_CHAT {}".format(data.message))
+
+
+@extendable
+def smsg_player_equipment(data):
+ netlog.info("SMSG_PLAYER_EQUIPMENT {}".format(data))
+ for item in data.equipment:
+ player_inventory[item.index] = (item.id, 1)
+
+
+@extendable
+def smsg_player_inventory(data):
+ netlog.info("SMSG_PLAYER_INVENTORY {}".format(data))
+ for item in data.inventory:
+ player_inventory[item.index] = (item.id, item.amount)
+
+
+@extendable
+def smsg_player_inventory_add(data):
+ netlog.info("SMSG_PLAYER_INVENTORY_ADD index={} id={} amount={}".format(
+ data.index, data.id, data.amount))
+ add_to_inventory(data.index, data.id, data.amount)
+
+
+@extendable
+def smsg_player_inventory_remove(data):
+ netlog.info("SMSG_PLAYER_INVENTORY_REMOVE index={} amount={}".format(
+ data.index, data.amount))
+ remove_from_inventory(data.index, data.amount)
+
+
+@extendable
+def smsg_player_inventory_use(data):
+ netlog.info("SMSG_PLAYER_INVENTORY_USE {}".format(data))
+ if data.amount > 0:
+ player_inventory[data.index] = (data.item_id, data.amount)
+ else:
+ del player_inventory[data.index]
+
+
+@extendable
+def smsg_player_move(data):
+ netlog.info("SMSG_PLAYER_MOVE {}".format(data))
+ global tick
+ tick = data.tick
+ beings_cache.findName(data.id, data.job)
+ beings_cache[data.id].x = data.coor_pair.dst_x
+ beings_cache[data.id].y = data.coor_pair.dst_y
+
+
+@extendable
+def smsg_player_stop(data):
+ netlog.info("SMSG_PLAYER_STOP id={} x={} y={}".format(
+ data.id, data.x, data.y))
+ beings_cache.findName(data.id)
+ beings_cache[data.id].x = data.x
+ beings_cache[data.id].y = data.y
+
+
+@extendable
+def smsg_player_update(data):
+ netlog.info("SMSG_PLAYER_UPDATE_ {}".format(data))
+ beings_cache.findName(data.id, data.job)
+ beings_cache[data.id].speed = data.speed
+ beings_cache[data.id].x = data.coor.x
+ beings_cache[data.id].y = data.coor.y
+
+
+@extendable
+def smsg_player_warp(data):
+ netlog.info("SMSG_PLAYER_WARP (map={}, x={}, y={}".format(
+ data.map, data.x, data.y))
+ player_pos['map'] = data.map
+ player_pos['x'] = data.x
+ player_pos['y'] = data.y
+ beings_cache.clear()
+
+
+@extendable
+def smsg_ip_response(data):
+ netlog.info("SMSG_IP_RESPONSE id={} ip={}".format(data.id, data.ip))
+
+
+@extendable
+def smsg_connection_problem(data):
+ error_codes = {
+ 0 : "Authentification failed",
+ 1 : "No servers available",
+ 2 : "Account already in use",
+ 3 : "Speed hack detected",
+ 8 : "Duplicated login",
+ }
+ msg = error_codes.get(data.code, 'code ' + str(data.code))
+ netlog.error("SMSG_CONNECTION_PROBLEM %d", data.code)
+ raise Exception(msg)
+
+
+@extendable
+def smsg_gm_chat(data):
+ netlog.info("SMSG_GM_CHAT {}".format(data.message))
+
+
+@extendable
+def smsg_party_info(data):
+ global party_info, party_members
+ party_info = data
+ for m in data.members:
+ party_members[m.id] = m.nick
+ netlog.info("SMSG_PARTY_INFO {}".format(data))
+
+
+@extendable
+def smsg_party_chat(data):
+ netlog.info("SMSG_PARTY_CHAT {} : {}".format(data.id, data.message))
+
+
+@extendable
+def smsg_trade_request(data):
+ netlog.info("SMSG_TRADE_REQUEST {}".format(data.nick))
+ # cmsg_trade_response("DECLINE")
+
+
+@extendable
+def smsg_trade_response(data):
+ netlog.info("SMSG_TRADE_RESPONSE {}".format(data.code))
+
+
+@extendable
+def smsg_trade_item_add(data):
+ netlog.info("SMSG_TRADE_ITEM_ADD id={} amount={}".format(
+ data.id, data.amount))
+ if data.id == 0:
+ trade_state['zeny_get'] = data.amount
+ else:
+ trade_state['items_get'].append((data.id, data.amount))
+
+
+@extendable
+def smsg_trade_item_add_response(data):
+ netlog.info(("SMSG_TRADE_ITEM_ADD_RESPONSE"
+ " index={} amount={} code={}").format(
+ data.index, data.amount, data.code))
+
+ index = data.index
+ amount = data.amount
+ code = data.code
+
+ if code == 0 and amount > 0:
+ if index > 0:
+ item_id, _ = player_inventory[index]
+ remove_from_inventory(index, amount)
+ trade_state['items_give'].append((item_id, amount))
+ elif index == 0:
+ trade_state['zeny_give'] = amount
+
+
+@extendable
+def smsg_trade_cancel(data):
+ netlog.info("SMSG_TRADE_CANCEL")
+ reset_trade_state(trade_state)
+
+
+@extendable
+def smsg_trade_ok(data):
+ netlog.info("SMSG_TRADE_OK who={}".format(data.who))
+
+
+@extendable
+def smsg_trade_complete(data):
+ netlog.info("SMSG_TRADE_COMPLETE")
+ global player_money
+ player_money += trade_state['zeny_get'] - trade_state['zeny_give']
+ # reset_trade_state(trade_state)
+
+
+@extendable
+def smsg_whisper(data):
+ netlog.info("SMSG_WHISPER {} : {}".format(data.nick, data.message))
+
+
+@extendable
+def smsg_whisper_response(data):
+ m = {0: "OK", 1: "Recepient is offline"}
+ netlog.info("SMSG_WHISPER_RESPONSE {}".format(m.get(data.code, "error")))
+
+
+@extendable
+def smsg_server_ping(data):
+ global tick
+ tick = data.tick
+ netlog.info("SMSG_SERVER_PING tick={}".format(data.tick))
+
+
+@extendable
+def smsg_map_login_success(data):
+ netlog.info("SMSG_MAP_LOGIN_SUCCESS {}".format(data))
+ global tick
+ tick = data.tick
+ player_pos['x'] = data.coor.x
+ player_pos['y'] = data.coor.y
+ player_pos['dir'] = data.coor.dir
+
+
+@extendable
+def smsg_walk_response(data):
+ global tick
+ tick = data.tick
+ player_pos['x'] = data.coor_pair.dst_x
+ player_pos['y'] = data.coor_pair.dst_y
+ netlog.info("SMSG_WALK_RESPONSE {}".format(data))
+
+
+@extendable
+def smsg_item_visible(data):
+ netlog.info("SMSG_ITEM_VISIBLE {}".format(data))
+ item = FloorItem(data.id, data.type, data.amount, data.x, data.y)
+ floor_items[data.id] = item
+
+
+@extendable
+def smsg_item_dropped(data):
+ netlog.info("SMSG_ITEM_DROPPED {}".format(data))
+ item = FloorItem(data.id, data.type, data.amount, data.x, data.y)
+ floor_items[data.id] = item
+
+
+@extendable
+def smsg_item_remove(data):
+ netlog.info("SMSG_ITEM_REMOVE id={}".format(data.id))
+ if data.id in floor_items:
+ del floor_items[data.id]
+
+
+@extendable
+def smsg_player_stat_update_x(data):
+ netlog.info("SMSG_PLAYER_STAT_UPDATE_X type={} value={}".format(
+ data.type, data.stat_value))
+ player_stats[data.type] = data.stat_value
+
+
+@extendable
+def smsg_being_self_effect(data):
+ netlog.info("SMSG_BEING_SELF_EFFECT id={} effect={}".format(
+ data.id, data.effect))
+
+
+@extendable
+def smsg_being_status_change(data):
+ netlog.info("SMSG_BEING_STATUS_CHANGE id={} status={} flag={}".format(
+ data.id, data.status, data.flag))
+
+
+@extendable
+def smsg_player_status_change(data):
+ netlog.info("SMSG_PLAYER_STATUS_CHANGE {}".format(data))
+
+
+@extendable
+def smsg_npc_message(data):
+ netlog.info("SMSG_NPC_MESSAGE id={} message={}".format(
+ data.id, data.message))
+
+
+@extendable
+def smsg_npc_choice(data):
+ netlog.info("SMSG_NPC_CHOICE {}".format(data.select))
+
+
+@extendable
+def smsg_npc_close(data):
+ netlog.info("SMSG_NPC_CLOSE id={}".format(data.id))
+
+
+@extendable
+def smsg_npc_next(data):
+ netlog.info("SMSG_NPC_NEXT id={}".format(data.id))
+
+
+@extendable
+def smsg_npc_int_input(data):
+ netlog.info("SMSG_NPC_INT_INPUT id={}".format(data.id))
+
+
+@extendable
+def smsg_npc_str_input(data):
+ netlog.info("SMSG_NPC_STR_INPUT id={}".format(data.id))
+
+
+@extendable
+def smsg_npc_command(data):
+ netlog.info("SMSG_NPC_COMMAND {}".format(data))
+
+
+@extendable
+def smsg_npc_buy(data):
+ netlog.info("SMSG_NPC_BUY {}".format(data))
+
+
+@extendable
+def smsg_player_skills(data):
+ netlog.info("SMSG_PLAYER_SKILLS {}".format(data))
+ for skill in data.skills:
+ player_skills[skill.id] = skill.level
+
+
+@extendable
+def smsg_player_skill_up(data):
+ netlog.info("SMSG_PLAYER_SKILL_UP {}".format(data))
+ player_skills.setdefault(data.id, data.level)
+
+
+@extendable
+def smsg_storage_status(data):
+ netlog.info("SMSG_STORAGE_STATUS used={} max_size={}".format(
+ data.used, data.max_size))
+
+
+@extendable
+def smsg_storage_add(data):
+ netlog.info("SMSG_STORAGE_ADD {}".format(data))
+ add_to_storage(data.index, data.id, data.amount)
+
+
+@extendable
+def smsg_storage_remove(data):
+ netlog.info("SMSG_STORAGE_REMOVE {}".format(data))
+ remove_from_storage(data.index, data.amount)
+
+
+@extendable
+def smsg_storage_close(data):
+ netlog.info("SMSG_STORAGE_CLOSE")
+
+
+@extendable
+def smsg_storage_equip(data):
+ netlog.info("SMSG_STORAGE_EQUIP {}".format(data))
+ for item in data.equipment:
+ player_storage[item.index] = (item.id, 1)
+
+
+@extendable
+def smsg_storage_items(data):
+ netlog.info("SMSG_STORAGE_ITEMS {}".format(data))
+ for item in data.storage:
+ player_storage[item.index] = (item.id, item.amount)
+
+
+@extendable
+def smsg_player_attack_range(data):
+ netlog.info("SMSG_PLAYER_ATTACK_RANGE %d", data.range)
+ global player_attack_range
+ player_attack_range = data.range
+
+
+@extendable
+def smsg_player_arrow_message(data):
+ netlog.info("SMSG_PLAYER_ARROW_MESSAGE %d", data.code)
+
+
+# --------------------------------------------------------------------
+protodef = {
+ 0x8000 : (smsg_ignore, Field("data", 2)), # ignore
+ 0x008a : (smsg_being_action,
+ Struct("data",
+ ULInt32("src_id"),
+ ULInt32("dst_id"),
+ ULInt32("tick"),
+ ULInt32("src_speed"),
+ ULInt32("dst_speed"),
+ ULInt16("damage"),
+ Padding(2),
+ Byte("type"),
+ Padding(2))),
+ 0x009c : (smsg_being_change_direction,
+ Struct("data",
+ ULInt32("id"),
+ Padding(2),
+ Byte("dir"))),
+ 0x00c3 : (smsg_ignore, Field("data", 6)), # being-change-looks
+ 0x01d7 : (smsg_ignore, Field("data", 9)), # being-change-looks2
+ 0x008d : (smsg_being_chat,
+ Struct("data",
+ ULInt16("length"),
+ ULInt32("id"),
+ StringZ("message", lambda ctx: ctx.length - 8))),
+ 0x00c0 : (smsg_being_emotion,
+ Struct("data",
+ ULInt32("id"),
+ Byte("emote"))),
+ 0x007b : (smsg_being_move,
+ Struct("data",
+ ULInt32("id"),
+ ULInt16("speed"),
+ Padding(6),
+ ULInt16("job"),
+ Padding(6),
+ ULInt32("tick"),
+ Padding(10),
+ ULInt32("hp"),
+ ULInt32("max_hp"),
+ # Probe("debug", show_stream=False, show_stack=False),
+ Padding(6),
+ BitStruct("coor_pair",
+ BitField("src_x", 10),
+ BitField("src_y", 10),
+ BitField("dst_x", 10),
+ BitField("dst_y", 10)),
+ Padding(5))),
+ 0x0086 : (smsg_being_move,
+ Struct("data",
+ ULInt32("id"),
+ # Padding(10)
+ BitStruct("coor_pair",
+ BitField("src_x", 10),
+ BitField("src_y", 10),
+ BitField("dst_x", 10),
+ BitField("dst_y", 10)),
+ ULInt32("tick"))),
+ 0x0095 : (smsg_being_name_response,
+ Struct("data",
+ ULInt32("id"),
+ StringZ("name", 24))),
+ 0x0080 : (smsg_being_remove,
+ Struct("data",
+ ULInt32("id"),
+ Byte("deadflag"))),
+ 0x0148 : (smsg_ignore, Field("data", 6)), # being-resurrect
+ 0x019b : (smsg_being_self_effect,
+ Struct("data",
+ ULInt32("id"),
+ ULInt32("effect"))),
+ 0x007c : (smsg_ignore, Field("data", 39)), # spawn
+ 0x0196 : (smsg_being_status_change,
+ Struct("data",
+ ULInt16("status"),
+ ULInt32("id"),
+ Flag("flag"))),
+ 0x0078 : (smsg_being_visible,
+ Struct("data",
+ ULInt32("id"),
+ ULInt16("speed"),
+ Padding(6),
+ ULInt16("job"),
+ Padding(16),
+ ULInt32("hp"),
+ ULInt32("max_hp"),
+ Padding(6),
+ BitStruct("coor",
+ BitField("x", 10),
+ BitField("y", 10),
+ Nibble("dir")),
+ Padding(5))),
+ 0x013c : (smsg_ignore, Field("data", 2)), # arrow-equip
+ 0x013b : (smsg_player_arrow_message,
+ Struct("data",
+ ULInt16("code"))),
+ 0x013a : (smsg_player_attack_range,
+ Struct("data",
+ ULInt16("range"))),
+ 0x008e : (smsg_player_chat,
+ Struct("data",
+ ULInt16("length"),
+ StringZ("message", lambda ctx: ctx.length - 4))),
+ 0x00aa : (smsg_ignore, # player-equip
+ Struct("data",
+ ULInt16("index"),
+ ULInt16("type"),
+ Byte("flag"))),
+ 0x00a4 : (smsg_player_equipment,
+ Struct("data",
+ ULInt16("length"),
+ Array(lambda ctx: (ctx.length - 4) / 20,
+ Struct("equipment",
+ ULInt16("index"),
+ ULInt16("id"),
+ Padding(16))))),
+ 0x0195 : (smsg_ignore, Field("data", 100)), # guild-party-info
+ 0x01ee : (smsg_player_inventory,
+ Struct("data",
+ ULInt16("length"),
+ Array(lambda ctx: (ctx.length - 4) / 18,
+ Struct("inventory",
+ ULInt16("index"),
+ ULInt16("id"),
+ Padding(2),
+ ULInt16("amount"),
+ Padding(10))))),
+ 0x00a0 : (smsg_player_inventory_add,
+ Struct("data",
+ ULInt16("index"),
+ ULInt16("amount"),
+ ULInt16("id"),
+ Padding(15))),
+ 0x00af : (smsg_player_inventory_remove,
+ Struct("data",
+ ULInt16("index"),
+ ULInt16("amount"))),
+ 0x01c8 : (smsg_player_inventory_use,
+ Struct("data",
+ ULInt16("index"),
+ ULInt16("item_id"),
+ Padding(4),
+ ULInt16("amount"),
+ Byte("type"))),
+ 0x01da : (smsg_player_move,
+ Struct("data",
+ ULInt32("id"),
+ ULInt16("speed"),
+ Padding(6),
+ ULInt16("job"),
+ Padding(8),
+ ULInt32("tick"),
+ Padding(22),
+ BitStruct("coor_pair",
+ BitField("src_x", 10),
+ BitField("src_y", 10),
+ BitField("dst_x", 10),
+ BitField("dst_y", 10)),
+ Padding(5))),
+ 0x0139 : (smsg_ignore, Field("data", 14)), # player-move-to-attack
+ 0x010f : (smsg_player_skills,
+ Struct("data",
+ ULInt16("length"),
+ Array(lambda ctx: (ctx.length - 4) / 37,
+ Struct("skills",
+ ULInt16("id"),
+ Padding(4),
+ ULInt16("level"),
+ Padding(29))))),
+ 0x010e : (smsg_player_skill_up,
+ Struct("data",
+ ULInt16("id"),
+ ULInt16("level"),
+ Padding(5))),
+ 0x00b0 : (smsg_player_stat_update_x,
+ Struct("data",
+ ULInt16("type"),
+ ULInt32("stat_value"))),
+ 0x00b1 : (smsg_player_stat_update_x,
+ Struct("data",
+ ULInt16("type"),
+ ULInt32("stat_value"))),
+ 0x0141 : (smsg_player_stat_update_x,
+ Struct("data",
+ ULInt32("type"),
+ ULInt32("stat_value"),
+ ULInt32("bonus"))),
+ 0x00bc : (smsg_player_stat_update_x,
+ Struct("data",
+ ULInt16("type"),
+ Flag("ok"),
+ Byte("stat_value"))),
+ 0x00bd : (smsg_ignore, Field("data", 42)), # player-stat-update-5
+ 0x00be : (smsg_player_stat_update_x,
+ Struct("data",
+ ULInt16("type"),
+ Byte("stat_value"))),
+ 0x0119 : (smsg_player_status_change,
+ Struct("data",
+ ULInt32("id"),
+ ULInt16("stun"),
+ ULInt16("effect"),
+ ULInt16("effect_hi"),
+ Padding(1))),
+ 0x0088 : (smsg_player_stop,
+ Struct("data",
+ ULInt32("id"),
+ ULInt16("x"),
+ ULInt16("y"))),
+ 0x00ac : (smsg_ignore, Field("data", 5)), # player-unequip
+ 0x01d8 : (smsg_player_update,
+ Struct("data",
+ ULInt32("id"),
+ ULInt16("speed"),
+ Padding(6),
+ ULInt16("job"),
+ Padding(30),
+ BitStruct("coor",
+ BitField("x", 10),
+ BitField("y", 10),
+ Nibble("dir")),
+ Padding(5))),
+ 0x01d9 : (smsg_player_update,
+ Struct("data",
+ ULInt32("id"),
+ ULInt16("speed"),
+ Padding(6),
+ ULInt16("job"),
+ Padding(30),
+ BitStruct("coor",
+ BitField("x", 10),
+ BitField("y", 10),
+ Nibble("dir")),
+ Padding(4))),
+ 0x0091 : (smsg_player_warp,
+ Struct("data",
+ StringZ("map", 16),
+ ULInt16("x"),
+ ULInt16("y"))),
+ 0x0020 : (smsg_ip_response,
+ Struct("data",
+ ULInt32("id"),
+ IpAddress("ip"))),
+ 0x019a : (smsg_ignore, Field("data", 12)), # pvp-set
+ 0x0081 : (smsg_connection_problem,
+ Struct("data", Byte("code"))),
+ 0x009a : (smsg_gm_chat,
+ Struct("data",
+ ULInt16("length"),
+ StringZ("message", lambda ctx: ctx.length - 4))),
+ 0x009e : (smsg_item_dropped,
+ Struct("data",
+ ULInt32("id"),
+ ULInt16("type"),
+ Byte("identify"),
+ ULInt16("x"),
+ ULInt16("y"),
+ Byte("sub_x"),
+ Byte("sub_y"),
+ ULInt16("amount"))),
+ 0x00a1 : (smsg_item_remove,
+ Struct("data", ULInt32("id"))),
+ 0x009d : (smsg_item_visible,
+ Struct("data",
+ ULInt32("id"),
+ ULInt16("type"),
+ Byte("identify"),
+ ULInt16("x"),
+ ULInt16("y"),
+ ULInt16("amount"),
+ Byte("sub_x"),
+ Byte("sub_y"))),
+ 0x00fb : (smsg_party_info,
+ Struct("data",
+ ULInt16("length"),
+ StringZ("name", 24),
+ Array(lambda ctx: (ctx.length - 28) / 46,
+ Struct("members",
+ ULInt32("id"),
+ StringZ("nick", 24),
+ StringZ("map", 16),
+ Flag("leader"),
+ Flag("online"))))),
+ 0x00fe : (smsg_ignore, Field("data", 28)), # party-invited
+ 0x0107 : (smsg_ignore, Field("data", 8)), # party-update-coords
+ 0x0106 : (smsg_ignore, Field("data", 8)), # party-update-hp
+ 0x0109 : (smsg_party_chat,
+ Struct("data",
+ ULInt16("length"),
+ ULInt32("id"),
+ # Probe("debug", show_stream=False, show_stack=False),
+ StringZ("message", lambda ctx: ctx.length - 8))),
+ 0x01b9 : (smsg_ignore, Field("data", 4)), # skill-cast-cancel
+ 0x013e : (smsg_ignore, Field("data", 22)), # skill-casting
+ 0x01de : (smsg_ignore, Field("data", 31)), # skill-damage
+ 0x011a : (smsg_ignore, Field("data", 13)), # skill-no-damage
+ 0x00e5 : (smsg_trade_request,
+ Struct("data",
+ StringZ("nick", 24))),
+ 0x00e7 : (smsg_trade_response,
+ Struct("data", Byte("code"))),
+ 0x00e9 : (smsg_trade_item_add,
+ Struct("data",
+ ULInt32("amount"),
+ ULInt16("id"),
+ Padding(11))),
+ 0x01b1 : (smsg_trade_item_add_response,
+ Struct("data",
+ ULInt16("index"),
+ ULInt16("amount"),
+ Byte("code"))),
+ 0x00ee : (smsg_trade_cancel,
+ StaticField("data", 0)),
+ 0x00f0 : (smsg_trade_complete,
+ StaticField("data", 1)),
+ 0x00ec : (smsg_trade_ok,
+ Struct("data", Byte("who"))),
+ 0x0097 : (smsg_whisper,
+ Struct("data",
+ ULInt16("length"),
+ StringZ("nick", 24),
+ StringZ("message", lambda ctx: ctx.length - 28))),
+ 0x0098 : (smsg_whisper_response,
+ Struct("data", Byte("code"))),
+ 0x0073 : (smsg_map_login_success,
+ Struct("data",
+ ULInt32("tick"),
+ # ULInt24("coor"),
+ BitStruct("coor",
+ BitField("x", 10),
+ BitField("y", 10),
+ Nibble("dir")),
+ Padding(2))),
+ 0x007f : (smsg_server_ping,
+ Struct("data",
+ ULInt32("tick"))),
+ 0x0087 : (smsg_walk_response,
+ Struct("data",
+ ULInt32("tick"),
+ BitStruct("coor_pair",
+ BitField("src_x", 10),
+ BitField("src_y", 10),
+ BitField("dst_x", 10),
+ BitField("dst_y", 10)),
+ Padding(1))),
+ 0x00b5 : (smsg_ignore, Field("data", 6)), # npc-next
+ 0x00b4 : (smsg_npc_message,
+ Struct("data",
+ ULInt16("length"),
+ ULInt32("id"),
+ StringZ("message", lambda ctx: ctx.length - 8))),
+ 0x00b7 : (smsg_npc_choice,
+ Struct("data",
+ ULInt16("length"),
+ ULInt32("id"),
+ StringZ("select", lambda ctx: ctx.length - 8))),
+ 0x00b6 : (smsg_npc_close,
+ Struct("data",
+ ULInt32("id"))),
+ 0x00b5 : (smsg_npc_next,
+ Struct("data",
+ ULInt32("id"))),
+ 0x0142 : (smsg_npc_int_input,
+ Struct("data",
+ ULInt32("id"))),
+ 0x01d4 : (smsg_npc_str_input,
+ Struct("data",
+ ULInt32("id"))),
+ 0x0212 : (smsg_npc_command,
+ Struct("data",
+ ULInt32("id"),
+ ULInt16("command"),
+ ULInt32("target_id"),
+ ULInt16("x"),
+ ULInt16("y"))),
+ 0x00c6 : (smsg_npc_buy,
+ Struct("data",
+ ULInt16("length"),
+ Array(lambda ctx: (ctx.length - 4) / 11,
+ Struct("items",
+ ULInt32("price"),
+ Padding(4),
+ ULInt16("type"),
+ ULInt32("id"))))),
+ 0x00f2 : (smsg_storage_status,
+ Struct("data",
+ ULInt16("used"),
+ ULInt16("max_size"))),
+ 0x00f4 : (smsg_storage_add,
+ Struct("data",
+ ULInt16("index"),
+ ULInt32("amount"),
+ ULInt16("id"),
+ Padding(11))),
+ 0x00f6 : (smsg_storage_remove,
+ Struct("data",
+ ULInt16("index"),
+ ULInt32("amount"))),
+ 0x00f8 : (smsg_storage_close,
+ Struct("data")), # ?????
+ 0x00a6 : (smsg_storage_equip,
+ Struct("data",
+ ULInt16("length"),
+ Array(lambda ctx: (ctx.length - 4) / 20,
+ Struct("equipment",
+ ULInt16("index"),
+ ULInt16("id"),
+ Padding(16))))),
+ 0x01f0 : (smsg_storage_items,
+ Struct("data",
+ ULInt16("length"),
+ Array(lambda ctx: (ctx.length - 4) / 18,
+ Struct("storage",
+ ULInt16("index"),
+ ULInt16("id"),
+ Padding(2),
+ ULInt16("amount"),
+ Padding(10))))),
+}
+
+
+# --------------------------------------------------------------------
+def cmsg_map_server_connect(account, char_id, session1, session2, gender):
+ netlog.info(("CMSG_MAP_SERVER_CONNECT account={} char_id={} "
+ "session1={} session2={} gender={}").format(account,
+ char_id, session1, session2, gender))
+
+ send_packet(server, CMSG_MAP_SERVER_CONNECT,
+ (ULInt32("account"), account),
+ (ULInt32("char_id"), char_id),
+ (ULInt32("session1"), session1),
+ (ULInt32("session2"), session2),
+ (Gender("gender"), gender))
+
+
+def cmsg_map_loaded():
+ netlog.info("CMSG_MAP_LOADED")
+ ULInt16("opcode").build_stream(CMSG_MAP_LOADED, server)
+
+
+def cmsg_map_server_ping(tick_=-1):
+ netlog.info("CMSG_MAP_SERVER_PING tick={}".format(tick))
+ if tick_ < 0:
+ tick_ = tick + 1
+ send_packet(server, CMSG_MAP_SERVER_PING,
+ (ULInt32("tick"), tick_))
+
+
+def cmsg_trade_response(answer):
+ if answer in ("OK", "ACCEPT", "YES", True):
+ answer = 3
+ elif answer in ("DECLINE", "CANCEL", "NO", False):
+ answer = 4
+
+ s = {3: "ACCEPT", 4: "DECLINE"}
+
+ netlog.info("CMSG_TRADE_RESPONSE {}".format(s[answer]))
+ send_packet(server, CMSG_TRADE_RESPONSE,
+ (Byte("answer"), answer))
+
+
+def cmsg_trade_request(player_id):
+ netlog.info("CMSG_TRADE_REQUEST id={}".format(player_id))
+ send_packet(server, CMSG_TRADE_REQUEST,
+ (ULInt32("id"), player_id))
+
+
+def cmsg_trade_item_add_request(index, amount):
+ netlog.info("CMSG_TRADE_ITEM_ADD_REQUEST index={} amount={}".format(
+ index, amount))
+
+ # Workaround for TMWA, I'm pretty sure it has a bug related to
+ # not sending back the amount of GP player added to trade
+ if index == 0 and amount <= player_money:
+ trade_state['zeny_give'] = amount
+
+ send_packet(server, CMSG_TRADE_ITEM_ADD_REQUEST,
+ (ULInt16("index"), index),
+ (ULInt32("amount"), amount))
+
+
+def cmsg_trade_ok():
+ netlog.info("CMSG_TRADE_OK")
+ ULInt16("opcode").build_stream(CMSG_TRADE_OK, server)
+
+
+def cmsg_trade_add_complete():
+ netlog.info("CMSG_TRADE_ADD_COMPLETE")
+ ULInt16("opcode").build_stream(CMSG_TRADE_ADD_COMPLETE, server)
+
+
+def cmsg_trade_cancel_request():
+ netlog.info("CMSG_TRADE_CANCEL_REQUEST")
+ ULInt16("opcode").build_stream(CMSG_TRADE_CANCEL_REQUEST, server)
+
+
+def cmsg_name_request(id_):
+ netlog.info("CMSG_NAME_REQUEST id={}".format(id_))
+ send_packet(server, CMSG_NAME_REQUEST,
+ (ULInt32("id"), id_))
+
+
+def cmsg_chat_message(msg):
+ netlog.info("CMSG_CHAT_MESSAGE {}".format(msg))
+ l = len(msg)
+ send_packet(server, CMSG_CHAT_MESSAGE,
+ (ULInt16("len"), l + 5),
+ (StringZ("msg", l + 1), msg))
+
+
+def cmsg_chat_whisper(to_, msg):
+ netlog.info("CMSG_CHAT_WHISPER to {} : {}".format(to_, msg))
+ last_whisper['to'] = to_
+ last_whisper['msg'] = msg
+ l = len(msg)
+ send_packet(server, CMSG_CHAT_WHISPER,
+ (ULInt16("len"), l + 29),
+ (StringZ("nick", 24), to_),
+ (StringZ("msg", l + 1), msg))
+
+
+def cmsg_party_message(msg):
+ netlog.info("CMSG_PARTY_MESSAGE {}".format(msg))
+ l = len(msg)
+ send_packet(server, CMSG_PARTY_MESSAGE,
+ (ULInt16("len"), l + 4),
+ (String("msg", l), msg))
+
+
+def cmsg_player_change_dest(x_, y_):
+ netlog.info("CMSG_PLAYER_CHANGE_DEST x={} y={}".format(x_, y_))
+
+ class C:
+ opcode = CMSG_PLAYER_CHANGE_DEST
+
+ class coor:
+ x = x_
+ y = y_
+ dir = 0
+
+ d = Struct("packet",
+ ULInt16("opcode"),
+ BitStruct("coor", BitField("x", 10),
+ BitField("y", 10), Nibble("dir")))
+
+ d.build_stream(C, server)
+
+
+def cmsg_player_change_dir(new_dir):
+ netlog.info("CMSG_PLAYER_CHANGE_DIR {}".format(new_dir))
+ send_packet(server, CMSG_PLAYER_CHANGE_DIR,
+ (ULInt16("unused"), 0),
+ (ULInt8("dir"), new_dir))
+
+
+def cmsg_player_change_act(id_, action):
+ netlog.info("CMSG_PLAYER_CHANGE_ACT id={} action={}".format(id_, action))
+ send_packet(server, CMSG_PLAYER_CHANGE_ACT,
+ (ULInt32("id"), id_),
+ (Byte("action"), action))
+
+
+def cmsg_player_respawn():
+ netlog.info("CMSG_PLAYER_RESPAWN")
+ send_packet(server, CMSG_PLAYER_RESPAWN,
+ (Byte("action"), 0))
+
+
+def cmsg_player_emote(emote):
+ netlog.info("CMSG_PLAYER_EMOTE {}".format(emote))
+ send_packet(server, CMSG_PLAYER_EMOTE,
+ (Byte("emote"), emote))
+
+
+def cmsg_item_pickup(item_id):
+ netlog.info("CMSG_ITEM_PICKUP id={}".format(item_id))
+ send_packet(server, CMSG_ITEM_PICKUP,
+ (ULInt32("id"), item_id))
+
+
+def cmsg_player_stop_attack():
+ netlog.info("CMSG_PLAYER_STOP_ATTACK")
+ ULInt16("opcode").build_stream(CMSG_PLAYER_STOP_ATTACK, server)
+
+
+def cmsg_player_equip(index):
+ netlog.info("CMSG_PLAYER_EQUIP index={}".format(index))
+ send_packet(server, CMSG_PLAYER_EQUIP,
+ (ULInt16("index"), index),
+ (ULInt16("unused"), 0))
+
+
+def cmsg_player_unequip(index):
+ netlog.info("CMSG_PLAYER_UNEQUIP index={}".format(index))
+ send_packet(server, CMSG_PLAYER_UNEQUIP,
+ (ULInt16("index"), index))
+
+
+def cmsg_player_inventory_use(index, item_id):
+ netlog.info("CMSG_PLAYER_INVENTORY_USE index={} id={}".format(
+ index, item_id))
+ send_packet(server, CMSG_PLAYER_INVENTORY_USE,
+ (ULInt16("index"), index),
+ (ULInt32("id"), item_id))
+
+
+def cmsg_player_inventory_drop(index, amount):
+ netlog.info("CMSG_PLAYER_INVENTORY_DROP index={} amount={}".format(
+ index, amount))
+ send_packet(server, CMSG_PLAYER_INVENTORY_DROP,
+ (ULInt16("index"), index),
+ (ULInt16("amount"), amount))
+
+
+# --------------- NPC ---------------------
+def cmsg_npc_talk(npcId):
+ netlog.info("CMSG_NPC_TALK id={}".format(npcId))
+ send_packet(server, CMSG_NPC_TALK,
+ (ULInt32("npcId"), npcId),
+ (Byte("unused"), 0))
+
+
+def cmsg_npc_next_request(npcId):
+ netlog.info("CMSG_NPC_NEXT_REQUEST id={}".format(npcId))
+ send_packet(server, CMSG_NPC_NEXT_REQUEST,
+ (ULInt32("npcId"), npcId))
+
+
+def cmsg_npc_close(npcId):
+ netlog.info("CMSG_NPC_CLOSE id={}".format(npcId))
+ send_packet(server, CMSG_NPC_CLOSE,
+ (ULInt32("npcId"), npcId))
+
+
+def cmsg_npc_list_choice(npcId, choice):
+ netlog.info("CMSG_NPC_LIST_CHOICE id={} choice={}".format(npcId, choice))
+ send_packet(server, CMSG_NPC_LIST_CHOICE,
+ (ULInt32("npcId"), npcId),
+ (Byte("choice"), choice))
+
+
+def cmsg_npc_int_response(npcId, value):
+ netlog.info("CMSG_NPC_INT_RESPONSE id={} value={}".format(npcId, value))
+ send_packet(server, CMSG_NPC_INT_RESPONSE,
+ (ULInt32("npcId"), npcId),
+ (SLInt32("value"), value))
+
+
+def cmsg_npc_str_response(npcId, value):
+ netlog.info("CMSG_NPC_STR_RESPONSE id={} value={}".format(npcId, value))
+ l = len(value)
+ send_packet(server, CMSG_NPC_STR_RESPONSE,
+ (ULInt16("len"), l + 9),
+ (ULInt32("npcId"), npcId),
+ (StringZ("value", l + 1), value))
+
+
+def cmsg_npc_buy_sell_request(npcId, action):
+ netlog.info("CMSG_NPC_BUY_SELL_REQUEST id={} action={}".format(
+ npcId, action))
+ send_packet(server, CMSG_NPC_BUY_SELL_REQUEST,
+ (ULInt32("npcId"), npcId),
+ (Byte("action"), action))
+
+
+def cmsg_npc_buy_request(itemId, amount):
+ netlog.info("CMSG_NPC_BUY_REQUEST itemId={} amount={}".format(
+ itemId, amount))
+ send_packet(server, CMSG_NPC_BUY_REQUEST,
+ (ULInt16("len"), 8),
+ (ULInt16("amount"), amount),
+ (ULInt16("itemId"), itemId))
+
+
+def cmsg_npc_sell_request(index, amount):
+ netlog.info("CMSG_NPC_SELL_REQUEST index={} amount={}".format(
+ index, amount))
+ send_packet(server, CMSG_NPC_SELL_REQUEST,
+ (ULInt16("len"), 8),
+ (ULInt16("index"), index),
+ (ULInt16("amount"), amount))
+
+
+# --------------- STATS, SKILLS --------------------------
+def cmsg_stat_update_request(stat, value):
+ netlog.info("CMSG_STAT_UPDATE_REQUEST stat={} value={}".format(
+ stat, value))
+ send_packet(server, CMSG_STAT_UPDATE_REQUEST,
+ (ULInt16("stat"), stat),
+ (Byte("value"), value))
+
+
+def cmsg_skill_levelup_request(skillId):
+ netlog.info("CMSG_SKILL_LEVELUP_REQUEST skillId={}".format(skillId))
+ send_packet(server, CMSG_SKILL_LEVELUP_REQUEST,
+ (ULInt16("id"), skillId))
+
+
+# ------------------- STORAGE -------------------------------
+def cmsg_move_to_storage(index, amount):
+ netlog.info("CMSG_MOVE_TO_STORAGE index={} amount={}".format(
+ index, amount))
+ send_packet(server, CMSG_MOVE_TO_STORAGE,
+ (ULInt16("index"), index),
+ (ULInt32("amount"), amount))
+
+
+def cmsg_move_from_storage(index, amount):
+ netlog.info("CMSG_MOVE_FROM_STORAGE index={} amount={}".format(
+ index, amount))
+ send_packet(server, CMSG_MOVE_FROM_STORAGE,
+ (ULInt16("index"), index),
+ (ULInt32("amount"), amount))
+
+
+def cmsg_storage_close():
+ netlog.info("CMSG_CLOSE_STORAGE")
+ ULInt16("opcode").build_stream(CMSG_CLOSE_STORAGE, server)
+
+
+# --------------------------------------------------------------------
+
+last_ping_ts = 0
+
+
+def connect(host, port):
+
+ def ping_logic(ts):
+ global last_ping_ts
+ if last_ping_ts and ts > last_ping_ts + 15:
+ last_ping_ts = time.time()
+ cmsg_map_server_ping()
+
+ global server, beings_cache
+ beings_cache = BeingsCache(cmsg_name_request)
+ #server = SocketWrapper(host=host, port=port, protodef=protodef)
+ server = SocketWrapper(host="52.174.196.146", port=port, protodef=protodef)
+
+ import logicmanager
+ logicmanager.logic_manager.add_logic(ping_logic)
+
+
+@atexit.register
+def cleanup():
+ if server is not None:
+ server.close()