diff --git a/traque-back/game.js b/traque-back/game.js
index 4a170cf..2011d97 100644
--- a/traque-back/game.js
+++ b/traque-back/game.js
@@ -1,5 +1,3 @@
-import { Socket } from "socket.io";
-
export default class Game {
constructor() {
this.teams = [];
@@ -31,7 +29,8 @@ export default class Game {
chased: null,
currentLocation: [0, 0],
lastSentLocation: [0, 0],
- enemyLocation: [0, 0]
+ enemyLocation: [0, 0],
+ sockets: []
});
this.updateTeamChasing();
return true;
diff --git a/traque-back/index.js b/traque-back/index.js
index 581b62a..af0ae89 100644
--- a/traque-back/index.js
+++ b/traque-back/index.js
@@ -158,7 +158,9 @@ io.of("player").on("connection", (socket) => {
socket.on("disconnect", () => {
console.log("user disconnected");
- game.getTeam(teamId).sockets = game.getTeam(teamId).sockets.filter(s => s !== socket.id);
+ if(teamId !== null && game.getTeam(teamId) !== undefined){
+ game.getTeam(teamId).sockets = game.getTeam(teamId).sockets.filter(s => s !== socket.id);
+ }
});
socket.on("login", (teamId) => {
@@ -175,7 +177,6 @@ io.of("player").on("connection", (socket) => {
});
socket.on("send_position", () => {
- console.log("send_position", position);
game.sendLocation(teamId);
game.getTeam(teamId).sockets.forEach(s => {
io.of("player").to(s).emit("enemy_position", game.getTeam(teamId).enemyLocation);
diff --git a/traque-front/app/page.js b/traque-front/app/page.js
deleted file mode 100644
index 2d175e1..0000000
--- a/traque-front/app/page.js
+++ /dev/null
@@ -1,13 +0,0 @@
-"use client"
-import LoginForm from "@/components/team/loginForm";
-
-export default function Home() {
- function login(teamId) {
- console.log(teamId);
- }
- return (
-
-
-
- );
-}
diff --git a/traque-front/app/team/layout.js b/traque-front/app/team/layout.js
new file mode 100644
index 0000000..f877101
--- /dev/null
+++ b/traque-front/app/team/layout.js
@@ -0,0 +1,12 @@
+import { TeamConnexionProvider } from "@/context/teamConnexionContext";
+import { TeamProvider } from "@/context/teamContext";
+
+export default function AdminLayout({ children}) {
+ return (
+
+
+ {children}
+
+
+ )
+}
\ No newline at end of file
diff --git a/traque-front/app/team/page.js b/traque-front/app/team/page.js
new file mode 100644
index 0000000..141267a
--- /dev/null
+++ b/traque-front/app/team/page.js
@@ -0,0 +1,19 @@
+"use client"
+import LoginForm from "@/components/team/loginForm";
+import useGame from "@/hook/useGame";
+import { redirect } from "next/navigation";
+import { useEffect } from "react";
+
+export default function Home() {
+ const { login, loggedIn } = useGame();
+ useEffect(() => {
+ if (loggedIn) {
+ redirect("/team/track");
+ }
+ }, [loggedIn]);
+ return (
+
+ login(parseInt(value))}/>
+
+ );
+}
diff --git a/traque-front/app/track/page.js b/traque-front/app/team/track/page.js
similarity index 80%
rename from traque-front/app/track/page.js
rename to traque-front/app/team/track/page.js
index 3d7f9cb..8f6bf3b 100644
--- a/traque-front/app/track/page.js
+++ b/traque-front/app/team/track/page.js
@@ -1,8 +1,9 @@
"use client";
import Button from '@/components/util/button';
+import useGame from '@/hook/useGame';
import dynamic from 'next/dynamic';
+import { redirect } from 'next/navigation';
import React, { useEffect } from 'react'
-import useGame from '../../hook/useGame';
//Load the map without SSR
const LiveMap = dynamic(() => import('@/components/team/map'), {
@@ -10,7 +11,12 @@ const LiveMap = dynamic(() => import('@/components/team/map'), {
});
export default function Track() {
- const { currentPosition, enemyPosition, updateCurrentPosition, sendCurrentPosition } = useGame();
+ const { currentPosition, enemyPosition, loggedIn, sendCurrentPosition } = useGame();
+ useEffect(() => {
+ if (!loggedIn) {
+ redirect("/team");
+ }
+ }, [loggedIn]);
return (
diff --git a/traque-front/components/team/map.jsx b/traque-front/components/team/map.jsx
index 290a234..085167d 100644
--- a/traque-front/components/team/map.jsx
+++ b/traque-front/components/team/map.jsx
@@ -7,12 +7,14 @@ import "leaflet/dist/leaflet.css";
const DEFAULT_ZOOM = 17;
+
+// Pan to the center of the map when the position of the user is updated for the first time
function MapPan(props) {
const map = useMap();
const [initialized, setInitialized] = useState(false);
useEffect(() => {
- if(!initialized && JSON.stringify(props.center) != "[0,0]") {
+ if(!initialized && props.center) {
map.flyTo(props.center, DEFAULT_ZOOM);
setInitialized(true)
}
@@ -22,20 +24,6 @@ function MapPan(props) {
}
export default function LiveMap({enemyPosition, currentPosition, ...props}) {
- const [positionSet, setPositionSet] = useState(false);
- useEffect(() => {
- if(!positionSet && JSON.stringify(currentPosition) != "[0,0]") {
- setPositionSet(true);
- }
- }, [currentPosition]);
- const [enemyPositionSet, setEnemyPositionSet] = useState(false);
- useEffect(() => {
- if(!enemyPositionSet && JSON.stringify(enemyPosition) != "[0,0]") {
- setEnemyPositionSet(true);
- }
- }, [enemyPosition]);
-
-
return (
@@ -43,7 +31,7 @@ export default function LiveMap({enemyPosition, currentPosition, ...props}) {
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
- {positionSet &&
}
- {enemyPositionSet && {
const [loggedIn, setLoggedIn] = useState(false);
const { adminSocket } = useSocket();
-
+
function login(password) {
adminSocket.emit("login", password);
}
-
- useEffect(() => {
- function updateLoginStatus(status) {
- setLoggedIn(status);
- }
- adminSocket.on("login_response", updateLoginStatus);
-
- return () => {
- adminSocket.off("login_response", updateLoginStatus);
- };
- }, []);
-
+
+ useSocketListener(adminSocket, "login_response", setLoggedIn);
+
return (
- {children}
+ {children}
);
}
@@ -33,5 +25,5 @@ function useAdminConnexion() {
return useContext(adminContext);
}
-export { AdminConnexionProvider, useAdminConnexion};
+export { AdminConnexionProvider, useAdminConnexion };
diff --git a/traque-front/context/adminContext.jsx b/traque-front/context/adminContext.jsx
index e6ce9ec..f4af89d 100644
--- a/traque-front/context/adminContext.jsx
+++ b/traque-front/context/adminContext.jsx
@@ -1,11 +1,26 @@
"use client";
-import { createContext, useContext, useState } from "react";
+import { createContext, useContext, useEffect, useState } from "react";
+import { useSocket } from "./socketContext";
+import { useSocketListener } from "@/hook/useSocketListener";
+import { useAdminConnexion } from "./adminConnexionContext";
const adminContext = createContext();
function AdminProvider({children}) {
const [teams, setTeams] = useState([]);
const [started, setStarted] = useState(false);
+ const { adminSocket } = useSocket();
+ const {loggedIn} = useAdminConnexion();
+
+ //Send a request to get the teams when the user logs in
+ useEffect(() => {
+ adminSocket.emit("get_teams");
+ }, [loggedIn]);
+
+ //Bind listeners to update the team list and the game status on socket message
+ useSocketListener(adminSocket, "teams", setTeams);
+ useSocketListener(adminSocket, "game_started", setStarted);
+
return (
{children}
diff --git a/traque-front/context/socketContext.jsx b/traque-front/context/socketContext.jsx
index 7c2bd12..0ef0526 100644
--- a/traque-front/context/socketContext.jsx
+++ b/traque-front/context/socketContext.jsx
@@ -4,16 +4,16 @@ import { createContext, useContext } from "react";
const { io } = require("socket.io-client");
const SOCKET_URL = "http://localhost:3000";
-const USER_SOCKET_URL = SOCKET_URL + "/user";
+const USER_SOCKET_URL = SOCKET_URL + "/player";
const ADMIN_SOCKET_URL = SOCKET_URL + "/admin";
-export const userSocket = io(USER_SOCKET_URL);
+export const teamSocket = io(USER_SOCKET_URL);
export const adminSocket = io(ADMIN_SOCKET_URL);
export const SocketContext = createContext();
export default function SocketProvider({ children }) {
return (
- {children}
+ {children}
);
}
diff --git a/traque-front/context/teamConnexionContext.jsx b/traque-front/context/teamConnexionContext.jsx
new file mode 100644
index 0000000..0aec041
--- /dev/null
+++ b/traque-front/context/teamConnexionContext.jsx
@@ -0,0 +1,31 @@
+"use client";
+import { createContext, useContext, useState } from "react";
+import { useSocket } from "./socketContext";
+import { useSocketListener } from "@/hook/useSocketListener";
+
+const teamConnexionContext = createContext();
+const TeamConnexionProvider = ({ children }) => {
+ const [loggedIn, setLoggedIn] = useState(false);
+ const [teamId, setTeamId] = useState(null);
+ const { teamSocket } = useSocket();
+
+ function login(id) {
+ teamSocket.emit("login", id);
+ setTeamId(id);
+ }
+
+ useSocketListener(teamSocket, "login_response", setLoggedIn);
+
+ return (
+
+ {children}
+
+ );
+}
+
+function useTeamConnexion() {
+ return useContext(teamConnexionContext);
+}
+
+export { TeamConnexionProvider, useTeamConnexion};
+
diff --git a/traque-front/context/teamContext.jsx b/traque-front/context/teamContext.jsx
new file mode 100644
index 0000000..b3eca3f
--- /dev/null
+++ b/traque-front/context/teamContext.jsx
@@ -0,0 +1,36 @@
+"use client";
+import { useLocation } from "@/hook/useLocation";
+import { useSocketListener } from "@/hook/useSocketListener";
+import { createContext, useContext, useEffect, useState } from "react";
+import { useSocket } from "./socketContext";
+import { useTeamConnexion } from "./teamConnexionContext";
+
+const teamContext = createContext()
+function TeamProvider({children}) {
+ const [enemyPosition, setEnemyPosition] = useState();
+ const currentPosition = useLocation(10000);
+ const {teamSocket} = useSocket();
+ const {loggedIn} = useTeamConnexion();
+
+ useSocketListener(teamSocket, "enemy_position", setEnemyPosition);
+
+ //Send the current position to the server when the user is logged in
+ useEffect(() => {
+ console.log("sending position", currentPosition);
+ if(loggedIn) {
+ teamSocket.emit("update_position", currentPosition);
+ }
+ }, [loggedIn, currentPosition]);
+
+ return (
+
+ {children}
+
+ );
+}
+
+function useTeamContext() {
+ return useContext(teamContext);
+}
+
+export { TeamProvider, useTeamContext };
\ No newline at end of file
diff --git a/traque-front/hook/useAdmin.jsx b/traque-front/hook/useAdmin.jsx
index 077fe30..45e9ede 100644
--- a/traque-front/hook/useAdmin.jsx
+++ b/traque-front/hook/useAdmin.jsx
@@ -1,28 +1,14 @@
import { useAdminContext } from "@/context/adminContext";
import { useSocket } from "@/context/socketContext";
-import { Underdog } from "next/font/google";
-
-const { useEffect, useState } = require("react");
export default function useAdmin(){
- const {teams, setTeams, started, setStarted} = useAdminContext();
+ const {teams, started} = useAdminContext();
const {adminSocket} = useSocket();
function pollTeams() {
adminSocket.emit("get_teams");
}
- useEffect(() => {
- pollTeams();
- }, []);
- useEffect(() => {
- adminSocket.emit("get_teams");
- adminSocket.on("teams", setTeams);
- return () => {
- adminSocket.off("teams", setTeams);
- }
- }, []);
-
function getTeam(teamId) {
return teams.find(team => team.id === teamId);
}
@@ -56,13 +42,6 @@ export default function useAdmin(){
adminSocket.emit("stop_game");
}
- useState(() => {
- adminSocket.on("game_started", setStarted);
- return () => {
- adminSocket.off("game_started", setStarted);
- }
- }, []);
-
return { teams, started, pollTeams, getTeam, getTeamName, reorderTeams, addTeam, removeTeam, startGame, stopGame, setTeamName };
}
\ No newline at end of file
diff --git a/traque-front/hook/useGame.jsx b/traque-front/hook/useGame.jsx
index d422bf5..67fe9ef 100644
--- a/traque-front/hook/useGame.jsx
+++ b/traque-front/hook/useGame.jsx
@@ -1,61 +1,17 @@
"use client";
import { useSocket } from "@/context/socketContext";
-import { useEffect, useState } from "react";
+import { useTeamConnexion } from "@/context/teamConnexionContext";
+import { useTeamContext } from "@/context/teamContext";
export default function useGame() {
- const {userSocket} = useSocket();
- const [loggedIn, setLoggedIn] = useState(false);
- const [teamId, setTeamId] = useState(null);
- const [enemyPosition, setEnemyPosition] = useState([0, 0]);
- const [currentPosition, setCurrentPosition] = useState([0, 0]);
-
- function updateCurrentPosition(position) {
- setCurrentPosition(position);
- userSocket.emit("update_position", position);
- }
+ const {teamSocket} = useSocket();
+ const {loggedIn, login, teamId} = useTeamConnexion();
+ const {currentPosition, enemyPosition} = useTeamContext();
function sendCurrentPosition() {
- userSocket.emit("send_position", currentPosition);
+ teamSocket.emit("send_position", currentPosition);
}
- useEffect(() => {
- function updateEnemyPosition(position) {
- setEnemyPosition(position);
- }
- userSocket.on("enemy_position", updateEnemyPosition);
- return () => {
- userSocket.off("enemy_position", updateEnemyPosition);
- }
- }, []);
-
- function login(teamId) {
- setTeamId(teamId);
- userSocket.emit("login", teamId);
- }
- useEffect(() => {
- function updateLoginStatus(status) {
- setLoggedIn(status);
- }
- userSocket.on("login_reponse", updateLoginStatus);
-
- return () => {
- userSocket.off("login_response", updateLoginStatus);
- }
- }, []);
-
- useEffect(() => {
- function udpate() {
- console.log("update")
- const position = navigator.geolocation.getCurrentPosition((position) => {
- updateCurrentPosition([position.coords.latitude, position.coords.longitude]);
- }, () => { }, { enableHighAccuracy: true, timeout: Infinity, maximumAge: 0 });
- }
- setInterval(udpate, 1000);
- return () => {
- clearInterval(udpate);
- }
- }, []);
-
- return { updateCurrentPosition, sendCurrentPosition, login, enemyPosition, currentPosition, loggedIn, teamId };
+ return { sendCurrentPosition, login, enemyPosition, currentPosition, loggedIn, teamId };
}
\ No newline at end of file
diff --git a/traque-front/hook/useLocation.jsx b/traque-front/hook/useLocation.jsx
new file mode 100644
index 0000000..70e8c17
--- /dev/null
+++ b/traque-front/hook/useLocation.jsx
@@ -0,0 +1,21 @@
+"use client";
+import { useEffect, useState } from "react";
+
+/**
+ * A hook that returns the location of the user and updates it periodically
+ * @returns {Object} The location of the user
+ */
+export function useLocation(interval) {
+ const [location, setLocation] = useState();
+ useEffect(() => {
+ function update() {
+ navigator.geolocation.getCurrentPosition((position) => {
+ setLocation([position.coords.latitude, position.coords.longitude]);
+ setTimeout(update, interval);
+ }, () => { }, { enableHighAccuracy: true, timeout: Infinity, maximumAge: 0 });
+ }
+ update();
+ }, []);
+
+ return location;
+}
\ No newline at end of file
diff --git a/traque-front/hook/useSocketListener.jsx b/traque-front/hook/useSocketListener.jsx
new file mode 100644
index 0000000..85c89e8
--- /dev/null
+++ b/traque-front/hook/useSocketListener.jsx
@@ -0,0 +1,10 @@
+import { useState } from "react";
+
+export function useSocketListener(socket, event, callback) {
+ return useState(() => {
+ socket.on(event, callback);
+ return () => {
+ socket.off(event, callback);
+ }
+ }, []);
+}
\ No newline at end of file