Jeu de belote en ligne.

belote.py 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. import flask
  4. from flask_socketio import SocketIO
  5. import flask_login as fll
  6. import datetime
  7. from db import db, User, Game, Player, levels_users
  8. from belote_jeu import N_TURN, N_PLAYERS
  9. import belote_ws
  10. import settings
  11. login_manager = fll.LoginManager()
  12. app = flask.Flask(__name__)
  13. app.config.from_pyfile('settings.cfg')
  14. #######################################################################
  15. # Plugin management #
  16. #######################################################################
  17. # SocketIO
  18. socketio = SocketIO(app, cors_allowed_origins=settings.PUBLIC_URL)
  19. login_manager.init_app(app)
  20. # Database management
  21. db.app = app
  22. db.init_app(app)
  23. db.create_all()
  24. db.session.commit()
  25. # Insert admin if not exist
  26. if User.query.limit(1).first() is None:
  27. admin = User(login='admin', name='Admin', level=levels_users['Admin'])
  28. admin.set_password('admin')
  29. db.session.add(admin)
  30. db.session.commit()
  31. # Login management
  32. @login_manager.user_loader
  33. def load_user(user_id):
  34. if user_id is None :
  35. return None
  36. return User.query.get(user_id)
  37. @login_manager.unauthorized_handler
  38. def unauthorized_callback():
  39. return flask.redirect('/login?next=' + flask.request.path)
  40. #######################################################################
  41. # Global variables or fuctions #
  42. #######################################################################
  43. URL_DEFAULT = '/games'
  44. namespaces_join = {}
  45. namespaces = {}
  46. def create_namespace(gid, join=False):
  47. if(join):
  48. namespace = belote_ws.CustomNamespaceJoin(gid)
  49. namespaces_join[gid] = True
  50. socketio.on_namespace(namespace)
  51. else:
  52. namespace = belote_ws.CustomNamespacePlay(gid)
  53. namespaces[gid] = True
  54. socketio.on_namespace(namespace)
  55. @app.route('/')
  56. def home():
  57. user = fll.current_user
  58. if not hasattr(user, 'level'):
  59. user = None
  60. return flask.render_template("index.html", user=user)
  61. #######################################################################
  62. # Login, logout #
  63. #######################################################################
  64. @app.route('/login', methods=['GET', 'POST'])
  65. def login():
  66. if fll.current_user.is_authenticated:
  67. return flask.redirect(URL_DEFAULT)
  68. if flask.request.method=="POST":
  69. username = flask.request.form['username']
  70. password = flask.request.form['password']
  71. user = User.query.get(username)
  72. if user and user.level > levels_users['Inactive'] and user.check_password(password):
  73. fll.login_user(user)
  74. user.last_login = datetime.datetime.utcnow()
  75. next = flask.request.args.get('next')
  76. if next is not None and next[0]=='/':
  77. return flask.redirect(next)
  78. else:
  79. return flask.redirect(URL_DEFAULT)
  80. else:
  81. app.logger.warning('Login fail for user {} from {}'.format(user, flask.request.remote_addr))
  82. flask.flash('Nom d\'utilisateur ou mot de passe incorrect')
  83. return flask.render_template('login.html', user=None)
  84. @app.route('/logout')
  85. def logout():
  86. fll.logout_user()
  87. return flask.redirect('/')
  88. #######################################################################
  89. # Games : list #
  90. #######################################################################
  91. @app.route('/games')
  92. @fll.login_required
  93. def games():
  94. games = Game.query.all()
  95. mesjeux = []
  96. joinable = []
  97. for g in games:
  98. if g.admin==fll.current_user.login:
  99. mesjeux.append(g)
  100. elif g.can_join(fll.current_user):
  101. joinable.append(g)
  102. cancreategames = fll.current_user.level >= levels_users['Can create games']
  103. candeletegames = fll.current_user.level >= levels_users['Admin']
  104. return flask.render_template('games.html', mesjeux=mesjeux, joinable=joinable, user=fll.current_user, \
  105. cancreategames=cancreategames, candeletegames=candeletegames)
  106. #######################################################################
  107. # Create, join and play to a game #
  108. #######################################################################
  109. @app.route('/add_game', methods=['GET', 'POST'])
  110. @fll.login_required
  111. def game_add():
  112. player = fll.current_user
  113. if fll.current_user.level >= levels_users['Can create games']:
  114. if flask.request.method=="POST":
  115. name = flask.request.form['name']
  116. if name is None or name == '':
  117. return flask.redirect('/add_game')
  118. game = Game(name=name, admin=player.login)
  119. db.session.add(game)
  120. db.session.commit()
  121. game.join(fll.current_user)
  122. db.session.commit()
  123. return flask.redirect('/game/{}'.format(game.id))
  124. return flask.render_template('game_add.html', user=fll.current_user)
  125. return flask.redirect(URL_DEFAULT)
  126. @app.route('/game/<roomid>/join')
  127. @fll.login_required
  128. def game_page_join(roomid):
  129. game = Game.query.get(roomid)
  130. if game:
  131. player = fll.current_user
  132. if game.start:
  133. return flask.redirect('/game/{}'.format(roomid))
  134. if game.can_join(player):
  135. if game.id not in namespaces_join:
  136. create_namespace(game.id, join=True)
  137. return flask.render_template('game_join.html', game=game, user=player, admin=game.admin)
  138. return flask.redirect(URL_DEFAULT)
  139. @app.route('/game/<roomid>/start')
  140. @fll.login_required
  141. def game_page_start(roomid):
  142. game = Game.query.get(roomid)
  143. if game:
  144. player = fll.current_user
  145. if game.isadmin(player) and game.fixplayers and not game.start:
  146. return flask.render_template('game_start.html', game=game, user=player)
  147. return flask.redirect('/game/{}'.format(roomid))
  148. return flask.redirect(URL_DEFAULT)
  149. @app.route('/game/<roomid>')
  150. @fll.login_required
  151. def game_page(roomid):
  152. game = Game.query.get(roomid)
  153. if game:
  154. player = fll.current_user
  155. if not game.start:
  156. return flask.redirect('/game/{}/join'.format(roomid))
  157. if game.authorized(player) :
  158. if game.id not in namespaces:
  159. create_namespace(game.id)
  160. return flask.render_template('game.html', game=game, user=player)
  161. return flask.redirect(URL_DEFAULT)
  162. @app.route('/game/<roomid>/del')
  163. @fll.login_required
  164. def delgame(roomid):
  165. game = Game.query.get(roomid)
  166. if game is not None and fll.current_user.login == game.admin:
  167. confirm = flask.request.args.get('confirm')
  168. if confirm is not None:
  169. db.session.delete(game)
  170. db.session.commit()
  171. else:
  172. return flask.render_template('game_del.html', game=game, user=fll.current_user)
  173. return flask.redirect(URL_DEFAULT)
  174. #######################################################################
  175. # Users management #
  176. #######################################################################
  177. @app.route('/admin/users')
  178. @fll.login_required
  179. def admin_users():
  180. if fll.current_user.level < levels_users['Can manage simple users']:
  181. return flask.redirect(URL_DEFAULT)
  182. users = User.query.all()
  183. return flask.render_template('admin_users.html', user=fll.current_user, users=users)
  184. @app.route('/admin/users/<user>', methods=['GET', 'POST'])
  185. @fll.login_required
  186. def edituser(user):
  187. new = user=='new'
  188. user = User.query.get(user)
  189. if not new and user is None:
  190. return flask.redirect('/admin/users')
  191. if fll.current_user.level >= levels_users['Admin'] or \
  192. fll.current_user.level >= levels_users['Can manage simple users'] and \
  193. fll.current_user.level > user.level:
  194. if flask.request.method=="POST":
  195. username = flask.request.form['username']
  196. name = flask.request.form['name']
  197. password = flask.request.form['password']
  198. level = int(flask.request.form['level'])
  199. if level not in levels_users.values():
  200. flask.flash('Level unknown')
  201. return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
  202. if fll.current_user.level < levels_users['Admin']:
  203. if not level < fll.current_user.level:
  204. flask.flash('Invalid level')
  205. return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
  206. if len(password)<2 and new:
  207. flask.flash('Mot de passe trop court')
  208. return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
  209. if new and User.query.get(username) is not None:
  210. flask.flash('Login déjà utilisé ! Choisissez un autre !')
  211. return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
  212. if new:
  213. usertoadd = User(login=username, name=name, level=level)
  214. usertoadd.set_password(password)
  215. db.session.add(usertoadd)
  216. else:
  217. user.name = name
  218. if len(password)>=2:
  219. user.set_password(password)
  220. user.level = level
  221. db.session.commit()
  222. return flask.redirect('/admin/users')
  223. else:
  224. return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
  225. return flask.redirect(URL_DEFAULT)
  226. @app.route('/admin/users/<user>/del')
  227. @fll.login_required
  228. def deluser(user):
  229. user = User.query.get(user)
  230. if user is None:
  231. return flask.redirect('/admin/users')
  232. if fll.current_user.level >= levels_users['Admin'] or \
  233. fll.current_user.level >= levels_users['Can manage simple users'] and \
  234. fll.current_user.level > user.level:
  235. confirm = flask.request.args.get('confirm')
  236. if confirm is not None:
  237. delete_game(user=user)
  238. db.session.delete(user)
  239. db.session.commit()
  240. return flask.redirect('/admin/users')
  241. return flask.render_template("admin_user_del.html", user=fll.current_user, useredit=user)
  242. @app.route('/password', methods=['GET', 'POST'])
  243. @fll.login_required
  244. def change_password():
  245. if flask.request.method=="POST":
  246. password = flask.request.form['password']
  247. if len(password) < 5:
  248. flask.flash('Mot de passe trop court !')
  249. else:
  250. fll.current_user.set_password(password)
  251. db.session.commit()
  252. return flask.redirect(URL_DEFAULT)
  253. return flask.render_template("password.html", user=fll.current_user)
  254. #######################################################################
  255. # Game managament #
  256. #######################################################################
  257. @app.route('/admin/games')
  258. @fll.login_required
  259. def admin_games():
  260. if fll.current_user.level < levels_users['Can manage games']:
  261. return flask.redirect(URL_DEFAULT)
  262. games = Game.query.all()
  263. return flask.render_template('admin_games.html', user=fll.current_user, games=games)
  264. @app.route('/admin/games/<roomid>/del')
  265. @fll.login_required
  266. def admin_game_del(roomid):
  267. game = Game.query.get(roomid)
  268. if game is None:
  269. return flask.redirect('/admin/games')
  270. if fll.current_user.level >= levels_users['Can manage games']:
  271. confirm = flask.request.args.get('confirm')
  272. if confirm is not None:
  273. delete_game(game=game)
  274. return flask.redirect('/admin/games')
  275. else:
  276. return flask.render_template('game_del.html', user=fll.current_user, game=game)
  277. return flask.redirect(URL_DEFAULT)
  278. @app.route('/game/<roomid>/see')
  279. @fll.login_required
  280. def see_game(roomid):
  281. game = Game.query.get(roomid)
  282. if game is None:
  283. return flask.redirect('/games')
  284. if fll.current_user.level >= levels_users['Can manage games'] or game.authorized(fll.current_user) and game.turn==N_TURN*N_PLAYERS:
  285. #return game.serialize_state_anonymous()
  286. return flask.render_template('game_see.html', user=fll.current_user, game=game, serialized=game.serialize_state_anonymous())
  287. return flask.redirect(URL_DEFAULT)
  288. @app.route('/admin/games/new', methods=['GET', 'POST'])
  289. @fll.login_required
  290. def admin_games_new():
  291. if fll.current_user.level < levels_users['Can manage games']:
  292. return flask.redirect(URL_DEFAULT)
  293. if flask.request.method=="POST":
  294. name = flask.request.form['name']
  295. login0 = flask.request.form['player0']
  296. login1 = flask.request.form['player1']
  297. login2 = flask.request.form['player2']
  298. login3 = flask.request.form['player3']
  299. admin = flask.request.form['admin']
  300. if 'fixplayers' in flask.request.form:
  301. start = True
  302. else:
  303. start = False
  304. try:
  305. admin = int(admin)
  306. except:
  307. return 'Error'
  308. if admin<0 or admin>3:
  309. return 'Error'
  310. adminn = -1
  311. lusers = []
  312. if login0 != '':
  313. user0 = User.query.get(login0)
  314. if user0 is None:
  315. flask.flash('Utilisateur inconnu {}'.format(login0))
  316. else:
  317. lusers.append(user0)
  318. if admin==0: adminn = 0
  319. if login1 != '':
  320. user1 = User.query.get(login1)
  321. if user1 is None:
  322. flask.flash('Utilisateur inconnu {}'.format(login1))
  323. else:
  324. if admin==1: adminn = len(lusers)
  325. lusers.append(user1)
  326. if login2 != '':
  327. user2 = User.query.get(login2)
  328. if user2 is None:
  329. flask.flash('Utilisateur inconnu {}'.format(login2))
  330. else:
  331. if admin==2: adminn = len(lusers)
  332. lusers.append(user2)
  333. if login3 != '':
  334. user3 = User.query.get(login3)
  335. if user3 is None:
  336. flask.flash('Utilisateur inconnu {}'.format(login3))
  337. else:
  338. if admin==3: adminn = len(lusers)
  339. lusers.append(user3)
  340. if adminn <0:
  341. flask.flash('Vous devez définir un maitre du jeu (admin).')
  342. elif len(name)<2:
  343. flask.flash('Nom trop court !')
  344. else:
  345. game = Game(name=name, admin=lusers[adminn].login)
  346. db.session.add(game)
  347. db.session.commit()
  348. ordered_players = []
  349. for u in lusers:
  350. ordered_players.append(u.login)
  351. game.join(u)
  352. db.session.commit()
  353. if start and game.can_start():
  354. game.fix_players()
  355. game.start_game(ordered_players)
  356. return flask.redirect('/admin/games')
  357. return flask.render_template('admin_game_add.html', user=fll.current_user)
  358. def delete_game(user=None, game=None):
  359. if user is not None:
  360. games = Game.query.filter_by(admin=user.login).all()
  361. for g in games:
  362. for p in g.players:
  363. db.session.delete(p)
  364. db.session.delete(g)
  365. db.session.commit()
  366. if isinstance(game, int):
  367. g = Game.query.get(game)
  368. if g is not None:
  369. for p in g.players:
  370. db.session.delete(p)
  371. db.session.delete(g)
  372. db.session.commit()
  373. elif game is not None:
  374. for p in game.players:
  375. db.session.delete(p)
  376. db.session.delete(game)
  377. db.session.commit()
  378. if __name__ == '__main__':
  379. socketio.run(app, debug=True, host='127.0.0.1', port=8080 )