added training

This commit is contained in:
2026-03-09 21:48:34 +01:00
parent d1017bae51
commit d81f8a611f
110 changed files with 4393 additions and 0 deletions

View File

@@ -0,0 +1 @@
3.13

View File

@@ -0,0 +1,29 @@
FROM python:3.11-bullseye
WORKDIR /app
COPY . /app
RUN pip install --no-cache-dir -r requirements.txt
RUN apt-get update && apt-get install -y openjdk-11-jdk && apt-get clean
RUN cd $(python -c "import os, pyspark; print(os.path.dirname(pyspark.__file__))")/jars && \
rm log4j* && \
wget https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.jar && \
wget https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.14.1/log4j-api-2.14.1.jar && \
wget https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-slf4j-impl/2.14.1/log4j-slf4j-impl-2.14.1.jar && \
wget https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-1.2-api/2.14.1/log4j-1.2-api-2.14.1.jar
RUN useradd -m appuser
RUN mv /app/flag.txt /
RUN mv /app/instance /
RUN chown -R appuser:appuser /app
RUN chmod 444 /flag.txt
USER appuser
EXPOSE 5000
CMD ["python", "run.py"]

View File

@@ -0,0 +1,60 @@
from pyspark.sql import SparkSession
import math
import time
log4j_config_path = "log4j.properties"
spark = SparkSession.builder \
.appName("Anticheat") \
.config("spark.driver.extraJavaOptions",
"-Dcom.sun.jndi.ldap.object.trustURLCodebase=true -Dlog4j.configuration=file:" + log4j_config_path) \
.config("spark.executor.extraJavaOptions",
"-Dcom.sun.jndi.ldap.object.trustURLCodebase=true -Dlog4j.configuration=file:" + log4j_config_path) \
.getOrCreate()
logger = spark._jvm.org.apache.log4j.LogManager.getLogger("Anticheat")
def log_action(user_id, action):
logger.info(f"User: {user_id} - {action}")
user_states = {}
# Anti-cheat thresholds
MAX_SPEED = 1000 # Max units per second
def analyze_movement(user_id, new_x, new_y, new_angle):
global user_states
# Initialize user state if not present
if user_id not in user_states:
user_states[user_id] = {
'last_x': new_x,
'last_y': new_y,
'last_time': time.time(),
'violations': 0,
}
user_state = user_states[user_id]
last_x = user_state['last_x']
last_y = user_state['last_y']
last_time = user_state['last_time']
# Calculate distance and time elapsed
distance = math.sqrt((new_x - last_x)**2 + (new_y - last_y)**2)
time_elapsed = time.time() - last_time
speed = distance / time_elapsed if time_elapsed > 0 else 0
# Check for speed violations
if speed > MAX_SPEED:
return True
# Update the user state
user_states[user_id].update({
'last_x': new_x,
'last_y': new_y,
'last_time': time.time(),
})
return False

View File

@@ -0,0 +1,38 @@
from flask import Flask
from flask_socketio import SocketIO
from flask_login import LoginManager
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
socketio = SocketIO()
login_manager = LoginManager()
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config.from_object('config.Config')
socketio.init_app(app, cors_allowed_origins="*")
login_manager.init_app(app)
db.init_app(app)
from game.models import User
with app.app_context():
db.create_all()
from game.utils import init_db
# Prepopulate the db
with app.app_context():
init_db()
from game.routes import bp as game_bp
app.register_blueprint(game_bp)
# Import the socket events after the app is created
from game.socket import init_socket_events
from game.utils import update_birds_from_db
with app.app_context():
players = update_birds_from_db()
init_socket_events(socketio, players)
return app

View File

@@ -0,0 +1,5 @@
import os
class Config:
SECRET_KEY = os.urandom(24)
SQLALCHEMY_DATABASE_URI = 'sqlite:////instance/database.db'
SQLALCHEMY_TRACK_MODIFICATIONS = True

View File

@@ -0,0 +1,6 @@
services:
sparkling_game:
build: .
restart: always
ports:
- "0.0.0.0:80:5000"

View File

