86dd6bcdaf
This reverts commit 1e97dc0182
.
469 lines
16 KiB
C#
469 lines
16 KiB
C#
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;
|
|
/// <summary>
|
|
/// TextMeshPro that show the value of the current rooom code
|
|
/// </summary>
|
|
public TextMeshProUGUI roomCodeLabel;
|
|
public AudioClip playerJoinSFX;
|
|
public List<TextMeshProUGUI> waitingForPlayersLabels = new List<TextMeshProUGUI>();
|
|
|
|
[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<TextMeshProUGUI> waitingForPropositionsLabels = new List<TextMeshProUGUI>();
|
|
private Dictionary<string, TextMeshProUGUI> propositionLabelsByID = new Dictionary<string, TextMeshProUGUI>();
|
|
private DateTime endOfPropositionDate = DateTime.MinValue;
|
|
private bool allPlayersHasProposedTwoPictures = false;
|
|
private Dictionary<string, Proposition[]> propositionsPerPlayers = new Dictionary<string, Proposition[]>();
|
|
|
|
[Header("Other")]
|
|
public PromptList promptList;
|
|
private Room myRoom = null;
|
|
public float votingTime = 20;
|
|
|
|
private float votingCurrentTime = 0;
|
|
|
|
/// <summary>
|
|
/// Contain the infos about the current displayed question (during votes)
|
|
/// </summary>
|
|
private Question currentQuestion;
|
|
|
|
private List<Question> questions;
|
|
|
|
/// <summary>
|
|
/// When this is equal to questions.Count, go to score page
|
|
/// </summary>
|
|
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)
|
|
{
|
|
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 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SendRoomState(GameState _newState)
|
|
{
|
|
realtimeDB.Child("rooms").Child(myRoom.code).Child("currentState").SetValueAsync((int)_newState);
|
|
}
|
|
|
|
private void ResetAllPlayerLabels()
|
|
{
|
|
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();
|
|
}
|
|
|
|
/// <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);
|
|
List<int> alreadyUsedCodes = new List<int>();
|
|
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<string, Room> onlineRooms = JsonConvert.DeserializeObject<Dictionary<string, Room>>(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);
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Automatically called at start of game
|
|
/// </summary>
|
|
[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);
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when the first player clicked "Start"
|
|
/// </summary>
|
|
public void HostHasStartedGame()
|
|
{
|
|
waitingForPropositionsPage.SetActive(true);
|
|
waitingForPlayersPage.SetActive(false);
|
|
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}");
|
|
}
|
|
|
|
//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}");
|
|
}
|
|
}
|
|
|
|
public void GeneratePrompts()
|
|
{
|
|
System.Random rnd = new();
|
|
List<Prompt> prompts = promptList.prompts.OrderBy(x => rnd.Next()).Take(myRoom.players.Count() * 2).ToList();
|
|
List<Player> fullPlayers = myRoom.players.Values.ToList().Concat(myRoom.players.Values.ToList()).OrderBy(x => rnd.Next()).ToList();
|
|
Dictionary<string, Question> questions = new();
|
|
|
|
foreach (Prompt prompt in prompts)
|
|
{
|
|
Dictionary<string, Proposition> propositions = new();
|
|
Player basePlayer = fullPlayers[0];
|
|
|
|
propositions.Add(Guid.NewGuid().ToString(), new Proposition()
|
|
{
|
|
owner = basePlayer,
|
|
creationDate = DateTime.Now.ToOADate()
|
|
});
|
|
|
|
fullPlayers.RemoveAt(0);
|
|
|
|
for (int i = 0; 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Automatically called when something change in your room
|
|
/// </summary>
|
|
private void OnRoomUpdate(object sender, ValueChangedEventArgs value)
|
|
{
|
|
if (value.DatabaseError != null)
|
|
{
|
|
Debug.LogError(value.DatabaseError.Message);
|
|
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;
|
|
}
|
|
string JSON = value.Snapshot.GetRawJsonValue();
|
|
Debug.Log($"your room has been updated :\n{JSON}");
|
|
GameState lastState = (GameState)myRoom.currentState;
|
|
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;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when we enter in a new game state
|
|
/// </summary>
|
|
private void OnNewGameStateStarted()
|
|
{
|
|
switch (myRoom.currentState)
|
|
{
|
|
case (int)GameState.WaitingForOtherPlayersToJoin:
|
|
Debug.Log("New State : WaitingForOtherPlayersToJoin");
|
|
break;
|
|
case (int)GameState.Explanation:
|
|
Debug.Log("New State : Explanation");
|
|
waitingForPlayersPage.SetActive(false);
|
|
explanationPage.SetActive(true);
|
|
endOfExplanationDate = DateTime.Now.AddSeconds(explanationTime);
|
|
AudioSource.PlayClipAtPoint(counterSFX, Vector3.zero);
|
|
|
|
//generate all the questions during the explanation
|
|
GeneratePrompts();
|
|
|
|
break;
|
|
case (int)GameState.MakeProposition:
|
|
Debug.Log("New State : MakeProposition");
|
|
HostHasStartedGame();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <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);
|
|
ResetAllPlayerLabels();
|
|
|
|
Debug.Log($"players count = {_players.Count}");
|
|
List<Player> orderedPlayers = _players.OrderBy(x => x.creationDate).ToList();
|
|
for (int i = 0; i < orderedPlayers.Count; i++)
|
|
{
|
|
Debug.Log($"player {i} = {orderedPlayers[i].name}");
|
|
waitingForPlayersLabels[i].text = orderedPlayers[i].name;
|
|
}
|
|
}
|
|
}
|