Initial commit: OpenRA game engine
Fork from OpenRA/OpenRA with one-click launch script (start-ra.cmd) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
183
OpenRA.Game/Primitives/TypeDictionary.cs
Normal file
183
OpenRA.Game/Primitives/TypeDictionary.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you 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. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Primitives
|
||||
{
|
||||
public class TypeDictionary : IEnumerable<object>
|
||||
{
|
||||
static readonly Func<Type, ITypeContainer> CreateTypeContainer = t =>
|
||||
(ITypeContainer)typeof(TypeContainer<>).MakeGenericType(t).GetConstructor(Type.EmptyTypes).Invoke(null);
|
||||
|
||||
readonly Dictionary<Type, ITypeContainer> data;
|
||||
|
||||
public TypeDictionary()
|
||||
{
|
||||
data = [];
|
||||
}
|
||||
|
||||
public TypeDictionary(TypeDictionary cloneFrom)
|
||||
{
|
||||
data = cloneFrom.data.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Clone());
|
||||
}
|
||||
|
||||
ITypeContainer InnerGet(Type t)
|
||||
{
|
||||
return data.GetOrAdd(t, CreateTypeContainer);
|
||||
}
|
||||
|
||||
public void Add(object val)
|
||||
{
|
||||
var t = val.GetType();
|
||||
|
||||
foreach (var i in t.GetInterfaces())
|
||||
InnerAdd(i, val);
|
||||
foreach (var tt in t.BaseTypes())
|
||||
InnerAdd(tt, val);
|
||||
}
|
||||
|
||||
void InnerAdd(Type t, object val)
|
||||
{
|
||||
InnerGet(t).Add(val);
|
||||
}
|
||||
|
||||
public bool Contains<T>()
|
||||
{
|
||||
return data.ContainsKey(typeof(T));
|
||||
}
|
||||
|
||||
public bool Contains(Type t)
|
||||
{
|
||||
return data.ContainsKey(t);
|
||||
}
|
||||
|
||||
public T Get<T>()
|
||||
{
|
||||
return Get<T>(true);
|
||||
}
|
||||
|
||||
public T GetOrDefault<T>()
|
||||
{
|
||||
return Get<T>(false);
|
||||
}
|
||||
|
||||
T Get<T>(bool throwsIfMissing)
|
||||
{
|
||||
if (!data.TryGetValue(typeof(T), out var container))
|
||||
{
|
||||
if (throwsIfMissing)
|
||||
throw new InvalidOperationException($"TypeDictionary does not contain instance of type `{typeof(T)}`");
|
||||
return default;
|
||||
}
|
||||
|
||||
var list = ((TypeContainer<T>)container).Objects;
|
||||
if (list.Count > 1)
|
||||
throw new InvalidOperationException($"TypeDictionary contains multiple instances of type `{typeof(T)}`");
|
||||
return list[0];
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<T> WithInterface<T>()
|
||||
{
|
||||
if (data.TryGetValue(typeof(T), out var container))
|
||||
return ((TypeContainer<T>)container).Objects;
|
||||
return [];
|
||||
}
|
||||
|
||||
public void Remove<T>(T val)
|
||||
{
|
||||
var t = val.GetType();
|
||||
|
||||
foreach (var i in t.GetInterfaces())
|
||||
InnerRemove(i, val);
|
||||
foreach (var tt in t.BaseTypes())
|
||||
InnerRemove(tt, val);
|
||||
}
|
||||
|
||||
void InnerRemove(Type t, object val)
|
||||
{
|
||||
if (!data.TryGetValue(t, out var container))
|
||||
return;
|
||||
|
||||
container.Remove(val);
|
||||
if (container.Count == 0)
|
||||
data.Remove(t);
|
||||
}
|
||||
|
||||
public void TrimExcess()
|
||||
{
|
||||
data.TrimExcess();
|
||||
foreach (var t in data.Keys)
|
||||
InnerGet(t).TrimExcess();
|
||||
}
|
||||
|
||||
public IEnumerator<object> GetEnumerator()
|
||||
{
|
||||
return WithInterface<object>().GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
interface ITypeContainer
|
||||
{
|
||||
int Count { get; }
|
||||
void Add(object value);
|
||||
void Remove(object value);
|
||||
void TrimExcess();
|
||||
ITypeContainer Clone();
|
||||
}
|
||||
|
||||
sealed class TypeContainer<T> : ITypeContainer
|
||||
{
|
||||
public List<T> Objects { get; private init; } = [];
|
||||
|
||||
public int Count => Objects.Count;
|
||||
|
||||
public void Add(object value)
|
||||
{
|
||||
Objects.Add((T)value);
|
||||
}
|
||||
|
||||
public void Remove(object value)
|
||||
{
|
||||
Objects.Remove((T)value);
|
||||
}
|
||||
|
||||
public void TrimExcess()
|
||||
{
|
||||
Objects.TrimExcess();
|
||||
}
|
||||
|
||||
public ITypeContainer Clone()
|
||||
{
|
||||
return new TypeContainer<T>() { Objects = Objects.ToList() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class TypeExts
|
||||
{
|
||||
public static IEnumerable<Type> BaseTypes(this Type t)
|
||||
{
|
||||
while (t != null)
|
||||
{
|
||||
yield return t;
|
||||
t = t.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user