Newer
Older
navi-1 / app.py
@Eugene Sukhodolskiy Eugene Sukhodolskiy on 25 Apr 5 KB ?
import os
from flask import Flask, request, render_template
from flask_socketio import SocketIO, emit, join_room, leave_room
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime, timedelta
import uuid
import threading
import time

app = Flask(__name__)
app.config['SECRET_KEY'] = 'super-secret-key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///game.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)
socketio = SocketIO(app, cors_allowed_origins="*")

# --- Models ---

class Session(db.Model):
    id = db.Column(db.String(36), primary_key=True)
    room_id = db.Column(db.String(50), unique=True, nullable=False)
    player1_id = db.Column(db.String(36), nullable=True)
    player2_id = db.Column(db.String(36), nullable=True)
    current_turn_player_id = db.Column(db.String(36), nullable=True)
    status = db.Column(db.String(20), default='active') # active, finished, abandoned
    last_action_at = db.Column(db.DateTime, default=datetime.utcnow)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

class Player(db.Model):
    id = db.Column(db.String(36), primary_key=True)
    session_id = db.Column(db.String(36), db.ForeignKey('session.id'))
    nickname = db.Column(db.String(50), nullable=False)
    side = db.Column(db.String(10)) # player1, player2
    player_token = db.Column(db.String(100), unique=True)
    sid = db.Column(db.String(100), nullable=True)

class Unit(db.Model):
    id = db.Column(db.String(36), primary_key=True)
    session_id = db.Column(db.String(36), db.ForeignKey('session.id'))
    owner_id = db.Column(db.String(36), db.ForeignKey('player.id'))
    unit_type = db.Column(db.String(20))
    x = db.Column(db.Integer)
    y = db.Column(db.Integer)
    health = db.Column(db.Integer)

# --- Routes ---

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/game/<room_id>')
def game(room_id):
    return render_template('game.html', room_id=room_id)

# --- Helpers ---

def update_session_activity(session_id):
    session = Session.query.get(session_id)
    if session:
        session.last_action_at = datetime.utcnow()
        db.session.commit()

# --- SocketIO Events ---

@socketio.on('create_game')
def handle_create_game(data):
    nickname = data.get('nickname')
    room_id = str(uuid.uuid4())[:8]
    session_id = str(uuid.uuid4())
    player_id = str(uuid.uuid4())
    player_token = str(uuid.uuid4())

    new_session = Session(id=session_id, room_id=room_id, status='active', current_turn_player_id=player_id)
    new_player = Player(id=player_id, session_id=session_id, nickname=nickname, side='player1', player_token=player_token, sid=request.sid)
    
    db.session.add(new_session)
    db.session.add(new_player)
    new_session.player1_id = player_id
    db.session.commit()

    join_room(room_id)
    emit('game_created', {'room_id': room_id, 'player_token': player_token}, room=request.sid)

@socketio.on('join_game')
def handle_join_game(data):
    room_id = data.get('room_id')
    session = Session.query.filter_by(room_id=room_id).first()

    player_token = data.get('player_token')
    nickname = data.get('nickname')

    if not session or session.status != 'active':
        emit('error', {'message': 'Room not found or game ended'})
        return

    if player_token:
        # Case 1: Re-connecting player (creator)
        player = Player.query.filter_by(player_token=player_token).first()
        if player and player.session_id == session.id:
            player.sid = request.sid
            if nickname: # Update nickname if provided during reconnect
                player.nickname = nickname
            db.session.commit()
            join_room(room_id)
            emit('game_joined', {'room_id': room_id}, room=request.sid)
            return
        else:
            emit('error', {'message': 'Invalid player token'})
            return

    # Case 2: Second player joining via link
    if session.player2_id is None:
        player_id = str(uuid.uuid4())
        new_player = Player(id=player_id, session_id=session.id, nickname=nickname, side='player2', player_token=str(uuid.uuid4()), sid=request.sid)
        session.player2_id = player_id
        db.session.add(new_player)
        db.session.commit()

        join_room(room_id)
        emit('game_joined', {'room_id': room_id}, room=request.sid)
        emit('player_joined', {'nickname': nickname}, room=room_id)
    else:
        emit('error', {'message': 'Room full'})

@socketio.on('connect')
def handle_connect():
    # Logic for reconnection would go here: 
    # Check if sid is associated with an existing player_token in the database
    pass

# --- Background Task ---

def cleanup_task():
    with app.app_context(): # Note: app.app_context()
        while True:
            time.sleep(60)
            timeout_limit = datetime.utcnow() - timedelta(minutes=20)
            expired_sessions = Session.query.filter(Session.status == 'active', Session.last_action_at < timeout_limit).all()
            
            for s in expired_sessions:
                s.status = 'abandoned'
                db.session.commit()
                print(f"Session {s.room_id} abandoned due to inactivity.")

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    
    threading.Thread(target=cleanup_task, daemon=True).start()
    socketio.run(app, debug=True, port=5000)