#W# Added: More things to the ToAdd Folder.
This commit is contained in:
parent
cdc061dc54
commit
910cdde124
16 changed files with 3389 additions and 0 deletions
270
ToAdd/GraveRobbing/PandoraChestLogger.cs
Normal file
270
ToAdd/GraveRobbing/PandoraChestLogger.cs
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/**********************************
|
||||
* Author: Unknow *
|
||||
* Update/Fixed/Refactoring: Expa *
|
||||
* Script: PandoraChestLogger.cs *
|
||||
* Optimized for ModernUO 0.15.4.18*
|
||||
**********************************/
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Server;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
|
||||
static class PandoraChestLogger
|
||||
{
|
||||
static readonly ConcurrentQueue<string> Q = new();
|
||||
static CancellationTokenSource _cts;
|
||||
static Task _worker;
|
||||
static string _logFile;
|
||||
static long _maxSize;
|
||||
static int _flushInterval;
|
||||
static readonly object _initLock = new();
|
||||
static readonly JsonSerializerOptions _jsonOpts = new() { WriteIndented = false, PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
lock (_initLock)
|
||||
{
|
||||
if (_worker != null && !_worker.IsCompleted) return;
|
||||
var baseDir = AppDomain.CurrentDomain.BaseDirectory ?? Environment.CurrentDirectory;
|
||||
var logsDir = Path.Combine(baseDir, "Logs");
|
||||
try { Directory.CreateDirectory(logsDir); } catch (Exception ex) { try { Console.Error.WriteLine($"PandoraChestLogger.Initialize: CreateDirectory failed: {ex}"); } catch { } }
|
||||
_logFile = Path.Combine(logsDir, "PandoraChestSpawns.log");
|
||||
var cfg = PandoraChestConfig.Current ?? new PandoraChestConfig();
|
||||
_maxSize = cfg.Logging?.MaxFileSizeBytes ?? (10 * 1024 * 1024);
|
||||
_flushInterval = Math.Max(50, cfg.Logging?.FlushIntervalMs ?? 500);
|
||||
_cts = new CancellationTokenSource();
|
||||
_worker = Task.Run(() => Worker(_cts.Token));
|
||||
}
|
||||
}
|
||||
|
||||
static async Task Worker(CancellationToken token)
|
||||
{
|
||||
var batch = new List<string>();
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
try { await Task.Delay(_flushInterval, token).ConfigureAwait(false); } catch { break; }
|
||||
batch.Clear();
|
||||
while (Q.TryDequeue(out var l)) { batch.Add(l); if (batch.Count >= 1000) break; }
|
||||
if (batch.Count == 0) continue;
|
||||
try
|
||||
{
|
||||
RotateIfNeeded();
|
||||
if (!string.IsNullOrEmpty(_logFile)) File.AppendAllLines(_logFile, batch, Encoding.UTF8);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { Console.Error.WriteLine($"PandoraChestLogger.Worker AppendAllLines failed: {ex}"); } catch { }
|
||||
foreach (var l in batch) try { Console.WriteLine(l); } catch { }
|
||||
}
|
||||
}
|
||||
var rem = new List<string>();
|
||||
while (Q.TryDequeue(out var l)) rem.Add(l);
|
||||
if (rem.Count > 0)
|
||||
{
|
||||
try { RotateIfNeeded(); if (!string.IsNullOrEmpty(_logFile)) File.AppendAllLines(_logFile, rem, Encoding.UTF8); } catch (Exception ex) { try { Console.Error.WriteLine($"PandoraChestLogger.Worker final flush failed: {ex}"); } catch { } }
|
||||
}
|
||||
}
|
||||
|
||||
static void RotateIfNeeded()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(_logFile)) return;
|
||||
var fi = new FileInfo(_logFile);
|
||||
if (fi.Exists && fi.Length > _maxSize)
|
||||
{
|
||||
var dest = _logFile + "." + DateTime.UtcNow.ToString("yyyyMMddHHmmss");
|
||||
try { File.Move(_logFile, dest); } catch (Exception ex) { try { Console.Error.WriteLine($"PandoraChestLogger.RotateIfNeeded File.Move failed: {ex}"); } catch { } }
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { try { Console.Error.WriteLine($"PandoraChestLogger.RotateIfNeeded failed: {ex}"); } catch { } }
|
||||
}
|
||||
|
||||
static string HashShort(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return "(null)";
|
||||
try
|
||||
{
|
||||
using var sha = SHA256.Create();
|
||||
var h = sha.ComputeHash(Encoding.UTF8.GetBytes(input));
|
||||
var hex = BitConverter.ToString(h).Replace("-", "").ToLowerInvariant();
|
||||
return hex.Length >= 16 ? hex.Substring(0, 16) : hex;
|
||||
}
|
||||
catch (Exception ex) { try { Console.Error.WriteLine($"PandoraChestLogger.HashShort failed: {ex}"); } catch { } return "(err)"; }
|
||||
}
|
||||
|
||||
static void EnqueueStructured(object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
var line = JsonSerializer.Serialize(obj, _jsonOpts);
|
||||
Q.Enqueue(line);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { Console.Error.WriteLine($"PandoraChestLogger.EnqueueStructured failed: {ex}"); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogInfo(string tag, string context = null, Mobile who = null, object extra = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var entry = new
|
||||
{
|
||||
timestamp = DateTime.UtcNow,
|
||||
level = "INFO",
|
||||
tag,
|
||||
context,
|
||||
player = who?.Name ?? "(null)",
|
||||
accountHash = SafeAccountHash(who),
|
||||
extra
|
||||
};
|
||||
EnqueueStructured(entry);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public static void LogWarn(string tag, string context = null, Mobile who = null, object extra = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var entry = new
|
||||
{
|
||||
timestamp = DateTime.UtcNow,
|
||||
level = "WARN",
|
||||
tag,
|
||||
context,
|
||||
player = who?.Name ?? "(null)",
|
||||
accountHash = SafeAccountHash(who),
|
||||
extra
|
||||
};
|
||||
EnqueueStructured(entry);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public static void LogError(string tag, Exception ex, string context = null, Mobile who = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var entry = new
|
||||
{
|
||||
timestamp = DateTime.UtcNow,
|
||||
level = "ERROR",
|
||||
tag,
|
||||
context,
|
||||
player = who?.Name ?? "(null)",
|
||||
accountHash = SafeAccountHash(who),
|
||||
exception = ex?.ToString()
|
||||
};
|
||||
EnqueueStructured(entry);
|
||||
}
|
||||
catch (Exception inner)
|
||||
{
|
||||
try { Console.Error.WriteLine($"PandoraChestLogger.LogError failed to enqueue: {inner}. Original: {tag} {context} {ex}"); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
static string SafeAccountHash(Mobile who)
|
||||
{
|
||||
try { var acc = who?.Account?.Username ?? who?.Account?.ToString() ?? "(null)"; return HashShort(acc); } catch { return "(err)"; }
|
||||
}
|
||||
|
||||
public static void LogSpawn(Mobile who, Map map, Point3D loc, int level, double spawnChance)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!PandoraChestConfig.Current.Logging.Enabled) return;
|
||||
}
|
||||
catch { /* best-effort: fall through */ }
|
||||
|
||||
try
|
||||
{
|
||||
var entry = new
|
||||
{
|
||||
timestamp = DateTime.UtcNow,
|
||||
level = "INFO",
|
||||
tag = "SPAWN",
|
||||
player = who?.Name ?? "(null)",
|
||||
accountHash = SafeAccountHash(who),
|
||||
map = map?.ToString(),
|
||||
loc = new { x = loc.X, y = loc.Y, z = loc.Z },
|
||||
levelNumber = level,
|
||||
chance = spawnChance
|
||||
};
|
||||
EnqueueStructured(entry);
|
||||
}
|
||||
catch (Exception ex) { LogError("PandoraChestLogger.LogSpawn", ex, null, who); }
|
||||
}
|
||||
|
||||
public static void LogContents(Mobile who, int level, IEnumerable<string> items)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!PandoraChestConfig.Current.Logging.Enabled) return;
|
||||
}
|
||||
catch { }
|
||||
|
||||
try
|
||||
{
|
||||
var entry = new
|
||||
{
|
||||
timestamp = DateTime.UtcNow,
|
||||
level = "INFO",
|
||||
tag = "CONTENTS",
|
||||
player = who?.Name ?? "(null)",
|
||||
accountHash = SafeAccountHash(who),
|
||||
levelNumber = level,
|
||||
items = items
|
||||
};
|
||||
EnqueueStructured(entry);
|
||||
}
|
||||
catch (Exception ex) { LogError("PandoraChestLogger.LogContents", ex, null, who); }
|
||||
}
|
||||
|
||||
public static void LogMagicDrop(Mobile who, int level, Item it)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!PandoraChestConfig.Current.Logging.Enabled) return;
|
||||
}
|
||||
catch { }
|
||||
|
||||
try
|
||||
{
|
||||
var entry = new
|
||||
{
|
||||
timestamp = DateTime.UtcNow,
|
||||
level = "INFO",
|
||||
tag = "MAGIC",
|
||||
player = who?.Name ?? "(null)",
|
||||
accountHash = SafeAccountHash(who),
|
||||
levelNumber = level,
|
||||
itemType = it?.GetType().Name,
|
||||
itemName = it?.Name,
|
||||
hue = it?.Hue ?? 0
|
||||
};
|
||||
EnqueueStructured(entry);
|
||||
}
|
||||
catch (Exception ex) { LogError("PandoraChestLogger.LogMagicDrop", ex, null, who); }
|
||||
}
|
||||
|
||||
public static void Shutdown(int waitMs = 3000)
|
||||
{
|
||||
try
|
||||
{
|
||||
_cts?.Cancel();
|
||||
_worker?.Wait(waitMs);
|
||||
}
|
||||
catch (Exception ex) { try { Console.Error.WriteLine($"PandoraChestLogger.Shutdown error: {ex}"); } catch { } }
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue