/************************************************************************* * ModernUO * * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GenerateSpawnersCommand.cs * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *************************************************************************/ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text.Json; using Microsoft.Extensions.FileSystemGlobbing; using Microsoft.Extensions.FileSystemGlobbing.Abstractions; using Server.Collections; using Server.Json; using Server.Logging; using Server.Network; using Server.Utilities; namespace Server.Engines.Spawners { public static class GenerateSpawnersCommand { private static readonly ILogger logger = LogFactory.GetLogger(typeof(GenerateSpawnersCommand)); public static void Initialize() { CommandSystem.Register("GenerateSpawners", AccessLevel.Developer, GenerateSpawners_OnCommand); } private static void GenerateSpawners_OnCommand(CommandEventArgs e) { var from = e.Mobile; if (e.Arguments.Length == 0) { from.SendMessage("Usage: [GenerateSpawners "); return; } var di = new DirectoryInfo(Core.BaseDirectory); var patternMatches = new Matcher() .AddInclude(e.Arguments[0]) .Execute(new DirectoryInfoWrapper(di)) .Files; List files = new List(); foreach (var match in patternMatches) { files.Add(new FileInfo(match.Path)); } if (files.Count == 0) { from.SendMessage("GenerateSpawners: No files found matching the pattern"); return; } var watch = Stopwatch.StartNew(); var allSpawners = new Dictionary(); foreach (var item in World.Items.Values) { if (item is ISpawner spawner) { allSpawners[spawner.Guid] = spawner; } } var options = JsonConfig.GetOptions(new TextDefinitionConverterFactory()); var totalGenerated = 0; var totalFailures = 0; for (var i = 0; i < files.Count; i++) { var file = files[i]; from.SendMessage($"GenerateSpawners: Generating spawners from {file.Name}..."); logger.Information("{User} is generating spawners from {File}", from, file.FullName); NetState.FlushAll(); try { var spawners = JsonConfig.Deserialize>(file.FullName); ParseSpawnerList(spawners, options, allSpawners, out var generated, out var failed); totalGenerated += generated; totalFailures += failed; } catch (JsonException) { from.SendMessage( $"GenerateSpawners: Exception parsing {file.FullName}, file may not be in the correct format." ); } } watch.Stop(); logger.Information( "Generated {Count} spawners ({Duration:F2} seconds, {Failures} failures)", totalGenerated, watch.Elapsed.TotalSeconds, totalFailures ); from.SendMessage( $"GenerateSpawners: Generated {totalGenerated} spawners ({watch.Elapsed.TotalSeconds:F2} seconds, {totalFailures} failures)" ); } private static void ParseSpawnerList( List spawners, JsonSerializerOptions options, Dictionary allSpawners, out int totalGenerated, out int failureCount ) { failureCount = 0; totalGenerated = 0; using var queue = PooledRefQueue.Create(); for (var i = 0; i < spawners.Count; i++) { var json = spawners[i]; var type = AssemblyHandler.FindTypeByName(json.Type); if (type == null || !typeof(BaseSpawner).IsAssignableFrom(type)) { logger.Error($"Invalid spawner type {json.Type ?? "(-null-)"} ({i})."); failureCount++; continue; } json.GetProperty("location", options, out Point3D location); json.GetProperty("map", options, out Map map); // Delete all spawners at this location. // Probably shouldn't do this outside of migrations? Is there a better way to find/fix spawners? foreach (var spawner in map.GetItemsInRange(location, 0)) { if (spawner.GetType() == type) { queue.Enqueue(spawner); allSpawners.Remove(spawner.Guid); } } while (queue.Count > 0) { queue.Dequeue().Delete(); } try { var spawner = type.CreateInstance(json, options); spawner!.MoveToWorld(location, map); spawner!.Respawn(); if (allSpawners.Remove(spawner.Guid, out var oldSpawner)) { oldSpawner.Delete(); } allSpawners.Add(spawner.Guid, spawner); totalGenerated++; } catch (Exception ex) { json.GetProperty("guid", options, out Guid guid); TraceException(ex, $"Failed to generate spawner {guid}."); failureCount++; } } } private static void TraceException(Exception ex, string message = "") { try { using var op = new StreamWriter("spawner-errors.log", true); op.WriteLine("# {0}", Core.Now); if (!string.IsNullOrEmpty(message)) { op.WriteLine(message); } op.WriteLine(ex); op.WriteLine(); op.WriteLine(); } catch { // ignored } #if DEBUG logger.Error(ex, message); #endif } } }