@@ -0,0 +1 @@
srdnlen{REDACTED}

View File

@@ -0,0 +1,5 @@
from flask import Blueprint
bp = Blueprint('game', __name__)
from . import routes, socket # Import routes and socket modules

View File

@@ -0,0 +1,9 @@
from flask_login import UserMixin
from app import db
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(300), nullable=False, unique=True)
color = db.Column(db.String(10), nullable=True)
is_playing = db.Column(db.Boolean, nullable=True)

View File

@@ -0,0 +1,59 @@
from flask import render_template, redirect, url_for, request, flash
from flask_login import login_user, logout_user, login_required, current_user
from . import bp
from .models import *
from app import login_manager
from .utils import *
from random import randint
@login_manager.user_loader
def load_user(user_id):
user = User.query.get(int(user_id))
return user
@login_manager.unauthorized_handler
def unauthorized_callback():
return redirect('/login')
@bp.route('/')
@login_required
def home():
return render_template('home.html')
@bp.route('/play')
@login_required
def play():
current_players = User.query.filter_by(is_playing=True).with_entities(User.id).all()
current_players = [user_id[0] for user_id in current_players]
userID = int(current_user.get_id())
if userID in current_players:
return render_template('play.html')
else:
return render_template("spectate.html", position=randint(1, 300))
@bp.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
user = User.query.filter(User.username == username).first()
if user is None:
return redirect(url_for('game.login'))
if user.password == password:
login_user(user)
return redirect(url_for('game.home'))
return redirect(url_for('game.login'))
return render_template('login.html')
@bp.route('/logout')
@login_required
def logout():
logout_user()
flash('Logged out successfully!')
return redirect(url_for('game.login'))

View File

@@ -0,0 +1,48 @@
from flask_socketio import emit
from flask_login import current_user, login_required
from threading import Lock
from .models import *
from anticheat import log_action, analyze_movement
lock = Lock()
def init_socket_events(socketio, players):
@socketio.on('connect')
@login_required
def handle_connect():
user_id = int(current_user.get_id())
log_action(user_id, "is connecting")
if user_id in players.keys():
# Player already exists, send their current position
emit('connected', {'user_id': user_id, 'x': players[user_id]['x'], 'y': players[user_id]['y'], 'angle': players[user_id]['angle']})
else:
# TODO: Check if the lobby is full and add the player to the queue
log_action(user_id, f"is spectating")
emit('update_bird_positions', players, broadcast=True)
@socketio.on('move_bird')
@login_required
def handle_bird_movement(data):
user_id = data.get('user_id')
if user_id in players:
del data['user_id']
if players[user_id] != data:
with lock:
players[user_id] = {
'x': data['x'],
'y': data['y'],
'color': 'black',
'angle': data.get('angle', 0)
}
if analyze_movement(user_id, data['x'], data['y'], data.get('angle', 0)):
log_action(user_id, f"was cheating with final position ({data['x']}, {data['y']}) and final angle: {data['angle']}")
# del players[user_id] # Remove the player from the game - we are in beta so idc
emit('update_bird_positions', players, broadcast=True)
@socketio.on('disconnect')
@login_required
def handle_disconnect(data):
user_id = current_user.get_id()
if user_id in players:
del players[user_id]
emit('update_bird_positions', players, broadcast=True)

View File

@@ -0,0 +1,35 @@
from .models import db, User
import secrets
import string
from uuid import uuid4 as userID
def init_db():
if User.query.first() is None:
for i in range(10):
username = 'user' + str(userID())
password = ''.join(secrets.choice(string.ascii_uppercase + string.digits) for _ in range(16))
user = User(username=username, password=password, color=secrets.choice(['black', 'blue', 'white', 'green', 'red', 'grey', 'yellow', 'cyan', 'orange', 'pink']), is_playing=True)
db.session.add(user)
db.session.commit()
user = User(username='user1337', password='user1337', color=secrets.choice(['black', 'blue', 'white', 'green', 'red', 'grey', 'yellow', 'cyan', 'orange', 'pink']))
db.session.add(user)
db.session.commit()
def get_players():
current_players = User.query.filter_by(is_playing=True).with_entities(User.id).all()
current_players = [user_id[0] for user_id in current_players]
return current_players
from random import randint, uniform
def update_birds_from_db():
players = {}
current_players = get_players()
for user_id in current_players:
players[user_id] = {
'x': randint(0,500),
'y': randint(0,500),
'color': 'black', # TODO: implement color from db
'angle': uniform(0,6)
}
return players

