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()