snakes_mp/Assets/Scripts/NetGameManager.cs
2025-05-27 23:50:42 +05:30

227 lines
7.0 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using TMPro;
using UnityEngine.SceneManagement;
public class NetGameManager : NetworkBehaviour
{
public const int WAITING_TIME = 120;
public static NetGameManager instance;
public TMP_Text statusText;
public TMP_Text scoreText_me;
public TMP_Text scoreText_enemy;
public GameOverScreen gameOverScreen;
public bool gameStarted = false;
float waitingTimer = WAITING_TIME;
public PlayerType winner = PlayerType.Client;
public bool gameOver = false;
[Header("MP")]
public GameObject fruitPrefab;
public List<GameObject> spawnedFruits = new List<GameObject>();
public int maxFruits = 10;
public float fruitSpawnInterval = 5f;
void Awake()
{
instance = this;
}
void Start()
{
Application.targetFrameRate = 60;
}
public void UpdateScore(bool isLocalPlayer, int score){
if(isLocalPlayer){
scoreText_me.text = score.ToString();
}else{
scoreText_enemy.text = score.ToString();
}
}
public bool playerSpawned = false;
// Update is called once per frame
void Update()
{
if(!isServer) return;
SnakeController[] players = FindObjectsOfType<SnakeController>();
if(!gameStarted){
if(players.Length >1){
gameStarted = true;
RpcSetGameStarted(true);
StartCoroutine(StartGame());
}else{
if(players.Length == 1){
if(waitingTimer >0){
waitingTimer -= Time.deltaTime;
SetStatusText("Waiting for another player...\nRemaining time: " + waitingTimer.ToString("F0") + " seconds");
}else{
SetStatusText("Opponent abandoned the game");
GameOver(players[0].playerType);
}
}
}
}else{
if(players.Length == 1){
if(playerSpawned){
SetStatusText("Opponent left the game");
GameOver(players[0].playerType);
}else{
gameStarted = false;
SetStatusText("Waiting for another player...");
}
}
ManageAndPopulateFruits();
}
}
[ClientRpc]
void RpcSetGameStarted(bool started){
gameStarted = started;
}
void ManageAndPopulateFruits(){
if(spawnedFruits.Count < maxFruits){
SpawnFruit();
}
}
void SpawnFruit(){
GameObject fruit = Instantiate(fruitPrefab, GetRandomPoint(), Quaternion.identity);
NetworkServer.Spawn(fruit);
spawnedFruits.Add(fruit);
}
public void DestroyFruit(Vector2 position){
int index = -1;
for(int i=0; i < spawnedFruits.Count; i++){
if(spawnedFruits[i].transform.position == (Vector3)position){
index = i;
break;
}
}
if(index != -1){
NetworkServer.Destroy(spawnedFruits[index]);
spawnedFruits.RemoveAt(index);
}
}
Vector2 GetRandomPoint(){
SnakeController p = FindObjectOfType<SnakeController>();
float x = p.transform.position.x - (int)(UnityEngine.Random.Range(-p.fieldSize.x/2f, p.fieldSize.x/2f));
float y = p.transform.position.y - (int)(UnityEngine.Random.Range(-p.fieldSize.y/2f, p.fieldSize.y/2f));
return new Vector2(x, y);
}
public IEnumerator StartGame(){
int counter = 10;
SnakeController[] players = FindObjectsOfType<SnakeController>();
while(counter > 0){
players = FindObjectsOfType<SnakeController>();
if(players.Length <= 1){
yield break;
}
SetStatusText("Game starts in " + counter);
yield return new WaitForSeconds(1f);
counter--;
}
SetStatusText("Game Started");
foreach (SnakeController player in players)
{
player.StartGame();
}
playerSpawned = true;
yield return new WaitForSeconds(1f);
SetStatusText("");
}
void SetStatusText(string text){
if(!isServer){return;}
statusText.text = text;
RpcSetStatusText(text);
}
[ClientRpc]
void RpcSetStatusText(string text){
statusText.text = text;
}
public void GameOver(SnakeController player){
winner = player.playerType;
GameOver(winner);
}
public void GameOver(PlayerType winner){
if(gameOver){return;}
SetStatusText($"Player {(winner == PlayerType.Master ? "1" : "2")} wins!");
Debug.Log("Game Over " + winner);
gameOver = true;
string winnerKey = winner == PlayerType.Master ? "master" : "client";
StartCoroutine(CoroutineGameOver(winnerKey));
RpcGameOver(winnerKey);
}
[ClientRpc]
void RpcGameOver(string winnerKey){
bool won = (winnerKey == "master" && GameData.isMaster) || (winnerKey == "client" && !GameData.isMaster);
gameOverScreen.Show(won);
Debug.Log("Game Over " + winnerKey);
}
IEnumerator CoroutineGameOver(string winnerKey){
string winnerPubkey = winnerKey == "master" ? GameData.betData.owner : GameData.betData.joiner;
string winnerId = winnerKey == "master" ? GameData.betData.owner_id : GameData.betData.joiner_id;
int masterScore=0, clientScore =0;
Dictionary<string, string> data = new Dictionary<string, string>(){
{"gameAddress", GameData.address},
{"winnerPublicKey", winnerPubkey},
{"winnerUsername", winnerId},
{"wager", (GameData.betData.wager * 1e8).ToString()},
{"game_id", GameData.betData.id.ToString()},
{"loserUsername", winnerKey == "master" ? GameData.betData.joiner_id : GameData.betData.owner_id},
{"masterScore", masterScore.ToString()},
{"clientScore", clientScore.ToString()},
{"masterId", GameData.betData.owner_id},
{"clientId", GameData.betData.joiner_id}
};
string queryString = "?";
foreach(KeyValuePair<string, string> entry in data) {
queryString += entry.Key + "=" + WWW.EscapeURL(entry.Value) + "&";
}
queryString = queryString.TrimEnd('&');
string validator_url = GameData.isDev ? Constants.VALIDATOR_URL_DEV : Constants.VALIDATOR_URL_PROD;
string url = validator_url + "finalize" + queryString;
Debug.Log("Finalizing game on " + url);
WWW www = new WWW(url);
yield return www;
if(www.error == null){
RpcProcessed();
}
Debug.Log("Processed game, quitting in 10 seconds...");
yield return new WaitForSeconds(10f);
Application.Quit();
}
[ClientRpc]
void RpcProcessed(){
gameOverScreen.Processed();
}
}