You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
4.9 KiB
C#
96 lines
4.9 KiB
C#
3 weeks ago
|
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
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// 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
|
||
|
/// </summary>
|
||
|
/// <param name="message"></param>
|
||
|
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}");
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Quick tool to get insights in your current scene, to be able to debug client and server hierarchies.
|
||
|
/// </summary>
|
||
|
public static void PrintSceneHierarchy()
|
||
|
{
|
||
|
List<GameObject> rootObjects = new List<GameObject>();
|
||
|
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<Component> allComponents = new();
|
||
|
obj.GetComponents(allComponents);
|
||
|
var nullCount = allComponents.FindAll(component => component == null).Count;
|
||
|
return $"{obj.name}\tnb null {nullCount}/{allComponents.Count}";
|
||
|
}
|
||
|
}
|
||
|
}
|