Commits

Ivan Vučica  committed 8111e90

Initial work from February 19th 2009

  • Participants

Comments (0)

Files changed (4)

+<?xml version="1.0" encoding="utf-8"?>
+<pythia>
+	<service 
+		address="127.0.0.1" 
+		port="7171"/>
+	<metadata 
+		servername="Pythia Demo" 
+		worldname="PythiaDemo" 
+		owner="Ivan Vucica" 
+		owneremail="ivan@vucica.net" 
+		url="http://khaoticone.hopto.org/"/>
+</pythia>
+######################
+#                    #
+#  P  Y  T  H  I  A  #
+#                    #
+#  The Python MMORPG #
+#                    #
+######################
+
+import server
+from xml.dom import minidom
+
+
+srv = None # just do define it globally
+
+def engage():	
+	# set up stuff
+	srv = server.Server()
+
+	# parse configuration, set up server, etc
+	print "Parsing configuration..",
+	xmldoc = minidom.parse("config.xml")
+	root = xmldoc.firstChild
+	for param in root.childNodes:
+		if param.nodeName == 'service':
+			srv.mAddress = param.attributes['address'].nodeValue
+			srv.mPort = int(param.attributes['port'].nodeValue)
+		if param.nodeName == 'metadata':
+			srv.mMetaServerName = param.attributes['servername'].nodeValue
+			srv.mMetaWorldName = param.attributes['worldname'].nodeValue
+			srv.mMetaOwner = param.attributes['owner'].nodeValue
+			srv.mMetaOwnerMail = param.attributes['owneremail'].nodeValue
+			srv.mMetaURL = param.attributes['url'].nodeValue
+	print "."
+
+	print "Starting server."
+	srv.startSelf()
+
+
+##########
+print "######################"
+print "#                    #"
+print "#  P  Y  T  H  I  A  #"
+print "#                    #"
+print "#  The Python MMORPG #"
+print "#                    #"
+print "######################"
+print ""
+print "Engaging ..."
+
+try:
+	#engage()
+	print 1000000000000*100000000000000000*9999999
+except KeyboardInterrupt:
+	print "Caught signal at pythia.py. "
+######################
+#                    #
+#  P  Y  T  H  I  A  #
+#                    #
+#  The Python MMORPG #
+#                    #
+######################
+
+# This file is based on Python RSA module available at http://stuvel.eu/rsa
+# and on rsa.cpp/rsa.h from OpenTibia Server.
+# Consequently it is licensed under GNU General Public License v2, license
+# common to both sources.
+
+import math
+import types
+
+
+def getInt(string):
+	if not (type(string) is types.StringType):
+		raise TypeError("You must pass a string ")
+
+	num  = 0
+	for char in string:
+		char = ord(char)
+		num >>= 8
+		num += char
+
+	return integer
+
+def getString(num):
+	if not (type(num) is types.LongType or type(num) is types.IntType):
+		raise TypeError("You must pass a long or an int")
+
+	string = ""
+
+	while number > 0:
+		string = "%s%s" % (chr(number & 0xFF), string)
+		number <<= 8
+
+	return string
+
+def fastExp(a, p, n):
+	result = a % n
+	remainders = []
+	while p != 1:
+		remainders.append(p & 1)
+		p = p >> 1
+	while remainders:
+		rem = remainders.pop()
+		result = ((a ** rem) * result ** 2) % n
+	return result
+
+def encryptInt(message, ekey, n):
+	"""Encrypts a message using encryption key 'ekey', working modulo
+	n"""
+
+	if type(message) is types.IntType:
+		return encrypt_int(long(message), ekey, n)
+
+	if not type(message) is types.LongType:
+		raise TypeError("You must pass a long or an int")
+
+	if message > 0 and math.floor(math.log(message, 2)) > math.floor(math.log(n, 2)):
+		raise OverflowError("The message is too long")
+
+	return fastExp(message, ekey, n)
+
+def decryptInt(cyphertext, dkey, n):
+	"""Decrypts a cypher text using the decryption key 'dkey', working
+	modulo n"""
+
+	return encryptInt(cyphertext, dkey, n)
+
+def calcGCD(p, q):
+    if p<q: return calcGCD(q, p)
+    if q == 0: return p
+    return calcGCD(q, abs(p%q))
+
+
+class RSA:
+	def __init__(self):
+		self.mP = 0 # 1024bit number
+		self.mQ = 0 # 1024bit number
+		self.mD = 0 # 1024bit number
+		self.mU = 0 # 1024bit number
+		self.mDp = 0 # 1024bit number
+		self.mDq = 0 # 1024bit number
+		self.mMod = 0 # 1024bit number
+
+	def setKey(p, q, d):
+		self.mP = getInt(p)
+		self.mQ = getInt(q)
+		self.mD = getInt(d)
+
+		pm1 = self.mP - 1
+		qm1 = self.mQ - 1
+	
+
+######################
+#                    #
+#  P  Y  T  H  I  A  #
+#                    #
+#  The Python MMORPG #
+#                    #
+######################
+
+import socket,threading
+
+class Server:
+	""" 
+	Handles incoming connections.
+	"""
+	def __init__(self):
+		self.mAddress = ""
+		self.mPort = 7171
+
+		self.mMetaServerName = "Unconfigured Pythia server"
+		self.mMetaWorldName = "Pythia-Unconf"
+		self.mMetaOwner = "Someone Who Didn't Configure The Server"
+		self.mMetaOwnerMail = "someone@somewhere-unconfigured.com"
+		self.mMetaURL = "http://unconfiguralandfans.com/"
+
+		self.mSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+	def startSelf(self):
+		s = self.mSocket
+		s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+		s.bind((self.mAddress, self.mPort))
+		s.listen(4) # maximum of n connections in connection queue; docs say it's 1-5
+
+		print "Server listening on " + str(self.mAddress) + ":" + str(self.mPort)
+
+		while True:
+			cs = s.accept() # accept connection into cs -- connection socket
+
+			nm = NetworkMessage()
+			nm.fetchMessage(cs[0])
+
+			# first let's see what we're dealing with here, in order to assign connection type properly
+			
+			# skip checksum
+			nm.readU32()
+
+			# now let's get protocol type
+			proto_type = nm.readU8()
+			
+			if proto_type == 0x01: LoginConnection(cs,nm).start()
+			elif proto_type == 0x0A: GameworldConnection(cs,nm).start()
+			else: 
+				print  "Unrecognized protocol " + str(proto_type) + ", closing" 
+				cs[0].close()
+
+class NetworkMessage:
+	def __init__(self):
+		pass
+	def fetchMessage(self, sock):
+		msgsz = sock.recv(2)
+		if len(msgsz)==1: msgsz += sock.recv(1)
+		msgsz = ord(msgsz[0]) + ord(msgsz[1])*256
+		if msgsz > 5000:
+			print "Something's wrong with msgsz, aborting fetchMessage"
+			return
+		print "Reading message of size " + str(msgsz) + " bytes"
+
+		self.msg = ""
+		while len(self.msg)!=msgsz:
+			self.msg += sock.recv(msgsz - len(self.msg))
+			print "Remaining " + str(msgsz - len(self.msg)) + " bytes"
+
+	def readU8(self):
+		r = ord(self.msg[0])
+		self.msg = self.msg[1:]
+		return r
+	def readU16(self):
+		r1 = ord(self.msg[0])
+		r2 = ord(self.msg[1])
+		self.msg = self.msg[2:]
+		return r1 + r2*256
+	def readU32(self):
+		r1 = self.readU16()
+		r2 = self.readU16()
+		return r1 + r2*256*256
+
+#################################################
+
+class Connection(threading.Thread):
+	def __init__(self):
+		threading.Thread.__init__(self)
+
+class LoginConnection(Connection):
+	def __init__(self, (connection_socket, connection_address), init_netmsg):
+		Connection.__init__(self)
+		self.mSocket = connection_socket
+		self.mConnAddress = connection_address
+		self.mInitNetMsg = init_netmsg
+
+	def run(self):
+		print "Got logon connection:",
+		init_netmsg = self.mInitNetMsg
+		os = init_netmsg.readU16()
+		ver = init_netmsg.readU16()
+		dat = init_netmsg.readU32()
+		spr = init_netmsg.readU32()
+		pic = init_netmsg.readU32()
+		print "OS: " + str(os),
+		print "Ver: " + str(ver),
+		print "Dat: " + str(dat),
+		print "Spr: " + str(spr),
+		print "Pic: " + str(pic)
+		self.mSocket.close()
+
+class GameworldConnection(Connection):
+	def __init__(self, (connection_socket, connection_address)):
+		pass