summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRawng <rawng.github@gmail.com>2015-06-01 19:56:10 -0400
committerRawng <rawng.github@gmail.com>2015-06-01 19:56:10 -0400
commit1e8140f0f62ecfd0ccbe046cf6ea9faed926f327 (patch)
treeca92a7a0cd2256ffbb0b43563ee183400d1aacc5
parentb4f19a8e28cf09b7c8a042c8faf068217669bca5 (diff)
parentb7e3823588ae9354daf5fc0a0cbf27b699afb7d5 (diff)
downloadmanamarket-1e8140f0f62ecfd0ccbe046cf6ea9faed926f327.tar.gz
manamarket-1e8140f0f62ecfd0ccbe046cf6ea9faed926f327.tar.bz2
manamarket-1e8140f0f62ecfd0ccbe046cf6ea9faed926f327.tar.xz
manamarket-1e8140f0f62ecfd0ccbe046cf6ea9faed926f327.zip
Merge branch 'storage'
-rw-r--r--config.py.template2
-rw-r--r--data_template/delisted.xml2
-rw-r--r--data_template/stack.xml2
-rwxr-xr-xmain.py307
-rw-r--r--net/packet_out.py5
-rw-r--r--net/protocol.py14
-rw-r--r--storage.py117
-rw-r--r--tradey.py83
-rw-r--r--utils.py23
9 files changed, 478 insertions, 77 deletions
diff --git a/config.py.template b/config.py.template
index e55b2f6..d8af804 100644
--- a/config.py.template
+++ b/config.py.template
@@ -2,7 +2,7 @@ server = "server.themanaworld.org"
port = 6901
account = ""
password = ""
-relist_time = 604800 # Time in seconds before an item needs to be relisted.
+delist_time = 604800 # Time in seconds before an item needs to be relisted.
character = 0 #slot character is in, 0 for first, 1 for second, 2 for third
admin = ""
#nosell = [] # Items which can't be sold - just add the itemid to the list.
diff --git a/data_template/delisted.xml b/data_template/delisted.xml
new file mode 100644
index 0000000..8208566
--- /dev/null
+++ b/data_template/delisted.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" ?>
+<items/>
diff --git a/data_template/stack.xml b/data_template/stack.xml
new file mode 100644
index 0000000..8208566
--- /dev/null
+++ b/data_template/stack.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" ?>
+<items/>
diff --git a/main.py b/main.py
index 9625fd9..21fddff 100755
--- a/main.py
+++ b/main.py
@@ -22,7 +22,6 @@ this program. If not, see <http://www.gnu.org/licenses/>.
Additionally to the GPL, you are *strongly* encouraged to share any modifications
you do on these sources.
"""
-
import logging
import logging.handlers
import socket
@@ -41,6 +40,7 @@ from net.packet import *
from net.protocol import *
from net.packet_out import *
from player import *
+from storage import *
import tradey
import utils
import eliza
@@ -50,12 +50,74 @@ shop_broadcaster = utils.Broadcast()
trader_state = utils.TraderState()
ItemDB = utils.ItemDB()
player_node = Player('')
+storage = Storage()
beingManager = BeingManager()
user_tree = tradey.UserTree()
sale_tree = tradey.ItemTree()
+stack_tree = tradey.StackTree()
+delisted_tree = tradey.DelistedTree()
ItemLog = utils.ItemLog()
+DelistedLog = utils.DelistedLog()
logger = logging.getLogger('ManaLogger')
+def do_delist():
+ cleaned = 0
+ for elem in sale_tree.root:
+ item = Item()
+ item.index = player_node.find_inventory_index(int(elem.get('itemId')))
+ item.itemId = int(elem.get('itemId'))
+ item.amount = int(elem.get('amount'))
+ if time.time() - float(elem.get('add_time')) > config.delist_time:
+ try:
+ storage.storage_send(item.index, item.amount)
+ except:
+ logger.info("Couldn't send item to storage")
+ return -10
+ storage.add_item(item)
+ player_node.remove_item(item.index, item.amount)
+ delisted_tree.add_item(elem.get('name'), item.itemId, item.amount)
+ sale_tree.remove_item_uid(int(elem.get('uid')))
+ DelistedLog.add_item(item.itemId, item.amount, elem.get('name'))
+ cleaned += 1
+ if cleaned > 0:
+ logger.info("Delisting routine done. %d items added to delisted.xml", cleaned)
+ stacked = len(stack_tree.u_id)
+ if stacked > 0:
+ if cleaned - stacked < 0:
+ stacked = cleaned
+ while stacked:
+ err = unstack()
+ if err == -10:
+ return err
+ else:
+ stacked -= 1
+ return 0
+
+def unstack():
+ elem = stack_tree.get_uid(stack_tree.next_id)
+ index = storage.find_storage_index(int(elem.get('itemId')))
+ try:
+ storage.storage_get(index, int(elem.get('amount')))
+ except:
+ logger.info("Couldn't remove item from storage")
+ return -10
+
+ storage.remove_item(index, int(elem.get('amount')))
+ sale_tree.add_item(elem.get('name'), int(elem.get('itemId')), int(elem.get('amount')), int(elem.get('price')))
+ stack_tree.remove_item_uid(stack_tree.next_id)
+ return 0
+
+def storage_operation(func, args=[]):
+ storage.storage_open()
+ # Using a timed thread here to wait storage response.
+ # Main reason: time.sleep() stops execution, thus it denies storage to open. Yeah, that bad.
+ timer = utils.CustomTimer(2.0, func, args)
+ timer.start()
+ result = timer.join()
+ if timer.finished:
+ storage.storage_close()
+ return result
+
def process_whisper(nick, msg, mapserv):
msg = filter(lambda x: x in utils.allowed_chars, msg)
if len(msg) == 0:
@@ -90,7 +152,7 @@ def process_whisper(nick, msg, mapserv):
mapserv.sendall(whisper(nick, "No items for sale."))
for elem in sale_tree.root:
- if time.time() - float(elem.get('add_time')) < config.relist_time: # Check if an items time is up.
+ if time.time() - float(elem.get('add_time')) < config.delist_time: # Check if an items time is up.
msg = "[selling] [" + elem.get("uid") + "] " + elem.get("amount") + " [@@" + \
elem.get("itemId") + "|" + ItemDB.getItem(int(elem.get("itemId"))).name + "@@] for " + elem.get("price") + "gp each"
mapserv.sendall(whisper(nick, msg))
@@ -100,7 +162,7 @@ def process_whisper(nick, msg, mapserv):
data = '\302\202B1'
for elem in sale_tree.root:
- if time.time() - float(elem.get('add_time')) < config.relist_time:
+ if time.time() - float(elem.get('add_time')) < config.delist_time: # Check if an items time is up.
data += utils.encode_str(int(elem.get("itemId")), 2)
data += utils.encode_str(int(elem.get("price")), 4)
data += utils.encode_str(int(elem.get("amount")), 3)
@@ -131,10 +193,10 @@ def process_whisper(nick, msg, mapserv):
items_for_sale = False
for elem in sale_tree.root:
if elem.get('name') == nick:
- if time.time() - float(elem.get('add_time')) > config.relist_time:
- msg = "[expired] ["
- else:
+ if time.time() - float(elem.get('add_time')) < config.delist_time:
msg = "[selling] ["
+ else:
+ msg = "[expired] ["
msg += elem.get("uid") + "] " + elem.get("amount") + " [@@" + elem.get("itemId") + "|" + \
ItemDB.getItem(int(elem.get("itemId"))).name + "@@] for " + elem.get("price") + "gp each"
@@ -145,8 +207,38 @@ def process_whisper(nick, msg, mapserv):
mapserv.sendall(whisper(nick, msg))
+ for elem in stack_tree.root:
+ if elem.get('name') == nick:
+ msg = "[waiting] ["
+
+ msg += elem.get("uid") + "] " + elem.get("amount") + " [@@" + elem.get("itemId") + "|" + \
+ ItemDB.getItem(int(elem.get("itemId"))).name + "@@] for " + elem.get("price") + "gp each"
+
+ if items_for_sale == False:
+ mapserv.sendall(whisper(nick, "Your have the following items waiting for sale:"))
+ items_for_sale = True
+
+ mapserv.sendall(whisper(nick, msg))
+
if items_for_sale == False:
mapserv.sendall(whisper(nick, "You have no items for sale."))
+
+ items_for_getback = False
+ for elem in delisted_tree.root:
+ if elem.get('name') == nick:
+ msg = "[expired] ["
+
+ msg += elem.get("uid") + "] " + elem.get("amount") + " [@@" + elem.get("itemId") + "|" + \
+ ItemDB.getItem(int(elem.get("itemId"))).name + "@@]"
+
+ if items_for_getback == False:
+ mapserv.sendall(whisper(nick, "Your have the following items waiting for getback:"))
+ items_for_getback = True
+
+ mapserv.sendall(whisper(nick, msg))
+
+ if items_for_sale == False:
+ mapserv.sendall(whisper(nick, "You have no expired items."))
money = int(user.get('money'))
mapserv.sendall(whisper(nick, "You have " + str(money) + "gp to collect."))
@@ -157,7 +249,7 @@ def process_whisper(nick, msg, mapserv):
# Sends help information
if len(broken_string) == 1:
mapserv.sendall(whisper(nick, "Welcome to ManaMarket!"))
- mapserv.sendall(whisper(nick, "The basic commands for the bot are: !list, !find <id> or <Item Name>, !buy <amount> <uid>, !add <amount> <price> <Item Name>, !money, !relist <uid>, !info, !getback <uid> "))
+ mapserv.sendall(whisper(nick, "The basic commands for the bot are: !list, !find <id> or <Item Name>, !buy <amount> <uid>, !add <amount> <price> <Item Name>, !money, !info, !getback <uid> "))
mapserv.sendall(whisper(nick, "For a detailed description of each command, type !help <command> e.g. !help !buy"))
mapserv.sendall(whisper(nick, "For example to purchase an item shown in the list as:"))
mapserv.sendall(whisper(nick, "[selling] [6] 5 [@@640|Iron Ore@@] for 1000gp each"))
@@ -189,8 +281,6 @@ def process_whisper(nick, msg, mapserv):
mapserv.sendall(whisper(nick, "!add <amount> <price> <Item Name> - Add an item to the sell list (requires that you have an account)."))
elif broken_string[1] == '!money':
mapserv.sendall(whisper(nick, "!money - Allows you to collect money for any sales made on your behalf."))
- elif broken_string[1] == '!relist':
- mapserv.sendall(whisper(nick, "!relist <uid> - Allows you to relist an item which has expired."))
elif broken_string[1] == '!info':
mapserv.sendall(whisper(nick, "!info - Displays basic information about your account."))
elif broken_string[1] == '!getback':
@@ -217,6 +307,9 @@ def process_whisper(nick, msg, mapserv):
if not trader_state.Trading.testandset():
mapserv.sendall(whisper(nick, "I'm currently busy with a trade. Try again shortly"))
return
+ if not storage.Open.testandset():
+ mapserv.sendall(whisper(nick, "I'm currently busy with storage. Try again shortly"))
+ return
trader_state.money = nick
player_id = beingManager.findId(nick)
@@ -238,20 +331,32 @@ def process_whisper(nick, msg, mapserv):
if item.isdigit(): # an id
for elem in sale_tree.root:
- if ((time.time() - float(elem.get('add_time'))) < config.relist_time) \
- and int(elem.get("itemId")) == int(item): # Check if an items time is up.
+ if int(elem.get("itemId")) == int(item):
msg = "[selling] [" + elem.get("uid") + "] " + elem.get("amount") + " [@@" + elem.get("itemId") + "|" \
+ ItemDB.getItem(int(elem.get("itemId"))).name + "@@] for " + elem.get("price") + "gp each"
mapserv.sendall(whisper(nick, msg))
items_found = True
+ if not items_found: # if no items on list, gives priority to stack
+ for elem in stack_tree.root:
+ if int(elem.get("itemId")) == int(item):
+ msg = "[selling] [" + elem.get("uid") + "] " + elem.get("amount") + " [@@" + elem.get("itemId") + "|" \
+ + ItemDB.getItem(int(elem.get("itemId"))).name + "@@] for " + elem.get("price") + "gp each"
+ mapserv.sendall(whisper(nick, msg))
+ items_found = True
else: # an item name
for elem in sale_tree.root:
- if ((time.time() - float(elem.get('add_time'))) < config.relist_time) \
- and item.lower() in ItemDB.getItem(int(elem.get("itemId"))).name.lower(): # Check if an items time is up.
+ if item.lower() in ItemDB.getItem(int(elem.get("itemId"))).name.lower():
msg = "[selling] [" + elem.get("uid") + "] " + elem.get("amount") + " [@@" + elem.get("itemId") + "|" \
+ ItemDB.getItem(int(elem.get("itemId"))).name + "@@] for " + elem.get("price") + "gp each"
mapserv.sendall(whisper(nick, msg))
items_found = True
+ if not items_found: # if no items on list, gives priority to stack
+ for elem in stack_tree.root:
+ if item.lower() in ItemDB.getItem(int(elem.get("itemId"))).name.lower():
+ msg = "[selling] [" + elem.get("uid") + "] " + elem.get("amount") + " [@@" + elem.get("itemId") + "|" \
+ + ItemDB.getItem(int(elem.get("itemId"))).name + "@@] for " + elem.get("price") + "gp each"
+ mapserv.sendall(whisper(nick, msg))
+ items_found = True
if not items_found:
mapserv.sendall(whisper(nick, "Item not found."))
@@ -267,6 +372,8 @@ def process_whisper(nick, msg, mapserv):
if trader_state.Trading.test():
mapserv.sendall(whisper(nick, "I'm busy with a trade."))
+ elif storage.Open.test():
+ mapserv.sendall(whisper(nick, "I'm busy with storage."))
else:
mapserv.sendall(whisper(nick, "I'm free."))
@@ -283,7 +390,12 @@ def process_whisper(nick, msg, mapserv):
if broken_string[1].isdigit():
uid = int(broken_string[1])
- item_info = sale_tree.get_uid(uid)
+ if uid <= MAX_INVENTORY:
+ item_info = sale_tree.get_uid(uid)
+ elif uid > MAX_INVENTORY and uid <= MAX_STORAGE:
+ item_info = stack_tree.get_uid(uid)
+ else:
+ item_info = delisted_tree.get_uid(uid)
if item_info == -10:
mapserv.sendall(whisper(nick, "Item not found. Please check the uid number and try again."))
@@ -480,6 +592,9 @@ def process_whisper(nick, msg, mapserv):
if not trader_state.Trading.testandset():
mapserv.sendall(whisper(nick, "I'm currently busy with a trade. Try again shortly"))
return
+ if not storage.Open.testandset():
+ mapserv.sendall(whisper(nick, "I'm currently busy with storage. Try again shortly"))
+ return
trader_state.item = item
player_id = beingManager.findId(nick)
@@ -501,7 +616,10 @@ def process_whisper(nick, msg, mapserv):
if broken_string[1].isdigit() and broken_string[2].isdigit():
amount = int(broken_string[1])
uid = int(broken_string[2])
- item_info = sale_tree.get_uid(uid)
+ if uid <= MAX_INVENTORY:
+ item_info = sale_tree.get_uid(uid)
+ else:
+ item_info = stack_tree.get_uid(uid)
if item_info == -10:
mapserv.sendall(whisper(nick, "Item not found. Please check the uid number and try again."))
@@ -526,6 +644,9 @@ def process_whisper(nick, msg, mapserv):
if not trader_state.Trading.testandset():
mapserv.sendall(whisper(nick, "I'm currently busy with a trade. Try again shortly"))
return
+ if not storage.Open.testandset():
+ mapserv.sendall(whisper(nick, "I'm currently buse with storage. Try again shortly"))
+ return
trader_state.item = item
player_id = beingManager.findId(nick)
@@ -560,43 +681,6 @@ def process_whisper(nick, msg, mapserv):
elif check == -10:
mapserv.sendall(whisper(nick, "User removal failed. Please check spelling."))
- elif broken_string[0] == "!relist":
- # Relist an item which has expired - !relist <uid>.
- if user == -10 or len(broken_string) != 2:
- mapserv.sendall(whisper(nick, "Syntax incorrect."))
- return
-
- if int(user.get("accesslevel")) < 5:
- mapserv.sendall(whisper(nick, "You don't have the correct permissions."))
- return
-
- if broken_string[1].isdigit():
- uid = int(broken_string[1])
- item_info = sale_tree.get_uid(uid)
-
- if item_info == -10:
- mapserv.sendall(whisper(nick, "Item not found. Please check the uid number and try again."))
- return
-
- if item_info.get('name') != nick:
- mapserv.sendall(whisper(nick, "That doesn't belong to you!"))
- return
-
- time_relisted = int(item_info.get('relisted'))
-
- if int(item_info.get('relisted')) < 3:
- sale_tree.get_uid(uid).set('add_time', str(time.time()))
- sale_tree.get_uid(uid).set('relisted', str(time_relisted + 1))
- sale_tree.save()
- mapserv.sendall(whisper(nick, "The item has been successfully relisted."))
- user_tree.get_user(nick).set('last_use', str(time.time()))
- user_tree.save()
- else:
- mapserv.sendall(whisper(nick, "This item can no longer be relisted. Please collect it using !getback "+str(uid)+"."))
- return
- else:
- mapserv.sendall(whisper(nick, "Syntax incorrect."))
-
elif broken_string[0] == "!getback":
# Trade the player back uid, remove from sale_items if trade successful - !getback <uid>.
if user == -10 or len(broken_string) != 2:
@@ -609,7 +693,12 @@ def process_whisper(nick, msg, mapserv):
if broken_string[1].isdigit():
uid = int(broken_string[1])
- item_info = sale_tree.get_uid(uid)
+ if uid <= MAX_INVENTORY:
+ item_info = sale_tree.get_uid(uid)
+ elif uid > MAX_INVENTORY and uid <= MAX_STORAGE:
+ item_info = stack_tree.get_uid(uid)
+ else:
+ item_info = delisted_tree.get_uid(uid)
if item_info == -10:
mapserv.sendall(whisper(nick, "Item not found. Please check the uid number and try again."))
@@ -630,10 +719,16 @@ def process_whisper(nick, msg, mapserv):
if not trader_state.Trading.testandset():
mapserv.sendall(whisper(nick, "I'm currently busy with a trade. Try again shortly"))
return
+ if not storage.Open.testandset():
+ mapserv.sendall(whisper(nick, "I'm currently busy with storage. Try again shortly"))
+ return
trader_state.item = item
player_id = beingManager.findId(nick)
if player_id != -10:
+ if uid > MAX_INVENTORY:
+ item.index = storage.find_storage_index(item.id)
+ storage_operation(storage.storage_get, (item.index, item.amount))
mapserv.sendall(trade_request(player_id))
trader_state.timer = time.time()
else:
@@ -770,6 +865,7 @@ def main():
pb = PacketBuffer()
shop_broadcaster.mapserv = mapserv
+ storage.mapserv = mapserv
# Map server packet loop
print "Entering map packet loop\n";
@@ -785,6 +881,12 @@ def main():
logger.info("Trade Cancelled - Timeout.")
trader_state.timer = time.time()
mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST)))
+
+ # The same applies to storage, now
+ if storage.Open.test():
+ if time.time() - storage.timer > 2*60:
+ logger.info("Storage operation cancelled - Timeout.")
+ storage.storage_close()
for packet in pb:
if packet.is_type(SMSG_MAP_LOGIN_SUCCESS): # connected
@@ -870,12 +972,19 @@ def main():
err = packet.read_int8()
if err == 0:
- if item.index in player_node.inventory:
- player_node.inventory[item.index].amount += item.amount
- else:
- player_node.inventory[item.index] = item
+ if len(player_node.inventory) < MAX_INVENTORY:
+ if item.index in player_node.inventory:
+ player_node.inventory[item.index].amount += item.amount
+ else:
+ player_node.inventory[item.index] = item
+ placement = "Inventory"
+ else: # If only one slot left, move to stack
+ err = storage_operation(storage.storage_send, (item.index, item.amount))
+ if err == 0:
+ item.index = storage.add_item(item)
+ placement = "Storage"
- logger.info("Picked up: %s, Amount: %s, Index: %s", ItemDB.getItem(item.itemId).name, str(item.amount), str(item.index))
+ logger.info("Picked up: %s, Amount: %s, Index: %s - %s", ItemDB.getItem(item.itemId).name, str(item.amount), str(item.index), placement)
elif packet.is_type(SMSG_PLAYER_INVENTORY_REMOVE):
index = packet.read_int16() - inventory_offset
@@ -884,6 +993,10 @@ def main():
logger.info("Remove item: %s, Amount: %s, Index: %s", ItemDB.getItem(player_node.inventory[index].itemId).name, str(amount), str(index))
player_node.remove_item(index, amount)
+ # Now taking an item from stack if inventory was full before
+ if len(player_node.inventory) == MAX_INVENTORY-1:
+ storage_operation(unstack)
+
elif packet.is_type(SMSG_PLAYER_INVENTORY):
player_node.inventory.clear() # Clear the inventory - incase of new index.
packet.skip(2)
@@ -922,6 +1035,47 @@ def main():
else:
logger.info("Inventory Check Passed.")
+ # IMO the best moment to run delisting
+ storage_operation(do_delist)
+
+ elif packet.is_type(SMSG_PLAYER_STORAGE_ITEMS):
+ storage.storage.clear() # Clear storage - same as inventory.
+ packet.skip(2)
+ number = (len(packet.data)-2) / 18
+ for loop in range(number):
+ item = Item()
+ item.index = packet.read_int16() - storage_offset
+ item.itemId = packet.read_int16()
+ packet.skip(2)
+ item.amount = packet.read_int16()
+ packet.skip(10)
+ storage.storage[item.index] = item
+
+ elif packet.is_type(SMSG_PLAYER_STORAGE_EQUIP):
+ packet.read_int16()
+ number = (len(packet.data)) / 20
+ for loop in range(number):
+ item = Item()
+ item.index = packet.read_int16() - storage_offset
+ item.itemId = packet.read_int16()
+ packet.skip(16)
+ item.amount = 1
+ storage.storage[item.index] = item
+
+ logger.info("Storage information received:")
+ for item in storage.storage:
+ logger.info("Name: %s, Id: %s, Index: %s, Amount: %s.", \
+ ItemDB.getItem(storage.storage[item].itemId).name, \
+ storage.storage[item].itemId, item, storage.storage[item].amount)
+
+ errorOccured = storage.check_storage(stack_tree, delisted_tree)
+ if errorOccured:
+ logger.info(errorOccured)
+ shop_broadcaster.stop()
+ sys.exit(1)
+ else:
+ logger.info("Storage Check Passed.")
+
elif packet.is_type(SMSG_TRADE_REQUEST):
name = packet.read_string(24)
logger.info("Trade request: " + name)
@@ -1062,24 +1216,39 @@ def main():
elif packet.is_type(SMSG_TRADE_COMPLETE):
commitMessage=""
- # The sale_tree is only ammended after a complete trade packet.
+ # we get here which tree it should be through UID
+ if trader_state.item.get == 1:
+ if len(player_node.inventory) <= MAX_INVENTORY:
+ tree = sale_tree
+ else:
+ tree = stack_tree
+ elif trader_state.item.get == 0:
+ if trader_state.item.uid <= MAX_INVENTORY:
+ tree = sale_tree
+ elif trader_state.item.uid > MAX_INVENTORY \
+ and trader_state.item.uid <= MAX_STORAGE:
+ tree = stack_tree
+ else:
+ tree = delisted_tree
+
+ # The trees are only ammended after a complete trade packet.
if trader_state.item and trader_state.money == 0:
if trader_state.item.get == 1: # !add
- sale_tree.add_item(trader_state.item.player, trader_state.item.id, trader_state.item.amount, trader_state.item.price)
+ tree.add_item(trader_state.item.player, trader_state.item.id, trader_state.item.amount, trader_state.item.price)
user_tree.get_user(trader_state.item.player).set('used_stalls', \
str(int(user_tree.get_user(trader_state.item.player).get('used_stalls')) + 1))
user_tree.get_user(trader_state.item.player).set('last_use', str(time.time()))
commitMessage = "Add"
elif trader_state.item.get == 0: # !buy \ !getback
- seller = sale_tree.get_uid(trader_state.item.uid).get('name')
- item = sale_tree.get_uid(trader_state.item.uid)
+ seller = tree.get_uid(trader_state.item.uid).get('name')
+ item = tree.get_uid(trader_state.item.uid)
current_amount = int(item.get("amount"))
- sale_tree.get_uid(trader_state.item.uid).set("amount", str(current_amount - trader_state.item.amount))
+ tree.get_uid(trader_state.item.uid).set("amount", str(current_amount - trader_state.item.amount))
if int(item.get("amount")) == 0:
- user_tree.get_user(sale_tree.get_uid(trader_state.item.uid).get('name')).set('used_stalls', \
- str(int(user_tree.get_user(sale_tree.get_uid(trader_state.item.uid).get('name')).get('used_stalls'))-1))
- sale_tree.remove_item_uid(trader_state.item.uid)
+ user_tree.get_user(tree.get_uid(trader_state.item.uid).get('name')).set('used_stalls', \
+ str(int(user_tree.get_user(tree.get_uid(trader_state.item.uid).get('name')).get('used_stalls'))-1))
+ tree.remove_item_uid(trader_state.item.uid)
current_money = int(user_tree.get_user(seller).get("money"))
user_tree.get_user(seller).set("money", str(current_money + trader_state.item.price * trader_state.item.amount))
@@ -1092,7 +1261,7 @@ def main():
user_tree.get_user(trader_state.money).set('money', str(0))
commitMessage = "Money"
- sale_tree.save()
+ tree.save()
user_tree.save()
tradey.saveData(commitMessage)
@@ -1104,6 +1273,10 @@ def main():
logger.info(errorOccured)
shop_broadcaster.stop()
sys.exit(1)
+
+ elif packet.is_type(SMSG_PLAYER_STORAGE_CLOSE):
+ storage.reset()
+
else:
pass
diff --git a/net/packet_out.py b/net/packet_out.py
index 1d6e2d2..873f087 100644
--- a/net/packet_out.py
+++ b/net/packet_out.py
@@ -15,8 +15,11 @@ def whisper(nick, message):
def chat(text):
chat_packet = PacketOut(CMSG_CHAT_MESSAGE)
- mes = player_node.name + " : " + text
+ #TODO Find another automated manner to get ManaMarket name (previous didn't work either)
+ # Maybe a temp solution would be setting as a variable in config.py
+ mes = "ManaMarket"+" : "+text
chat_packet.write_int16(len(mes) + 4 + 1)
+ # chat_packet = PacketOut(CMSG_CHAT_MESSAGE)
chat_packet.write_string(mes, len(mes) + 1)
return str(chat_packet)
diff --git a/net/protocol.py b/net/protocol.py
index fc64767..c8f4a6e 100644
--- a/net/protocol.py
+++ b/net/protocol.py
@@ -24,6 +24,13 @@ SMSG_PLAYER_EQUIPMENT = 0x00a4
SMSG_PLAYER_STAT_UPDATE_1 = 0x00b0
SMSG_PLAYER_STAT_UPDATE_2 = 0x00b1
+SMSG_PLAYER_STORAGE_EQUIP = 0x00a6
+SMSG_PLAYER_STORAGE_ITEMS = 0x01f0
+SMSG_PLAYER_STORAGE_STATUS = 0x00f2
+SMSG_PLAYER_STORAGE_ADD = 0x00f4
+SMSG_PLAYER_STORAGE_REMOVE = 0x00f6
+SMSG_PLAYER_STORAGE_CLOSE = 0x00f8
+
SMSG_BEING_VISIBLE = 0x0078
SMSG_BEING_MOVE = 0x007b
SMSG_BEING_REMOVE = 0x0080
@@ -42,6 +49,10 @@ CMSG_PLAYER_CHANGE_DEST = 0x0085
CMSG_PLAYER_EMOTE = 0x00bf
SMSG_WALK_RESPONSE = 0x0087
+CMSG_MOVE_TO_STORAGE = 0x00f3
+CMSG_MOVE_FROM_STORAGE = 0x00f5
+CMSG_CLOSE_STORAGE = 0x00f7
+
CMSG_TRADE_REQUEST = 0x00e4
CMSG_TRADE_RESPONSE = 0x00e6
CMSG_TRADE_ITEM_ADD_REQUEST = 0x00e8
@@ -63,3 +74,6 @@ SMSG_ITEM_REMOVE = 0x00a1
inventory_offset = 2
storage_offset = 1
+MAX_STORAGE = 300
+MAX_INVENTORY = 99
+MAX_AMOUNT = 30000
diff --git a/storage.py b/storage.py
new file mode 100644
index 0000000..8afda66
--- /dev/null
+++ b/storage.py
@@ -0,0 +1,117 @@
+#!/usr/bin/python
+
+"""
+ Copyright 2011, Dipesh Amin <yaypunkrock@gmail.com>
+ Copyright 2011, Stefan Beller <stefanbeller@googlemail.com>
+
+ This file is part of tradey, a trading bot in the mana world
+ see www.themanaworld.org
+
+ Storage Access class by Fernanda Monteiro <crie.fernanda@gmail.com>
+"""
+
+from utils import ItemDB
+from net.packet_out import chat
+from net.protocol import *
+from net.packet import *
+import time
+import copy
+import mutex
+
+class Storage:
+ def __init__(self):
+ self.storage = {}
+ self.timer = 0
+ self.mapserv = 0
+ self.Open = mutex.mutex()
+
+ def reset(self):
+ self.Open.unlock()
+ self.timer = 0
+
+ def find_storage_index(self, item_id):
+ for item in self.storage:
+ if item > 1:
+ if self.storage[item].itemId == item_id:
+ return item
+ return -10 # Not found - bug somewhere!
+
+ def add_item(self, item):
+ if not item.itemId or item.amount <= 0:
+ return -10 # Not an item - something is messy
+
+ index = self.find_storage_index(item.itemId)
+ if ItemDB().getItem(item.itemId).type != 'equip-ammo' and 'equip':
+ if index != -10:
+ if (item.amount > MAX_AMOUNT - self.storage[index].amount):
+ return -10
+ self.storage[index].amount += item.amount
+ return index
+
+ index = len(self.storage)
+ self.storage[index] = item
+ self.storage[index].amount = item.amount
+ return index
+
+ def remove_item(self, index, amount):
+ if index in self.storage:
+ self.storage[index].amount -= amount
+ if self.storage[index].amount == 0:
+ del self.storage[index]
+
+ def check_storage(self, stack_tree, delisted_tree):
+ # Check the inventory state.
+ test_node = copy.deepcopy(self.storage)
+ for elem in stack_tree.root:
+ item_found = False
+ for item in test_node:
+ if int(elem.get('itemId')) == test_node[item].itemId \
+ and int(elem.get('amount')) <= test_node[item].amount:
+ test_node[item].amount -= int(elem.get('amount'))
+ if test_node[item].amount == 0:
+ del test_node[item]
+ item_found = True
+ break
+
+ if not item_found:
+ return "Server and client storage out of sync."
+
+ for elem in delisted_tree.root:
+ item_found = False
+ for item in test_node:
+ if int(elem.get('itemId')) == test_node[item].itemId \
+ and int(elem.get('amount')) <= test_node[item].amount:
+ test_node[item].amount -= int(elem.get('amount'))
+ if test_node[item].amount == 0:
+ del test_node[item]
+ item_found = True
+ break
+
+ if not item_found:
+ return "Server and client storage out of sync."
+
+ def storage_send(self, index, amount):
+ packet = PacketOut(CMSG_MOVE_TO_STORAGE)
+ packet.write_int16(index + inventory_offset)
+ packet.write_int32(amount)
+ self.mapserv.sendall(str(packet))
+ return 0
+
+ def storage_get(self, index, amount):
+ packet = PacketOut(CMSG_MOVE_FROM_STORAGE)
+ packet.write_int16(index + storage_offset)
+ packet.write_int32(amount)
+ self.mapserv.sendall(str(packet))
+ return 0
+
+ def storage_open(self):
+ self.timer = time.time()
+ self.mapserv.sendall(chat("@storage"))
+
+ def storage_close(self):
+ self.reset()
+ self.mapserv.sendall(str(PacketOut(CMSG_CLOSE_STORAGE)))
+
+if __name__ == '__main__':
+ print "Do not run this file directly. Run main.py"
+
diff --git a/tradey.py b/tradey.py
index 01f855e..e30cbc4 100644
--- a/tradey.py
+++ b/tradey.py
@@ -61,19 +61,20 @@ class UserTree:
class ItemTree:
def __init__(self):
- self.tree = ElementTree(file="data/sale.xml")
+ self.save_file = 'data/sale.xml'
+ self.tree = ElementTree(file=self.save_file)
self.root = self.tree.getroot()
self.u_id = set()
+ self.id_itter = 1
for elem in self.root:
self.u_id.add(int(elem.get("uid")))
def getId(self):
- id_itter = 1
- while id_itter in self.u_id:
- id_itter += 1
- self.u_id.add(id_itter)
- return id_itter
+ while self.id_itter in self.u_id:
+ self.id_itter += 1
+ self.u_id.add(self.id_itter)
+ return self.id_itter
def remove_id(self, uid):
# Free up used id's.
@@ -85,7 +86,6 @@ class ItemTree:
user.set("itemId", str(item_id))
user.set("price", str(price))
user.set("add_time", str(time.time()))
- user.set("relisted", str(0))
user.set("amount", str(amount))
user.set("uid", str(self.getId()))
self.save()
@@ -107,11 +107,78 @@ class ItemTree:
def save(self):
# Be sure to call save() after any changes to the tree.
- f = open('data/sale.xml', 'w')
+ f = open(self.save_file, 'w')
dom = xml.dom.minidom.parseString(clean_xml(tostring(self.root)))
f.write(dom.toprettyxml(' '))
f.close()
+class StackTree(ItemTree):
+ def __init__(self):
+ self.save_file = 'data/stack.xml'
+ self.tree = ElementTree(file=self.save_file)
+ self.root = self.tree.getroot()
+ self.u_id = set()
+ self.id_itter = 101
+ self.next_id = self.id_itter
+
+ for elem in self.root:
+ self.u_id.add(int(elem.get("uid")))
+
+ def add_item(self, name, item_id, amount, price):
+ user = SubElement(self.root, "item")
+ user.set("name", name)
+ user.set("itemId", str(item_id))
+ user.set("price", str(price))
+ user.set("amount", str(amount))
+ user.set("uid", str(self.getId()))
+ self.save()
+
+ # Here we take the oldest id put into stack.xml so we have FIFO
+ def get_next_id(self):
+ if len(self.u_id) == 0:
+ self.next_id = self.id_itter
+ return
+ else:
+ # TODO Here I'm handling rotation (gets a high number then
+ # back to 101). Any better manners to do this?
+ if min(self.u_id) - self.next_id < 0:
+ next_uid = self.next_id - min(self.u_id)
+ self.next_id = self.id_itter + next_uid + 1
+ else:
+ next_uid = min(self.u_id) - self.next_id
+ self.next_id += next_uid
+ return
+
+
+ def remove_item_uid(self, uid):
+ for elem in self.root:
+ if elem.get("uid") == str(uid):
+ self.root.remove(elem)
+ self.remove_id(uid)
+ self.save()
+ return 1
+ self.get_next_id()
+ return -10
+
+class DelistedTree(ItemTree):
+ def __init__(self):
+ self.save_file = 'data/delisted.xml'
+ self.tree = ElementTree(file=self.save_file)
+ self.root = self.tree.getroot()
+ self.u_id = set()
+ self.id_itter = 301
+
+ for elem in self.root:
+ self.u_id.add(int(elem.get("uid")))
+
+ def add_item(self, name, item_id, amount):
+ user = SubElement(self.root, "item")
+ user.set("name", name)
+ user.set("itemId", str(item_id))
+ user.set("amount", str(amount))
+ user.set("uid", str(self.getId()))
+ self.save()
+
def saveData(commitmessage = "commit"):
# This assumes the current working directory is the tradey directory.
os.chdir("data")
diff --git a/utils.py b/utils.py
index 52d51b6..6273bef 100644
--- a/utils.py
+++ b/utils.py
@@ -13,6 +13,7 @@ import time
import mutex
import threading
from net.packet_out import *
+from threading import _Timer
allowed_chars = "abcdefghijklmnoprstquvwxyzABCDEFGHIJKLMNOPRSTQUVWXYZ1234567890-_+=!@$%^&*();'<>,.?/~`| "
@@ -86,6 +87,15 @@ class ItemLog:
file_node.write(str(item_id)+" "+str(amount)+" "+str(price)+" "+str(time.time())+" "+name+"\n")
file_node.close()
+class DelistedLog:
+ def __init__(self):
+ self.log_file = 'data/logs/delisted.log'
+
+ def add_item(self, item_id, amount, name):
+ file_node = open(self.log_file, 'a')
+ file_node.write(str(item_id)+" "+str(amount)+" "+str(time.time())+" "+name+"\n")
+ file_node.close()
+
class TraderState:
""" Stores information regarding a trade request"""
def __init__(self):
@@ -129,5 +139,18 @@ class Broadcast:
self.Active = False
self.shop_broadcast.join()
+class CustomTimer(_Timer):
+ def __init__(self, interval, function, args=[], kwargs={}):
+ self._original_function = function
+ super(CustomTimer, self).__init__(interval, self._do_execute, args, kwargs)
+ self._result = None
+
+ def _do_execute(self, *a, **kw):
+ self._result = self._original_function(*a, **kw)
+
+ def join(self):
+ super(CustomTimer, self).join()
+ return self._result
+
if __name__ == '__main__':
print "Do not run this file directly. Run main.py"