View File

@@ -0,0 +1 @@
REDACTED

View File

@@ -0,0 +1,8 @@
log4j.rootLogger=INFO, file
# File Appender
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=gamelogs.log
log4j.appender.file.Append=true
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c - %m%n

View File

@@ -0,0 +1,6 @@
def main():
print("Hello from src!")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,7 @@
[project]
name = "src"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = []

View File

@@ -0,0 +1,5 @@
Flask==3.1.0
Flask-Login==0.6.3
Flask-SocketIO==5.5.1
Flask-SQLAlchemy==3.1.1
pyspark==3.3.0

View File

@@ -0,0 +1,6 @@
from app import create_app, socketio
app = create_app()
if __name__ == '__main__':
socketio.run(app, host="0.0.0.0", debug=False, port=5000, allow_unsafe_werkzeug=True)

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #87CEEB;
background: linear-gradient(270deg, #ff7e5f, #feb47b, #86a8e7, #7f7fd5);
background-size: 800% 800%;
animation: gradientAnimation 10s ease infinite;
font-family: 'Impact', Charcoal, sans-serif;
}
@keyframes gradientAnimation {
0% { background-position: 0% 50%; }
25% { background-position: 50% 50%; }
50% { background-position: 100% 50%; }
75% { background-position: 50% 50%; }
100% { background-position: 0% 50%; }
}
.join-button {
font-size: 3em;
padding: 20px 40px;
background-color: #ff5722;
color: white;
border: none;
border-radius: 10px;
cursor: pointer;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
transition: transform 0.2s;
}
.join-button:hover {
transform: scale(1.1);
}
</style>
</head>
<body>
<button class="join-button">Join Game</button>
<script>
document.querySelector('.join-button').addEventListener('click', function() {
window.location.href = '/play';
});
</script>
</body>
</html>
<link href="https://fonts.googleapis.com/css2?family=Bokor&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Bokor', sans-serif;
}
.join-button {
font-family: 'Bokor', sans-serif;
}
</style>

View File

