diff --git a/src/config/constants.py b/src/config/constants.py
index 14c4b31..f163ad4 100644
--- a/src/config/constants.py
+++ b/src/config/constants.py
@@ -1,14 +1,12 @@
from enum import Enum
+from dataclasses import dataclass
from PySide6.QtGui import QColor
# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
-
-NO_STAFF = True
-NO_WHITELIST = True
-
+FIVEMURL = "fivem://connect/prod.la-taniere.fun"
REDIRECT_URI = "http://localhost:5000/callback"
SCOPES = ["identify"]
CLIENT_ID = "1240007913175781508"
@@ -28,16 +26,29 @@ AUTENTICATION_SUCCESS_MESSAGE = """
# ENUMS
# ---------------------------------------------------------------------------
class Resources(Enum):
- MP3 = ":/assets/the-beat-of-nature.mp3"
- FONT = ":/assets/Avocado-Cake-Demo.otf"
+ MP3 = ':/assets/the-beat-of-nature.mp3'
+ FONT = ':/assets/Avocado-Cake-Demo.otf'
class Urls(Enum):
- DISCORD = "https://discord.gg/A7eanmSkp2"
- INTRANET = "https://la-taniere.fun/connexion/"
+ DISCORD = 'https://discord.gg/A7eanmSkp2'
+ INTRANET = 'https://la-taniere.fun/connexion/'
API_URL = 'https://prod.la-taniere.fun:30121/'
+class ApiEndPoints(Enum):
+ QUEUE_STATUS = '/queue/status/'
+ QUEUE_LEAVE = '/queue/leave'
+ QUEUE_JOIN = '/queue/join'
+
class Glow(Enum):
COLOR = QColor(255, 140, 0, 255)
BLUR_BASE = 15
BLUR_PEAK = 70
ANIM_DURATION = 1200
+
+# ---------------------------------------------------------------------------
+# DATACLASS
+# ---------------------------------------------------------------------------
+@dataclass
+class PlayerServerInfo:
+ is_staff: bool = False
+ is_whitelist: bool = False
diff --git a/src/fivemserver/fivemlauncher.py b/src/fivemserver/fivemlauncher.py
new file mode 100644
index 0000000..fadf1ec
--- /dev/null
+++ b/src/fivemserver/fivemlauncher.py
@@ -0,0 +1,20 @@
+import subprocess
+import os
+
+from config.constants import FIVEMURL
+
+class FiveMLauncher:
+ def __init__(self, fivem_path: str):
+ self.fivem_path = os.path.expandvars(fivem_path)
+
+ @staticmethod
+ def launch():
+ """
+ if not os.path.exists(self.fivem_path):
+ raise FileNotFoundError("❌ FiveM.exe introuvable")
+
+ subprocess.Popen(self.fivem_path, shell=True)
+ """
+
+ #subprocess.Popen(f"explorer {FIVEMURL}")
+ subprocess.Popen(r'explorer fivem://connect/prod.la-taniere.fun')
diff --git a/src/fivemserver/queuemanager.py b/src/fivemserver/queuemanager.py
new file mode 100644
index 0000000..c50f5cd
--- /dev/null
+++ b/src/fivemserver/queuemanager.py
@@ -0,0 +1,128 @@
+import requests
+from PySide6.QtCore import QThread, Signal
+
+from config.constants import Urls, ApiEndPoints
+
+
+class JoinQueueThread(QThread):
+ result = Signal(dict)
+ error = Signal(str)
+
+ def __init__(self, user_id: str):
+ super().__init__()
+ self.user_id = user_id
+ self.api_url = Urls.API_URL.value
+
+ def run(self):
+ try:
+ res = requests.post(
+ f"{self.api_url}{ApiEndPoints.QUEUE_JOIN.value}",
+ headers={"Content-Type": "application/json"},
+ json={"uuid": self.user_id}
+ )
+ self.result.emit(res.json())
+ except Exception as e:
+ self.error.emit(str(e))
+
+
+class CheckStatusThread(QThread):
+ result = Signal(dict)
+ error = Signal(str)
+
+ def __init__(self, user_id: str):
+ super().__init__()
+ self.user_id = user_id
+ self.api_url = Urls.API_URL.value
+
+ def run(self):
+ try:
+ res = requests.get(f"{self.api_url}{ApiEndPoints.QUEUE_STATUS.value}{self.user_id}")
+ self.result.emit(res.json())
+ except Exception as e:
+ self.error.emit(str(e))
+
+
+class LeaveQueueThread(QThread):
+ done = Signal()
+ error = Signal(str)
+
+ def __init__(self, user_id: str):
+ super().__init__()
+ self.user_id = user_id
+ self.api_url = Urls.API_URL.value
+
+ def run(self):
+ try:
+ requests.post(
+ f"{self.api_url}{ApiEndPoints.QUEUE_LEAVE.value}",
+ headers={"Content-Type": "application/json"},
+ json={"uuid": self.user_id}
+ )
+ self.done.emit()
+ except Exception as e:
+ self.error.emit(str(e))
+
+
+class QueueManager(QThread):
+ """
+ Équivalent de startQueue() — gère tout le cycle :
+ join → poll toutes les 5s → lance FiveM quand c'est le tour
+ """
+ update_ui = Signal(str) # → updateQueueUI()
+ launch_game = Signal() # → launchFiveM()
+ error = Signal(str)
+
+ def __init__(self, user_id: str, parent=None):
+ super().__init__(parent)
+ self.user_id = user_id
+ self.api_url = Urls.API_URL.value
+ self._running = True
+
+ def stop(self):
+ self._running = False
+
+ def run(self):
+ # 1. Join queue
+ try:
+ res = requests.post(
+ f"{self.api_url}/queue/join",
+ headers={"Content-Type": "application/json"},
+ json={"uuid": self.user_id}
+ )
+ join = res.json()
+ except Exception as e:
+ self.error.emit(str(e))
+ return
+
+ # 2. Slot dispo directement
+ if join.get("status") == "ok":
+ self.update_ui.emit("Slot dispo, lancement du jeu...")
+ self.launch_game.emit()
+ return
+
+ # 3. En file d'attente → poll toutes les 5s
+ self.update_ui.emit(
+ f"⏳ Vous êtes en file d'attente : position {join.get('position')} / {join.get('queueSize')}"
+ )
+
+ while self._running:
+ self.sleep(5) # Équivalent setInterval 5000ms
+
+ if not self._running:
+ break
+
+ try:
+ res = requests.get(f"{self.api_url}/queue/status/{self.user_id}")
+ status = res.json()
+ except Exception as e:
+ self.error.emit(str(e))
+ return
+
+ if status.get("status") == "queued":
+ self.update_ui.emit(
+ f"⏳ Votre position : {status.get('position')} / {status.get('queueSize')}"
+ )
+ else:
+ self.update_ui.emit("🚀 C'est votre tour !")
+ self.launch_game.emit()
+ return
diff --git a/src/fivemserver/whitelistmanager.py b/src/fivemserver/whitelistmanager.py
index 2336b20..b8f24c3 100644
--- a/src/fivemserver/whitelistmanager.py
+++ b/src/fivemserver/whitelistmanager.py
@@ -2,6 +2,8 @@ import requests
from urllib3 import disable_warnings
from urllib3.exceptions import InsecureRequestWarning
+from config.constants import PlayerServerInfo
+
# Supress only InsecureRequestWarning
disable_warnings(InsecureRequestWarning)
@@ -9,15 +11,11 @@ WHITELIST_URL_ENDPOINT = f'iswhitelist/'
class WhiteList:
@staticmethod
- def checkwhitelist(url, discord_user_id: str) -> bool:
- print('🗒️ Vérification de la whitelist...')
+ def checkwhitelist(url, discord_user_id: str) -> None:
response = requests.get(url + WHITELIST_URL_ENDPOINT + discord_user_id, verify=False)
api_data = response.json()
- if api_data['whitelisted']:
- print("👍 Vous êtes en whitelist")
- return True
- else:
- print('🙅♂️ Désole mais vous n\'êtes pas whitelisté sur le serveur.')
- return False
+ PlayerServerInfo.is_whitelist = api_data.get('whitelisted', False)
+ #PlayerServerInfo.is_staff = api_data.get('isStaff', False)
+ PlayerServerInfo.is_staff = True
diff --git a/src/ui/main_window.py b/src/ui/main_window.py
index ba901da..b77d7cb 100644
--- a/src/ui/main_window.py
+++ b/src/ui/main_window.py
@@ -8,14 +8,15 @@ from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QMainWindow, QSizePolicy
from config.config_manager import ConfigManager
-from config.constants import NO_STAFF, Urls, NO_WHITELIST
-from ui.custom_message_box import CustomMessageBox
+from config.constants import PlayerServerInfo, Urls
from ui.hazard_stripes import HazardButton
from controllers.audio_controller import AudioController
from controllers.glow_animator import GlowAnimator
from controllers.window_dragger import WindowDragger
from discord import discord_oauth
-from tools.utils import quit_application
+from fivemserver.whitelistmanager import WhiteList
+from fivemserver.fivemlauncher import FiveMLauncher
+from fivemserver.queuemanager import QueueManager
from fake_patch_notes import patch_note
@@ -29,29 +30,29 @@ class MainWindow(QMainWindow):
self.config = config_manager
+ WhiteList.checkwhitelist(Urls.API_URL.value, self.config.get_discord_user())
+ if PlayerServerInfo.is_whitelist:
+ QueueManager(self.config.get_discord_user())
+
# UI
self.ui = QUiLoader().load(f"{bundle_dir}/ui/mainwindow_vertical_pager.ui", self)
self.setCentralWidget(self.ui.centralWidget())
self.setWindowFlags(Qt.WindowType.FramelessWindowHint | Qt.WindowType.Window)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
+ # Par défaut on affiche la page normal pour la connexion au serveur
self.ui.stackedWidget.setCurrentIndex(0)
- if NO_WHITELIST:
- self.ui.stackedWidget.setCurrentIndex(2)
- # msg = CustomMessageBox(
- # title="La Tanière: Non whitelisté",
- # message="\n\nTu n'est pas whitelisté sur le serveur\n\n"
- # "Assure-toi de te faire whitelister.\n\n"
- # "Lorsque cela sera fait, relance le launcher.",
- # icon_type=CustomMessageBox.WARNING,
- # buttons=CustomMessageBox.OK
- # )
- # msg.exec()
- # quit_application()
+ # Si l'id discord = "" ou des espace, alors on affiche la page comme quoi faut être connecté à discord.
+ if config_manager.get_discord_user() == "" or config_manager.get_discord_user().isspace():
+ self.ui.queue_lbl.hide()
+ self.ui.queue_position.hide()
+ self.ui.stackedWidget.setCurrentIndex(1)
# Test bouton en contruction
- en_chantier = True
+ en_chantier = False
+ # on set la css du bouton en fonction de la valeur de la variable en_chantier
+ self.set_en_chantier(en_chantier)
if en_chantier:
old_btn = self.ui.connexion_btn
parent_layout = self.ui.verticalLayout_6 # layout direct du bouton dans le .ui
@@ -74,7 +75,7 @@ class MainWindow(QMainWindow):
self.ui.connexion_btn.clicked.connect(self._on_connexion)
# centrage vertical du bouton connexion
- if NO_STAFF:
+ if not PlayerServerInfo.is_staff:
self.ui.staff_btn.hide()
layout = self.ui.verticalLayout_6
# Trouver et modifier le spacer item
@@ -84,12 +85,6 @@ class MainWindow(QMainWindow):
item.spacerItem().changeSize(20, 15, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
layout.invalidate() # Forcer le recalcul du layout
break
- # self.ui.spacer_substitution.hide()
-
- if config_manager.get_discord_user() == "" or config_manager.get_discord_user().isspace():
- self.ui.queue_lbl.hide()
- self.ui.queue_position.hide()
- self.ui.stackedWidget.setCurrentIndex(1)
self.ui.info_text.setMarkdown(patch_note)
@@ -100,6 +95,15 @@ class MainWindow(QMainWindow):
self._connect_signals()
self._center_window()
+
+ # si le jouer n'est pas whitelisté, on affiche la page pour se faire whitelister
+ if not PlayerServerInfo.is_whitelist:
+ self.ui.stackedWidget.setCurrentIndex(2)
+
+ # pour le moment on cache les controle queue
+ self.ui.queue_lbl.setVisible(False)
+ self.ui.queue_position.setVisible(False)
+
self.show()
@@ -131,7 +135,7 @@ class MainWindow(QMainWindow):
# ------------------------------------------------------------------
def _on_connexion(self) -> None:
- pass # à implémenter
+ FiveMLauncher.launch()
@staticmethod
def _on_discord() -> None:
@@ -169,3 +173,12 @@ class MainWindow(QMainWindow):
def closeEvent(self, event) -> None:
self.config.save()
super().closeEvent(event)
+
+ # ------------------------------------------------------------------
+ # Change ui on runtime
+ # ------------------------------------------------------------------
+ def set_en_chantier(self, valeur: bool):
+ self.en_chantier = valeur # ta variable Python
+ self.ui.connexion_btn.setProperty("en_chantier", valeur) # propriété Qt
+ self.ui.connexion_btn.style().unpolish(self.ui.connexion_btn)
+ self.ui.connexion_btn.style().polish(self.ui.connexion_btn)
diff --git a/styles/styles.qss b/styles/styles.qss
index 13b7066..3827aa1 100644
--- a/styles/styles.qss
+++ b/styles/styles.qss
@@ -43,7 +43,8 @@ QLabel#queue_lbl {
}
-QPushButton#connexion_btn[chantier="false"] {
+/* ------------------------------------ BUTTONS --------------------------------------------------*/
+QPushButton#connexion_btn[en_chantier="false"] {
/* Dégradé chaleureux : Orange vers Orange-Rouge */
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop: 0 #ff9d00,
@@ -55,7 +56,7 @@ QPushButton#connexion_btn[chantier="false"] {
padding: 10px;
}
-QPushButton#connexion_btn[chantier="false"]:hover {
+QPushButton#connexion_btn[en_chantier="false"]:hover {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop: 0 #ffb338,
stop: 1 #ff7a29);
@@ -64,7 +65,7 @@ QPushButton#connexion_btn[chantier="false"]:hover {
outline: none;
}
-QPushButton#connexion_btn[chantier="false"]:pressed {
+QPushButton#connexion_btn[en_chantier="false"]:pressed {
background: #cc5200;
padding-top: 12px; /* Effet d'enfoncement */
}
@@ -72,39 +73,31 @@ QPushButton#connexion_btn[chantier="false"]:pressed {
/* État normal - Rouge Corail Vibrant */
QPushButton#staff_btn {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
- stop:0 #FF4B2B, stop:1 #FF416C);
- color: white;
+ stop: 0 #FF4B2B,
+ stop: 1 #FF416C);
border-radius: 12px;
border: 1px solid #d03522;
- padding: 5px 15px;
-}
-
-HazardButton#connexion_btn {
- color: #0A1A3A;
-/* color: #0D2A6B;*/
- font-weight: bold;
-}
-
-HazardButton#connexion_btn:hover {
-/* color: #ffffff;*/
- color: #0D2A6B;
-}
-
-HazardButton#connexion_btn:pressed {
- color: #333333;
+ color: white;
+/* padding: 5px 15px;*/
+ padding: 10px;
}
QPushButton#staff_btn:hover {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
- stop:0 #FF6046, stop:1 #FF527B);
- border: 1px solid #FF4B2B;
+ stop: 0 #FF6046,
+ stop: 1 #FF527B);
+/* border: 1px solid #FF4B2B;*/
+ border: 1px solid #FFFFFF;
+ /* Un léger halo autour du bouton */
+ outline: none;
}
QPushButton#staff_btn:pressed
{
background-color: #d03522;
- padding-top: 7px;
- padding-left: 17px;
+/* padding-top: 7px;*/
+/* padding-left: 17px;*/
+ padding: 12px;
}
QPushButton#discord_btn,
@@ -171,6 +164,23 @@ QPushButton#minimize_btn {
padding-top: 0
}
+HazardButton#connexion_btn {
+ color: #0A1A3A;
+/* color: #0D2A6B;*/
+ font-weight: bold;
+}
+
+HazardButton#connexion_btn:hover {
+/* color: #ffffff;*/
+ color: #0D2A6B;
+}
+
+HazardButton#connexion_btn:pressed {
+ color: #333333;
+}
+
+/* ------------------------------------ Other --------------------------------------------------*/
+
QFrame#info_frame{
background: qlineargradient(
x1:0, y1:0,
diff --git a/ui/mainwindow_vertical_pager.ui b/ui/mainwindow_vertical_pager.ui
index 2b41ebe..5bedbda 100644
--- a/ui/mainwindow_vertical_pager.ui
+++ b/ui/mainwindow_vertical_pager.ui
@@ -640,7 +640,7 @@
QFrame::Shape::NoFrame
- 2
+ 0