Skip to content

Commit ac42e0d

Browse files
committed
refactor: remove Linq from the CLI query layer and its tests (MTT-16534)
- Loops and List/Array methods (Find, FindAll, Exists, ConvertAll, GetRange) replace every Linq call in Editor/CliQueries and Tests/Editor/Cli. - New helpers keep the output unchanged: StableSort keeps OrderBy's tie order for managers, spawned objects and stats messages. WithoutDuplicates keeps Distinct's first-seen order. JoinNames builds the comma-separated lists in messages. - Session's IsConnected and CountOtherClients replace the hidden Linq Contains and Count on ConnectedClientsIds.
1 parent 9c91c3b commit ac42e0d

8 files changed

Lines changed: 314 additions & 139 deletions

File tree

‎com.unity.netcode.gameobjects/Editor/CliQueries/NgoCliQueries.Authoring.cs‎

Lines changed: 75 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4-
using System.Linq;
54
using System.Text.RegularExpressions;
65
using Unity.Netcode.GameObjects.Editor.Configuration;
76
using Unity.Netcode.Transports.UTP;
@@ -38,7 +37,7 @@ internal static CreateNetworkManagerResult CreateNetworkManager(string name, Net
3837
var existing = FindManagers();
3938
if (existing.Count > 0)
4039
{
41-
throw new InvalidOperationException($"The open scenes already have a NetworkManager ({string.Join(", ", existing.Select(m => $"{GetHierarchyPath(m.transform)} in {SceneLabel(m.gameObject.scene)}"))}). A session uses one; inspect it with netcode.gameobjects.config get.");
40+
throw new InvalidOperationException($"The open scenes already have a NetworkManager ({JoinNames(existing, m => $"{GetHierarchyPath(m.transform)} in {SceneLabel(m.gameObject.scene)}")}). A session uses one; inspect it with netcode.gameobjects.config get.");
4241
}
4342

4443
var scene = SceneManager.GetActiveScene();
@@ -83,7 +82,7 @@ internal static CreateNetworkManagerResult CreateNetworkManager(string name, Net
8382
EditorSceneManager.MarkSceneDirty(scene);
8483

8584
result.Created = true;
86-
result.PrefabsLists = manager.NetworkConfig.Prefabs.NetworkPrefabsLists.Where(l => l != null).Select(AssetDatabase.GetAssetPath).ToList();
85+
result.PrefabsLists = PrefabsListPaths(manager.NetworkConfig.Prefabs.NetworkPrefabsLists);
8786
if (result.PrefabsLists.Count == 0)
8887
{
8988
result.Notes.Add("No network prefabs list attached (default prefab generation is off). Register prefabs with netcode.gameobjects register_prefab.");
@@ -126,7 +125,7 @@ internal static PrefabListResult ListPrefabs(string listFilter)
126125
Manager = GetHierarchyPath(manager.transform),
127126
Scene = manager.gameObject.scene.name,
128127
PlayerPrefab = config?.PlayerPrefab != null ? AssetPathOrName(config.PlayerPrefab) : null,
129-
Lists = config?.Prefabs?.NetworkPrefabsLists.Where(l => l != null).Select(AssetDatabase.GetAssetPath).ToList() ?? new List<string>(),
128+
Lists = config?.Prefabs != null ? PrefabsListPaths(config.Prefabs.NetworkPrefabsLists) : new List<string>(),
130129
});
131130
}
132131
return result;
@@ -260,7 +259,7 @@ internal static RegisterPrefabResult RegisterPrefab(string prefab, string listPa
260259
}
261260

262261
var result = new RegisterPrefabResult { DryRun = dryRun, Prefab = prefabPath };
263-
var registeredIn = FindPrefabsLists().Where(l => IsRegisteredIn(l, prefabAsset)).Select(AssetDatabase.GetAssetPath).ToList();
262+
var registeredIn = PrefabsListPaths(FindPrefabsLists().FindAll(l => IsRegisteredIn(l, prefabAsset)));
264263
var list = ResolveTargetList(listPath, dryRun, result.Notes);
265264
result.List = list != null ? AssetDatabase.GetAssetPath(list) : NetworkPrefabProcessor.DefaultNetworkPrefabsPath;
266265

@@ -298,18 +297,29 @@ private static NetworkPrefabsList ResolveTargetList(string listPath, bool dryRun
298297
return list;
299298
}
300299

