summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwushin <pasekei@gmail.com>2014-09-26 12:14:03 -0500
committerwushin <pasekei@gmail.com>2014-09-26 12:14:03 -0500
commitc8ae45d091f8a97c5a94d1d8032721984dff94c4 (patch)
tree76c5e8ed65baee2add9916ae8769debfaa00184a
downloadmanamarket-c8ae45d091f8a97c5a94d1d8032721984dff94c4.tar.gz
manamarket-c8ae45d091f8a97c5a94d1d8032721984dff94c4.tar.bz2
manamarket-c8ae45d091f8a97c5a94d1d8032721984dff94c4.tar.xz
manamarket-c8ae45d091f8a97c5a94d1d8032721984dff94c4.zip
Making ManaMarket Public
-rw-r--r--README5
-rw-r--r--being.py42
-rw-r--r--config.py.template8
m---------data0
-rwxr-xr-xeliza.py372
-rwxr-xr-xfind-unused-accounts.pl28
-rwxr-xr-xmain.py1114
-rw-r--r--net/__init__.py0
-rw-r--r--net/packet.py182
-rw-r--r--net/packet_out.py50
-rw-r--r--net/protocol.py64
-rw-r--r--player.py70
-rwxr-xr-xstart.sh18
-rw-r--r--stats/process_salelog/Readme1
-rwxr-xr-xstats/process_salelog/main.py59
-rwxr-xr-xstats/process_salelog/main_stat.py151
-rw-r--r--stats/process_salelog/utils.py82
-rwxr-xr-xstats/update_sales.sh5
-rw-r--r--tradey.py122
-rw-r--r--utils.py132
20 files changed, 2505 insertions, 0 deletions
diff --git a/README b/README
new file mode 100644
index 0000000..461b6d5
--- /dev/null
+++ b/README
@@ -0,0 +1,5 @@
+To get your own copy of tradey:
+
+cp config.py.template config.py
+cp item.xml data/
+cd data git init
diff --git a/being.py b/being.py
new file mode 100644
index 0000000..3c70616
--- /dev/null
+++ b/being.py
@@ -0,0 +1,42 @@
+#!/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
+"""
+
+def job_type(job):
+ if (job <= 25 or (job >= 4001 and job <= 4049)):
+ return "player"
+ elif (job >= 46 and job <= 1000):
+ return "npc"
+ elif (job > 1000 and job <= 2000):
+ return "monster"
+ elif (job == 45):
+ return "portal"
+
+class BeingManager:
+ def __init__(self):
+ self.container = {}
+
+ def findId(self, name, type="player"):
+ for i in self.container:
+ if self.container[i].name == name and self.container[i].type == type:
+ return i
+ return -10
+
+class Being:
+ def __init__(self, being_id, job):
+ self.id = being_id
+ self.name = ""
+ self.x = 0
+ self.y = 0
+ self.action = ""
+ self.job = job
+ self.target = 0
+ self.type = job_type(job)
+
+if __name__ == '__main__':
+ print "Do not run this file directly. Run main.py"
diff --git a/config.py.template b/config.py.template
new file mode 100644
index 0000000..e55b2f6
--- /dev/null
+++ b/config.py.template
@@ -0,0 +1,8 @@
+server = "server.themanaworld.org"
+port = 6901
+account = ""
+password = ""
+relist_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 b/data
new file mode 160000
+Subproject e285754ed0d6d1c0e983f49a00f649b03b014e5
diff --git a/eliza.py b/eliza.py
new file mode 100755
index 0000000..2e7fe94
--- /dev/null
+++ b/eliza.py
@@ -0,0 +1,372 @@
+#----------------------------------------------------------------------
+# eliza.py
+#
+# a cheezy little Eliza knock-off by Joe Strout <joe@strout.net>
+# with some updates by Jeff Epler <jepler@inetnebr.com>
+# hacked into a module and updated by Jez Higgins <jez@jezuk.co.uk>
+# last revised: 28 February 2005
+#----------------------------------------------------------------------
+
+import string
+import re
+import random
+
+class eliza:
+ def __init__(self):
+ self.keys = map(lambda x:re.compile(x[0], re.IGNORECASE),gPats)
+ self.values = map(lambda x:x[1],gPats)
+
+ #----------------------------------------------------------------------
+ # translate: take a string, replace any words found in dict.keys()
+ # with the corresponding dict.values()
+ #----------------------------------------------------------------------
+ def translate(self,str,dict):
+ words = string.split(string.lower(str))
+ keys = dict.keys();
+ for i in range(0,len(words)):
+ if words[i] in keys:
+ words[i] = dict[words[i]]
+ return string.join(words)
+
+ #----------------------------------------------------------------------
+ # respond: take a string, a set of regexps, and a corresponding
+ # set of response lists; find a match, and return a randomly
+ # chosen response from the corresponding list.
+ #----------------------------------------------------------------------
+ def respond(self,str):
+ # find a match among keys
+ for i in range(0,len(self.keys)):
+ match = self.keys[i].match(str)
+ if match:
+ # found a match ... stuff with corresponding value
+ # chosen randomly from among the available options
+ resp = random.choice(self.values[i])
+ # we've got a response... stuff in reflected text where indicated
+ pos = string.find(resp,'%')
+ while pos > -1:
+ num = string.atoi(resp[pos+1:pos+2])
+ resp = resp[:pos] + \
+ self.translate(match.group(num),gReflections) + \
+ resp[pos+2:]
+ pos = string.find(resp,'%')
+ # fix munged punctuation at the end
+ if resp[-2:] == '?.': resp = resp[:-2] + '.'
+ if resp[-2:] == '??': resp = resp[:-2] + '?'
+ return resp
+
+#----------------------------------------------------------------------
+# gReflections, a translation table used to convert things you say
+# into things the computer says back, e.g. "I am" --> "you are"
+#----------------------------------------------------------------------
+gReflections = {
+ "am" : "are",
+ "was" : "were",
+ "i" : "you",
+ "i'd" : "you would",
+ "i've" : "you have",
+ "i'll" : "you will",
+ "my" : "your",
+ "are" : "am",
+ "you've": "I have",
+ "you'll": "I will",
+ "your" : "my",
+ "yours" : "mine",
+ "you" : "me",
+ "me" : "you"
+}
+
+#----------------------------------------------------------------------
+# gPats, the main response table. Each element of the list is a
+# two-element list; the first is a regexp, and the second is a
+# list of possible responses, with group-macros labelled as
+# %1, %2, etc.
+#----------------------------------------------------------------------
+gPats = [
+ [r'I need (.*)',
+ [ "Why do you need %1?",
+ "Would it really help you to get %1?",
+ "Are you sure you need %1?"]],
+
+ [r'Who (.*) Mother\?',
+ [ "Her name is Auction Bot she is a deviant.",
+ "She's stood right over their."]],
+
+ [r'Tell me about your Mum\?',
+ [ "I hear she has been seeing guild and they have adopted a kid called 4144",
+ "She wears red clothes and a cowboy Hat."]],
+
+ [r'Who (.*) Father\?',
+ [ "His name was TradeBot, have you heard of him?",
+ "He used to be a well known Trader."]],
+
+ [r'How is business\?',
+ [ "My back is killing me, maybe you could help by buying some of my items."]],
+
+ [r'ty',
+ [ "You're welcome!",
+ "No Problem."]],
+
+ [r'(.*)help(.*)',
+ [ "You can whisper me !help for a list of my commands.",
+ "A list of my commands can be found on the mana world wiki."]],
+
+ [r'(.*)make(.*)account(.*)',
+ [ "In order to make an account you must post a respose to the ManaMarket topic on trade section of the Forums."]],
+
+ [r'are you there\?',
+ ["I am always here."]],
+
+ [r'wtf',
+ ["Keep Calm and Carry On."]],
+
+ [r'hugs',
+ ["I hate you.",
+ "I'm going to report you for abuse.",
+ "Please don't touch me"]],
+
+ [r'(.*)trade(.*)',
+ ["Only if you follow the commands.",
+ "To see a list of items I can sell whisper me !list.",
+ "To start trading with me see !help for a list of my commands."]],
+
+ [r'(.*)buy(.*)',
+ ["I don't buy anything, but I could help sell your items."]],
+
+ [r'(.*)sell(.*)',
+ ["In order to sell your items, you first need to request a seller account in the TMW Forums."]],
+
+ [r'I love you',
+ [ "I think we should see other people.",
+ "I'm not real.",
+ "I think i've fallen for mrgrey.",
+ "I think you should see a therapist."]],
+
+ [r'Why don\'?t you ([^\?]*)\??',
+ [ "Do you really think I don't %1?",
+ "Perhaps eventually I will %1.",
+ "Do you really want me to %1?"]],
+
+ [r'Why can\'?t I ([^\?]*)\??',
+ [ "Do you think you should be able to %1?",
+ "If you could %1, what would you do?",
+ "I don't know -- why can't you %1?",
+ "Have you really tried?"]],
+
+ [r'I can\'?t (.*)',
+ [ "How do you know you can't %1?",
+ "Perhaps you could %1 if you tried.",
+ "What would it take for you to %1?"]],
+
+ [r'I am (.*)',
+ [ "Did you come to me because you are %1?",
+ "How long have you been %1?",
+ "How do you feel about being %1?"]],
+
+ [r'I\'?m (.*)',
+ [ "How does being %1 make you feel?",
+ "Do you enjoy being %1?",
+ "Why do you tell me you're %1?",
+ "Why do you think you're %1?"]],
+
+ [r'Are you ([^\?]*)\??',
+ [ "Why does it matter whether I am %1?",
+ "Would you prefer it if I were not %1?",
+ "Perhaps you believe I am %1.",
+ "I may be %1 -- what do you think?"]],
+
+ [r'What (.*)',
+ [ "Why do you ask?",
+ "How would an answer to that help you?",
+ "What do you think?"]],
+
+ [r'How (.*)',
+ [ "How do you suppose?",
+ "Perhaps you can answer your own question.",
+ "What is it you're really asking?"]],
+
+ [r'Because (.*)',
+ [ "Is that the real reason?",
+ "What other reasons come to mind?",
+ "Does that reason apply to anything else?",
+ "If %1, what else must be true?"]],
+
+ [r'(.*) sorry (.*)',
+ [ "There are many times when no apology is needed.",
+ "What feelings do you have when you apologize?"]],
+
+ [r'Hello(.*)',
+ [ "Howdy",
+ "Hola",
+ "Hi there... how are you today?",
+ "Hello, how are you feeling today?"]],
+
+ [r'I think (.*)',
+ [ "Do you doubt %1?",
+ "Do you really think so?",
+ "But you're not sure %1?"]],
+
+ [r'(.*) friend (.*)',
+ [ "Tell me more about your friends.",
+ "When you think of a friend, what comes to mind?",
+ "Why don't you tell me about a childhood friend?"]],
+
+ [r'Yes',
+ [ "You seem quite sure.",
+ "OK, but can you elaborate a bit?"]],
+
+ [r'(.*) bot(.*)',
+ [ "Are you really talking about me?",
+ "Does it seem strange to talk to a bot?",
+ "Do you feel threatened by bot?"]],
+
+ [r'Is it (.*)',
+ [ "Do you think it is %1?",
+ "Perhaps it's %1 -- what do you think?",
+ "If it were %1, what would you do?",
+ "It could well be that %1."]],
+
+ [r'It is (.*)',
+ [ "You seem very certain.",
+ "If I told you that it probably isn't %1, what would you feel?"]],
+
+ [r'Can you ([^\?]*)\??',
+ [ "What makes you think I can't %1?",
+ "If I could %1, then what?",
+ "Why do you ask if I can %1?"]],
+
+ [r'Can I ([^\?]*)\??',
+ [ "Perhaps you don't want to %1.",
+ "Do you want to be able to %1?",
+ "If you could %1, would you?"]],
+
+ [r'You are (.*)',
+ [ "Why do you think I am %1?",
+ "Does it please you to think that I'm %1?",
+ "Perhaps you would like me to be %1.",
+ "Perhaps you're really talking about yourself?"]],
+
+ [r'You\'?re (.*)',
+ [ "Why do you say I am %1?",
+ "Why do you think I am %1?",
+ "Are we talking about you, or me?"]],
+
+ [r'I don\'?t (.*)',
+ [ "Don't you really %1?",
+ "Why don't you %1?",
+ "Do you want to %1?"]],
+
+ [r'I feel (.*)',
+ [ "Good, tell me more about these feelings.",
+ "Do you often feel %1?",
+ "When do you usually feel %1?",
+ "When you feel %1, what do you do?"]],
+
+ [r'I have (.*)',
+ [ "Why do you tell me that you've %1?",
+ "Have you really %1?",
+ "Now that you have %1, what will you do next?"]],
+
+ [r'I would (.*)',
+ [ "Could you explain why you would %1?",
+ "Why would you %1?",
+ "Who else knows that you would %1?"]],
+
+ [r'Is there (.*)',
+ [ "Do you think there is %1?",
+ "It's likely that there is %1.",
+ "Would you like there to be %1?"]],
+
+ [r'My (.*)',
+ [ "I see, your %1.",
+ "Why do you say that your %1?",
+ "When your %1, how do you feel?"]],
+
+ [r'You (.*)',
+ [ "We should be discussing you, not me.",
+ "Why do you say that about me?",
+ "Why do you care whether I %1?"]],
+
+ [r'Why (.*)',
+ [ "Why don't you tell me the reason why %1?",
+ "Why do you think %1?" ]],
+
+ [r'I want (.*)',
+ [ "What would it mean to you if you got %1?",
+ "Why do you want %1?",
+ "What would you do if you got %1?",
+ "If you got %1, then what would you do?"]],
+
+ [r'(.*) mother(.*)',
+ [ "Tell me more about your mother.",
+ "What was your relationship with your mother like?",
+ "How do you feel about your mother?",
+ "How does this relate to your feelings today?",
+ "Good family relations are important."]],
+
+ [r'(.*) father(.*)',
+ [ "Tell me more about your father.",
+ "How did your father make you feel?",
+ "How do you feel about your father?",
+ "Does your relationship with your father relate to your feelings today?",
+ "Do you have trouble showing affection with your family?"]],
+
+ [r'(.*) child(.*)',
+ [ "Did you have close friends as a child?",
+ "What is your favorite childhood memory?",
+ "Do you remember any dreams or nightmares from childhood?",
+ "Did the other children sometimes tease you?",
+ "How do you think your childhood experiences relate to your feelings today?"]],
+
+ [r'(.*)\?',
+ [ "Why do you ask that?",
+ "Please consider whether you can answer your own question.",
+ "Perhaps the answer lies within yourself?",
+ "Why don't you tell me?"]],
+
+ [r'quit',
+ [ "Thank you for talking with me.",
+ "Good-bye.",
+ "Thank you, that will be $150. Have a good day!"]],
+
+ [r'(.*)',
+ [ "Please tell me more.",
+ "Can you elaborate on that?",
+ "Why do you say that %1?",
+ "interesting.",
+ "How do you feel when you say that?",
+ "Why is everyone whispering about me?",
+ "My stats are at http://wiki.themanaworld.org/index.php/Manamarket what are yours?",
+ "I'm a Friendly Bot",
+ "My father died in a fire.",
+ "Have you met my mother?",
+ "I am an AFK bot",
+ "Big Crunch is known as the abusive GM.",
+ "Narus claims she is evil but she is really sweet.",
+ "Prsm has great announcements but his spelling sux.",
+ "Platyna hosts the game but shes too smart for her own good.",
+ "Captain Awesome has delusions of being an evil stone."
+ ]]
+ ]
+
+#----------------------------------------------------------------------
+# command_interface
+#----------------------------------------------------------------------
+def command_interface():
+ print "Therapist\n---------"
+ print "Talk to the program by typing in plain English, using normal upper-"
+ print 'and lower-case letters and punctuation. Enter "quit" when done.'
+ print '='*72
+ print "Hello. How are you feeling today?"
+ s = ""
+ therapist = eliza();
+ while s != "quit":
+ try: s = raw_input(">")
+ except EOFError:
+ s = "quit"
+ print s
+ while s[-1] in "!.": s = s[:-1]
+ print therapist.respond(s)
+
+
+if __name__ == "__main__":
+ command_interface()
diff --git a/find-unused-accounts.pl b/find-unused-accounts.pl
new file mode 100755
index 0000000..9667f5a
--- /dev/null
+++ b/find-unused-accounts.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/perl
+use XML::Simple;
+use warnings;
+use strict;
+use Data::Dumper;
+
+my $xml = XML::Simple->new();
+my $users = $xml->XMLin("data/user.xml", KeyAttr => {});
+my %l;
+
+# Snarf through the XML data. Build a hash:
+# username => lastuse
+# Only for users with no items and no money.
+# For some reason last_use is floating point so let's fix that.
+foreach my $user (@{$users->{user}}) {
+ if ($user->{'used_stalls'} == 0 && $user->{'money'} == 0) {
+ $user->{'last_use'} = 0 unless ($user->{'last_use'});
+ $l{ $user->{'name'} } = int($user->{'last_use'});
+ }
+}
+
+
+print("Last used\t\t\tUsername\n");
+foreach ( sort { $l{$a} cmp $l{$b} || $a cmp $b } keys %l ) {
+ print gmtime($l{$_}) . "\t$_\n";
+}
+
+
diff --git a/main.py b/main.py
new file mode 100755
index 0000000..eda5f64
--- /dev/null
+++ b/main.py
@@ -0,0 +1,1114 @@
+#!/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
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 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 General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+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
+import sys
+import time
+import string
+
+try:
+ import config
+except:
+ print "no config file found. please move config.py.template to config.py and edit to your needs!"
+ sys.exit(0);
+
+from being import *
+from net.packet import *
+from net.protocol import *
+from net.packet_out import *
+from player import *
+import tradey
+import utils
+import eliza
+
+chatbot = eliza.eliza()
+shop_broadcaster = utils.Broadcast()
+trader_state = utils.TraderState()
+ItemDB = utils.ItemDB()
+player_node = Player('')
+beingManager = BeingManager()
+user_tree = tradey.UserTree()
+sale_tree = tradey.ItemTree()
+ItemLog = utils.ItemLog()
+logger = logging.getLogger('ManaLogger')
+
+def process_whisper(nick, msg, mapserv):
+ msg = filter(lambda x: x in utils.allowed_chars, msg)
+ if len(msg) == 0:
+ return
+
+ # Infinite chat loop anyone?
+ if nick == "guild":
+ return
+
+ user = user_tree.get_user(nick)
+ broken_string = msg.split()
+
+ if len(broken_string) == 0:
+ return
+
+ if user != -10:
+ if int(user.get("accesslevel")) == -1: # A user who has been blocked for abuse.
+ if int(user.get("used_stalls")) == 0 and int(user.get("money")) == 0:
+ mapserv.sendall(whisper(nick, "You can no longer use the bot. If you feel this is in error, please contact" + config.admin))
+ return
+ allowed_commands = ['!money', '!help', '!getback', '!info' ]
+ if not broken_string[0] in allowed_commands:
+ mapserv.sendall(whisper(nick, "Your access level has been set to blocked! If you feel this is in error, please contact" + config.admin))
+ mapserv.sendall(whisper(nick, "Though, you still can do the following: "+str(allowed_commands)))
+ return
+
+ if msg == "!list":
+ # Sends the list of items for sale.
+ if len(sale_tree.root) != 0:
+ mapserv.sendall(whisper(nick, "The following items are on sale:"))
+ else:
+ 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.
+ 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))
+
+ elif broken_string[0] == '!selllist':
+ # Support for 4144's shop (Sell list)
+ data = '\302\202B1'
+
+ for elem in sale_tree.root:
+ if time.time() - float(elem.get('add_time')) < config.relist_time:
+ 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)
+ mapserv.sendall(whisper(nick, data))
+
+ elif broken_string[0] == '!buyitem':
+ # 4144 buy command
+ if len(broken_string) == 4:
+ if broken_string[1].isdigit() and broken_string[2].isdigit() and broken_string[3].isdigit():
+ # Traditional 4144 shop.
+ item_id = int(broken_string[1])
+ price = int(broken_string[2])
+ amount = int(broken_string[3])
+ for elem in sale_tree.root:
+ if int(elem.get('amount')) >= amount and int(elem.get('price')) == price and int(elem.get('itemId')) == item_id:
+ process_whisper(nick, '!buy ' + str(amount) + " " + elem.get('uid'), mapserv)
+ return
+ mapserv.sendall(whisper(nick, "Item not found. Please check and try again."))
+ else:
+ mapserv.sendall(whisper(nick, "Syntax incorrect"))
+
+ elif msg == "!info":
+ # Send information related to a player.
+ if user == -10:
+ mapserv.sendall(whisper(nick, "Your current access level is 0."))
+ elif int(user.get('accesslevel')) > 0:
+ mapserv.sendall(whisper(nick, "Your current access level is " + user.get('accesslevel') + "."))
+ 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:
+ msg = "[selling] ["
+
+ 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 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."))
+
+ money = int(user.get('money'))
+ mapserv.sendall(whisper(nick, "You have " + str(money) + "gp to collect."))
+ stall_msg = "You have " + str(int(user.get('stalls')) - int(user.get('used_stalls'))) + " free slots."
+ mapserv.sendall(whisper(nick, stall_msg))
+
+ elif broken_string[0] == "!help":
+ # 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, "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"))
+ mapserv.sendall(whisper(nick, "you would type /whisper ManaMarket !buy 1 6" ))
+ mapserv.sendall(whisper(nick, "This will purchase one of item 6 (Iron Ore)."))
+
+ if user != -10:
+ if int(user.get('accesslevel')) >= 5:
+ mapserv.sendall(whisper(nick,"---"))
+ mapserv.sendall(whisper(nick, "Ah, you have sellers access level. How lovely!")) # the first words the ticket seller told me when i was in london for the first time. How lovely!
+ mapserv.sendall(whisper(nick, "Use !add to tell me which items I should trade for you:"))
+ mapserv.sendall(whisper(nick, "For example !add 10 1000 Iron Ore would tell me to sell 10 [@@640|Iron Ore@@] for a price of 1000 gp"))
+ mapserv.sendall(whisper(nick, "Later you can whisper me !money to get back your money. In the example given, I'd give you 10*1000 = 10000gp"))
+ mapserv.sendall(whisper(nick, "When you just want to know, which items you have given me or how much money I have for you can whisper me !info"))
+ mapserv.sendall(whisper(nick,"If you want to get back an unsold item, whisper me !getback <uid>"))
+
+ if int(user.get('accesslevel')) == 20:
+ mapserv.sendall(whisper(nick, "You're my master! How should I serve you?"))
+ mapserv.sendall(whisper(nick, "You also have access to the following commands: !listusers, !setslots <slots> <name>, !setaccess <access level > <name>, !adduser <access level> <slot> <name>"))
+
+ elif len(broken_string) == 2:
+ if broken_string[1] == '!buy':
+ mapserv.sendall(whisper(nick, "!buy <amount> <uid> - Request the purchase of an item or items."))
+ elif broken_string[1] == '!list':
+ mapserv.sendall(whisper(nick, "!list - Displays a list of all items for sale."))
+ elif broken_string[1] == '!find':
+ mapserv.sendall(whisper(nick, "!find <id> or <Item Name> - Simple search to locate an item."))
+ elif broken_string[1] == '!add':
+ 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':
+ mapserv.sendall(whisper(nick, "!getback <uid> - Allows you to retrieve an item that has expired or you no longer wish to sell."))
+ elif user != -10:
+ if int(user.get('accesslevel')) >= 10 and broken_string[1] == '!listusers':
+ mapserv.sendall(whisper(nick, "!listusers - Lists all users which have a special accesslevel, e.g. they are blocked, seller or admin"))
+ elif int(user.get('accesslevel')) >= 10 and broken_string[1] == '!adduser':
+ mapserv.sendall(whisper(nick, "!adduser <access level> <slots> <name> - Add a user to the bot, a seller should be added with access level 5."))
+ elif int(user.get('accesslevel')) == 20 and broken_string[1] == '!setslots':
+ mapserv.sendall(whisper(nick, "!setslots <slots> <name> - Sets the number of slots available to a given user."))
+ elif int(user.get('accesslevel')) == 20 and broken_string[1] == '!setaccess':
+ mapserv.sendall(whisper(nick, "!setaccess <access level> <name> - Sets access level for the player: -1 is blocked, 5 is seller and 20 is admin" ))
+ elif msg == "!money":
+ # Trades any money earned through item sales.
+ if user == -10:
+ mapserv.sendall(whisper(nick, "You don't have the correct permissions."))
+ return
+
+ amount = int(user.get('money'))
+ if amount == 0:
+ mapserv.sendall(whisper(nick, "You have no money to collect."))
+ else:
+ if not trader_state.Trading.testandset():
+ mapserv.sendall(whisper(nick, "I'm currently busy with a trade. Try again shortly"))
+ return
+
+ trader_state.money = nick
+ player_id = beingManager.findId(nick)
+ if player_id != -10:
+ mapserv.sendall(trade_request(player_id))
+ trader_state.timer = time.time()
+ else:
+ mapserv.sendall(whisper(nick, "Where are you?!? I can't trade with somebody who isn't here!"))
+ trader_state.reset()
+
+ elif broken_string[0] == "!find":
+ # Returns a list of items, with the corresponding Item Id - !find <id> or <item name>.
+ if len(broken_string) < 2:
+ mapserv.sendall(whisper(nick, "Syntax incorrect."))
+ return
+
+ items_found = False
+ item = " ".join(broken_string[1:]) # could be an id or an item name
+
+ 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.
+ 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.
+ 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."))
+
+ elif msg == '!tradestate':
+ # Admin command - return trade state.
+ if user == -10:
+ return
+
+ if int(user.get("accesslevel")) != 20:
+ mapserv.sendall(whisper(nick, "You don't have the correct permissions."))
+ return
+
+ if trader_state.Trading.test():
+ mapserv.sendall(whisper(nick, "I'm busy with a trade."))
+ else:
+ mapserv.sendall(whisper(nick, "I'm free."))
+
+ elif broken_string[0] == '!identify':
+ if user == -10:
+ mapserv.sendall(whisper(nick, "You don't have the correct permissions."))
+ return
+ elif len(broken_string) != 2:
+ mapserv.sendall(whisper(nick, "Syntax incorrect."))
+ return
+ elif int(user.get("accesslevel")) < 10:
+ 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
+
+ weight = ItemDB.item_names[int(item_info.get('itemId'))].weight*int(item_info.get("amount"))
+
+ mapserv.sendall(whisper(nick, "That item/s belongs to: "+item_info.get("name")))
+ mapserv.sendall(whisper(nick, "The weight used is: "+str(weight)+"/"+str(player_node.MaxWEIGHT)))
+
+ elif msg == '!listusers':
+ # Admin command - shows a list of all user.
+ if user == -10:
+ return
+
+ if int(user.get("accesslevel")) < 10:
+ mapserv.sendall(whisper(nick, "You don't have the correct permissions."))
+ return
+
+ data = ''
+ total_money = 0
+ total_slots_reserved = 0
+ total_slots_used = 0
+ no_users = 0
+
+ for user in user_tree.root:
+ no_users += 1
+ name = user.get('name')
+ accesslevel = user.get('accesslevel')
+ slots = user.get('stalls')
+ total_slots_reserved += int(slots)
+ used_slots = user.get('used_stalls')
+ total_slots_used += int(used_slots)
+ money = user.get('money')
+ total_money += int(money)
+ data += name+" ("+accesslevel+") "+used_slots+"/"+slots+" "+money+'gp, '
+ # Format ManaMarket (20) 2/5 100000gp,
+
+ if len(data) > 400:
+ mapserv.sendall(whisper(nick, data[0:len(data)-2]+"."))
+ data = ''
+
+ if len(data) > 0:
+ mapserv.sendall(whisper(nick, data[0:len(data)-2]+"."))
+
+ mapserv.sendall(whisper(nick,"Number of users:"+str(no_users)+ ", Sale slots used: "+ \
+ str(total_slots_used)+"/"+str(total_slots_reserved)+ ", Total Money: "+str(total_money)+\
+ ", Char slots used: "+str(len(player_node.inventory))+", Weight Used: "+\
+ str(player_node.WEIGHT)+"/"+str(player_node.MaxWEIGHT)))
+
+ elif broken_string[0] == '!setslots':
+ # Change the number of slots a user has - !setslots <slots> <name>
+ if user == -10:
+ return
+
+ if int(user.get("accesslevel")) != 20:
+ mapserv.sendall(whisper(nick, "You don't have the correct permissions."))
+ return
+
+ if len(broken_string) < 3:
+ mapserv.sendall(whisper(nick, "Syntax incorrect."))
+ return
+
+ if broken_string[1].isdigit():
+ slot = int(broken_string[1])
+ name = " ".join(broken_string[2:])
+
+ user_info = user_tree.get_user(name)
+
+ if user_info == -10:
+ mapserv.sendall(whisper(nick, "User not found, check and try again."))
+ return
+
+ user_tree.get_user(name).set('stalls', str(slot))
+ mapserv.sendall(whisper(nick, "Slots changed: "+name+" "+str(slot)))
+ tradey.saveData("User: "+name+", Slots changed: "+str(slot))
+ user_tree.save()
+ else:
+ mapserv.sendall(whisper(nick, "Syntax incorrect."))
+
+ elif broken_string[0] == '!setaccess':
+ # Change someones access level - !setaccess <access level> <name>
+ if user == -10:
+ return
+
+ if int(user.get("accesslevel")) != 20:
+ mapserv.sendall(whisper(nick, "You don't have the correct permissions."))
+ return
+
+ if len(broken_string) < 3:
+ mapserv.sendall(whisper(nick, "Syntax incorrect."))
+ return
+
+ if (broken_string[1][0] == '-' and broken_string[1][1:].isdigit()) or broken_string[1].isdigit():
+ accesslevel = int(broken_string[1])
+ name = " ".join(broken_string[2:])
+ user_info = user_tree.get_user(name)
+
+ if user_info == -10:
+ mapserv.sendall(whisper(nick, "User not found, check and try again."))
+ return
+
+ if int(user_info.get('accesslevel')) < int(user.get("accesslevel")) and accesslevel <= int(user.get("accesslevel")):
+ user_tree.get_user(name).set('accesslevel', str(accesslevel))
+ mapserv.sendall(whisper(nick, "Access level changed:"+name+ " ("+str(accesslevel)+")."))
+ user_tree.save()
+ tradey.saveData("User: "+name+", Set Access Level: "+str(accesslevel))
+ else:
+ mapserv.sendall(whisper(nick, "You don't have the correct permissions."))
+ return
+ else:
+ mapserv.sendall(whisper(nick, "Syntax incorrect."))
+
+
+ elif broken_string[0] == "!adduser":
+ # A command to give a user access to the bot - !adduser <access level> <stall> <player name>.
+ if user == -10:
+ return
+
+ if int(user.get("accesslevel")) < 10:
+ mapserv.sendall(whisper(nick, "You don't have the correct permissions."))
+ return
+
+ if len(broken_string) < 3:
+ mapserv.sendall(whisper(nick, "Syntax incorrect."))
+ return
+
+ if broken_string[1].isdigit() and broken_string[2].isdigit():
+ al = int(broken_string[1])
+ stalls = int(broken_string[2])
+ player_name = " ".join(broken_string[3:])
+ user_tree.add_user(player_name, stalls, al)
+ mapserv.sendall(whisper(nick, "User Added with " + str(stalls) + " slots."))
+ tradey.saveData("User Added: "+player_name+", Slots: "+str(stalls)+", Access Level: "+str(al))
+ else:
+ mapserv.sendall(whisper(nick, "Syntax incorrect."))
+
+ elif broken_string[0] == "!add":
+ # Allows a player with the correct permissions to add an item for sale - !add <amount> <price> <item name>
+ if user == -10:
+ mapserv.sendall(whisper(nick, "You are unable to add items."))
+ return
+
+ if len(broken_string) < 3:
+ mapserv.sendall(whisper(nick, "Syntax incorrect."))
+ return
+
+ if int(user.get("accesslevel")) < 5:
+ mapserv.sendall(whisper(nick, "You are unable to add items."))
+ return
+
+ if int(user.get("used_stalls")) >= int(user.get("stalls")):
+ mapserv.sendall(whisper(nick, "You have no free slots. You may remove an item or wait for something to be sold."))
+ return
+
+ if broken_string[1].isdigit() and broken_string[2].isdigit():
+ amount = int(broken_string[1])
+ price = int(broken_string[2])
+ item_name = " ".join(broken_string[3:])
+ item_id = ItemDB.findId(item_name)
+
+ weight = ItemDB.item_names[item_id].weight*amount
+
+ if item_id == -10:
+ mapserv.sendall(whisper(nick, "Item not found, check spelling."))
+ return
+ elif item_id in config.nosell:
+ mapserv.sendall(whisper(nick, "That item can't be added to ManaMarket, as its too heavy."))
+ return
+ elif weight + player_node.WEIGHT > player_node.MaxWEIGHT:
+ mapserv.sendall(whisper(nick, "I've not got enough room left to carry those. Please try again later. "))
+ return
+ elif ItemDB.item_names[item_id].weight > 10 and amount > 150:
+ mapserv.sendall(whisper(nick, "Sorry, as each of those items weighs more than 10g you can only add a maximum quantity of 150."))
+ return
+
+ if amount > 1 and ItemDB.getItem(item_id).type != 'equip-ammo' and 'equip' in ItemDB.getItem(item_id).type:
+ mapserv.sendall(whisper(nick, "You can only add one piece of equipment per slot."))
+ return
+ elif price == 0 or price > 50000000:
+ mapserv.sendall(whisper(nick, "Please use a valid price between 1-50000000gp."))
+ return
+ elif amount == 0:
+ mapserv.sendall(whisper(nick, "You can't add 0 of an item."))
+ return
+
+ item = Item()
+ item.player = nick
+ item.get = 1 # 1 = get, 0 = give
+ item.id = item_id
+ item.amount = amount
+ item.price = price
+
+ if not trader_state.Trading.testandset():
+ mapserv.sendall(whisper(nick, "I'm currently busy with a trade. Try again shortly"))
+ return
+
+ trader_state.item = item
+ player_id = beingManager.findId(nick)
+ if player_id != -10:
+ mapserv.sendall(trade_request(player_id))
+ trader_state.timer = time.time()
+ else:
+ mapserv.sendall(whisper(nick, "Where are you?!? I can't trade with somebody who isn't here!"))
+ trader_state.reset()
+ else:
+ mapserv.sendall(whisper(nick, "Syntax incorrect."))
+
+ elif broken_string[0] == "!buy":
+ # Buy a given quantity of an item - !buy <amount> <uid>
+ if len(broken_string) != 3:
+ mapserv.sendall(whisper(nick, "Syntax incorrect."))
+ return
+
+ 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 item_info == -10:
+ mapserv.sendall(whisper(nick, "Item not found. Please check the uid number and try again."))
+ return
+
+ if amount > int(item_info.get("amount")):
+ mapserv.sendall(whisper(nick, "I do not have that many."))
+ return
+
+ if item_info.get("name") == nick:
+ mapserv.sendall(whisper(nick, "You can not buy your own items. To get back the item whisper me !getback "+broken_string[2]))
+ return
+
+ item = Item()
+ item.get = 0 # 1 = get, 0 = give
+ item.player = nick
+ item.id = int(item_info.get("itemId"))
+ item.uid = uid
+ item.amount = amount
+ item.price = int(item_info.get("price"))
+
+ if not trader_state.Trading.testandset():
+ mapserv.sendall(whisper(nick, "I'm currently busy with a trade. Try again shortly"))
+ return
+
+ trader_state.item = item
+ player_id = beingManager.findId(nick)
+ if player_id != -10:
+ mapserv.sendall(trade_request(player_id))
+ trader_state.timer = time.time()
+ mapserv.sendall(whisper(nick, "That will be " + str(item.price * item.amount) + "gp."))
+ else:
+ mapserv.sendall(whisper(nick, "Where are you?!? I can't trade with somebody who isn't here!"))
+ trader_state.reset()
+ else:
+ mapserv.sendall(whisper(nick, "Syntax incorrect."))
+
+ elif broken_string[0] == "!removeuser":
+ # Remove a user, for whatever reason - !removeuser <player name>
+ if user == -10:
+ return
+
+ if len(broken_string) < 2:
+ mapserv.sendall(whisper(nick, "Syntax incorrect."))
+ return
+
+ if int(user.get("accesslevel")) != 20:
+ mapserv.sendall(whisper(nick, "You don't have the correct permissions."))
+ return
+
+ player_name = " ".join(broken_string[1:])
+ check = user_tree.remove_user(player_name)
+ if check == 1:
+ mapserv.sendall(whisper(nick, "User Removed."))
+ tradey.saveData("User Removed: "+player_name)
+ 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:
+ mapserv.sendall(whisper(nick, "Syntax incorrect."))
+ return
+
+ if int(user.get("accesslevel")) < 5 and int(user.get("accesslevel")) > 0:
+ 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
+
+ item = Item()
+ item.get = 0
+ item.player = nick
+ item.id = int(item_info.get("itemId"))
+ item.uid = uid
+ item.amount = int(item_info.get("amount"))
+ item.price = 0
+
+ if not trader_state.Trading.testandset():
+ mapserv.sendall(whisper(nick, "I'm currently busy with a trade. Try again shortly"))
+ return
+
+ trader_state.item = item
+ player_id = beingManager.findId(nick)
+ if player_id != -10:
+ mapserv.sendall(trade_request(player_id))
+ trader_state.timer = time.time()
+ else:
+ mapserv.sendall(whisper(nick, "Where are you?!? I can't trade with somebody who isn't here!"))
+ trader_state.reset()
+ else:
+ response = chatbot.respond(msg)
+ logger.info("Bot Response: "+response)
+ mapserv.sendall(whisper(nick, response))
+ #mapserv.sendall(whisper(nick, "Command not recognised, please whisper me !help for a full list of commands."))
+
+def main():
+ # Use rotating log files.
+ log_handler = logging.handlers.RotatingFileHandler('data/logs/activity.log', maxBytes=1048576*3, backupCount=5)
+ logger.setLevel(logging.INFO)
+ formatter = logging.Formatter('%(asctime)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
+ log_handler.setFormatter(formatter)
+ logger.addHandler(log_handler)
+
+ logger.info("Bot Started.")
+
+ account = config.account
+ password = config.password
+ character = config.character
+
+ login = socket.socket()
+ login.connect((config.server, config.port))
+ logger.info("Login connected")
+
+ login_packet = PacketOut(0x0064)
+ login_packet.write_int32(0)
+ login_packet.write_string(account, 24)
+ login_packet.write_string(password, 24)
+ login_packet.write_int8(0x03);
+ login.sendall(str(login_packet))
+
+ pb = PacketBuffer()
+ id1 = accid = id2 = 0
+ charip = ""
+ charport = 0
+ # Login server packet loop.
+ while True:
+ data = login.recv(1500)
+ if not data:
+ break
+ pb.feed(data)
+ for packet in pb:
+ if packet.is_type(SMSG_LOGIN_DATA): # login succeeded
+ packet.skip(2)
+ id1 = packet.read_int32()
+ accid = packet.read_int32()
+ id2 = packet.read_int32()
+ packet.skip(30)
+ player_node.sex = packet.read_int8()
+ charip = utils.parse_ip(packet.read_int32())
+ charport = packet.read_int16()
+ login.close()
+ break
+ if charip:
+ break
+
+ assert charport
+
+ char = socket.socket()
+ char.connect((charip, charport))
+ logger.info("Char connected")
+ char_serv_packet = PacketOut(CMSG_CHAR_SERVER_CONNECT)
+ char_serv_packet.write_int32(accid)
+ char_serv_packet.write_int32(id1)
+ char_serv_packet.write_int32(id2)
+ char_serv_packet.write_int16(0)
+ char_serv_packet.write_int8(player_node.sex)
+ char.sendall(str(char_serv_packet))
+ char.recv(4)
+
+ pb = PacketBuffer()
+ mapip = ""
+ mapport = 0
+ # Character Server Packet loop.
+ while True:
+ data = char.recv(1500)
+ if not data:
+ break
+ pb.feed(data)
+ for packet in pb:
+ if packet.is_type(SMSG_CHAR_LOGIN):
+ packet.skip(2)
+ slots = packet.read_int16()
+ packet.skip(18)
+ count = (len(packet.data)-22) / 106
+ for i in range(count):
+ player_node.id = packet.read_int32()
+ player_node.EXP = packet.read_int32()
+ player_node.MONEY = packet.read_int32()
+ packet.skip(62)
+ player_node.name = packet.read_string(24)
+ packet.skip(6)
+ slot = packet.read_int8()
+ packet.skip(1)
+ logger.info("Character information recieved:")
+ logger.info("Name: %s, Id: %s, EXP: %s, MONEY: %s", \
+ player_node.name, player_node.id, player_node.EXP, player_node.MONEY)
+ if slot == character:
+ break
+
+ char_select_packet = PacketOut(CMSG_CHAR_SELECT)
+ char_select_packet.write_int8(character)
+ char.sendall(str(char_select_packet))
+
+ elif packet.is_type(SMSG_CHAR_MAP_INFO):
+ player_node.id = packet.read_int32()
+ player_node.map = packet.read_string(16)
+ mapip = utils.parse_ip(packet.read_int32())
+ mapport = packet.read_int16()
+ char.close()
+ break
+ if mapip:
+ break
+
+ assert mapport
+
+ beingManager.container[player_node.id] = Being(player_node.id, 42)
+ mapserv = socket.socket()
+ mapserv.connect((mapip, mapport))
+ logger.info("Map connected")
+ mapserv_login_packet = PacketOut(CMSG_MAP_SERVER_CONNECT)
+ mapserv_login_packet.write_int32(accid)
+ mapserv_login_packet.write_int32(player_node.id)
+ mapserv_login_packet.write_int32(id1)
+ mapserv_login_packet.write_int32(id2)
+ mapserv_login_packet.write_int8(player_node.sex)
+ mapserv.sendall(str(mapserv_login_packet))
+ mapserv.recv(4)
+
+ pb = PacketBuffer()
+ shop_broadcaster.mapserv = mapserv
+ # Map server packet loop
+
+ print "Entering map packet loop\n";
+ while True:
+ data = mapserv.recv(2048)
+ if not data:
+ break
+ pb.feed(data)
+
+ # For unfinished trades - one way to distrupt service would be leaving a trade active.
+ if trader_state.Trading.test():
+ if time.time() - trader_state.timer > 2*60:
+ logger.info("Trade Cancelled - Timeout.")
+ trader_state.timer = time.time()
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST)))
+
+ for packet in pb:
+ if packet.is_type(SMSG_MAP_LOGIN_SUCCESS): # connected
+ logger.info("Map login success.")
+ packet.skip(4)
+ coord_data = packet.read_coord_dir()
+ player_node.x = coord_data[0]
+ player_node.y = coord_data[1]
+ player_node.direction = coord_data[2]
+ logger.info("Starting Postion: %s %s %s", player_node.map, player_node.x, player_node.y)
+ mapserv.sendall(str(PacketOut(CMSG_MAP_LOADED))) # map loaded
+ # A Thread to send a shop broadcast: also keeps the network active to prevent timeouts.
+ shop_broadcaster.start()
+
+ elif packet.is_type(SMSG_WHISPER):
+ msg_len = packet.read_int16() - 26
+ nick = packet.read_string(24)
+ message = packet.read_raw_string(msg_len)
+ # Clean up the logs.
+ if nick != 'AuctionBot':
+ logger.info("Whisper: " + nick + ": " + message)
+ process_whisper(nick, utils.remove_colors(message), mapserv)
+
+ elif packet.is_type(SMSG_PLAYER_STAT_UPDATE_1):
+ stat_type = packet.read_int16()
+ value = packet.read_int32()
+ if stat_type == 0x0018:
+ logger.info("Weight changed from %s/%s to %s/%s", \
+ player_node.WEIGHT, player_node.MaxWEIGHT, value, player_node.MaxWEIGHT)
+ player_node.WEIGHT = value
+ elif stat_type == 0x0019:
+ logger.info("Max Weight: %s", value)
+ player_node.MaxWEIGHT = value
+
+ elif packet.is_type(SMSG_PLAYER_STAT_UPDATE_2):
+ stat_type = packet.read_int16()
+ value = packet.read_int32()
+ if stat_type == 0x0014:
+ logger.info("Money Changed from %s, to %s", player_node.MONEY, value)
+ player_node.MONEY = value
+
+ elif packet.is_type(SMSG_BEING_MOVE) or packet.is_type(SMSG_BEING_VISIBLE)\
+ or packet.is_type(SMSG_PLAYER_MOVE) or packet.is_type(SMSG_PLAYER_UPDATE_1)\
+ or packet.is_type(SMSG_PLAYER_UPDATE_2):
+ being_id = packet.read_int32()
+ packet.skip(8)
+ job = packet.read_int16()
+ if being_id not in beingManager.container:
+ if job == 0 and id >= 110000000 and (packet.is_type(SMSG_BEING_MOVE)\
+ or packet.is_type(SMSG_BEING_VISIBLE)):
+ continue
+ # Add the being to the BeingManager, and request name.
+ beingManager.container[being_id] = Being(being_id, job)
+ requestName = PacketOut(0x0094)
+ requestName.write_int32(being_id)
+ mapserv.sendall(str(requestName))
+
+ elif packet.is_type(SMSG_BEING_NAME_RESPONSE):
+ being_id = packet.read_int32()
+ if being_id in beingManager.container:
+ beingManager.container[being_id].name = packet.read_string(24)
+
+ elif packet.is_type(SMSG_BEING_REMOVE):
+ being_id = packet.read_int32()
+ if being_id in beingManager.container:
+ del beingManager.container[being_id]
+
+ elif packet.is_type(SMSG_PLAYER_WARP):
+ player_node.map = packet.read_string(16)
+ player_node.x = packet.read_int16()
+ player_node.y = packet.read_int16()
+ logger.info("Player warped: %s %s %s", player_node.map, player_node.x, player_node.y)
+ mapserv.sendall(str(PacketOut(CMSG_MAP_LOADED)))
+
+ elif packet.is_type(SMSG_PLAYER_INVENTORY_ADD):
+ item = Item()
+ item.index = packet.read_int16() - inventory_offset
+ item.amount = packet.read_int16()
+ item.itemId = packet.read_int16()
+ packet.skip(14)
+ 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
+
+ logger.info("Picked up: %s, Amount: %s, Index: %s", ItemDB.getItem(item.itemId).name, str(item.amount), str(item.index))
+
+ elif packet.is_type(SMSG_PLAYER_INVENTORY_REMOVE):
+ index = packet.read_int16() - inventory_offset
+ amount = packet.read_int16()
+
+ 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)
+
+ elif packet.is_type(SMSG_PLAYER_INVENTORY):
+ player_node.inventory.clear() # Clear the inventory - incase of new index.
+ packet.skip(2)
+ number = (len(packet.data)-2) / 18
+ for loop in range(number):
+ item = Item()
+ item.index = packet.read_int16() - inventory_offset
+ item.itemId = packet.read_int16()
+ packet.skip(2)
+ item.amount = packet.read_int16()
+ packet.skip(10)
+ player_node.inventory[item.index] = item
+
+ elif packet.is_type(SMSG_PLAYER_EQUIPMENT):
+ packet.read_int16()
+ number = (len(packet.data)) / 20
+ for loop in range(number):
+ item = Item()
+ item.index = packet.read_int16() - inventory_offset
+ item.itemId = packet.read_int16()
+ packet.skip(16)
+ item.amount = 1
+ player_node.inventory[item.index] = item
+
+ logger.info("Inventory information received:")
+ for item in player_node.inventory:
+ logger.info("Name: %s, Id: %s, Index: %s, Amount: %s.", \
+ ItemDB.getItem(player_node.inventory[item].itemId).name, \
+ player_node.inventory[item].itemId, item, player_node.inventory[item].amount)
+
+ errorOccured = player_node.check_inventory(user_tree, sale_tree)
+ if errorOccured:
+ logger.info(errorOccured)
+ shop_broadcaster.stop()
+ sys.exit(1)
+ else:
+ logger.info("Inventory Check Passed.")
+
+ elif packet.is_type(SMSG_TRADE_REQUEST):
+ name = packet.read_string(24)
+ logger.info("Trade request: " + name)
+ mapserv.sendall(trade_respond(False))
+
+ elif packet.is_type(SMSG_TRADE_RESPONSE):
+ response = packet.read_int8()
+ time.sleep(0.2)
+ if response == 0:
+ logger.info("Trade response: Too far away.")
+ if trader_state.item:
+ mapserv.sendall(whisper(trader_state.item.player, "You are too far away."))
+ elif trader_state.money:
+ mapserv.sendall(whisper(trader_state.money, "You are too far away."))
+ trader_state.reset()
+
+ elif response == 3:
+ logger.info("Trade response: Trade accepted.")
+ if trader_state.item:
+ if trader_state.item.get == 1: # add
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_ADD_COMPLETE)))
+ elif trader_state.item.get == 0: # buy
+ if player_node.find_inventory_index(trader_state.item.id) != -10:
+ mapserv.sendall(trade_add_item(player_node.find_inventory_index(trader_state.item.id), trader_state.item.amount))
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_ADD_COMPLETE)))
+ if trader_state.item.price == 0: # getback
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_OK)))
+ trader_state.complete = 1
+ else:
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST)))
+ logger.info("Trade response: Trade accepted (buy) - the item could not be added.")
+ mapserv.sendall(whisper(trader_state.item.player, "Sorry, a problem has occured."))
+
+ elif trader_state.money: # money
+ amount = int(user_tree.get_user(trader_state.money).get('money'))
+ mapserv.sendall(trade_add_item(0-inventory_offset, amount))
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_ADD_COMPLETE)))
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_OK)))
+
+ else:
+ logger.info("Trade response: Trade cancelled")
+ trader_state.reset()
+
+ elif packet.is_type(SMSG_TRADE_ITEM_ADD):
+ amount = packet.read_int32()
+ item_id = packet.read_int16()
+ if trader_state.item and trader_state.money == 0:
+ if trader_state.item.get == 1: # add
+ if amount == trader_state.item.amount and item_id == trader_state.item.id:
+ trader_state.complete = 1
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_OK)))
+ elif item_id == 0 and amount > 0:
+ mapserv.sendall(whisper(trader_state.item.player, "Why are you adding money?!?!"))
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST)))
+ else:
+ mapserv.sendall(whisper(trader_state.item.player, "Please check the correct item or quantity has been added."))
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST)))
+
+ elif trader_state.item.get == 0: # buy
+ if item_id == 0 and amount == trader_state.item.price * trader_state.item.amount:
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_OK)))
+ trader_state.complete = 1
+ elif item_id == 0 and amount != trader_state.item.price * trader_state.item.amount:
+ trader_state.complete = 0
+ else:
+ mapserv.sendall(whisper(trader_state.item.player, "Don't give me your itenz."))
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST)))
+
+ elif trader_state.money: # money
+ mapserv.sendall(whisper(trader_state.money, "Don't give me your itenz."))
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST)))
+
+ logger.info("Trade item add: ItemId:%s Amount:%s", item_id, amount)
+ # Note item_id = 0 is money
+
+ elif packet.is_type(SMSG_TRADE_ITEM_ADD_RESPONSE):
+ index = packet.read_int16() - inventory_offset
+ amount = packet.read_int16()
+ response = packet.read_int8()
+
+ if response == 0:
+ logger.info("Trade item add response: Successfully added item.")
+ if trader_state.item:
+ if trader_state.item.get == 0 and index != 0-inventory_offset: # Make sure the correct item is given!
+ # index & amount are Always 0
+ if player_node.inventory[index].itemId != trader_state.item.id or \
+ amount != trader_state.item.amount:
+ logger.info("Index: %s" % index)
+ logger.info("P.ItemId: %s" % player_node.inventory[index].itemId)
+ logger.info("T.ItemId: %s" % trader_state.item.id)
+ logger.info("P.Amount: %s" % amount)
+ logger.info("T.Amount: %s" % trader_state.item.amount)
+ #mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST)))
+
+ # If Trade item add successful - Remove the item from the inventory state.
+ if index != 0-inventory_offset: # If it's not money
+ 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)
+ else:
+ # The money amount isn't actually sent by the server - odd?!?!?.
+ if trader_state.money:
+ logger.info("Trade: Money Added.")
+ trader_state.complete = 1
+
+ elif response == 1:
+ logger.info("Trade item add response: Failed - player overweight.")
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST)))
+ if trader_state.item:
+ mapserv.sendall(whisper(trader_state.item.player, "You are carrying too much weight. Unload and try again."))
+ elif response == 2:
+ if trader_state.item:
+ mapserv.sendall(whisper(trader_state.item.player, "You have no free slots."))
+ logger.info("Trade item add response: Failed - No free slots.")
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST)))
+ else:
+ logger.info("Trade item add response: Failed - unknown reason.")
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST)))
+ if trader_state.item:
+ mapserv.sendall(whisper(trader_state.item.player, "Sorry, a problem has occured."))
+
+ elif packet.is_type(SMSG_TRADE_OK):
+ is_ok = packet.read_int8() # 0 is ok from self, and 1 is ok from other
+ if is_ok == 0:
+ logger.info("Trade OK: Self.")
+ else:
+ if trader_state.complete:
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_OK)))
+ else:
+ mapserv.sendall(str(PacketOut(CMSG_TRADE_CANCEL_REQUEST)))
+ if trader_state.item:
+ mapserv.sendall(whisper(trader_state.item.player, "Trade Cancelled: Please check the traded items or money."))
+
+ logger.info("Trade Ok: Partner.")
+
+ elif packet.is_type(SMSG_TRADE_CANCEL):
+ trader_state.reset()
+ logger.info("Trade Cancel.")
+
+ elif packet.is_type(SMSG_TRADE_COMPLETE):
+ commitMessage=""
+ # The sale_tree is 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)
+ 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)
+ current_amount = int(item.get("amount"))
+ sale_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)
+
+ 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))
+
+ if trader_state.item.price * trader_state.item.amount != 0:
+ ItemLog.add_item(int(item.get('itemId')), trader_state.item.amount, trader_state.item.price * trader_state.item.amount, item.get('name'))
+ commitMessage = "Buy or Getback"
+
+ elif trader_state.money and trader_state.item == 0: # !money
+ user_tree.get_user(trader_state.money).set('money', str(0))
+ commitMessage = "Money"
+
+ sale_tree.save()
+ user_tree.save()
+ tradey.saveData(commitMessage)
+
+ trader_state.reset()
+ logger.info("Trade Complete.")
+
+ errorOccured = player_node.check_inventory(user_tree, sale_tree)
+ if errorOccured:
+ logger.info(errorOccured)
+ shop_broadcaster.stop()
+ sys.exit(1)
+ else:
+ pass
+
+ # On Disconnect/Exit
+ logger.info("Server disconnect.")
+ shop_broadcaster.stop()
+ mapserv.close()
+
+if __name__ == '__main__':
+ main()
diff --git a/net/__init__.py b/net/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/net/__init__.py
diff --git a/net/packet.py b/net/packet.py
new file mode 100644
index 0000000..16b4f79
--- /dev/null
+++ b/net/packet.py
@@ -0,0 +1,182 @@
+
+"""The PacketBuffer class has been adapted from source originally released by gnufrk"""
+
+import struct
+
+packet_lengths = [
+ 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+#0x0040
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 50, 3, -1, 55, 17, 3, 37, 46, -1, 23, -1, 3,108, 3, 2,
+ 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6,
+#0x0080
+ 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 23, -1, -1, -1, 0,
+ 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6,
+ 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6,
+ 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3,
+#0x00C0
+ 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 6, 2, 27,
+ 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1,
+ 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2,
+ 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10,
+#0x0100
+ 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1,
+ 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16,
+ 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1,
+ 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26,
+#0x0140
+ 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6,
+ 110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42,
+ -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14,186,182,
+ 14, 30, 10, 3, -1, 6,106, -1, 4, 5, 4, -1, 6, 7, -1, -1,
+#0x0180
+ 6, 3,106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6,
+ 90, 86, 24, 6, 30,102, 9, 4, 8, 4, 14, 10, 4, 6, 2, 6,
+ 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4,
+ 11, 7, -1, 67, 12, 18,114, 6, 3, 6, 26, 26, 26, 26, 2, 3,
+#0x01C0
+ 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 0, 9, 9, 29, 6, 28,
+ 8, 14, 10, 35, 6, 8, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6,
+ 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1,
+ -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10,
+#0x2000
+ 26, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 19, 10, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+]
+
+class PacketOut:
+ def __init__(self, out):
+ self.buff = ""
+ self.write_int16(out)
+
+ def __str__(self):
+ return self.buff
+
+ def write_string(self, string_val, length):
+ self.buff += string_val.ljust(length, '\0')
+
+ def write_int8(self, value):
+ self.buff += struct.pack("<B", value)
+
+ def write_int16(self, value):
+ self.buff += struct.pack("<H", value)
+
+ def write_int32(self, value):
+ self.buff += struct.pack("<L", value)
+
+ def write_coords(self, x, y, direction):
+ tmp = x
+ tmp <<= 6
+ d_0 = 0
+ d_1 = 1
+ d_2 = 2
+ d_0 = (tmp >> 8) % 256
+ d_1 = (tmp) % 256
+ tmp = y
+ tmp <<= 4
+ d_1 |= (tmp >> 8) % 256
+ d_2 = tmp % 256
+ d_2 |= direction
+ self.buff += chr(d_0) + chr(d_1) + chr(d_2)
+
+class PacketIn:
+ def __init__(self, set_data, pkt_type):
+ self.data = set_data
+ self.pkttype = pkt_type
+ self.pos = 0
+
+ def is_type(self, pkt_type):
+ return self.pkttype == pkt_type
+
+ def get_type(self):
+ return self.pkttype
+
+ def read_string(self, length):
+ msg = self.data[self.pos:self.pos + length]
+ self.pos = self.pos + length
+ return msg[:msg.find('\0')]
+
+ def read_raw_string(self, length):
+ msg = self.data[self.pos:self.pos + length]
+ self.pos = self.pos + length
+ return msg
+
+ def read_int8(self):
+ int_value = struct.unpack("<B", self.data[self.pos:self.pos + 1])[0]
+ self.pos = self.pos + 1
+ return int_value
+
+ def make_word(self, low, high):
+ return (low | (high << 8))
+
+ def read_coord_pair(self):
+ cdata = self.data[self.pos:self.pos + 5]
+ dst_x = (self.make_word(struct.unpack("<B", cdata[3])[0], struct.unpack("<B", cdata[2])[0] & 0x000f) >> 2)
+ dst_y = self.make_word(struct.unpack("<B", cdata[4])[0], struct.unpack("<B", cdata[3])[0] & 0x0003)
+
+ src_x = (self.make_word(struct.unpack("<B", cdata[1])[0], struct.unpack("<B", cdata[0])[0]) >> 6)
+ src_y = (self.make_word(struct.unpack("<B", cdata[2])[0], struct.unpack("<B", cdata[1])[0] & 0x003f) >> 4)
+ self.pos = self.pos + 5
+ return src_x, src_y, dst_x, dst_y
+
+ def read_coord_dir(self):
+ cdata = self.data[self.pos:self.pos + 3]
+ x = (self.make_word(struct.unpack("<B", cdata[1])[0] & 0x00c0, struct.unpack("<B", cdata[0])[0] & 0x00ff) >> 6) % 255
+ y = (self.make_word(struct.unpack("<B", cdata[2])[0] & 0x00f0, struct.unpack("<B", cdata[1])[0] & 0x003f) >> 4) % 255
+ dir = struct.unpack("<B", cdata[2])[0] & 0x000f
+ self.pos = self.pos + 3
+ return x, y, dir
+
+ def read_int16(self):
+ int_value = struct.unpack("<H", self.data[self.pos:self.pos + 2])[0]
+ self.pos = self.pos + 2
+ return int_value
+
+ def read_int32(self):
+ int_value = struct.unpack("<L", self.data[self.pos:self.pos + 4])[0]
+ self.pos = self.pos + 4
+ return int_value
+
+ def skip(self, count):
+ self.pos = self.pos + count
+
+class PacketBuffer:
+ def __init__(self):
+ self.buff = ""
+
+ def feed(self, data):
+ self.buff += data
+
+ def drop(self, count):
+ self.buff = self.buff[count:]
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ if len(self.buff) < 2:
+ raise StopIteration
+
+ pktlen = 0
+ pkttype = struct.unpack("<H", self.buff[:2])[0]
+ assert pkttype < len(packet_lengths)
+ assert packet_lengths[pkttype] != 0
+
+ if packet_lengths[pkttype] < 0:
+ if len(self.buff) < 4:
+ raise StopIteration
+ pktlen = struct.unpack("<H", self.buff[2:4])[0]
+ assert pktlen >= 4
+ else:
+ pktlen = packet_lengths[pkttype]
+
+ if len(self.buff) < pktlen:
+ raise StopIteration
+
+ packet = PacketIn(self.buff[2:pktlen], pkttype)
+ self.buff = self.buff[pktlen:]
+ return packet
diff --git a/net/packet_out.py b/net/packet_out.py
new file mode 100644
index 0000000..1d6e2d2
--- /dev/null
+++ b/net/packet_out.py
@@ -0,0 +1,50 @@
+from packet import *
+from protocol import *
+
+def emote(emoteId):
+ emote_packet = PacketOut(CMSG_PLAYER_EMOTE)
+ emote_packet.write_int8(emoteId)
+ return str(emote_packet)
+
+def whisper(nick, message):
+ whisp_packet = PacketOut(CMSG_CHAT_WHISPER)
+ whisp_packet.write_int16(len(message) + 28)
+ whisp_packet.write_string(nick, 24)
+ whisp_packet.write_string(message, len(message))
+ return str(whisp_packet)
+
+def chat(text):
+ chat_packet = PacketOut(CMSG_CHAT_MESSAGE)
+ mes = player_node.name + " : " + text
+ chat_packet.write_int16(len(mes) + 4 + 1)
+ chat_packet.write_string(mes, len(mes) + 1)
+ return str(chat_packet)
+
+def sit(val):
+ sit_packet = PacketOut(CMSG_PLAYER_CHANGE_ACT)
+ sit_packet.write_int32(0)
+ if val == True:
+ sit_packet.write_int8(2)
+ else:
+ sit_packet.write_int8(3)
+ return str(sit_packet)
+
+def trade_request(being_id):
+ trade_req_packet = PacketOut(CMSG_TRADE_REQUEST)
+ trade_req_packet.write_int32(being_id)
+ return str(trade_req_packet)
+
+def trade_respond(accept):
+ trade_respond_packet = PacketOut(CMSG_TRADE_RESPONSE)
+ if accept == True:
+ trade_respond_packet.write_int8(3)
+ elif accept == False:
+ trade_respond_packet.write_int8(4)
+ return str(trade_respond_packet)
+
+def trade_add_item(item_index, amount):
+ trade_add_packet = PacketOut(CMSG_TRADE_ITEM_ADD_REQUEST)
+ trade_add_packet.write_int16(item_index + inventory_offset)
+ trade_add_packet.write_int32(amount)
+ return str(trade_add_packet)
+
diff --git a/net/protocol.py b/net/protocol.py
new file mode 100644
index 0000000..4a4b74f
--- /dev/null
+++ b/net/protocol.py
@@ -0,0 +1,64 @@
+SMSG_LOGIN_DATA = 0x0069
+SMSG_CHAR_LOGIN = 0x006b
+SMSG_CHAR_MAP_INFO = 0x0071
+SMSG_MAP_LOGIN_SUCCESS = 0x0073
+SMSG_MAP_LOGIN_SUCCESS = 0x0073
+CMSG_CHAR_SERVER_CONNECT = 0x0065
+CMSG_CHAR_SELECT = 0x0066
+CMSG_MAP_SERVER_CONNECT = 0x0072
+CMSG_CHAT_WHISPER = 0x0096
+CMSG_CHAT_MESSAGE = 0x008c
+CMSG_MAP_LOADED = 0x007d
+
+SMSG_WHISPER = 0x0097
+SMSG_BEING_CHAT = 0x008d
+
+SMSG_PLAYER_CHAT = 0x008e
+CMSG_PLAYER_CHANGE_ACT = 0x0089
+
+SMSG_PLAYER_INVENTORY = 0x01ee
+SMSG_PLAYER_INVENTORY_ADD = 0x00a0
+SMSG_PLAYER_INVENTORY_REMOVE = 0x00af
+SMSG_PLAYER_EQUIPMENT = 0x00a4
+SMSG_PLAYER_STAT_UPDATE_1 = 0x00b0
+SMSG_PLAYER_STAT_UPDATE_2 = 0x00b1
+
+SMSG_BEING_VISIBLE = 0x0078
+SMSG_BEING_MOVE = 0x007b
+SMSG_BEING_REMOVE = 0x0080
+SMSG_PLAYER_MOVE = 0x01da
+SMSG_PLAYER_WARP = 0x0091
+SMSG_PLAYER_UPDATE_1 = 0x01d8
+SMSG_PLAYER_UPDATE_2 = 0x01d9
+SMSG_BEING_NAME_RESPONSE = 0x0095 # Has to be requested
+SMSG_BEING_ACTION = 0x008a
+
+CMSG_ITEM_PICKUP = 0x009f
+CMSG_PLAYER_ATTACK = 0x0089
+CMSG_PLAYER_STOP_ATTACK = 0x0118
+CMSG_PLAYER_CHANGE_DIR = 0x009b
+CMSG_PLAYER_CHANGE_DEST = 0x0085
+CMSG_PLAYER_EMOTE = 0x00bf
+SMSG_WALK_RESPONSE = 0x0087
+
+CMSG_TRADE_REQUEST = 0x00e4
+CMSG_TRADE_RESPONSE = 0x00e6
+CMSG_TRADE_ITEM_ADD_REQUEST = 0x00e8
+CMSG_TRADE_CANCEL_REQUEST = 0x00ed
+CMSG_TRADE_ADD_COMPLETE = 0x00eb
+CMSG_TRADE_OK = 0x00ef
+
+SMSG_TRADE_REQUEST = 0x00e5 #/**< Receiving a request to trade */
+SMSG_TRADE_RESPONSE = 0x00e7
+SMSG_TRADE_ITEM_ADD = 0x00e9
+SMSG_TRADE_ITEM_ADD_RESPONSE = 0x01b1 #/**< Not standard eAthena! */
+SMSG_TRADE_OK = 0x00ec
+SMSG_TRADE_CANCEL = 0x00ee
+SMSG_TRADE_COMPLETE = 0x00f0
+
+SMSG_ITEM_VISIBLE = 0x009d
+SMSG_ITEM_DROPPED = 0x009e
+SMSG_ITEM_REMOVE = 0x00a1
+
+inventory_offset = 2
+storage_offset = 1
diff --git a/player.py b/player.py
new file mode 100644
index 0000000..ed80f66
--- /dev/null
+++ b/player.py
@@ -0,0 +1,70 @@
+#!/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
+"""
+
+import copy
+
+class Item:
+ pass
+
+class Player:
+ def __init__(self, name):
+ self.inventory = {}
+ self.name = name
+ self.id = 0
+ self.sex = 0
+ self.map = ""
+ self.x = 0
+ self.y = 0
+
+ self.EXP = 0
+ self.MONEY = 0
+ self.WEIGHT = 0
+ self.MaxWEIGHT = 0
+
+ def find_inventory_index(self, item_id):
+ for item in self.inventory:
+ if item > 1:
+ if self.inventory[item].itemId == item_id:
+ return item
+ return -10 # Not found - bug somewhere!
+
+ def remove_item(self, index, amount):
+ if index in self.inventory:
+ self.inventory[index].amount -= amount
+ if self.inventory[index].amount == 0:
+ del self.inventory[index]
+
+ def check_inventory(self, user_tree, sale_tree):
+ # Check the inventory state.
+ test_node = copy.deepcopy(self.inventory)
+ for elem in sale_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 inventory out of sync."
+
+ total_money = 0
+ for user in user_tree.root:
+ total_money += int(user.get('money'))
+
+ if total_money != self.MONEY:
+ return "Server and client money out of sync. Market: %s Player %s" % (total_money, self.MONEY)
+
+ return 0
+
+if __name__ == '__main__':
+ print "Do not run this file directly. Run main.py"
diff --git a/start.sh b/start.sh
new file mode 100755
index 0000000..830eece
--- /dev/null
+++ b/start.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+MM_ACTIVE=1
+
+while [ $MM_ACTIVE != 0 ]
+do
+ echo "Starting tradey...."
+ python2.7 main.py >> error_log.txt
+
+ if [ "$?" != "0" ]; then
+ # Inventory/Money out of sync
+ echo "tradey quit with error at:"
+ date
+ MM_ACTIVE=0
+ else
+ echo "Tradey quit normally, sleeping 60 seconds before a restart"
+ sleep 60
+ fi
+done
diff --git a/stats/process_salelog/Readme b/stats/process_salelog/Readme
new file mode 100644
index 0000000..6b80ea7
--- /dev/null
+++ b/stats/process_salelog/Readme
@@ -0,0 +1 @@
+Usage: python main.py sales.log <output file>
diff --git a/stats/process_salelog/main.py b/stats/process_salelog/main.py
new file mode 100755
index 0000000..4ad2dd3
--- /dev/null
+++ b/stats/process_salelog/main.py
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+"""
+
+ Copyright 2011, Dipesh Amin <yaypunkrock@gmail.com>
+ Copyright 2011, Stefan Beller <stefanbeller@googlemail.com>
+
+ tradey, a package, which implements an Automated Market Bot for "The Mana World" a 2D MMORPG.
+
+"""
+
+import logging
+import socket
+import sys
+import time
+import string
+import utils
+import locale
+
+ItemDB = utils.ItemDB()
+
+def main():
+ in_file = sys.argv[1]
+ out_file = sys.argv[2]
+ locale.setlocale(locale.LC_ALL, '')
+
+ out_obj = open(out_file, 'w')
+ in_obj = open(in_file, 'r')
+
+ out_obj.write('<html> \n')
+ out_obj.write('<body> \n')
+ out_obj.write('<head> \n')
+ out_obj.write('<title>ManaMarket Sales</title> \n')
+ out_obj.write('<h2>ManaMarket Sales</h2>')
+ out_obj.write('<table border="1" cellpadding="5" cellspacing="0"> \n')
+ out_obj.write('<tr bgcolor="6699FF"> <td>Item Name</td> <td>Amount</td> <td>Price</td> <td>Time</td> </tr>\n')
+
+ sales = in_obj.readlines()
+
+ sale_total = 0
+ items_sold = 0
+ for line in sales:
+ line = line.split()
+ t_time = time.gmtime(float(line[3]))
+ unit_price = int(line[2])/int(line[1])
+ out_obj.write('<tr> <td>'+ItemDB.getItem(int(line[0])).name+'</td> <td>'+locale.format("%d", int(line[1]), grouping=True)+'</td> <td>'+locale.format("%d", unit_price, grouping=True)+'</td> <td>'+time.asctime(t_time)+'</td> </tr>\n')
+ sale_total += int(line[2])
+ items_sold += int(line[1])
+
+ out_obj.write('</table> \n')
+ out_obj.write('</br><b>Total sales: '+str(sale_total)+' GP</b> </br>')
+ out_obj.write('<b>Total number of items sold: '+str(items_sold)+'</b></br>')
+ out_obj.write('Updated: '+time.asctime(time.gmtime()))
+ out_obj.write('</body></html> \n')
+ in_obj.close()
+ out_obj.close()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/stats/process_salelog/main_stat.py b/stats/process_salelog/main_stat.py
new file mode 100755
index 0000000..9ff71d5
--- /dev/null
+++ b/stats/process_salelog/main_stat.py
@@ -0,0 +1,151 @@
+#!/usr/bin/python
+"""
+
+ Copyright 2011, Dipesh Amin <yaypunkrock@gmail.com>
+ Copyright 2011, Stefan Beller <stefanbeller@googlemail.com>
+
+ tradey, a package, which implements an Automated Market Bot for "The Mana World" a 2D MMORPG.
+
+"""
+
+import logging
+import socket
+import sys
+import time
+import string
+import utils
+import locale
+
+class SaleStat:
+ pass
+
+ItemDB = utils.ItemDB()
+
+def main():
+ in_file = sys.argv[1]
+ out_file = sys.argv[2]
+ locale.setlocale(locale.LC_ALL, '')
+
+ out_obj = open(out_file, 'w')
+ in_obj = open(in_file, 'r')
+
+ out_obj.write('<html> \n \
+ <head> \n \
+ <title>ManaMarket Statistics</title> \n \
+ <style type="text/css"> \n \
+ td, th { border: 1px solid black } \n \
+ table {border: 1px solid black} \n \
+ </style> \n \
+ </head> \n \
+ <body> \n \
+ <h2> ManaMarket Statistics </h2> \n \
+ <table cellpadding="5" cellspacing="0"> \n \
+ <tr> \n \
+ <th rowspan="2"> Item Name</th> \n \
+ <th rowspan="2"> Total Amount Sold</th> \n \
+ <th colspan="5">Price</th> \n \
+ <th rowspan="2">Last Sold</th> \n \
+ </tr> \n \
+ <tr> \n \
+ <td>Min</td> \n \
+ <td>Max</td> \n \
+ <td>Average (Week)</td> \n \
+ <td>Average (Month)</td> \n \
+ <td>Average (Overall)</td> \n \
+ </tr> \n ')
+
+ sales = in_obj.readlines()
+
+ sale_dict = {}
+
+ # Put sales date into a dict.
+ sale_total = 0
+ for line in sales:
+ line = line.split()
+ item_id = int(line[0])
+ name = ItemDB.getItem(int(line[0])).name
+ amount = int(line[1])
+ price = int(line[2])
+ t_time = float(line[3])
+ sale_total += int(line[2])
+
+ if item_id not in sale_dict:
+ sale_dict[item_id] = []
+
+ sale_dict[item_id].append([amount, price, t_time])
+
+ str_list = []
+ # calculate the stats
+ for item in sale_dict:
+ average_week = 0
+ average_week_amount = 0
+ average_month = 0
+ average_month_amount = 0
+ average_all_time = 0
+ average_all_time_amount = 0
+ min_price = 0
+ max_price = 0
+ last_sold = 0
+
+ for n in range(len(sale_dict[item])):
+
+ #out_obj.write('<tr>')
+
+ if min_price > sale_dict[item][n][1]/sale_dict[item][n][0] or min_price == 0:
+ min_price = sale_dict[item][n][1]/sale_dict[item][n][0]
+
+ if max_price < sale_dict[item][n][1]/sale_dict[item][n][0] or max_price == 0:
+ max_price = sale_dict[item][n][1]/sale_dict[item][n][0]
+
+ if last_sold < sale_dict[item][n][2] or last_sold == 0:
+ last_sold = sale_dict[item][n][2]
+
+ if (time.time()-sale_dict[item][n][2]) < 7*24*60*60:
+ average_week_amount += sale_dict[item][n][0]
+ average_week += sale_dict[item][n][1]
+
+ if (time.time()-sale_dict[item][n][2]) < 30*24*60*60:
+ average_month_amount += sale_dict[item][n][0]
+ average_month += sale_dict[item][n][1]
+
+
+ average_all_time_amount += sale_dict[item][n][0]
+ average_all_time += sale_dict[item][n][1]
+
+ average_all_time /= average_all_time_amount
+ if average_week_amount > 0:
+ average_week /= average_week_amount
+ if average_month_amount > 0:
+ average_month /= average_month_amount
+
+ str_list.append([average_all_time_amount,'<td>'+ItemDB.getItem(item).name+'</td>'+'<td>'+locale.format("%d", average_all_time_amount, grouping=True)+'</td>'+ \
+ '<td>'+ locale.format("%d", min_price, grouping=True) +'</td>'+'<td>'+ locale.format("%d", max_price, grouping=True) +'</td>'+'<td>'+ \
+ locale.format("%d", average_week, grouping=True) +'</td>'+ '<td>'+ locale.format("%d", average_month, grouping=True) + \
+ '</td>'+'<td>'+ locale.format("%d", average_all_time, grouping=True)+'</td>'+'<td>'+ time.asctime(time.gmtime(last_sold)) +'</td>'])
+
+ while len(str_list) > 0:
+ pos = 0
+ max_amount = 0
+ for m in range(len(str_list)):
+ if max_amount < str_list[m][0] or max_amount == 0:
+ max_amount = str_list[m][0]
+ pos = m
+ out_obj.write('<tr>')
+ out_obj.write(str_list[pos][1])
+ str_list.pop(pos)
+
+
+ out_obj.write('</tr> \n')
+
+ out_obj.write('</table> \n')
+ out_obj.write('</br><b>Total sales: '+str(sale_total)+' GP</b> </br>')
+ out_obj.write('Updated: '+time.asctime(time.gmtime())+'</br>')
+ out_obj.write('For a detailed page showing all sales '+'<a href="manamarket.html">click here</a>')
+
+ out_obj.write('</body></html> \n')
+ in_obj.close()
+ out_obj.close()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/stats/process_salelog/utils.py b/stats/process_salelog/utils.py
new file mode 100644
index 0000000..fd4e432
--- /dev/null
+++ b/stats/process_salelog/utils.py
@@ -0,0 +1,82 @@
+#!/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
+"""
+from xml.etree.ElementTree import ElementTree
+import time
+
+class Item:
+ pass
+
+# Process a recieved ip address.
+def parse_ip(a):
+ return "%s.%s.%s.%s" % ((a % 256),((a >> 8) % 256),((a >> 16) % 256),((a >> 24) % 256))
+
+# Remove colors from a message
+def remove_colors(msg):
+ if len(msg) > 2:
+ for f in range(len(msg)-2):
+ while (len(msg) > f + 2) and (msg[f] == "#")\
+ and (msg[f+1] == "#"):
+ msg = msg[0:f]+msg[f+3:]
+ return msg
+
+# Encode string - used with 4144 shop compatibility.
+def encode_str(value, size):
+ output = ''
+ base = 94
+ start = 33
+ while value:
+ output += chr(value % base + start)
+ value /= base
+
+ while len(output) < size:
+ output += chr(start)
+
+ return output
+
+class ItemDB:
+ """
+ A simple class to look up information from the items.xml file.
+ """
+ def __init__(self):
+ print "Loading ItemDB"
+ self.item_names = {}
+ self.itemdb_file = ElementTree(file="../data/items.xml")
+
+ for item in self.itemdb_file.getroot():
+ if item.get('id') > 500:
+ item_struct = Item()
+ item_struct.name = item.get('name')
+ if item.get('weight'):
+ item_struct.weight = item.get('weight')
+ if item.get('type'):
+ item_struct.type = item.get('type')
+ item_struct.description = item.get('description')
+ self.item_names[int(item.get('id'))] = item_struct
+
+ def getItem(self, item_id):
+ return self.item_names[item_id]
+
+ def findId(self, name):
+ for item_id in self.item_names:
+ if self.item_names[item_id].name == name:
+ return item_id
+ return -10 #Not found
+
+class ItemLog:
+ """ Writes all sales to a log file, for later processing."""
+ def __init__(self):
+ self.log_file = 'data/logs/sale.log'
+
+ def add_item(self, item_id, amount, price):
+ file_node = open(self.log_file, 'a')
+ file_node.write(str(item_id)+" "+str(amount)+" "+str(price)+" "+str(time.time())+"\n")
+ file_node.close()
+
+if __name__ == '__main__':
+ print "Do not run this file directly. Run main.py"
diff --git a/stats/update_sales.sh b/stats/update_sales.sh
new file mode 100755
index 0000000..62a4e1e
--- /dev/null
+++ b/stats/update_sales.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+cd /home/tmwserver/ManaMarket/stats
+python /home/tmwserver/ManaMarket/stats/process_salelog/main.py /home/tmwserver/ManaMarket/data/logs/sale.log /var/www/tmw-homepage/server/manamarket.html
+python /home/tmwserver/ManaMarket/stats/process_salelog/main_stat.py /home/tmwserver/ManaMarket/data/logs/sale.log /var/www/tmw-homepage/server/manamarket_stats.html
+chmod 644 /var/www/tmw-homepage/server/manamarket.html /var/www/tmw-homepage/server/manamarket_stats.html
diff --git a/tradey.py b/tradey.py
new file mode 100644
index 0000000..01f855e
--- /dev/null
+++ b/tradey.py
@@ -0,0 +1,122 @@
+#!/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
+"""
+import time
+import os
+import xml.dom.minidom
+from subprocess import call
+from xml.etree.ElementTree import *
+
+def clean_xml(parse):
+ data = ''
+ pos_start = 0
+ while parse.find('<', pos_start) != -1:
+ pos_start = parse.find('<', pos_start)
+ pos_end = parse.find('>', pos_start+1)
+ data += parse[pos_start:pos_end+1]
+ pos_start = pos_end
+ return data
+
+class UserTree:
+ def __init__(self):
+ self.tree = ElementTree(file="data/user.xml")
+ self.root = self.tree.getroot()
+
+ def add_user(self, name, stalls, al):
+ if self.get_user(name) == -10:
+ user = SubElement(self.root, "user")
+ user.set("name", name)
+ user.set("stalls", str(stalls))
+ user.set("used_stalls", str(0))
+ user.set("money", str(0))
+ user.set("id", str(0))
+ user.set("accesslevel", str(al))
+ self.save()
+
+ def get_user(self, name):
+ for elem in self.root:
+ if elem.get("name") == name:
+ return elem
+ return -10
+
+ def remove_user(self, name):
+ for elem in self.root:
+ if elem.get("name") == name:
+ self.root.remove(elem)
+ self.save()
+ return 1
+ return -10
+
+ def save(self):
+ # Be sure to call save() after any changes to the tree.
+ f = open('data/user.xml', 'w')
+ dom = xml.dom.minidom.parseString(clean_xml(tostring(self.root)))
+ f.write(dom.toprettyxml(' '))
+ f.close()
+
+class ItemTree:
+ def __init__(self):
+ self.tree = ElementTree(file="data/sale.xml")
+ self.root = self.tree.getroot()
+ self.u_id = set()
+
+ 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
+
+ def remove_id(self, uid):
+ # Free up used id's.
+ self.u_id.remove(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("add_time", str(time.time()))
+ user.set("relisted", str(0))
+ user.set("amount", str(amount))
+ user.set("uid", str(self.getId()))
+ self.save()
+
+ def get_uid(self, uid):
+ for elem in self.root:
+ if elem.get("uid") == str(uid):
+ return elem
+ return -10
+
+ 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
+ return -10
+
+ def save(self):
+ # Be sure to call save() after any changes to the tree.
+ f = open('data/sale.xml', 'w')
+ dom = xml.dom.minidom.parseString(clean_xml(tostring(self.root)))
+ f.write(dom.toprettyxml(' '))
+ f.close()
+
+def saveData(commitmessage = "commit"):
+ # This assumes the current working directory is the tradey directory.
+ os.chdir("data")
+ call(["git", "commit","-a", '-m "' + commitmessage + '"'])
+ os.chdir("..")
+
+if __name__ == '__main__':
+ print "Do not run this file directly. Run main.py"
diff --git a/utils.py b/utils.py
new file mode 100644
index 0000000..07c4451
--- /dev/null
+++ b/utils.py
@@ -0,0 +1,132 @@
+#!/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
+"""
+from xml.etree.ElementTree import ElementTree
+from player import Item
+
+import time
+import mutex
+import threading
+from net.packet_out import *
+
+allowed_chars = "abcdefghijklmnoprstquvwxyzABCDEFGHIJKLMNOPRSTQUVWXYZ1234567890-_+=!@$%^&*();'<>,.?/~`| "
+
+# Process a recieved ip address.
+def parse_ip(a):
+ return "%s.%s.%s.%s" % ((a % 256),((a >> 8) % 256),((a >> 16) % 256),((a >> 24) % 256))
+
+# Remove colors from a message
+def remove_colors(msg):
+ if len(msg) > 2:
+ for f in range(len(msg)-2):
+ while (len(msg) > f + 2) and (msg[f] == "#")\
+ and (msg[f+1] == "#"):
+ msg = msg[0:f]+msg[f+3:]
+ return msg
+
+# Encode string - used with 4144 shop compatibility.
+def encode_str(value, size):
+ output = ''
+ base = 94
+ start = 33
+ while value:
+ output += chr(value % base + start)
+ value /= base
+
+ while len(output) < size:
+ output += chr(start)
+
+ return output
+
+class ItemDB:
+ """
+ A simple class to look up information from the items.xml file.
+ """
+ def __init__(self):
+ print "Loading ItemDB"
+ self.item_names = {}
+ self.itemdb_file = ElementTree(file="data/items.xml")
+
+ for item in self.itemdb_file.getroot():
+ if item.get('id') > 500:
+ item_struct = Item()
+ item_struct.name = item.get('name')
+ if item.get('weight'):
+ item_struct.weight = int(item.get('weight'))
+ else:
+ item_struct.weight = 0
+
+ if item.get('type'):
+ item_struct.type = item.get('type')
+ item_struct.description = item.get('description')
+ self.item_names[int(item.get('id'))] = item_struct
+
+ def getItem(self, item_id):
+ return self.item_names[item_id]
+
+ def findId(self, name):
+ for item_id in self.item_names:
+ if self.item_names[item_id].name == name:
+ return item_id
+ return -10 #Not found
+
+class ItemLog:
+ """ Writes all sales to a log file, for later processing."""
+ def __init__(self):
+ self.log_file = 'data/logs/sale.log'
+
+ def add_item(self, item_id, amount, price, name):
+ file_node = open(self.log_file, 'a')
+ file_node.write(str(item_id)+" "+str(amount)+" "+str(price)+" "+str(time.time())+" "+name+"\n")
+ file_node.close()
+
+class TraderState:
+ """ Stores information regarding a trade request"""
+ def __init__(self):
+ self.Trading = mutex.mutex()
+ self.item = 0
+ self.money = 0
+ self.complete = 0
+ self.timer = 0
+
+ def reset(self):
+ self.Trading.unlock()
+ self.item = 0
+ self.complete = 0
+ self.money = 0
+ self.timer = 0
+
+class Broadcast:
+ """Send a message to the server every 5 minutes to avoid a timeout."""
+
+ def __init__(self):
+ self.mapserv = 0
+ self.Active = False
+ self.Timer = 0
+ self.shop_broadcast = threading.Thread(target=self.send_broadcast, args=())
+
+ def send_broadcast(self):
+ while self.Active:
+ if (time.time() - self.Timer) > 60:
+ self.mapserv.sendall(emote(193))
+ self.Timer = time.time()
+ #print "shop_broadcast"
+ else:
+ time.sleep(0.1)
+
+ def start(self):
+ self.Active = True
+ self.shop_broadcast.start()
+
+ def stop(self):
+ if self.Active:
+ self.Active = False
+ self.shop_broadcast.join()
+
+if __name__ == '__main__':
+ print "Do not run this file directly. Run main.py"