using Firebase.Database; using Firebase.Extensions; using System; using System.Collections.Generic; using TMPro; using UnityEngine; using Newtonsoft.Json; using System.Linq; public class RoomManager : MonoBehaviour { [Header("Waiting For Players Page")] public GameObject waitingForPlayersPage; /// /// TextMeshPro that show the value of the current rooom code /// public TextMeshProUGUI roomCodeLabel; public List waitingForPlayersLabels = new List(); private Dictionary waitingPlayersById = new Dictionary(); public PromptList promptList; [Header("Explanation Page")] public GameObject explanationPage; public TextMeshProUGUI explanationCounter; public float explanationTime; private DateTime endOfExplanationDate = DateTime.MinValue; public AudioClip counterSFX; [Header("Waiting For Proposition Page")] public GameObject waitingForPropositionsPage; public TextMeshProUGUI propositionCounter; public float propositionTime = 60; public List waitingForPropositionsLabels = new List(); private DateTime endOfPropositionDate = DateTime.MinValue; private bool allPlayersHasProposedTwoPictures = false; [Header("Other")] private Room myRoom = null; public float votingTime = 20; private float votingCurrentTime = 0; /// /// Contain the infos about the current displayed question (during votes) /// private Question currentQuestion; private List questions; /// /// When this is equal to questions.Count, go to score page /// private int numberOfQuestionVoted = 0; DatabaseReference realtimeDB; private void Awake() { FirebaseInitializer.Instance.onFirebaseReady += Initialize; } private void Start() { explanationPage.SetActive(false); waitingForPropositionsPage.SetActive(false); waitingForPlayersPage.SetActive(true); votingCurrentTime = votingTime; ResetAllPlayerLabels(); } private void Update() { if (myRoom == null) return; //While Explanation State if (myRoom.currentState == (int)GameState.Explanation && endOfExplanationDate != DateTime.MinValue) { TimeSpan duration = endOfExplanationDate - DateTime.Now; explanationCounter.text = ((int)duration.TotalSeconds).ToString("D1"); if (duration.TotalMilliseconds <= 0) { Debug.Log("It's time to make proposition !", this); SendRoomState(GameState.MakeProposition); } } //while MakeProposition State if (myRoom.currentState == (int)GameState.MakeProposition && endOfPropositionDate != DateTime.MinValue) { TimeSpan duration = endOfPropositionDate - DateTime.Now; propositionCounter.text = ((int)duration.TotalSeconds).ToString("D1"); foreach (TextMeshProUGUI tmp in waitingForPropositionsLabels) { if (tmp.gameObject.activeSelf) { } } } } private void SendRoomState(GameState _newState) { realtimeDB.Child("rooms").Child(myRoom.code).Child("currentState").SetValueAsync((int)_newState); } private void ResetAllPlayerLabels() { waitingPlayersById.Clear(); for (int i = 0; i < waitingForPlayersLabels.Count; i++) { waitingForPlayersLabels[i].text = $"Waiting for P{i + 1}"; } } private void OnApplicationQuit() { Debug.Log($"delete room {myRoom.code}"); realtimeDB.Child("rooms").Child(myRoom.code).RemoveValueAsync().ContinueWithOnMainThread(task => { Debug.Log($"room {myRoom.code} has been deleted"); myRoom = null; }); } private void Initialize() { FirebaseInitializer.Instance.onFirebaseReady -= Initialize; realtimeDB = FirebaseDatabase.DefaultInstance.RootReference; Debug.Log("Realtime DB initialized"); CreateNewRoom(); } /// /// Check all the rooms in the server and give back the number already taken /// private void WhichCodesAreAlreadyUsed(Action> callback_OnCodesChecked) { Debug.Log("Checking other rooms to get which codes are already used", this); List alreadyUsedCodes = new List(); try { realtimeDB.Child("rooms").GetValueAsync().ContinueWithOnMainThread(task => { Debug.Log("looking into the online rooms", this); if (task.IsFaulted) { Debug.LogException(task.Exception); } else if (task.IsCompleted) { DataSnapshot snapshot = task.Result; if (snapshot.Value != null) { string JSON = snapshot.GetRawJsonValue(); Debug.Log($"found some rooms :\n{JSON}", this); Dictionary onlineRooms = JsonConvert.DeserializeObject>(JSON); foreach (Room r in onlineRooms.Values) { Debug.Log($"Code {r.code} is already used by another party", this); alreadyUsedCodes.Add(int.Parse(r.code)); } } else { Debug.Log($"Your party is the first one!", this); } } callback_OnCodesChecked?.Invoke(alreadyUsedCodes); }); } catch (Exception ex) { Debug.LogException(ex); } } /// /// Automatically called at start of game /// [ContextMenu("Create New Room")] public void CreateNewRoom() { WhichCodesAreAlreadyUsed(codes => { Room newRoom = new Room(GenerateRandomAvailableCode(codes).ToString("D4")); myRoom = newRoom; try { string JSON = JsonConvert.SerializeObject(newRoom); realtimeDB.Child("rooms").Child(newRoom.code).SetRawJsonValueAsync(JSON).ContinueWithOnMainThread(task => { //then subscribe to it realtimeDB.Child("rooms").Child(newRoom.code).ValueChanged += OnRoomUpdate; roomCodeLabel.text = myRoom.code; Debug.Log($"room {myRoom.code} has been created on the server"); }); } catch (Exception e) { Debug.LogException(e); } }); } /// /// Generate a code between 0 and 1000 that is not in the list /// /// the list of code you don"t want to get /// private int GenerateRandomAvailableCode(List _impossibleCodes) { int random = UnityEngine.Random.Range(0, 1000); while (_impossibleCodes.Contains(random)) { Debug.Log($"{random} is already taken, choosing another room code", this); random = UnityEngine.Random.Range(0, 1000); } return random; } public void PlayerSendProposition(Proposition _proposition) { } /// /// Called when the first player clicked "Start" /// public void HostHasStartedGame() { waitingForPropositionsPage.SetActive(true); waitingForPlayersPage.SetActive(false); endOfPropositionDate = DateTime.Now.AddSeconds(propositionTime); for (int i = 0; i < waitingForPropositionsLabels.Count; i++) { TextMeshProUGUI tmp = waitingForPropositionsLabels[i]; tmp.gameObject.SetActive(i < myRoom.players.Count); } } /// /// Start the proposition timer /// public void StartPropositionTimer() { } /// /// Automatically called when the proposition timer has finished /// public void PropositionTimerFinished() { } /// /// Start the voting timer /// public void StartVotingTimer() { } /// /// Automatically called when the voting timer has finished /// public void VotingTimerFinished() { } /// /// Automatically called when a proposition is updated (someone has voted or a picture has been proposed) /// public void OnPropositionUpdate() { } public void GeneratePrompts() { System.Random rnd = new(); List prompts = promptList.prompts.OrderBy(x => rnd.Next()).Take(myRoom.players.Count() * 2).ToList(); List fullPlayers = myRoom.players.Values.ToList().Concat(myRoom.players.Values.ToList()).OrderBy(x => rnd.Next()).ToList(); Dictionary questions = new(); foreach (Prompt prompt in prompts) { Dictionary propositions = new(); Player basePlayer = fullPlayers[0]; propositions.Add(Guid.NewGuid().ToString(), new Proposition() { owner = basePlayer, creationDate = DateTime.Now.ToOADate() }); for (int i = 1; i < fullPlayers.Count(); i++) { Player secondPlayer = fullPlayers[i]; if (basePlayer.id != secondPlayer.id) { propositions.Add(Guid.NewGuid().ToString(), new Proposition() { owner = secondPlayer, creationDate = DateTime.Now.ToOADate() }); fullPlayers.RemoveAt(0); break; } } questions.Add(Guid.NewGuid().ToString(), new Question() { promptId = prompt.id, propositions = propositions, creationDate = DateTime.Now.ToOADate(), }); } string JSON = JsonConvert.SerializeObject(questions); realtimeDB.Child("rooms").Child(myRoom.code).Child("questions").SetRawJsonValueAsync(JSON); } /// /// Automatically called when something change in your room /// private void OnRoomUpdate(object sender, ValueChangedEventArgs value) { if (value.DatabaseError != null) { Debug.LogError(value.DatabaseError.Message); return; } string JSON = value.Snapshot.GetRawJsonValue(); Debug.Log($"your room has been updated :\n{JSON}"); try { myRoom = JsonConvert.DeserializeObject(JSON); } catch (Exception ex) { Debug.LogException(ex); } switch (myRoom.currentState) { case (int)GameState.WaitingForOtherPlayersToJoin: UpdateConnectedPlayerList(myRoom.GetPlayerList()); break; case (int)GameState.Explanation: waitingForPlayersPage.SetActive(false); explanationPage.SetActive(true); endOfExplanationDate = DateTime.Now.AddSeconds(explanationTime); AudioSource.PlayClipAtPoint(counterSFX, Vector3.zero); break; case (int)GameState.MakeProposition: HostHasStartedGame(); break; default: break; } } /// /// Update the player labels on the WaitingForPlayer page /// /// private void UpdateConnectedPlayerList(List _players) { ResetAllPlayerLabels(); Debug.Log($"players count = {_players.Count}"); List orderedPlayers = _players.OrderBy(x => x.creationDate).ToList(); for (int i = 0; i < orderedPlayers.Count; i++) { waitingPlayersById.Add(orderedPlayers[i].id, waitingForPlayersLabels[i]); Debug.Log($"player {i} = {orderedPlayers[i].name}"); waitingForPlayersLabels[i].text = orderedPlayers[i].name; } } }