301-
var managerLists = FindManagers()
302-
.SelectMany(m => m.NetworkConfig?.Prefabs?.NetworkPrefabsLists ?? new List<NetworkPrefabsList>())
303-
.Where(l => l != null)
304-
.Distinct()
305-
.ToList();
300+
var managerLists = new List<NetworkPrefabsList>();
301+
foreach (var manager in FindManagers())
302+
{
303+
var lists = manager.NetworkConfig?.Prefabs?.NetworkPrefabsLists;
304+
if (lists == null)
305+
{
306+
continue;
307+
}
308+
foreach (var list in lists)
309+
{
310+
if (list != null && !managerLists.Contains(list))
311+
{
312+
managerLists.Add(list);
313+
}
314+
}
315+
}
306316
if (managerLists.Count == 1)
307317
{
308318
return managerLists[0];
309319
}
310320
if (managerLists.Count > 1)
311321
{
312-
throw new ArgumentException($"The scene's NetworkManagers use several lists. Pass list with one of: {string.Join(", ", managerLists.Select(AssetDatabase.GetAssetPath))}.");
322+
throw new ArgumentException($"The scene's NetworkManagers use several lists. Pass list with one of: {JoinNames(managerLists, AssetDatabase.GetAssetPath)}.");
313323
}
314324

315325
var defaultPath = NetworkPrefabProcessor.DefaultNetworkPrefabsPath;
@@ -341,7 +351,8 @@ internal static MakePrefabResult MakePrefab(string prefab, bool dryRun)
341351
{
342352
result.AlreadyNetworkObject = true;
343353
result.PrefabIdHash = existing.GlobalObjectIdHash;
344-
result.RegisteredIn = FindPrefabsLists().Where(l => IsRegisteredIn(l, prefabAsset)).Select(AssetDatabase.GetAssetPath).FirstOrDefault();
354+
var registeredIn = FindPrefabsLists().Find(l => IsRegisteredIn(l, prefabAsset));
355+
result.RegisteredIn = registeredIn != null ? AssetDatabase.GetAssetPath(registeredIn) : null;
345356
return result;
346357
}
347358
if (prefabAsset.GetComponentInChildren<NetworkManager>(true) != null)
@@ -375,7 +386,8 @@ internal static MakePrefabResult MakePrefab(string prefab, bool dryRun)
375386
{
376387
result.Notes.Add("The prefab id hash is generated on the next import; check it with netcode.gameobjects list_prefabs.");
377388
}
378-
result.RegisteredIn = FindPrefabsLists().Where(l => IsRegisteredIn(l, reloaded)).Select(AssetDatabase.GetAssetPath).FirstOrDefault();
389+
var registeredList = FindPrefabsLists().Find(l => IsRegisteredIn(l, reloaded));
390+
result.RegisteredIn = registeredList != null ? AssetDatabase.GetAssetPath(registeredList) : null;
379391
if (result.RegisteredIn == null)
380392
{
381393
result.Notes.Add("Not in a network prefabs list yet. Register it with netcode.gameobjects register_prefab.");
@@ -446,7 +458,7 @@ internal static ProjectAuditResult Audit(string scope, string folder, int limit,
446458
{
447459
result.Truncated = true;
448460
}
449-
result.Issues = issues.Take(limit).ToList();
461+
result.Issues = issues.GetRange(0, Math.Min(limit, issues.Count));
450462
return result;
451463
}
452464

@@ -470,7 +482,15 @@ internal static AuditResult AuditPrefabs(string folder, int limit)
470482
var result = new AuditResult();
471483
var lists = FindPrefabsLists();
472484
// NetworkManager registers its player prefab when it starts, list or not.
473-
var playerPrefabs = new HashSet<GameObject>(FindManagers().Select(m => m.NetworkConfig?.PlayerPrefab).Where(p => p != null));
485+
var playerPrefabs = new HashSet<GameObject>();
486+
foreach (var manager in FindManagers())
487+
{
488+
var playerPrefab = manager.NetworkConfig?.PlayerPrefab;
489+
if (playerPrefab != null)
490+
{
491+
playerPrefabs.Add(playerPrefab);
492+
}
493+
}
474494
var autoGenerate = NetcodeForGameObjectsProjectSettings.instance.GenerateDefaultNetworkPrefabs;
475495
var hashes = new Dictionary<uint, string>();
476496
var scriptCache = new Dictionary<string, bool>();
@@ -496,7 +516,7 @@ void Add(string severity, string code, string asset, string message)
496516
}
497517