@@ -0,0 +1,79 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
<style>
body {
font-family: Arial, sans-serif;
background: linear-gradient(200deg, #0c36e0, #184c8c, #2de5fd); /* Animated gradient */
background-size: 800% 800%;
animation: gradientAnimation 10s ease infinite;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
@keyframes gradientAnimation {
0% { background-position: 0% 50%; }
25% { background-position: 50% 50%; }
50% { background-position: 100% 50%; }
75% { background-position: 50% 50%; }
100% { background-position: 0% 50%; }
}
.login-container {
background-color: rgba(255, 255, 255, 0.9);
padding: 30px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
width: 350px;
}
h1 {
text-align: center;
margin-bottom: 20px;
color: #333;
}
label {
display: block;
margin-bottom: 8px;
color: #555;
}
input[type="text"], input[type="password"] {
width: 100%;
padding: 12px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 5px;
box-sizing: border-box;
}
button {
width: 100%;
padding: 12px;
background-color: #007BFF;
border: none;
border-radius: 5px;
color: white;
font-size: 18px;
cursor: pointer;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div class="login-container">
<h1>Login</h1>
<form method="post">
<label for="username">Username:</label>
<input type="text" name="username" required>
<label for="password">Password:</label>
<input type="password" name="password" required>
<button type="submit">Login</button>
</form>
</div>
</body>
</html>

View File

@@ -0,0 +1,194 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bird Movement Game</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.1/socket.io.js"></script>
<style>
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #87CEEB;
background: linear-gradient(200deg, #1a2a6c, #1fb275, #2de5fd); /* Animated gradient */
background-size: 800% 800%;
animation: gradientAnimation 10s ease infinite;
font-family: 'Impact', Charcoal, sans-serif;
}
@keyframes gradientAnimation {
0% { background-position: 0% 50%; }
25% { background-position: 50% 50%; }
50% { background-position: 100% 50%; }
75% { background-position: 50% 50%; }
100% { background-position: 0% 50%; }
}
#gameCanvas {
border: 2px solid #333;
border-radius: 12px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
background-color: rgba(255, 255, 255, 0.8); /* Light background for better visibility */
}
.bird {
width: 50px;
height: 50px;
position: absolute;
}
.scoreboard {
position: absolute;
top: 10px;
left: 10px;
color: white;
font-size: 20px;
background: rgba(0, 0, 0, 0.5);
padding: 10px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<style>
#gameCanvas {
border: 16px solid #333;
border-radius: 8px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.7);
background: radial-gradient(circle, rgba(255, 255, 255, 0.9), rgba(240, 240, 240, 0.9));
background-size: cover;
transition: transform 0.3s ease, border-color 0.3s ease;
position: relative;
overflow: hidden;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(240, 240, 240, 0.9));
background-size: cover;
transition: transform 0.3s ease, border-color 0.3s ease;
}
</style>
<script>
const socket = io();
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let myBird;
let targetX;
let targetY;
socket.on('connected', (data) => {
myBird = data;
targetX = myBird.x;
targetY = myBird.y;
updateBirdPosition();
});
const speed = 10;
const keyMap = {};
const birdImage = new Image();
birdImage.src = '/static/img/bird.png';
function drawBird(bird) {
const birdWidth = 65;
const birdHeight = 65;
ctx.save();
ctx.translate(bird.x + birdWidth / 2, bird.y + birdHeight / 2);
ctx.rotate(bird.angle);
ctx.drawImage(birdImage, -birdWidth / 2, -birdHeight / 2, birdWidth, birdHeight);
ctx.restore();
}
function drawAllBirds(players) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let userId in players) {
drawBird(players[userId]);
}
}
const angleSpeed = 0.2;
function updateBirdPosition() {
let dx = 0;
let dy = 0;
let targetAngle = myBird.angle;
let hasmoved = false;
// Update target position based on pressed keys
if (keyMap['ArrowUp']) {
dy = -speed;
targetAngle = 0 * Math.PI / 180;
hasmoved = true;
}
if (keyMap['ArrowDown']) {
dy = speed;
targetAngle = 180 * Math.PI / 180;
hasmoved = true;
}
if (keyMap['ArrowLeft']) {
dx = -speed;
targetAngle = 270 * Math.PI / 180;
hasmoved = true;
}
if (keyMap['ArrowRight']) {
dx = speed;
targetAngle = 90 * Math.PI / 180;
hasmoved = true;
}
// Smoothly rotate towards the target angle
let angleDifference = targetAngle - myBird.angle;
if (angleDifference > Math.PI) {
targetAngle -= 2 * Math.PI;
} else if (angleDifference < -Math.PI) {
targetAngle += 2 * Math.PI;
}
angleDifference = targetAngle - myBird.angle;
if (Math.abs(angleDifference) > angleSpeed) {
myBird.angle += Math.sign(angleDifference) * angleSpeed;
} else {
myBird.angle = targetAngle;
}
targetX = Math.min(Math.max(0, targetX + dx), canvas.width - 50);
targetY = Math.min(Math.max(0, targetY + dy), canvas.height - 50);
const easing = 1;
myBird.x += (targetX - myBird.x) * easing;
myBird.y += (targetY - myBird.y) * easing;
if (hasmoved){
socket.emit('move_bird', myBird);
}
requestAnimationFrame(updateBirdPosition);
}
window.addEventListener('keydown', (event) => {
keyMap[event.key] = true;
});
window.addEventListener('keyup', (event) => {
keyMap[event.key] = false;
});
socket.on('update_bird_positions', (players) => {
drawAllBirds(players);
});
</script>
</body>
</html>

View File

