using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
using UnityEngine.SceneManagement;
using Debug = UnityEngine.Debug;
namespace Unity.Multiplayer.Samples
{
public static class DedicatedServerUtilities
{
public static bool IsServerBuildTarget
{
get
{
#if UNITY_SERVER
return true;
#else
return false;
#endif
}
}
///
/// Some notes on logging and dedicated servers:
///
/// With dedicated server, you don't have a view into your game like you'd have with other types of platforms. You'll rely a lot on logging to get
/// insights into what's happening on your servers, even more with your live fleets of thousands of servers.
/// Unity's default logging isn't usable for dedicated server use cases. The following requirements are missing:
/// - Structured logging: with 1000+ server fleets, you don't want to look at log files individually. You'll need log ingestion/analysis tools to be able
/// to parse all that data (think tools like elasticsearch for example). Making your logs structured so they are machine friendly (for example
/// having each log entry be a json object) makes this easier to integrate with those tools and easier to analyze.
/// - Log levels: most of the time, you won't want to receive info or debug logs, only warnings and errors (so you don't spam your analysis tools and so they don't
/// cost your a fortune). However, you'll also want to enable info and debug logs for specific servers when debugging them. Having a logger that
/// manages this for you is better than wrapping your logs yourself and managing this yourself.
/// - Log file rotation: Dedicated servers can run for days and days, while games on user devices will run for a few play sessions before being closed. This means your
/// log file will grow and grow. Rotation is an automated way to swap log files each x hours or days. This allows deleting and easier managing
/// of older log files.
/// - Performance: logging can be a performance costly operation, which contributes to your CPU perf costs, which in turn are translated to hosting monetary costs.
/// Having a logging library that's optimized for these scenarios is essential (burstable, threaded, etc).
/// This also includes not having to print full stack traces (not needed for most devops operations, but could be enabled for debugging)
///
/// A few solutions exists for this. ZLogger https://github.com/Cysharp/ZLogger and serilog https://www.nuget.org/packages/serilog/ for example.
/// Unity also has an experimental package com.unity.logging that answers the above needs as well. Once this is out of experimental, this will be
/// integrated in boss room. TODO for this first pass at DGS, we're using the default logger. TODO replace me - MTT-4038
///
///
public static void Log(string message)
{
// IMPORTANT FOR LOGGING READ ABOVE. The following isn't recommended for production use with dedicated game servers.
Debug.Log($"[{DateTime.UtcNow}] {Time.realtimeSinceStartup} {Time.time} pid[{Process.GetCurrentProcess().Id}] - {message}");
}
///
/// Quick tool to get insights in your current scene, to be able to debug client and server hierarchies.
///
public static void PrintSceneHierarchy()
{
List rootObjects = new List();
Scene scene = SceneManager.GetActiveScene();
scene.GetRootGameObjects(rootObjects);
string toPrint = "\n";
foreach (var rootObject in rootObjects)
{
toPrint += $"{GetInfoForObject(rootObject)}\n";
PrintChildObjectsRecursive(rootObject, depth: 0, ref toPrint);
}
Log(toPrint);
}
private static void PrintChildObjectsRecursive(GameObject parentObject, int depth, ref string toPrint)
{
if (parentObject.transform.childCount == 0)
{
return;
}
string tabs = new string(' ', ++depth * 4);
foreach (Transform child in parentObject.transform)
{
toPrint += $"{tabs}{GetInfoForObject(child.gameObject)}\n";
PrintChildObjectsRecursive(child.gameObject, depth, ref toPrint); // asdf
}
}
private static string GetInfoForObject(GameObject obj)
{
List allComponents = new();
obj.GetComponents(allComponents);
var nullCount = allComponents.FindAll(component => component == null).Count;
return $"{obj.name}\tnb null {nullCount}/{allComponents.Count}";
}
}
}