498518
var searchIn = string.IsNullOrWhiteSpace(folder) ? new[] { "Assets" } : new[] { folder.Trim() };
499-
foreach (var path in AssetDatabase.FindAssets("t:Prefab", searchIn).Select(AssetDatabase.GUIDToAssetPath).Distinct())
519+
foreach (var path in AssetPaths(AssetDatabase.FindAssets("t:Prefab", searchIn)))
500520
{
501521
if (!UsesNetcodeScripts(path, scriptCache))
502522
{
@@ -579,7 +599,7 @@ private static void AuditPrefab(GameObject prefabAsset, string path, List<Networ
579599
hashes[hash] = path;
580600
}
581601

582-
if (!autoGenerate && !playerPrefabs.Contains(prefabAsset) && !lists.Any(l => IsRegisteredIn(l, prefabAsset)))
602+
if (!autoGenerate && !playerPrefabs.Contains(prefabAsset) && !lists.Exists(l => IsRegisteredIn(l, prefabAsset)))
583603
{
584604
add("warning", "NOT_REGISTERED", path, "Not in any network prefabs list, so it can't be spawned. Register it with netcode.gameobjects register_prefab.");
585605
}
@@ -613,12 +633,41 @@ private static bool UsesNetcodeScripts(string prefabPath, Dictionary<string, boo
613633

614634
internal static List<NetworkPrefabsList> FindPrefabsLists()
615635
{
616-
return AssetDatabase.FindAssets($"t:{nameof(NetworkPrefabsList)}")
617-
.Select(AssetDatabase.GUIDToAssetPath)
618-
.Distinct()
619-
.Select(AssetDatabase.LoadAssetAtPath<NetworkPrefabsList>)
620-
.Where(l => l != null)
621-
.ToList();
636+
var lists = new List<NetworkPrefabsList>();
637+
foreach (var path in AssetPaths(AssetDatabase.FindAssets($"t:{nameof(NetworkPrefabsList)}")))
638+
{
639+
var list = AssetDatabase.LoadAssetAtPath<NetworkPrefabsList>(path);
640+
if (list != null)
641+
{
642+
lists.Add(list);
643+
}
644+
}
645+
return lists;
646+
}
647+
648+
/// <summary>The asset paths of found GUIDs, each once, in the order found.</summary>
649+
private static List<string> AssetPaths(string[] guids)
650+
{
651+
var paths = new List<string>(guids.Length);
652+
foreach (var guid in guids)
653+
{
654+
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
655+
}
656+
return WithoutDuplicates(paths);
657+
}
658+
659+
/// <summary>The asset paths of the prefabs lists that are set, skipping empty entries.</summary>
660+
internal static List<string> PrefabsListPaths(List<NetworkPrefabsList> lists)
661+
{
662+
var paths = new List<string>();
663+
foreach (var list in lists)
664+
{
665+
if (list != null)
666+
{
667+
paths.Add(AssetDatabase.GetAssetPath(list));
668+
}
669+
}
670+
return paths;
622671
}
623672

624673
/// <summary>Resolves a prefab path, GUID or name to the prefab asset.</summary>
@@ -641,16 +690,13 @@ internal static GameObject ResolvePrefab(string prefab)
641690
}
642691
else
643692
{
644-
var matches = AssetDatabase.FindAssets($"{prefab} t:Prefab")
645-
.Select(AssetDatabase.GUIDToAssetPath)
646-
.Where(p => string.Equals(Path.GetFileNameWithoutExtension(p), prefab, StringComparison.OrdinalIgnoreCase))
647-
.Distinct()
648-
.ToList();
693+
var matches = AssetPaths(AssetDatabase.FindAssets($"{prefab} t:Prefab"))
694+
.FindAll(p => string.Equals(Path.GetFileNameWithoutExtension(p), prefab, StringComparison.OrdinalIgnoreCase));
649695
if (matches.Count > 1)
650696
{
651697
throw new ArgumentException($"Several prefabs are named '{prefab}': {string.Join(", ", matches)}. Pass the path instead.");
652698
}
653-
path = matches.FirstOrDefault();
699+
path = matches.Count > 0 ? matches[0] : null;
654700
}
655701

656702
var asset = (string.IsNullOrEmpty(path) ? null : AssetDatabase.LoadAssetAtPath<GameObject>(path)) ?? throw new ArgumentException($"No prefab found for '{prefab}'. Search with asset find, or list network prefabs with netcode.gameobjects list_prefabs.");

‎com.unity.netcode.gameobjects/Editor/CliQueries/NgoCliQueries.Config.cs‎

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Globalization;
4-
using System.Linq;
54
using System.Reflection;
65
using Unity.Netcode.GameObjects.Editor.Configuration;
76
using Unity.Netcode.Transports.UTP;
@@ -33,7 +32,7 @@ internal static ConfigResult GetConfig(string managerFilter)
3332
Transport = DescribeTransport(config.NetworkTransport),
3433
LogLevel = manager.LogLevel.ToString(),
3534
PlayerPrefab = config.PlayerPrefab != null ? AssetPathOrName(config.PlayerPrefab) : null,
36-
PrefabsLists = config.Prefabs?.NetworkPrefabsLists.Where(l => l != null).Select(AssetDatabase.GetAssetPath).ToList() ?? new List<string>(),
35+
PrefabsLists = config.Prefabs != null ? PrefabsListPaths(config.Prefabs.NetworkPrefabsLists) : new List<string>(),
3736
GenerateDefaultNetworkPrefabs = NetcodeForGameObjectsProjectSettings.instance.GenerateDefaultNetworkPrefabs,
3837
DefaultNetworkPrefabsPath = NetworkPrefabProcessor.DefaultNetworkPrefabsPath,
3938
};
@@ -42,9 +41,8 @@ internal static ConfigResult GetConfig(string managerFilter)
4241
var value = field.GetValue(config);
4342
result.NetworkConfig[ToSnakeCase(field.Name)] = value is Enum ? value.ToString() : value;
4443
}
45-
result.SettableFields = SettableConfigFields().Select(f => ToSnakeCase(f.Name))
46-
.Concat(new[] { k_Address, k_Port, k_ListenAddress, k_PlayerPrefab, k_LogLevel })
47-
.ToList();
44+
result.SettableFields = SettableConfigFields().ConvertAll(f => ToSnakeCase(f.Name));
45+
result.SettableFields.AddRange(new[] { k_Address, k_Port, k_ListenAddress, k_PlayerPrefab, k_LogLevel });
4846
return result;
4947
}
5048

