using System; using System.Collections.Generic; using Robust.Shared.GameObjects; using Robust.Shared.Log; namespace Robust.Shared.ViewVariables; public delegate ViewVariablesPath? HandleTypePath(ViewVariablesPath path, string relativePath); public delegate ViewVariablesPath? HandleTypePath(T? obj, string relativePath); public delegate IEnumerable ListTypeCustomPaths(ViewVariablesPath path); public delegate IEnumerable ListTypeCustomPaths(T? obj); public delegate ViewVariablesPath? PathHandler(ViewVariablesPath path); public delegate ViewVariablesPath? PathHandler(T obj); public delegate ViewVariablesPath? PathHandlerNullable(T? obj); public delegate ViewVariablesPath? PathHandlerComponent(EntityUid uid, T component); public delegate TValue ComponentPropertyGetter(EntityUid uid, TComp comp); public delegate void ComponentPropertySetter(EntityUid uid, TValue value, TComp? comp); public abstract class ViewVariablesTypeHandler { internal abstract ViewVariablesPath? HandlePath(ViewVariablesPath path, string relativePath); internal abstract IEnumerable ListPath(ViewVariablesPath path); } public sealed class ViewVariablesTypeHandler : ViewVariablesTypeHandler { private readonly List _handlers = new(); private readonly Dictionary _paths = new(); internal ViewVariablesTypeHandler() { } /// /// Adding handler methods allow you to dynamically create and return ViewVariables paths for any sort of path. /// /// /// The handlers are iterated in the order they were registered in. /// Handlers registered with this method take precedence over handlers registered for specific relative paths. /// /// The same object instance, so you can chain method calls. public ViewVariablesTypeHandler AddHandler(HandleTypePath handle, ListTypeCustomPaths list) { ViewVariablesPath? HandleWrapper(ViewVariablesPath path, string relativePath) => handle((T?)path.Get(), relativePath); IEnumerable ListWrapper(ViewVariablesPath path) => list((T?) path.Get()); _handlers.Add(new TypeHandlerData(HandleWrapper, ListWrapper, handle, list)); return this; } /// public ViewVariablesTypeHandler AddHandler(HandleTypePath handle, ListTypeCustomPaths list) { _handlers.Add(new TypeHandlerData(handle, list)); return this; } /// /// Remove a specific handler method pair from the type handler. /// /// The same object instance, so you can chain method calls. /// If the methods specified were not registered. public ViewVariablesTypeHandler RemoveHandler(HandleTypePath handle, ListTypeCustomPaths list) { for (var i = 0; i < _handlers.Count; i++) { var data = _handlers[i]; if (data.OriginalHandle != handle || data.OriginalList != list) continue; _handlers.RemoveAt(i); return this; } throw new ArgumentException("The specified arguments were not found in the list!"); } /// public ViewVariablesTypeHandler RemoveHandler(HandleTypePath handle, ListTypeCustomPaths list) { for (var i = 0; i < _handlers.Count; i++) { var data = _handlers[i]; if (data.Handle != handle || data.List != list) continue; _handlers.RemoveAt(i); return this; } throw new ArgumentException("The specified arguments were not found in the list!"); } /// /// With this method you can register a handler to handle a specific path relative to the type instance. /// /// The same object instance, so you can chain method calls. public ViewVariablesTypeHandler AddPath(string path, PathHandler handler) { ViewVariablesPath? Wrapper(T? t) => t != null ? handler(t) : null; return AddPathNullable(path, (PathHandlerNullable) Wrapper); } /// /// As opposed to , here the passed object is nullable. /// public ViewVariablesTypeHandler AddPathNullable(string path, PathHandlerNullable handler) { ViewVariablesPath? Wrapper(ViewVariablesPath p) => handler((T?) p.Get()); return AddPath(path, Wrapper); } /// /// As opposed to the rest of "AddPath" methods, this one is specific to entity components. public ViewVariablesTypeHandler AddPath(string path, PathHandlerComponent handler) { ViewVariablesPath? Wrapper(ViewVariablesPath p) { if (p is not ViewVariablesComponentPath pc || pc.Get() is not {} obj) return null; return handler(pc.Owner, (T) obj); } return AddPath(path, Wrapper); } /// public ViewVariablesTypeHandler AddPath(string path, ComponentPropertyGetter getter, ComponentPropertySetter? setter = null) { // Gee, these wrappers are getting more and more complicated... ViewVariablesPath? Wrapper(ViewVariablesPath p) { if (p is not ViewVariablesComponentPath pc || pc.Get() is not {} obj) return null; var comp = (T) obj; var newPath = ViewVariablesPath.FromGetter(() => getter(pc.Owner, comp), typeof(TValue)); if (setter != null) { newPath = newPath.WithSetter(value => { // In case it explodes with a NRE or something! try { setter(pc.Owner, (TValue) value!, comp); } catch (NullReferenceException e) { Logger.ErrorS(nameof(ViewVariablesManager), e, $"NRE caught in setter for path \"{path}\" for type \"{typeof(T).Name}\"..."); } }); } return newPath; } return AddPath(path, Wrapper); } /// public ViewVariablesTypeHandler AddPath(string path, PathHandler handler) { _paths.Add(path, handler); return this; } /// /// Removes a handler for a specific relative path. /// /// The same object instance, so you can chain method calls. public ViewVariablesTypeHandler RemovePath(string path) { _paths.Remove(path); return this; } internal override ViewVariablesPath? HandlePath(ViewVariablesPath path, string relativePath) { // Dynamic handlers take precedence. Iterated by order of registration. foreach (var data in _handlers) { if (data.Handle(path, relativePath) is {} dynPath) return dynPath; } // Finally, try to get a static handler. return _paths.TryGetValue(relativePath, out var handler) ? handler(path) : null; } internal override IEnumerable ListPath(ViewVariablesPath path) { foreach (var data in _handlers) { foreach (var p in data.List(path)) { yield return p; } } foreach (var (p, handler) in _paths) { if (handler(path) is {}) yield return p; } } private sealed class TypeHandlerData { public readonly HandleTypePath Handle; public readonly ListTypeCustomPaths List; public readonly HandleTypePath? OriginalHandle; public readonly ListTypeCustomPaths? OriginalList; public TypeHandlerData(HandleTypePath handle, ListTypeCustomPaths list, HandleTypePath? origHandle = null, ListTypeCustomPaths? origList = null) { Handle = handle; List = list; OriginalHandle = origHandle; OriginalList = origList; } } }