@@ -0,0 +1,79 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
<style>
body {
font-family: Arial, sans-serif;
background: linear-gradient(200deg, #e68600, #293eda, #2de5fd); /* Animated gradient */
background-size: 800% 800%;
animation: gradientAnimation 10s ease infinite;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
@keyframes gradientAnimation {
0% { background-position: 0% 50%; }
25% { background-position: 50% 50%; }
50% { background-position: 100% 50%; }
75% { background-position: 50% 50%; }
100% { background-position: 0% 50%; }
}
.login-container {
background-color: rgba(255, 255, 255, 0.9);
padding: 30px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
width: 350px;
}
h1 {
text-align: center;
margin-bottom: 20px;
color: #333;
}
label {
display: block;
margin-bottom: 8px;
color: #555;
}
input[type="text"], input[type="password"] {
width: 100%;
padding: 12px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 5px;
box-sizing: border-box;
}
button {
width: 100%;
padding: 12px;
background-color: #007BFF;
border: none;
border-radius: 5px;
color: white;
font-size: 18px;
cursor: pointer;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div class="login-container">
<h1>Signup</h1>
<form method="post">
<label for="username">Username:</label>
<input type="text" name="username" required>
<label for="password">Password:</label>
<input type="password" name="password" required>
<button type="submit">Signup</button>
</form>
</div>
</body>
</html>

View File

@@ -0,0 +1,141 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bird Movement Game</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.1/socket.io.js"></script>
<style>
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #87CEEB;
background: linear-gradient(200deg, #1a2a6c, #1fb275, #2de5fd); /* Animated gradient */
background-size: 800% 800%;
animation: gradientAnimation 10s ease infinite;
font-family: 'Impact', Charcoal, sans-serif;
}
@keyframes gradientAnimation {
0% { background-position: 0% 50%; }
25% { background-position: 50% 50%; }
50% { background-position: 100% 50%; }
75% { background-position: 50% 50%; }
100% { background-position: 0% 50%; }
}
#gameCanvas {
border: 2px solid #333;
border-radius: 12px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
background-image: url('/static/img/background.jpg'); /* Add a background */
background-size: cover;
background-position: center;
margin: 0 100px;
}
.bird {
width: 50px;
height: 50px;
position: absolute;
}
.scoreboard {
position: absolute;
top: 10px;
left: 10px;
color: white;
font-size: 20px;
background: rgba(0, 0, 0, 0.5);
padding: 10px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.play {
margin-left: 0;
position: relative;
height: 600px;
width: 80px;
border-radius: 8px;
border: none;
background: #119cdd;
color: white;
font-size: 24px;
font-weight: bold;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-decoration: none;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.play a {
color: white;
text-decoration: none;
}
</style>
</head>
<body>
{% if position %}
<div class="scoreboard">You are #{{ position }} in the queue.</div>
{% endif %}
<canvas id="gameCanvas" width="800" height="600"></canvas>
<style>
#gameCanvas {
border: 16px solid #333;
border-radius: 8px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.7);
background: radial-gradient(circle, rgba(255, 255, 255, 0.9), rgba(240, 240, 240, 0.9));
background-size: cover;
transition: transform 0.3s ease, border-color 0.3s ease;
position: relative;
overflow: hidden;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(240, 240, 240, 0.9));
background-size: cover;
transition: transform 0.3s ease, border-color 0.3s ease;
}
</style>
<button class="play"><a href="/play">PLAY</a></button>
<script>
const socket = io();
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const birdImage = new Image();
birdImage.src = '/static/img/bird.png';
function drawBird(bird) {
const birdWidth = 65;
const birdHeight = 65;
ctx.save();
ctx.translate(bird.x + birdWidth / 2, bird.y + birdHeight / 2);
ctx.rotate(bird.angle);
ctx.drawImage(birdImage, -birdWidth / 2, -birdHeight / 2, birdWidth, birdHeight);
ctx.restore();
}
function drawAllBirds(birds) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let userId in birds) {
drawBird(birds[userId]);
}
}
socket.on('update_bird_positions', (birds) => {
drawAllBirds(birds)
});
</script>
</body>
</html>