@@ -163,7 +161,7 @@ internal static ConfigSetResult SetConfig(string field, string value, string man
163161
}
164162
default:
165163
{
166-
var configField = SettableConfigFields().FirstOrDefault(f => ToSnakeCase(f.Name) == name) ?? throw new ArgumentException($"Unknown field '{field}'. Settable: {string.Join(", ", GetConfig(managerFilter).SettableFields)}.");
164+
var configField = SettableConfigFields().Find(f => ToSnakeCase(f.Name) == name) ?? throw new ArgumentException($"Unknown field '{field}'. Settable: {string.Join(", ", GetConfig(managerFilter).SettableFields)}.");
167165
var parsed = ParseValue(configField.FieldType, value, name);
168166
ValidateConfigValue(configField.Name, name, parsed, result.Notes);
169167
target = manager;
@@ -295,7 +293,7 @@ private static void ValidateManager(NetworkManager manager, Action<string, strin
295293
// The dialog opens on entering play mode; in play mode the manager has moved to DontDestroyOnLoad.
296294
var scene = manager.gameObject.scene;
297295
var checkScene = config.EnableSceneManagement && !EditorApplication.isPlaying;
298-
if (checkScene && !string.IsNullOrEmpty(scene.path) && !EditorBuildSettings.scenes.Any(s => s.enabled && s.path == scene.path))
296+
if (checkScene && !string.IsNullOrEmpty(scene.path) && !Array.Exists(EditorBuildSettings.scenes, s => s.enabled && s.path == scene.path))
299297
{
300298
add("warning", "SCENE_NOT_IN_BUILD", where, $"Scene management is on and {scene.path} isn't in the build's scene list, so clients can't load it. Entering play mode opens a dialog asking to add it; add it first with scene add_to_build, or turn scene management off (netcode.gameobjects.config set enable_scene_management false).");
301299
}
@@ -309,8 +307,8 @@ private static void ValidateManager(NetworkManager manager, Action<string, strin
309307
add("error", "TICK_RATE_ZERO", where, "tick_rate is 0. Set it with netcode.gameobjects.config set tick_rate 30.");
310308
}
311309

312-
var lists = config.Prefabs?.NetworkPrefabsLists.Where(l => l != null).ToList() ?? new List<NetworkPrefabsList>();
313-
if (config.Prefabs != null && config.Prefabs.NetworkPrefabsLists.Any(l => l == null))
310+
var lists = config.Prefabs != null ? config.Prefabs.NetworkPrefabsLists.FindAll(l => l != null) : new List<NetworkPrefabsList>();
311+
if (config.Prefabs != null && config.Prefabs.NetworkPrefabsLists.Exists(l => l == null))
314312
{
315313
add("error", "MISSING_PREFABS_LIST", where, "A Network Prefabs Lists entry is empty or points to a deleted asset.");
316314
}
@@ -369,7 +367,7 @@ void Add(UnityEngine.GameObject prefab)
369367
private static void ValidateHybrid(NetworkConfig config, List<UnityEngine.GameObject> hybrid, string where, Action<string, string, string, string> add)
370368
{
371369
#if UNIFIED_NETCODE
372-
var names = string.Join(", ", hybrid.Take(5).Select(AssetPathOrName));
370+
var names = JoinNames(hybrid.GetRange(0, Math.Min(5, hybrid.Count)), AssetPathOrName);
373371
if (config.NetworkTopology == NetworkTopologyTypes.DistributedAuthority)
374372
{
375373
add("error", "HYBRID_WITH_DISTRIBUTED_AUTHORITY", where, $"{NetworkPrefabs.DistributedAuthorityHybridPrefabError} Hybrid prefabs: {names}.");
@@ -394,23 +392,28 @@ private static NetcodeConfig FindNetcodeConfig()
394392
{
395393
return NetcodeConfig.Global;
396394
}
397-
var configs = AssetDatabase.FindAssets($"t:{nameof(NetcodeConfig)}")
398-
.Select(AssetDatabase.GUIDToAssetPath)
399-
.Select(AssetDatabase.LoadAssetAtPath<NetcodeConfig>)
400-
.Where(c => c != null)
401-
.ToList();
402395
// A project has one NetcodeConfig (Netcode for Entities reports several as an error).
403-
return configs.FirstOrDefault();
396+
foreach (var guid in AssetDatabase.FindAssets($"t:{nameof(NetcodeConfig)}"))
397+
{
398+
var config = AssetDatabase.LoadAssetAtPath<NetcodeConfig>(AssetDatabase.GUIDToAssetPath(guid));
399+
if (config != null)
400+
{
401+
return config;
402+
}
403+
}
404+
return null;
404405
}
405406
#endif
406407

407408
// --- Helpers ---
408409

409-
private static IEnumerable<FieldInfo> SettableConfigFields()
410+
private static List<FieldInfo> SettableConfigFields()
410411
{
411-
return typeof(NetworkConfig).GetFields(BindingFlags.Instance | BindingFlags.Public)
412-
.Where(f => !f.IsInitOnly && (f.FieldType.IsPrimitive || f.FieldType.IsEnum))
413-
.OrderBy(f => f.MetadataToken);
412+
var fields = new List<FieldInfo>(typeof(NetworkConfig).GetFields(BindingFlags.Instance | BindingFlags.Public));
413+
fields.RemoveAll(f => f.IsInitOnly || !(f.FieldType.IsPrimitive || f.FieldType.IsEnum));
414+
// Declaration order; each field's metadata token is unique.
415+
fields.Sort((a, b) => a.MetadataToken.CompareTo(b.MetadataToken));
416+
return fields;
414417
}
415418

416419
private static object ParseValue(Type type, string value, string name)
@@ -420,7 +423,7 @@ private static object ParseValue(Type type, string value, string name)
420423
{
421424
if (type.IsEnum)
422425
{
423-
var match = Enum.GetNames(type).FirstOrDefault(n => string.Equals(n, text, StringComparison.OrdinalIgnoreCase)) ?? throw new FormatException();
426+
var match = Array.Find(Enum.GetNames(type), n => string.Equals(n, text, StringComparison.OrdinalIgnoreCase)) ?? throw new FormatException();
424427
return Enum.Parse(type, match);
425428
}
426429
if (type == typeof(bool))

0 commit comments

Comments
 (0)