summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTeddy <[email protected]>2013-09-02 12:19:25 +0800
committerTeddy <[email protected]>2013-09-02 12:19:25 +0800
commitf5b2d8c89daafaf2b54c21d00bea3aaddac7b5cb (patch)
tree00879e60ea91fa00c3ea1eef5ad985fe2618e5cb
parente13b38f847050b4ff93bf111104a927a0f2d57b5 (diff)
...
-rw-r--r--server/piztor/easy_import.py34
-rw-r--r--server/piztor/import.py2
-rw-r--r--server/piztor/model.py26
-rw-r--r--server/piztor/prober.py8
-rw-r--r--server/piztor/ptp.rst40
-rw-r--r--server/piztor/ptp_send.py48
-rw-r--r--server/piztor/server.py240
7 files changed, 331 insertions, 67 deletions
diff --git a/server/piztor/easy_import.py b/server/piztor/easy_import.py
index 261d44d..b614315 100644
--- a/server/piztor/easy_import.py
+++ b/server/piztor/easy_import.py
@@ -16,6 +16,12 @@ class UserData:
self.perm = perm
self.sub = sub
+class MarkerData:
+ def __init__(self, lat, lng, score):
+ self.lat = lat
+ self.lng = lng
+ self.score = score
+
def create_database():
engine = create_engine('mysql://' + path, echo = True)
Base.metadata.drop_all(engine)
@@ -26,7 +32,7 @@ def find_or_create_group(comp_no, sec_no, session):
q = session.query(GroupInfo).filter(GroupInfo.id == gid)
entry = q.first()
if not entry:
- entry = GroupInfo(gid = gid)
+ entry = GroupInfo(id = gid, score = 0)
return entry
@@ -52,11 +58,22 @@ def import_user_data(data):
session.add(um)
session.commit()
+def import_marker_data(data):
+ engine = create_engine('mysql://' + path, echo = True)
+ Session = sessionmaker(bind = engine)
+ session = Session()
+
+ for marker in data:
+ mk = MarkerInfo(lat = marker.lat, lng = marker.lng,
+ status = MARKER_FRESH, score = marker.score)
+ session.add(mk)
+ session.commit()
+
if __name__ == '__main__':
from sys import argv, exit
- if len(argv) != 2:
- print "Usage: " + argv[0] + " FILE"
+ if len(argv) != 3:
+ print "Usage: " + argv[0] + " FILE1 FILE2"
exit(0)
data = list()
@@ -64,10 +81,9 @@ if __name__ == '__main__':
while True:
line = f.readline().split()
if len(line) == 0: break
- idx = 0
comp_no = line[3]
sec_no = line[4]
- sub = [ (comp_no, sec_no), (comp_no, 0xff) ]
+ sub = [ (comp_no, 0xff), (comp_no, sec_no) ]
data.append(UserData(username = line[0],
nickname = line[1],
password = line[0],
@@ -76,7 +92,13 @@ if __name__ == '__main__':
sex = line[2],
perm = line[5],
sub = sub))
-
+ data2 = list()
+ with open(argv[2], 'r') as f:
+ while True:
+ line = f.readline().split()
+ if len(line) == 0: break
+ data2.append(MarkerData(lat = line[0], lng = line[1], score = line[2]))
create_database()
import_user_data(data)
+ import_marker_data(data2)
diff --git a/server/piztor/import.py b/server/piztor/import.py
index 7646afc..e552c91 100644
--- a/server/piztor/import.py
+++ b/server/piztor/import.py
@@ -26,7 +26,7 @@ def find_or_create_group(comp_no, sec_no, session):
q = session.query(GroupInfo).filter(GroupInfo.id == gid)
entry = q.first()
if not entry:
- entry = GroupInfo(gid = gid)
+ entry = GroupInfo(gid = gid, score = 0)
return entry
diff --git a/server/piztor/model.py b/server/piztor/model.py
index cade3ea..bbeeb31 100644
--- a/server/piztor/model.py
+++ b/server/piztor/model.py
@@ -1,6 +1,6 @@
from sqlalchemy import Table, Column
-from sqlalchemy import Integer, String, Float, ForeignKey, Boolean
-from sqlalchemy.dialects.mysql import BLOB, TINYINT
+from sqlalchemy import Integer, String, ForeignKey, Boolean
+from sqlalchemy.dialects.mysql import BLOB, TINYINT, DOUBLE
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref
from exc import *
@@ -12,6 +12,9 @@ _TOKEN_LEN = 16
MAX_USERNAME_SIZE = 20
MAX_PASSWORD_SIZE = 20
NOT_A_LAT = NOT_A_LNG = 300
+MARKER_FRESH = 0x00
+MARKER_DISPLAYED = 0x01
+MARKER_CHECKED = 0x02
_table_typical_settings = {
'mysql_engine' : 'InnoDB',
@@ -24,7 +27,7 @@ class _TableName: # avoid typoes
UserAuth = 'user_auth'
GroupInfo = 'group_info'
GroupSub = 'group_sub'
-
+ MarkerInfo = 'marker_info'
class GroupInfo(Base):
__tablename__ = _TableName.GroupInfo
@@ -32,9 +35,7 @@ class GroupInfo(Base):
id = Column(Integer, primary_key = True)
subscribers = None
-
- def __init__(self, gid):
- self.id = gid
+ score = Column(Integer, nullable = False)
group_sub = Table(_TableName.GroupSub, Base.metadata,
Column('uid', Integer, ForeignKey(_TableName.UserModel + '.id')),
@@ -82,8 +83,8 @@ class LocationInfo(Base):
uid = Column(Integer, ForeignKey(_TableName.UserModel + '.id'),
primary_key = True)
- lat = Column(Float(precesion = 64), nullable = False)
- lng = Column(Float(precesion = 64), nullable = False)
+ lat = Column(DOUBLE, nullable = False)
+ lng = Column(DOUBLE, nullable = False)
user = relationship("UserModel", uselist = False,
backref = backref("location", uselist = False,
cascade = "all, delete-orphan"))
@@ -136,4 +137,13 @@ class UserAuth(Base):
def get_token(self):
return self.token
+class MarkerInfo(Base):
+ __tablename__ = _TableName.MarkerInfo
+ __table_args__ = _table_typical_settings
+
+ id = Column(Integer, primary_key = True)
+ lat = Column(DOUBLE, nullable = False)
+ lng = Column(DOUBLE, nullable = False)
+ status = Column(TINYINT)
+ score = Column(Integer)
diff --git a/server/piztor/prober.py b/server/piztor/prober.py
index bb2bcd3..f1f1109 100644
--- a/server/piztor/prober.py
+++ b/server/piztor/prober.py
@@ -4,8 +4,8 @@ from time import sleep
from sys import argv
from ptp_send import *
-username = "haha"
-password = "haha"
+username = "yg"
+password = "yg"
#username = "12345678901234567890"
#password = "world123456789012345"
@@ -18,7 +18,9 @@ if len(argv) == 3:
token = user_auth(username, password)
-change_password(token, username, "ddd", "haha")
+game_start(token, username)
+#check_in(token, username, 0x23)
+#change_password(token, username, "ddd", "haha")
#print "Client: " + username + "logged in"
#open_push_tunnel(token, username)
#for i in xrange(100):
diff --git a/server/piztor/ptp.rst b/server/piztor/ptp.rst
index 24a9612..7e4acc4 100644
--- a/server/piztor/ptp.rst
+++ b/server/piztor/ptp.rst
@@ -1,5 +1,5 @@
-Piztor Transmission Protocol v2.0d
-----------------------------------
+Piztor Transmission Protocol v2.0d for game
+-------------------------------------------
- Pull
@@ -281,6 +281,29 @@ Piztor Transmission Protocol v2.0d
- ``0x01`` for invalid token
- ``0x03`` for wrong password
+ - Check-in ``0x09``
+
+ - Request
+
+ ::
+
+ +-------------+----1b-----+
+ | AUTH_HEAD | MARKER_ID |
+ +-------------+---uchar---+
+
+ - Response
+
+ ::
+
+ +--------+
+ | STATUS |
+ +--------+
+
+ - ``0x00`` for success
+ - ``0x01`` for invalid token
+ - ``0x05`` for check-in failure
+
+
- Push Notification
- General Request
@@ -319,7 +342,14 @@ Piztor Transmission Protocol v2.0d
::
- ----+--1b---+----8b----+----8b-----+----4b----+
- ... | LEVEL | LATITUDE | LONGITUDE | DEADLINE |
- ----+-uchar-+----------+-----------+----int---+
+ ----+--1b---+----8b----+----8b-----+----4b----+----1b-----+---4b--+
+ ... | LEVEL | LATITUDE | LONGITUDE | DEADLINE | MARKER_ID | SCORE |
+ ----+-uchar-+----------+-----------+----int---+---uchar---+--int--+
+
+ - Marker Removal Push
+
+ ::
+ ----+----1b-----+
+ ... | MARKER_ID |
+ ----+-----------+
diff --git a/server/piztor/ptp_send.py b/server/piztor/ptp_send.py
index 6c3b476..bcc996b 100644
--- a/server/piztor/ptp_send.py
+++ b/server/piztor/ptp_send.py
@@ -24,8 +24,8 @@ class _SectionSize:
LOCATION_ENTRY = USER_ID + LATITUDE + LONGITUDE
PADDING = 1
-host = "202.120.7.4"
-#host = "localhost"
+#host = "202.120.7.4"
+host = "localhost"
port = 2223
def pack_data(optcode, data):
@@ -80,11 +80,11 @@ def gen_send_text_mesg(token, username, mesg):
data += chr(0)
return pack_data(0x06, data)
-def gen_set_marker(token, username, lat, lng, deadline):
+def gen_set_marker(token, username, lat, lng, deadline, mid, score):
data = pack("!32s", token)
data += username
data += chr(0)
- data += pack("!ddL", lat, lng, deadline)
+ data += pack("!ddLBL", lat, lng, deadline, mid, score)
return pack_data(0x07, data)
def gen_change_password(token, username, old_pass, new_pass):
@@ -97,6 +97,19 @@ def gen_change_password(token, username, old_pass, new_pass):
data += chr(0)
return pack_data(0x08, data)
+def gen_check_in(token, username, mid):
+ data = pack("!32s", token)
+ data += username
+ data += chr(0)
+ data += pack("!B", mid)
+ return pack_data(0x09, data)
+
+def gen_game_start(token, username):
+ data = pack("!32s", token)
+ data += username
+ data += chr(0)
+ return pack_data(0x0a, data)
+
def send(data):
received = bytes()
from time import time
@@ -128,6 +141,7 @@ def user_auth(username, password):
if pl != len(resp):
logger.error("User authentication: incorrect packet length")
print "status: " + str(status)
+ print get_hex(resp[38:])
# print "token: " + get_hex(token)
except error:
logger.error("User authentication: can not parse the response")
@@ -188,8 +202,9 @@ def send_text_mesg(token, username, mesg):
except error:
logger.error("Send text mesg: can not parse the response")
-def set_marker(token, username, lat, lng, deadline):
- resp = send(gen_set_marker(token, username, lat, lng, deadline))
+def set_marker(token, username, lat, lng, deadline, mid, score):
+ resp = send(gen_set_marker(token, username, lat, lng, deadline,
+ mid, score))
try:
pl, optcode, status = unpack("!LBB", resp)
if pl != len(resp):
@@ -208,6 +223,27 @@ def change_password(token, username, old_pass, new_pass):
except error:
logger.error("Change password: can not pase the response")
+def check_in(token, username, mid):
+ resp = send(gen_check_in(token, username, mid))
+ try:
+ pl, optcode, status = unpack("!LBB", resp)
+ if pl != len(resp):
+ logger.error("Check-in: incorrect packet length")
+ print "status: " + str(status)
+ except error:
+ logger.error("Check-in: can not pase the response")
+
+def game_start(token, username):
+ resp = send(gen_game_start(token, username))
+ try:
+ pl, optcode, status = unpack("!LBB", resp)
+ if pl != len(resp):
+ logger.error("Game-start: incorrect packet length")
+ print "status: " + str(status)
+ except error:
+ logger.error("Game-start: can not parse the response")
+
+
def open_push_tunnel(token, username):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
diff --git a/server/piztor/server.py b/server/piztor/server.py
index 980ae03..7996e0a 100644
--- a/server/piztor/server.py
+++ b/server/piztor/server.py
@@ -44,6 +44,7 @@ class _SectionSize:
LONGITUDE = 8
PADDING = 1
DEADLINE = 4
+ MARKER_ID = 1
_MAX_AUTH_HEAD_SIZE = _SectionSize.USER_TOKEN + \
MAX_USERNAME_SIZE + \
@@ -54,6 +55,7 @@ _HEADER_SIZE = _SectionSize.LENGTH + \
_MAX_TEXT_MESG_SIZE = 1024
_MAX_SUB_LIST_SIZE = 10
_MAX_PENDING_PUSH = 10
+_INIT_MARKER_NUM = 2
class _OptCode:
user_auth = 0x00
@@ -65,6 +67,8 @@ class _OptCode:
send_text_mesg = 0x06
set_marker = 0x07
change_password = 0x08
+ check_in = 0x09
+ game_start = 0x0A
class _StatusCode:
sucess = 0x00
@@ -72,6 +76,7 @@ class _StatusCode:
insuf_lvl = 0x02
wrong_pass = 0x03
grp_not_found = 0x04
+ checkin_fail = 0x05
class PushData(object):
from hashlib import sha256
@@ -91,8 +96,16 @@ class PushLocationData(PushData):
self.pack(0x01, struct.pack("!Ldd", uid, lat, lng))
class PushMarkerData(PushData):
- def __init__(self, perm, lat, lng, deadline):
- self.pack(0x02, struct.pack("!Bddl", perm, lat, lng, deadline))
+ 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):
@@ -126,8 +139,8 @@ class PushTunnel(object):
def push(self):
if self.blocked:
return
- #print "Pushing via " + str(self.uid)
- #print "Pending size: " + str(len(self.pending))
+ 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
@@ -335,6 +348,17 @@ class UserAuthHandler(RequestHandler):
# USER_ENTRY
reply += RequestHandler.pack_user_entry(user)
reply += RequestHandler.pack_sub_list(user)
+ score1 = self.session.query(GroupInfo) \
+ .filter(GroupInfo.id == 5889).one().score
+ score2 = self.session.query(GroupInfo) \
+ .filter(GroupInfo.id == 5890).one().score
+
+ for mrk in self.session.query(MarkerInfo).all():
+ if mrk.status != MARKER_DISPLAYED:
+ continue
+ reply += struct.pack("!BddlBL", 2, mrk.lat, mrk.lng, 0x7fffffff,
+ mrk.id, mrk.score)
+ reply += struct.pack("!LL", score1, score2)
return self.pack(reply)
@@ -357,9 +381,9 @@ class UpdateLocationHandler(RequestHandler):
except struct.error:
raise BadReqError("Update location: Malformed request body")
-# logger.info("Trying to update location with "
-# "(token = {0}, username = {1}, lat = {2}, lng = {3})"\
-# .format(get_hex(token), username, lat, lng))
+ logger.info("Trying to update location with "
+ "(token = {0}, username = {1}, lat = {2}, lng = {3})"\
+ .format(get_hex(token), username, lat, lng))
uauth = RequestHandler.get_uauth(token, username, self.session)
# Authentication failure
@@ -384,15 +408,6 @@ class UpdateLocationHandler(RequestHandler):
pdata = PushLocationData(u.id, lat, lng)
for user in comp.subscribers:
uid = user.id
- if uid == uauth.uid: continue
- if pt.has_key(uid):
- tunnel = pt[uid]
- tunnel.add(pdata)
- tunnel.push()
-
- for user in sec.subscribers:
- uid = user.id
- if uid == uauth.uid: continue
if pt.has_key(uid):
tunnel = pt[uid]
tunnel.add(pdata)
@@ -638,6 +653,32 @@ class SetMarkerHandler(RequestHandler):
_SectionSize.LATITUDE + \
_SectionSize.LONGITUDE + \
_SectionSize.DEADLINE
+ @classmethod
+ def push_new_marker(cls, u, session):
+ left = session.query(MarkerInfo) \
+ .filter(MarkerInfo.status == MARKER_FRESH).all()
+ if len(left) == 0:
+ logger.warn("All markers have been used!")
+ return
+ marker = left[0]
+ marker.status = MARKER_DISPLAYED
+ session.commit()
+ pdata = PushMarkerData(perm = 2, lat = marker.lat, \
+ lng = marker.lng, \
+ deadline = 0x7fffffff, \
+ mid = marker.id, \
+ score = marker.score)
+ ulist = session.query(UserModel) \
+ .filter(UserModel.comp_id == u.comp_id).all()
+
+ pt = RequestHandler.push_tunnels
+ for user in ulist:
+ uid = user.id
+ if pt.has_key(uid):
+ tunnel = pt[uid]
+ tunnel.add(pdata)
+ tunnel.push()
+
def handle(self, tr_data, conn):
self.check_size(tr_data)
@@ -645,15 +686,15 @@ class SetMarkerHandler(RequestHandler):
try:
token, = struct.unpack("!32s", tr_data[:32])
username, tail = RequestHandler.trunc_padding(tr_data[32:])
- lat, lng, deadline = struct.unpack("!ddl", tail)
+ lat, lng, deadline, mid, score = struct.unpack("!ddlBL", tail)
if username is None:
raise struct.error
except struct.error:
raise BadReqError("Set marker: Malformed request body")
-# logger.info("Trying to set marker with "
-# "(token = {0}, username = {1})"\
-# .format(get_hex(token), username))
+ logger.info("Trying to set marker with "
+ "(token = {0}, username = {1})"\
+ .format(get_hex(token), username))
uauth = RequestHandler.get_uauth(token, username, self.session)
# Authentication failure
@@ -661,24 +702,8 @@ class SetMarkerHandler(RequestHandler):
logger.warning("Authentication failure")
return self.pack(struct.pack("!B", _StatusCode.auth_fail))
- pt = RequestHandler.push_tunnels
- u = uauth.user
- if u.perm == _PermCode.section:
- ulist = self.session.query(UserModel) \
- .filter(UserModel.sec_id == u.sec_id).all()
- elif u.perm == _PermCode.company:
- ulist = self.session.query(UserModel) \
- .filter(UserModel.comp_id == u.comp_id).all()
- else:
- return self.pack(struct.pack("!B", _StatusCode.insuf_lvl))
+ SetMarkerHandler.push_new_marker(uauth.user, self.session)
- for user in ulist:
- uid = user.id
- if uid == uauth.uid: continue
- if pt.has_key(uid):
- tunnel = pt[uid]
- tunnel.add(PushMarkerData(u.perm, lat, lng, deadline))
- tunnel.push()
logger.info("Set marker successfully!")
return self.pack(struct.pack("!B", _StatusCode.sucess))
@@ -727,6 +752,143 @@ class ChangePasswordHandler(RequestHandler):
return self.pack(struct.pack("!B", _StatusCode.sucess))
+class CheckInHandler(RequestHandler):
+
+ _optcode = _OptCode.check_in
+ _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \
+ _SectionSize.MARKER_ID
+
+ def handle(self, tr_data, conn):
+ self.check_size(tr_data)
+ logger.info("Reading check-in data...")
+ try:
+ token, = struct.unpack("!32s", tr_data[:32])
+ username, tail = RequestHandler.trunc_padding(tr_data[32:])
+ if username is None:
+ raise struct.error
+ mid, = struct.unpack("!B", tail)
+ except struct.error:
+ raise BadReqError("Check-in: Malformed request body")
+
+ logger.info("Trying to check-in with "
+ "(token = {0}, username = {1}, mid = {2})"\
+ .format(get_hex(token), username, str(mid)))
+
+ uauth = RequestHandler.get_uauth(token, username, self.session)
+ # Authentication failure
+ if uauth is None:
+ logger.warning("Authentication failure")
+ return self.pack(struct.pack("!B", _StatusCode.auth_fail))
+
+ try:
+ marker = self.session.query(MarkerInfo) \
+ .filter(MarkerInfo.id == mid).one()
+ except NoResultFound:
+ raise BadReqError("Check-in: no such marker")
+
+ if marker.status != MARKER_DISPLAYED:
+ return self.pack(struct.pack("!B", _StatusCode.checkin_fail))
+ marker.status = MARKER_CHECKED
+
+ pt = RequestHandler.push_tunnels
+ u = uauth.user
+
+ comp = self.session.query(GroupInfo) \
+ .filter(GroupInfo.id == u.comp_id).one()
+ sec = self.session.query(GroupInfo) \
+ .filter(GroupInfo.id == u.sec_id).one()
+
+ sec.score += marker.score
+ logger.info("Player {0} got score: {1}".format(username, str(marker.score)))
+
+ score1 = self.session.query(GroupInfo) \
+ .filter(GroupInfo.id == 5889).one().score
+ score2 = self.session.query(GroupInfo) \
+ .filter(GroupInfo.id == 5890).one().score
+
+ pdata = PushMarkerRemovalData(mid)
+ for user in comp.subscribers:
+ uid = user.id
+ if pt.has_key(uid):
+ tunnel = pt[uid]
+ tunnel.add(pdata)
+ tunnel.add(PushScoreData(score1, score2))
+ tunnel.push()
+
+ SetMarkerHandler.push_new_marker(u, self.session)
+
+ logger.info("User checked in successfully!")
+ self.session.commit()
+ return self.pack(struct.pack("!B", _StatusCode.sucess))
+
+class GameStartHandler(RequestHandler):
+
+ _optcode = _OptCode.game_start
+ _max_tr_data_size = _MAX_AUTH_HEAD_SIZE
+
+ def handle(self, tr_data, conn):
+ self.check_size(tr_data)
+ logger.info("Reading game-start data...")
+ try:
+ token, = struct.unpack("!32s", tr_data[:32])
+ username, tail = RequestHandler.trunc_padding(tr_data[32:])
+ if username is None:
+ raise struct.error
+ except struct.error:
+ raise BadReqError("Game-start: Malformed request body")
+
+ logger.info("Trying to game-start with "
+ "(token = {0}, username = {1})"\
+ .format(get_hex(token), username))
+
+ uauth = RequestHandler.get_uauth(token, username, self.session)
+ # Authentication failure
+ if uauth is None:
+ logger.warning("Authentication failure")
+ return self.pack(struct.pack("!B", _StatusCode.auth_fail))
+
+
+ pt = RequestHandler.push_tunnels
+ u = uauth.user
+
+ if u.perm != 2:
+ logger.warning("Insufficient privilege")
+ return self.pack(struct.pack("!B", _StatusCode.insuf_lvl))
+
+ team1 = self.session.query(GroupInfo) \
+ .filter(GroupInfo.id == 5889).one()
+ team2 = self.session.query(GroupInfo) \
+ .filter(GroupInfo.id == 5890).one()
+
+ team1.score = 0
+ team2.score = 0
+
+ for mrk in self.session.query(MarkerInfo).all():
+ mrk.status = MARKER_FRESH
+
+ users1 = self.session.query(UserModel) \
+ .filter(UserModel.sec_id == team1.id)
+
+ users2 = self.session.query(UserModel) \
+ .filter(UserModel.sec_id == team2.id)
+
+ for i in xrange(_INIT_MARKER_NUM):
+ SetMarkerHandler.push_new_marker(u, self.session)
+
+ comp = self.session.query(GroupInfo) \
+ .filter(GroupInfo.id == u.comp_id).one()
+
+ for user in comp.subscribers:
+ uid = user.id
+ if pt.has_key(uid):
+ tunnel = pt[uid]
+ tunnel.add(PushScoreData(team1.score, team2.score))
+ tunnel.push()
+
+ logger.info("GAME START!")
+ self.session.commit()
+ return self.pack(struct.pack("!B", _StatusCode.sucess))
+
class PTP(Protocol, TimeoutMixin):
@@ -738,7 +900,9 @@ class PTP(Protocol, TimeoutMixin):
OpenPushTunnelHandler,
SendTextMessageHandler,
SetMarkerHandler,
- ChangePasswordHandler]
+ ChangePasswordHandler,
+ CheckInHandler,
+ GameStartHandler]
handler_num = len(handlers)