from twisted.internet.protocol import Protocol
from twisted.internet.protocol import Factory
from twisted.internet.endpoints import TCP4ServerEndpoint
from twisted.protocols.policies import TimeoutMixin
from sqlalchemy import create_engine, and_
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
from collections import deque
from time import time
import struct
import os
import logging
from exc import *
from model import *
def get_hex(data):
return "".join([hex(ord(c))[2:].zfill(2) for c in data])
db_path = "root:helloworld@localhost/piztor_game"
#db_path = "piztor.sqlite"
FORMAT = "%(asctime)-15s %(message)s"
logging.basicConfig(format = FORMAT)
logger = logging.getLogger('piztor_server')
logger.setLevel(logging.INFO)
engine = create_engine('mysql://' + db_path, echo = False, pool_size = 1024)
class _PermCode:
normal = 0x00
section = 0x01
company = 0x02
class _SectionSize:
LENGTH = 4
OPT_ID = 1
STATUS = 1
USER_ID = 4
USER_TOKEN = 32
GROUP_ID = 2
LATITUDE = 8
LONGITUDE = 8
PADDING = 1
DEADLINE = 4
MARKER_ID = 1
_MAX_AUTH_HEAD_SIZE = _SectionSize.USER_TOKEN + \
MAX_USERNAME_SIZE + \
_SectionSize.PADDING
_HEADER_SIZE = _SectionSize.LENGTH + \
_SectionSize.OPT_ID
_MAX_TEXT_MESG_SIZE = 1024
_MAX_SUB_LIST_SIZE = 10
_MAX_PENDING_PUSH = 10
_INIT_MARKER_NUM = 5
class _OptCode:
user_auth = 0x00
update_location = 0x01
user_info = 0x02
update_subscription = 0x03
user_logout = 0x04
open_push_tunnel = 0x05
send_text_mesg = 0x06
set_marker = 0x07
change_password = 0x08
check_in = 0x09
game_start = 0x0A
class _StatusCode:
sucess = 0x00
auth_fail = 0x01
insuf_lvl = 0x02
wrong_pass = 0x03
grp_not_found = 0x04
checkin_fail = 0x05
class PushData(object):
from hashlib import sha256
def pack(self, optcode, data):
self.finger_print = sha256(data + struct.pack("!d", time())).digest()
buff = struct.pack("!B32s", optcode, self.finger_print)
buff += data
buff = struct.pack("!L", _SectionSize.LENGTH + len(buff)) + buff
self.data = buff
class PushTextMesgData(PushData):
def __init__(self, mesg):
self.pack(0x00, mesg + chr(0))
class PushLocationData(PushData):
def __init__(self, uid, lat, lng):
self.pack(0x01, struct.pack("!Ldd", uid, lat, lng))
class PushMarkerData(PushData):
def __init__(self, perm, lat, lng, deadline, mid, score):
self.pack(0x02, struct.pack("!BddlBL", perm, lat, lng, deadline,
mid, score))
class PushMarkerRemovalData(PushData):
def __init__(self, mid):
self.pack(0x03, struct.pack("!B", mid))
class PushScoreData(PushData):
def __init__(self, score1, score2):
self.pack(0x04, struct.pack("!LL", score1, score2))
class PushTunnel(object):
def __init__(self, uid):
self.uid = uid
self.pending = deque()
self.conn = None
self.blocked = False
def close(self):
print "closing TUNNEL"
if self.conn:
print "loseConn called"
self.conn.transport.loseConnection()
def add(self, pdata):
#logger.info("-- Push data enqued --")
self.pending.append(pdata)
if not self.blocked and len(self.pending) > _MAX_PENDING_PUSH:
#logger.info("-- Push queue is full, discarded an obsolete push --")
self.pending.popleft() # discard old push
def on_receive(self, data):
front = self.pending.popleft()
length, optcode, fingerprint = struct.unpack("!LB32s", data)
if front.finger_print != fingerprint:
raise PiztorError
#logger.info("-- Push data confirmed by client %s --", str(self.uid))
self.blocked = False
self.push()
def push(self):
if self.blocked:
return
print "Pushing via " + str(self.uid)
print "Pending size: " + str(len(self.pending))
#logger.info("Pushing...")
if (self.conn is None) or len(self.pending) == 0:
return
front = self.pending.popleft()
self.pending.appendleft(front)
self.conn.transport.write(front.data)
#logger.info("-- Wrote push: %s --", get_hex(front.data))
self.blocked = True
def clear(self):
self.pending.clear()
def connect(self, conn):
self.clear()
conn.tunnel = self
if self.conn: # only one long-connection per user
self.conn.transport.loseConnection()
self.conn = conn
def on_connection_lost(self, conn):
print "TUNNEL closed"
if conn == self.conn:
self.conn = None
def pack_uid(user):
return struct.pack("!L", user.id)
def pack_username(user):
buff = user.username
buff += chr(0)
return buff
def pack_nickname(user):
buff = user.nickname
buff += chr(0)
return buff
def pack_sex(user):
return struct.pack("!B", 0x01 if user.sex else 0x00<