Snaparazzi/Assets/Scripts/RoomManager.cs

451 lines
16 KiB
C#
Raw Normal View History

2024-01-27 14:08:12 +00:00
using Firebase.Database;
2024-01-27 14:54:23 +00:00
using Firebase.Extensions;
2024-01-27 18:58:02 +00:00
using System;
using System.Collections.Generic;
2024-01-27 14:54:23 +00:00
using TMPro;
2024-01-27 09:19:32 +00:00
using UnityEngine;
2024-01-27 18:54:06 +00:00
using Newtonsoft.Json;
using System.Linq;
2024-01-28 11:54:57 +00:00
using Google.MiniJSON;
2024-01-27 09:19:32 +00:00
public class RoomManager : MonoBehaviour
{
2024-01-27 21:24:50 +00:00
[Header("Waiting For Players Page")]
public GameObject waitingForPlayersPage;
/// <summary>
/// TextMeshPro that show the value of the current rooom code
/// </summary>
public TextMeshProUGUI roomCodeLabel;
public AudioClip playerJoinSFX;
2024-01-27 21:24:50 +00:00
public List<TextMeshProUGUI> waitingForPlayersLabels = new List<TextMeshProUGUI>();
2024-01-27 09:19:32 +00:00
2024-01-27 22:07:41 +00:00
[Header("Explanation Page")]
public GameObject explanationPage;
2024-01-28 00:03:16 +00:00
public TextMeshProUGUI explanationCounter;
public float explanationTime;
2024-01-27 22:07:41 +00:00
private DateTime endOfExplanationDate = DateTime.MinValue;
2024-01-28 00:03:16 +00:00
public AudioClip counterSFX;
2024-01-27 22:07:41 +00:00
2024-01-27 21:24:50 +00:00
[Header("Waiting For Proposition Page")]
public GameObject waitingForPropositionsPage;
2024-01-28 00:03:16 +00:00
public TextMeshProUGUI propositionCounter;
2024-01-27 12:19:56 +00:00
public float propositionTime = 60;
2024-01-28 00:03:16 +00:00
public List<TextMeshProUGUI> waitingForPropositionsLabels = new List<TextMeshProUGUI>();
private Dictionary<string, TextMeshProUGUI> propositionLabelsByID = new Dictionary<string, TextMeshProUGUI>();
2024-01-28 00:03:16 +00:00
private DateTime endOfPropositionDate = DateTime.MinValue;
private bool allPlayersHasProposedTwoPictures = false;
private Dictionary<string, Proposition[]> propositionsPerPlayers = new Dictionary<string, Proposition[]>();
2024-01-27 21:24:50 +00:00
2024-01-28 00:03:16 +00:00
[Header("Other")]
public PromptList promptList;
2024-01-27 21:24:50 +00:00
private Room myRoom = null;
2024-01-27 12:19:56 +00:00
public float votingTime = 20;
private float votingCurrentTime = 0;
/// <summary>
2024-01-28 10:46:24 +00:00
/// Contain all the question for this game session
2024-01-27 12:19:56 +00:00
/// </summary>
2024-01-28 10:46:24 +00:00
private Dictionary<string, Question> questions = new Dictionary<string, Question>();
2024-01-27 12:19:56 +00:00
/// <summary>
/// When this is equal to questions.Count, go to score page
/// </summary>
private int numberOfQuestionVoted = 0;
2024-01-27 14:08:12 +00:00
DatabaseReference realtimeDB;
private void Awake()
{
FirebaseInitializer.Instance.onFirebaseReady += Initialize;
}
2024-01-27 12:19:56 +00:00
private void Start()
{
2024-01-27 23:06:23 +00:00
explanationPage.SetActive(false);
waitingForPropositionsPage.SetActive(false);
waitingForPlayersPage.SetActive(true);
2024-01-27 12:19:56 +00:00
votingCurrentTime = votingTime;
2024-01-27 22:07:41 +00:00
ResetAllPlayerLabels();
2024-01-27 18:54:06 +00:00
}
2024-01-27 22:07:41 +00:00
private void Update()
{
2024-01-27 22:47:31 +00:00
if (myRoom == null)
return;
//While Explanation State
2024-01-27 22:47:31 +00:00
if (myRoom.currentState == (int)GameState.Explanation && endOfExplanationDate != DateTime.MinValue)
2024-01-27 22:07:41 +00:00
{
TimeSpan duration = endOfExplanationDate - DateTime.Now;
2024-01-28 00:03:16 +00:00
explanationCounter.text = ((int)duration.TotalSeconds).ToString("D1");
2024-01-27 22:07:41 +00:00
if (duration.TotalMilliseconds <= 0)
{
2024-01-27 22:47:31 +00:00
SendRoomState(GameState.MakeProposition);
2024-01-27 22:07:41 +00:00
}
}
2024-01-28 00:03:16 +00:00
//while MakeProposition State
2024-01-28 00:03:16 +00:00
if (myRoom.currentState == (int)GameState.MakeProposition && endOfPropositionDate != DateTime.MinValue)
{
TimeSpan duration = endOfPropositionDate - DateTime.Now;
propositionCounter.text = ((int)duration.TotalSeconds).ToString("D1");
//foreach labels
foreach (var labelByID in propositionLabelsByID)
{
//if the label is connected to a player
if (labelByID.Value.gameObject.activeSelf)
{
//check if this player has send 2 propositions
bool playerHasAnswerBoth = true;
//Debug.Log($"trying to check if player {labelByID.Key} has send 2 propositions");
//Debug.Log($"player {labelByID.Key} has send {propositionsPerPlayers[labelByID.Key].Length} propositions");
if (propositionsPerPlayers[labelByID.Key].Length < 2)
{
playerHasAnswerBoth = false;
}
else
{
foreach (Proposition p in propositionsPerPlayers[labelByID.Key])
{
if (string.IsNullOrEmpty(p.photoUrl))
{
Debug.Log($"player {labelByID.Key} proposition URL : {p.photoUrl}");
playerHasAnswerBoth = false;
}
}
}
//if its the case
if (playerHasAnswerBoth)
{
//put player label in green if finished
labelByID.Value.color = Color.green;
}
}
}
if (allPlayersHasProposedTwoPictures || duration.TotalMilliseconds <= 0)
{
SendRoomState(GameState.MakeVote);
}
2024-01-28 00:03:16 +00:00
}
2024-01-27 22:07:41 +00:00
}
2024-01-27 22:47:31 +00:00
private void SendRoomState(GameState _newState)
{
realtimeDB.Child("rooms").Child(myRoom.code).Child("currentState").SetValueAsync((int)_newState);
}
2024-01-27 22:07:41 +00:00
private void ResetAllPlayerLabels()
2024-01-27 18:54:06 +00:00
{
2024-01-27 21:24:50 +00:00
for (int i = 0; i < waitingForPlayersLabels.Count; i++)
2024-01-27 18:54:06 +00:00
{
2024-01-27 21:24:50 +00:00
waitingForPlayersLabels[i].text = $"Waiting for P{i + 1}";
2024-01-27 18:54:06 +00:00
}
2024-01-27 14:08:12 +00:00
}
private void OnApplicationQuit()
{
2024-01-27 18:54:06 +00:00
Debug.Log($"delete room {myRoom.code}");
2024-01-27 22:07:41 +00:00
realtimeDB.Child("rooms").Child(myRoom.code).RemoveValueAsync().ContinueWithOnMainThread(task =>
{
Debug.Log($"room {myRoom.code} has been deleted");
myRoom = null;
});
2024-01-27 14:08:12 +00:00
}
private void Initialize()
{
FirebaseInitializer.Instance.onFirebaseReady -= Initialize;
realtimeDB = FirebaseDatabase.DefaultInstance.RootReference;
Debug.Log("Realtime DB initialized");
2024-01-27 14:54:23 +00:00
CreateNewRoom();
}
/// <summary>
/// Check all the rooms in the server and give back the number already taken
/// </summary>
private void WhichCodesAreAlreadyUsed(Action<List<int>> callback_OnCodesChecked)
{
Debug.Log("Checking other rooms to get which codes are already used", this);
2024-01-27 14:54:23 +00:00
List<int> alreadyUsedCodes = new List<int>();
2024-01-27 20:07:17 +00:00
try
2024-01-27 14:54:23 +00:00
{
2024-01-27 20:07:17 +00:00
realtimeDB.Child("rooms").GetValueAsync().ContinueWithOnMainThread(task =>
2024-01-27 14:54:23 +00:00
{
Debug.Log("looking into the online rooms", this);
2024-01-27 20:07:17 +00:00
if (task.IsFaulted)
2024-01-27 14:54:23 +00:00
{
2024-01-27 20:07:17 +00:00
Debug.LogException(task.Exception);
2024-01-27 14:54:23 +00:00
}
2024-01-27 20:07:17 +00:00
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<string, Room> onlineRooms = JsonConvert.DeserializeObject<Dictionary<string, Room>>(JSON);
foreach (Room r in onlineRooms.Values)
2024-01-27 20:07:17 +00:00
{
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);
}
2024-01-27 14:54:23 +00:00
2024-01-27 14:08:12 +00:00
}
/// <summary>
/// Automatically called at start of game
/// </summary>
[ContextMenu("Create New Room")]
public void CreateNewRoom()
{
2024-01-27 14:54:23 +00:00
WhichCodesAreAlreadyUsed(codes =>
{
Room newRoom = new Room(GenerateRandomAvailableCode(codes).ToString("D4"));
2024-01-27 18:54:06 +00:00
myRoom = newRoom;
2024-01-27 17:50:04 +00:00
try
2024-01-27 14:54:23 +00:00
{
2024-01-27 20:07:17 +00:00
string JSON = JsonConvert.SerializeObject(newRoom);
2024-01-27 18:54:06 +00:00
2024-01-27 17:50:04 +00:00
realtimeDB.Child("rooms").Child(newRoom.code).SetRawJsonValueAsync(JSON).ContinueWithOnMainThread(task =>
{
2024-01-27 18:54:06 +00:00
//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");
2024-01-27 17:50:04 +00:00
});
2024-01-27 18:54:06 +00:00
}
2024-01-27 17:50:04 +00:00
catch (Exception e)
{
Debug.LogException(e);
}
2024-01-27 14:54:23 +00:00
});
}
/// <summary>
/// Generate a code between 0 and 1000 that is not in the list
/// </summary>
/// <param name="_impossibleCodes">the list of code you don"t want to get</param>
/// <returns></returns>
private int GenerateRandomAvailableCode(List<int> _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;
}
2024-01-27 12:19:56 +00:00
/// <summary>
/// Called when the first player clicked "Start"
/// </summary>
public void HostHasStartedGame()
2024-01-27 12:19:56 +00:00
{
2024-01-27 21:24:50 +00:00
waitingForPropositionsPage.SetActive(true);
waitingForPlayersPage.SetActive(false);
2024-01-28 00:03:16 +00:00
endOfPropositionDate = DateTime.Now.AddSeconds(propositionTime);
propositionLabelsByID.Clear();
//display only correct numbers of labels
for (int i = 0; i < waitingForPropositionsLabels.Count; i++)
{
TextMeshProUGUI tmp = waitingForPropositionsLabels[i];
tmp.gameObject.SetActive(i < myRoom.players.Count);
Debug.Log($"toggling {tmp.gameObject.name} accordingly to its player connection");
}
//registers the labels per player ID
List<Player> orderedPlayers = myRoom.GetPlayerList().OrderBy(x => x.creationDate).ToList();
for (int i = 0; i < orderedPlayers.Count; i++)
{
propositionLabelsByID.Add(orderedPlayers[i].id, waitingForPropositionsLabels[i]);
waitingForPropositionsLabels[i].text = orderedPlayers[i].name;
Debug.Log($"{waitingForPropositionsLabels[i].name} label should be {orderedPlayers[i].name}");
}
2024-01-27 09:19:32 +00:00
//Register all propositions of each players
foreach (Player p in myRoom.GetOrderedPlayerList())
{
List<Proposition> propositionsForPlayer = myRoom.GetPropositionsByPlayer(p);
propositionsPerPlayers.Add(p.id, propositionsForPlayer.ToArray());
Debug.Log($"registering propositions for player {p.name}");
}
2024-01-27 12:19:56 +00:00
}
2024-01-27 21:55:04 +00:00
public void GeneratePrompts()
2024-01-27 12:19:56 +00:00
{
2024-01-27 21:55:04 +00:00
System.Random rnd = new();
2024-01-28 11:54:57 +00:00
List<Prompt> prompts = promptList.prompts.OrderBy(x => rnd.Next()).Take(myRoom.players.Count()).ToList();
List<Player> players = myRoom.players.Values.ToList().OrderBy(x => rnd.Next()).ToList();
2024-01-27 21:55:04 +00:00
Dictionary<string, Question> questions = new();
2024-01-27 12:19:56 +00:00
2024-01-28 11:54:57 +00:00
for (int i = 0; i < players.Count(); i++)
2024-01-27 21:55:04 +00:00
{
Dictionary<string, Proposition> propositions = new();
2024-01-27 22:05:27 +00:00
2024-01-28 11:54:57 +00:00
for (int j = 0; j < 2; j++)
2024-01-27 22:05:27 +00:00
{
2024-01-28 11:54:57 +00:00
propositions.Add(j.ToString(), new Proposition()
2024-01-27 21:55:04 +00:00
{
2024-01-28 11:54:57 +00:00
owner = players[i + j < players.Count() ? i + j : 0],
creationDate = DateTime.Now.ToOADate()
});
2024-01-27 21:55:04 +00:00
}
2024-01-28 11:54:57 +00:00
questions.Add(i.ToString(), new Question()
2024-01-27 21:55:04 +00:00
{
2024-01-28 11:57:15 +00:00
id = Guid.NewGuid().ToString(),
2024-01-28 11:54:57 +00:00
promptId = prompts[i].id,
2024-01-27 21:55:04 +00:00
propositions = propositions,
creationDate = DateTime.Now.ToOADate(),
});
}
string JSON = JsonConvert.SerializeObject(questions);
realtimeDB.Child("rooms").Child(myRoom.code).Child("questions").SetRawJsonValueAsync(JSON);
2024-01-27 12:19:56 +00:00
}
/// <summary>
2024-01-27 18:54:06 +00:00
/// Automatically called when something change in your room
2024-01-27 12:19:56 +00:00
/// </summary>
2024-01-27 18:54:06 +00:00
private void OnRoomUpdate(object sender, ValueChangedEventArgs value)
2024-01-27 12:19:56 +00:00
{
2024-01-27 18:54:06 +00:00
if (value.DatabaseError != null)
2024-01-27 15:32:12 +00:00
{
2024-01-27 18:54:06 +00:00
Debug.LogError(value.DatabaseError.Message);
2024-01-27 15:32:12 +00:00
return;
}
if (value.Snapshot.Value == null)
{
Debug.Log("Trying to update room, but it's empty. Maybe you are exiting the app, so it's ok", this);
return;
}
2024-01-27 18:54:06 +00:00
string JSON = value.Snapshot.GetRawJsonValue();
2024-01-27 22:47:31 +00:00
Debug.Log($"your room has been updated :\n{JSON}");
GameState lastState = (GameState)myRoom.currentState;
2024-01-27 18:54:06 +00:00
try
{
myRoom = JsonConvert.DeserializeObject<Room>(JSON);
}
catch (Exception ex)
{
Debug.LogException(ex);
}
//this is done only when entering a new state
if (lastState != (GameState)myRoom.currentState)
{
OnNewGameStateStarted();
}
else
{
//this is done each time something change
switch (myRoom.currentState)
{
case (int)GameState.WaitingForOtherPlayersToJoin:
UpdateConnectedPlayerList(myRoom.GetPlayerList());
break;
case (int)GameState.Explanation:
break;
case (int)GameState.MakeProposition:
CheckPlayersPropositions();
break;
default:
break;
}
}
}
2024-01-27 12:19:56 +00:00
/// <summary>
/// Called when we enter in a new game state
/// </summary>
private void OnNewGameStateStarted()
{
2024-01-27 22:47:31 +00:00
switch (myRoom.currentState)
2024-01-27 18:54:06 +00:00
{
2024-01-27 22:47:31 +00:00
case (int)GameState.WaitingForOtherPlayersToJoin:
Debug.Log("New State : WaitingForOtherPlayersToJoin");
2024-01-27 18:54:06 +00:00
break;
2024-01-27 22:47:31 +00:00
case (int)GameState.Explanation:
Debug.Log("New State : Explanation");
2024-01-27 22:07:41 +00:00
waitingForPlayersPage.SetActive(false);
explanationPage.SetActive(true);
2024-01-28 00:03:16 +00:00
endOfExplanationDate = DateTime.Now.AddSeconds(explanationTime);
AudioSource.PlayClipAtPoint(counterSFX, Vector3.zero);
//generate all the questions during the explanation
GeneratePrompts();
2024-01-27 22:07:41 +00:00
break;
2024-01-27 22:47:31 +00:00
case (int)GameState.MakeProposition:
Debug.Log("New State : MakeProposition");
HostHasStartedGame();
2024-01-27 21:24:50 +00:00
break;
2024-01-27 18:54:06 +00:00
default:
break;
}
}
2024-01-27 12:19:56 +00:00
/// <summary>
/// Will update allPlayersHasProposedTwoPictures to true or false
/// </summary>
private void CheckPlayersPropositions()
{
Debug.Log("Check every Players Propositions");
allPlayersHasProposedTwoPictures = true;
foreach (var propositionsByPlayer in propositionsPerPlayers)
{
foreach (Proposition p in propositionsByPlayer.Value)
{
if (string.IsNullOrEmpty(p.photoUrl))
{
Debug.Log($"player {p.owner} has a proposition without an URL");
allPlayersHasProposedTwoPictures = false;
}
}
}
}
2024-01-27 18:54:06 +00:00
/// <summary>
/// Update the player labels on the WaitingForPlayer page
/// </summary>
/// <param name="_players"></param>
private void UpdateConnectedPlayerList(List<Player> _players)
{
AudioSource.PlayClipAtPoint(playerJoinSFX, Vector3.zero);
2024-01-27 22:07:41 +00:00
ResetAllPlayerLabels();
2024-01-27 18:54:06 +00:00
Debug.Log($"players count = {_players.Count}");
List<Player> orderedPlayers = _players.OrderBy(x => x.creationDate).ToList();
for (int i = 0; i < orderedPlayers.Count; i++)
2024-01-27 18:54:06 +00:00
{
Debug.Log($"player {i} = {orderedPlayers[i].name}");
waitingForPlayersLabels[i].text = orderedPlayers[i].name;
2024-01-27 18:54:06 +00:00
}
2024-01-27 12:19:56 +00:00
}
}