Merge branch 'main' of github.com:quentinrsl/traque into main

This commit is contained in:
2024-04-28 20:00:48 +00:00
13 changed files with 170 additions and 47 deletions

View File

@@ -1,4 +1,6 @@
"use client"; "use client";
import { GameSettings } from "@/components/admin/gameSettings";
import { PenaltySettings } from "@/components/admin/penaltySettings";
import { TeamReady } from "@/components/admin/teamReady"; import { TeamReady } from "@/components/admin/teamReady";
import BlueButton, { GreenButton, RedButton } from "@/components/util/button"; import BlueButton, { GreenButton, RedButton } from "@/components/util/button";
import { useAdminConnexion } from "@/context/adminConnexionContext"; import { useAdminConnexion } from "@/context/adminConnexionContext";
@@ -13,24 +15,28 @@ const LiveMap = dynamic(() => import('@/components/admin/mapPicker').then((mod)
ssr: false ssr: false
}); });
export default function AdminPage() { export default function AdminPage() {
const { useProtect } = useAdminConnexion(); const { useProtect } = useAdminConnexion();
const { gameState, changeState } = useAdmin(); const { gameState, changeState } = useAdmin();
useProtect(); useProtect();
return ( return (
<div className='min-h-full bg-gray-200 p-10 flex flex-row content-start gap-5'> <div className='min-h-full bg-gray-200 p-10 flex flex-row content-start gap-5'>
<div className="h-full"> <div className="h-full w-2/6">
<div className='w-max h-1/2 gap-3 bg-white p-10 flex flex-col text-center shadow-2xl '> <div className='w-full mb-5 h-1/2 gap-3 bg-white p-10 flex flex-col text-center shadow-2xl '>
<h2 className="text-2xl">Game state </h2> <h2 className="text-2xl">Game state </h2>
<strong className="p-5 bg-gray-900 text-white text-xl rounded">Current : {gameState}</strong> <strong className="p-5 bg-gray-900 text-white text-xl rounded">Current : {gameState}</strong>
<div className="flex flex-row">
<RedButton onClick={() => changeState(GameState.SETUP)}>Reset game</RedButton> <RedButton onClick={() => changeState(GameState.SETUP)}>Reset game</RedButton>
<GreenButton onClick={() => changeState(GameState.PLACEMENT)}>Start placement</GreenButton> <GreenButton onClick={() => changeState(GameState.PLACEMENT)}>Start placement</GreenButton>
<BlueButton onClick={() => changeState(GameState.PLAYING)}>Start game</BlueButton> <BlueButton onClick={() => changeState(GameState.PLAYING)}>Start game</BlueButton>
</div>
</div> </div>
<GameSettings />
</div> </div>
{gameState == GameState.PLACEMENT && <div className="max-h-5/6"><TeamReady /></div>} {gameState == GameState.PLACEMENT && <div className="max-h-5/6"><TeamReady /></div>}
{(gameState == GameState.SETUP || gameState == GameState.PLACEMENT) && <ZoneSelector />} {(gameState == GameState.SETUP || gameState == GameState.PLACEMENT) && <ZoneSelector />}
{(gameState == GameState.SETUP || gameState == GameState.PLACEMENT) && <PenaltySettings />}
{gameState == GameState.PLAYING && <div className='grow flex-1 row-span-2 bg-white p-10 flex shadow-2xl'> {gameState == GameState.PLAYING && <div className='grow flex-1 row-span-2 bg-white p-10 flex shadow-2xl'>
<LiveMap /> <LiveMap />
</div>} </div>}
</div> </div>
) )

View File

@@ -6,6 +6,7 @@ import { WaitingScreen } from '@/components/team/waitingScreen';
import { LogoutButton } from '@/components/util/button'; import { LogoutButton } from '@/components/util/button';
import { useSocket } from '@/context/socketContext'; import { useSocket } from '@/context/socketContext';
import { useTeamConnexion } from '@/context/teamConnexionContext'; import { useTeamConnexion } from '@/context/teamConnexionContext';
import { useTeamContext } from '@/context/teamContext';
import useGame from '@/hook/useGame'; import useGame from '@/hook/useGame';
import { GameState } from '@/util/gameState'; import { GameState } from '@/util/gameState';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
@@ -20,7 +21,8 @@ const PlacementMap = dynamic(() => import('@/components/team/map').then((mod) =>
}); });
export default function Track() { export default function Track() {
const { gameState, captured } = useGame(); const { gameState, captured } = useGame();
const { gameSettings} = useTeamContext()
const { useProtect } = useTeamConnexion(); const { useProtect } = useTeamConnexion();
const { teamSocket: socket } = useSocket(); const { teamSocket: socket } = useSocket();
useProtect(); useProtect();
@@ -34,7 +36,7 @@ export default function Track() {
} }
{gameState == GameState.PLAYING && captured && <div className='h-full'> {gameState == GameState.PLAYING && captured && <div className='h-full'>
<div className='text-center text-2xl'>Vous avez été capturé</div> <div className='text-center text-2xl'>Vous avez été capturé</div>
<div className='text-center text-md'>Instructions de retour ici</div> <div className='text-center text-md'>{gameSettings?.capturedMessage}</div>
</div>} </div>}
{gameState == GameState.PLACEMENT && {gameState == GameState.PLACEMENT &&
<div className='h-full'> <div className='h-full'>
@@ -45,8 +47,8 @@ export default function Track() {
{gameState == GameState.FINISHED && <div className='h-full'> {gameState == GameState.FINISHED && <div className='h-full'>
<LogoutButton /> <LogoutButton />
<div className='text-center text-2xl'>Partie terminée</div> <div className='text-center text-2xl'>Partie terminée</div>
{captured && <div className='text-center text-md'>Vous avez perdu</div>} {captured && <div className='text-center text-md'>{gameSettings?.loserEndGameMessage}</div>}
{!captured && <div className='text-center text-md'>Vous avez gagné</div>} {!captured && <div className='text-center text-md'>{gameSettings?.winnerEndGameMessage}</div>}
</div> </div>
} }
</> </>

View File

@@ -0,0 +1,49 @@
import useAdmin from "@/hook/useAdmin";
import { TextArea } from "../util/textInput";
import { GreenButton } from "../util/button";
import { useEffect, useState } from "react";
export const GameSettings = () => {
const {gameSettings, changeGameSettings} = useAdmin();
const [capturedMessage, setCapturedMessage] = useState("");
const [winnerEndMessage, setWinnerEndMessage] = useState("");
const [loserEndMessage, setLoserEndMessage] = useState("");
const [waitingMessage, setWaitingMessage] = useState("");
useEffect(() => {
console.log({gameSettings})
if (gameSettings) {
setCapturedMessage(gameSettings.capturedMessage);
setWinnerEndMessage(gameSettings.winnerEndGameMessage);
setLoserEndMessage(gameSettings.loserEndGameMessage);
setWaitingMessage(gameSettings.waitingMessage);
}
}, [gameSettings]);
function applySettings() {
changeGameSettings({capturedMessage: capturedMessage, winnerEndGameMessage: winnerEndMessage, loserEndGameMessage: loserEndMessage, waitingMessage: waitingMessage});
}
return (
<div className='w-full h-full gap-1 bg-white p-10 flex flex-col text-center shadow-2xl overflow-y-scroll'>
<h2 className="text-2xl">Other settings</h2>
<div>
<p>Waiting message</p>
<TextArea value={waitingMessage} onChange={(e) => setWaitingMessage(e.target.value)} />
</div>
<div>
<p>Captured message</p>
<TextArea value={capturedMessage} onChange={(e) => setCapturedMessage(e.target.value)} />
</div>
<div>
<p>Game finished message (winner)</p>
<TextArea value={winnerEndMessage} onChange={(e) => setWinnerEndMessage(e.target.value)} />
</div>
<div>
<p>Game finished message (loser)</p>
<TextArea value={loserEndMessage} onChange={(e) => setLoserEndMessage(e.target.value)} />
</div>
<GreenButton onClick={applySettings}>Apply</GreenButton>
</div>
)
}

View File

@@ -0,0 +1,42 @@
import useAdmin from "@/hook/useAdmin";
import TextInput from "../util/textInput";
import { GreenButton } from "../util/button";
import { useEffect, useState } from "react";
export const PenaltySettings = () => {
const {penaltySettings, changePenaltySettings} = useAdmin();
const [maxPenalties, setMaxPenalties] = useState("");
const [allowedTimeOutOfZone, setAllowedTimeOutOfZone] = useState("");
const [allowedTimeBetweenUpdates, setAllowedTimeBetweenUpdates] = useState("");
useEffect(() => {
if (penaltySettings) {
setMaxPenalties(penaltySettings.maxPenalties.toString());
setAllowedTimeOutOfZone(penaltySettings.allowedTimeOutOfZone.toString());
setAllowedTimeBetweenUpdates(penaltySettings.allowedTimeBetweenPositionUpdate.toString());
}
}, [penaltySettings]);
function applySettings() {
changePenaltySettings({maxPenalties: Number(maxPenalties), allowedTimeOutOfZone: Number(allowedTimeOutOfZone), allowedTimeBetweenPositionUpdate: Number(allowedTimeBetweenUpdates)});
}
return (
<div className='w-2/5 h-full gap-1 bg-white p-10 flex flex-col text-center shadow-2xl overflow-y-scroll'>
<h2 className="text-2xl">Penalties</h2>
<div>
<p>Maximum Penalties</p>
<TextInput value={maxPenalties} onChange={(e) => setMaxPenalties(e.target.value)}></TextInput>
</div>
<div>
<p>Time out of the zone before a penalty</p>
<TextInput value={allowedTimeOutOfZone} onChange={(e) => setAllowedTimeOutOfZone(e.target.value)}></TextInput>
</div>
<div>
<p>Allowed time between position updates</p>
<TextInput value={allowedTimeBetweenUpdates} onChange={(e) => setAllowedTimeBetweenUpdates(e.target.value)}></TextInput>
</div>
<GreenButton onClick={applySettings}>Apply</GreenButton>
</div>
)
}

View File

@@ -42,10 +42,9 @@ export default function TeamEdit({ selectedTeamId, setSelectedTeamId }) {
} }
return (team && return (team &&
<div className='flex flex-col h-full'> <div className='flex flex-col w-full h-full'>
<div className='flex flex-row gap-2'>
<div className='flex flex-row'> <div className='flex w-1/2 flex-col gap-2 h-min self-start'>
<div className='w-1/2 flex flex-col space-y-3 mx-2'>
<h2 className='text-2xl text-center'>Actions</h2> <h2 className='text-2xl text-center'>Actions</h2>
<form className='flex flex-row' onSubmit={handleRename}> <form className='flex flex-row' onSubmit={handleRename}>
<div className='w-4/5'> <div className='w-4/5'>
@@ -55,10 +54,12 @@ export default function TeamEdit({ selectedTeamId, setSelectedTeamId }) {
<BlueButton type="submit">Rename</BlueButton> <BlueButton type="submit">Rename</BlueButton>
</div> </div>
</form> </form>
<BlueButton onClick={() => updateTeam(team.id, { captured: !team.captured })}>{team.captured ? "Revive" : "Capture"}</BlueButton> <div className='flex flex-row'>
<RedButton onClick={handleRemove}>Remove</RedButton> <BlueButton onClick={() => updateTeam(team.id, { captured: !team.captured })}>{team.captured ? "Revive" : "Capture"}</BlueButton>
<RedButton onClick={handleRemove}>Remove</RedButton>
</div>
</div> </div>
<div className='w-1/2 flex flex-col space-y-2 mx-2'> <div className='flex w-1/2 flex-col space-y-2 h-min self-start'>
<h2 className='text-2xl text-center'>Team details</h2> <h2 className='text-2xl text-center'>Team details</h2>
<div> <div>
<p>Secret : {String(team.id).padStart(6, '0')}</p> <p>Secret : {String(team.id).padStart(6, '0')}</p>
@@ -77,18 +78,16 @@ export default function TeamEdit({ selectedTeamId, setSelectedTeamId }) {
</div> </div>
</div> </div>
</div> </div>
<div className='flex flex-row h-full w-full'> <div className='flex flex-row'>
<div className='w-1/2 h-full p-5 flex flex-col'> <p className='text-2xl text-center w-full'>Starting zone</p>
<h2 className='text-2xl text-center'>Starting area</h2> <p className='text-2xl text-center w-full'>Profile picture</p>
<div className='h-full p-5'> </div>
<CircularAreaPicker area={team.startingArea} setArea={(startingArea) => updateTeam(team.id, { startingArea })} markerPosition={team?.currentLocation} /> <div className='flex grow flex-row'>
</div> <div className='w-1/2'>
<CircularAreaPicker area={team.startingArea} setArea={(startingArea) => updateTeam(team.id, { startingArea })} markerPosition={team?.currentLocation} />
</div> </div>
<div className='w-1/2 h-full p-5 flex flex-col'> <div className='w-1/2'>
<h2 className='text-2xl text-center'>Team photo</h2> <img className='self-stretch' ref={teamImage} />
<div className='h-full p-5'>
<img ref={teamImage} className='w-full h-full object-contain' />
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -65,9 +65,9 @@ export default function ActionDrawer() {
<GreenButton onClick={handleCapture}>Capture target</GreenButton> <GreenButton onClick={handleCapture}>Capture target</GreenButton>
</div> </div>
</div> </div>
<div className="h-20"> {/* <div className="h-20">
<RedButton onClick={sendCurrentPosition}>Signal emergency</RedButton> <RedButton onClick={sendCurrentPosition}>Signal emergency</RedButton>
</div> </div> */}
</div> </div>
} }
<EnemyTeamModal visible={enemyModalVisible} onClose={() => setEnemyModalVisible(false)} /> <EnemyTeamModal visible={enemyModalVisible} onClose={() => setEnemyModalVisible(false)} />

View File

@@ -1,34 +1,36 @@
import { useSocketListener } from "@/hook/useSocketListener"; import { useSocketListener } from "@/hook/useSocketListener";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
export function Notification({socket }) { export function Notification({ socket }) {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [timeoutId, setTimeoutId] = useState(null); const [timeoutId, setTimeoutId] = useState(null);
const [notification, setNotification] = useState(null); const [notification, setNotification] = useState(null);
useSocketListener(socket, "error", (notification) => { useSocketListener(socket, "error", (notification) => {
console.log("error", notification); console.log("error", notification);
setNotification({type: "error", text: notification}); setNotification({ type: "error", text: notification });
setVisible(true); setVisible(true);
}); });
useSocketListener(socket, "success", (notification) => { useSocketListener(socket, "success", (notification) => {
console.log("success", notification); console.log("success", notification);
setNotification({type: "success", text: notification}); setNotification({ type: "success", text: notification });
setVisible(true); setVisible(true);
}); });
useSocketListener(socket, "warning", (notification) => { useSocketListener(socket, "warning", (notification) => {
console.log("warning", notification); console.log("warning", notification);
setNotification({type: "warning", text: notification}); setNotification({ type: "warning", text: notification });
setVisible(true); setVisible(true);
}); });
// Hide the notification after 5 seconds // Hide the notification after 5 seconds
useEffect(() => { useEffect(() => {
console.log({visible}); console.log({ visible });
if (visible && notification?.text != undefined) { if (visible && notification?.text != undefined) {
clearTimeout(timeoutId); clearTimeout(timeoutId);
setTimeoutId(setTimeout(() => { if (notification?.type == "success") {
setVisible(false); setTimeoutId(setTimeout(() => {
}, 3000)); setVisible(false);
}, 3000));
}
} }
}, [visible]); }, [visible]);
@@ -38,11 +40,11 @@ export function Notification({socket }) {
warning: "bg-yellow-500" warning: "bg-yellow-500"
} }
const classNames = 'fixed w-11/12 p-5 z-30 mx-auto inset-x-0 flex justify-center rounded-xl transition-all shadow-xl ' + (visible ? "top-5 " : "-translate-y-full "); const classNames = 'fixed w-11/12 p-5 z-30 mx-auto inset-x-0 flex justify-center rounded-xl transition-all shadow-xl ' + (visible ? "top-5 " : "-translate-y-full ");
return ( return (
Object.keys(bgColorMap).map((key) => Object.keys(bgColorMap).map((key) =>
notification?.type == key && notification?.type == key &&
<div key={key} className={classNames + bgColorMap[key]} onClick={() => setVisible(false)}> <div key={key} className={classNames + bgColorMap[key]} onClick={() => setVisible(false)}>
<p className='text-center text-xl'>{notification?.text}</p> <p className='text-center text-xl'>{notification?.text}</p>
</div> </div>
)); ));
} }

View File

@@ -7,7 +7,7 @@ export default function PlacementOverlay() {
const {logout} = useTeamConnexion(); const {logout} = useTeamConnexion();
return ( return (
<> <>
<Image src="/icons/logout.png" onClick={logout} className='w-12 h-12 bg-red-500 p-2 top-1 right-1 rounded-lg cursor-pointer bg-red fixed z-20' /> <img src="/icons/logout.png" onClick={logout} className='w-12 h-12 bg-red-500 p-2 top-1 right-1 rounded-lg cursor-pointer bg-red fixed z-20' />
<div className='fixed top-0 p-3 w-full bg-gray-300 shadow-xl rounded-b-xl flex flex-col z-10 justify-center items-center'> <div className='fixed top-0 p-3 w-full bg-gray-300 shadow-xl rounded-b-xl flex flex-col z-10 justify-center items-center'>
<div className='text-2xl my-3'>Placement</div> <div className='text-2xl my-3'>Placement</div>
<div className='text-md'>{name}</div> <div className='text-md'>{name}</div>

View File

@@ -2,9 +2,11 @@ import useGame from "@/hook/useGame"
import { GreenButton, LogoutButton } from "../util/button"; import { GreenButton, LogoutButton } from "../util/button";
import { useRef } from "react"; import { useRef } from "react";
import Image from "next/image"; import Image from "next/image";
import { useTeamContext } from "@/context/teamContext";
export function WaitingScreen() { export function WaitingScreen() {
const { name, teamId } = useGame(); const { name, teamId } = useGame();
const { gameSettings } = useTeamContext();
const imageRef = useRef(null); const imageRef = useRef(null);
const SERVER_URL = "https://" + process.env.NEXT_PUBLIC_SOCKET_HOST + ":" + process.env.NEXT_PUBLIC_SOCKET_PORT; const SERVER_URL = "https://" + process.env.NEXT_PUBLIC_SOCKET_HOST + ":" + process.env.NEXT_PUBLIC_SOCKET_PORT;
@@ -33,7 +35,7 @@ export function WaitingScreen() {
Equipe : {name} Equipe : {name}
</div> </div>
<div className='text-2xl text-center'> <div className='text-2xl text-center'>
Jeu en préparation, veuillez patienter... {gameSettings?.waitingMessage}
</div> </div>
<div className='text-2xl text-center my-10'> <div className='text-2xl text-center my-10'>
<p>Uploadez une photo tous les membres de l&apos;équipe sont visibles</p> <p>Uploadez une photo tous les membres de l&apos;équipe sont visibles</p>

View File

@@ -5,3 +5,9 @@ export default function TextInput({...props}) {
<input {...props} type="text" className="block w-full h-full p-4 rounded text-center ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600" /> <input {...props} type="text" className="block w-full h-full p-4 rounded text-center ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600" />
) )
} }
export function TextArea({...props}) {
return (
<textarea {...props} className="block w-full h-full p-4 rounded text-center ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600" />
)
}

View File

@@ -10,6 +10,8 @@ const adminContext = createContext();
function AdminProvider({ children }) { function AdminProvider({ children }) {
const [teams, setTeams] = useState([]); const [teams, setTeams] = useState([]);
const [zoneSettings, setZoneSettings] = useState(null) const [zoneSettings, setZoneSettings] = useState(null)
const [penaltySettings, setPenaltySettings] = useState(null);
const [gameSettings, setGameSettings] = useState(null);
const [zone, setZone] = useState(null); const [zone, setZone] = useState(null);
const [zoneExtremities, setZoneExtremities] = useState(null); const [zoneExtremities, setZoneExtremities] = useState(null);
const { adminSocket } = useSocket(); const { adminSocket } = useSocket();
@@ -25,10 +27,12 @@ function AdminProvider({ children }) {
//Bind listeners to update the team list and the game status on socket message //Bind listeners to update the team list and the game status on socket message
useSocketListener(adminSocket, "teams", setTeams); useSocketListener(adminSocket, "teams", setTeams);
useSocketListener(adminSocket, "zone_settings", setZoneSettings); useSocketListener(adminSocket, "zone_settings", setZoneSettings);
useSocketListener(adminSocket, "game_settings", setGameSettings);
useSocketListener(adminSocket, "penalty_settings", setPenaltySettings);
useSocketListener(adminSocket, "zone", setZone); useSocketListener(adminSocket, "zone", setZone);
useSocketListener(adminSocket, "new_zone", setZoneExtremities); useSocketListener(adminSocket, "new_zone", setZoneExtremities);
const value = useMemo(() => ({ zone, zoneExtremities, teams, zoneSettings, setZoneSettings, setTeams, gameState }), [zoneSettings, teams, gameState, zone, zoneExtremities]); const value = useMemo(() => ({ zone, zoneExtremities, teams, zoneSettings, penaltySettings, gameSettings, gameState }), [zoneSettings, teams, gameState, zone, zoneExtremities, penaltySettings, gameSettings]);
return ( return (
<adminContext.Provider value={value}> <adminContext.Provider value={value}>
{children} {children}

View File

@@ -11,6 +11,7 @@ const teamContext = createContext()
function TeamProvider({children}) { function TeamProvider({children}) {
const [teamInfos, setTeamInfos] = useState({}); const [teamInfos, setTeamInfos] = useState({});
const [gameState, setGameState] = useState(GameState.SETUP); const [gameState, setGameState] = useState(GameState.SETUP);
const [gameSettings, setGameSettings] = useState(null);
const [zone, setZone] = useState(null); const [zone, setZone] = useState(null);
const [zoneExtremities, setZoneExtremities] = useState(null); const [zoneExtremities, setZoneExtremities] = useState(null);
const measuredLocation = useLocation(10000); const measuredLocation = useLocation(10000);
@@ -27,6 +28,8 @@ function TeamProvider({children}) {
useSocketListener(teamSocket, "game_state", setGameState); useSocketListener(teamSocket, "game_state", setGameState);
useSocketListener(teamSocket, "zone", setZone); useSocketListener(teamSocket, "zone", setZone);
useSocketListener(teamSocket, "new_zone", setZoneExtremities); useSocketListener(teamSocket, "new_zone", setZoneExtremities);
useSocketListener(teamSocket, "game_settings", setGameSettings);
//Send the current position to the server when the user is logged in //Send the current position to the server when the user is logged in
@@ -37,7 +40,7 @@ function TeamProvider({children}) {
} }
}, [loggedIn, measuredLocation]); }, [loggedIn, measuredLocation]);
const value = useMemo(() => ({teamInfos, gameState, zone, zoneExtremities}), [teamInfos, gameState, zone, zoneExtremities]); const value = useMemo(() => ({teamInfos, gameState, zone, zoneExtremities, gameSettings}), [gameSettings, teamInfos, gameState, zone, zoneExtremities]);
return ( return (
<teamContext.Provider value={value}> <teamContext.Provider value={value}>
{children} {children}

View File

@@ -2,7 +2,8 @@ import { useAdminContext } from "@/context/adminContext";
import { useSocket } from "@/context/socketContext"; import { useSocket } from "@/context/socketContext";
export default function useAdmin(){ export default function useAdmin(){
const {teams, gameState, zoneSettings, zone, zoneExtremities } = useAdminContext(); const adminContext = useAdminContext();
const {teams} = adminContext;
const {adminSocket} = useSocket(); const {adminSocket} = useSocket();
function pollTeams() { function pollTeams() {
@@ -42,6 +43,13 @@ export default function useAdmin(){
adminSocket.emit("set_zone_settings", zone); adminSocket.emit("set_zone_settings", zone);
} }
return {teams, zoneSettings, gameState, zone, zoneExtremities,changeZoneSettings, pollTeams, getTeam, getTeamName, reorderTeams, addTeam, removeTeam, changeState, updateTeam }; function changePenaltySettings(penalties) {
adminSocket.emit("set_penalty_settings", penalties);
}
function changeGameSettings(settings) {
adminSocket.emit("set_game_settings", settings);
}
return {...adminContext,changeGameSettings, changeZoneSettings, changePenaltySettings, pollTeams, getTeam, getTeamName, reorderTeams, addTeam, removeTeam, changeState, updateTeam };
} }