riversong code showcase
This commit is contained in:
14
Source/Riversong/Game/CommonServices/Entities/Entity.cs
Normal file
14
Source/Riversong/Game/CommonServices/Entities/Entity.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public class Entity
|
||||
{
|
||||
public const int InvalidId = 0;
|
||||
|
||||
public int Id { get; private set; }
|
||||
|
||||
public static T Create<T>(int id) where T : Entity, new()
|
||||
{
|
||||
return new T { Id = id };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public class EntityCache : IEntityCache
|
||||
{
|
||||
private Dictionary<int, Cache> _caches = new();
|
||||
|
||||
public void CreateCache<T>(int key, Predicate<T> filter) where T : Entity
|
||||
{
|
||||
if (!_caches.TryGetValue(key, out var cache))
|
||||
{
|
||||
cache = new Cache<T>();
|
||||
_caches.Add(key, cache);
|
||||
}
|
||||
((Cache<T>)cache).Filter = filter;
|
||||
}
|
||||
|
||||
public List<T> Get<T>(int key) where T : Entity
|
||||
{
|
||||
if (!_caches.TryGetValue(key, out var cache))
|
||||
{
|
||||
cache = new Cache<T>();
|
||||
_caches.Add(key, cache);
|
||||
}
|
||||
return ((Cache<T>)cache).Entities;
|
||||
}
|
||||
|
||||
public void OnAdded(Entity entity)
|
||||
{
|
||||
foreach (var cache in _caches.Values) cache.TryAdd(entity);
|
||||
}
|
||||
|
||||
public void OnRemoved(Entity entity)
|
||||
{
|
||||
foreach (var cache in _caches.Values) cache.TryRemove(entity);
|
||||
}
|
||||
|
||||
private abstract class Cache
|
||||
{
|
||||
public void TryAdd(Entity entity)
|
||||
{
|
||||
if (FilterEntity(entity)) Add(entity);
|
||||
}
|
||||
|
||||
public void TryRemove(Entity entity)
|
||||
{
|
||||
if (FilterEntity(entity)) Remove(entity);
|
||||
}
|
||||
|
||||
protected abstract bool FilterEntity(Entity entity);
|
||||
|
||||
protected abstract void Add(Entity entity);
|
||||
|
||||
protected abstract void Remove(Entity entity);
|
||||
}
|
||||
|
||||
private class Cache<T> : Cache where T : Entity
|
||||
{
|
||||
public Predicate<T> Filter { get; set; }
|
||||
|
||||
public List<T> Entities { get; } = new();
|
||||
|
||||
protected override bool FilterEntity(Entity entity)
|
||||
{
|
||||
return entity is T typedEntity && Filter.Invoke(typedEntity);
|
||||
}
|
||||
|
||||
protected override void Add(Entity entity)
|
||||
{
|
||||
Entities.Add((T)entity);
|
||||
}
|
||||
|
||||
protected override void Remove(Entity entity)
|
||||
{
|
||||
Entities.Remove((T)entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public static class EntityCacheExtensions
|
||||
{
|
||||
public static List<Building> GetHarvesterBuildings(this IEntityCache entityCache)
|
||||
{
|
||||
return entityCache.Get<Building>((int)EntityCacheKeys.HarvesterBuildings);
|
||||
}
|
||||
|
||||
public static List<Building> GetHunterBuildings(this IEntityCache entityCache)
|
||||
{
|
||||
return entityCache.Get<Building>((int)EntityCacheKeys.HunterBuildings);
|
||||
}
|
||||
|
||||
public static List<Building> GetFarmBuildings(this IEntityCache entityCache)
|
||||
{
|
||||
return entityCache.Get<Building>((int)EntityCacheKeys.FarmBuildings);
|
||||
}
|
||||
|
||||
public static List<Building> GetProducers(this IEntityCache entityCache)
|
||||
{
|
||||
return entityCache.Get<Building>((int)EntityCacheKeys.ProducerBuildings);
|
||||
}
|
||||
|
||||
public static List<Building> GetProviders(this IEntityCache entityCache)
|
||||
{
|
||||
return entityCache.Get<Building>((int)EntityCacheKeys.ProviderBuildings);
|
||||
}
|
||||
|
||||
public static List<Building> GetBuildingsWithWorkers(this IEntityCache entityCache)
|
||||
{
|
||||
return entityCache.Get<Building>((int)EntityCacheKeys.BuildingsWithWorkers);
|
||||
}
|
||||
|
||||
public static List<Building> GetHouses(this IEntityCache entityCache)
|
||||
{
|
||||
return entityCache.Get<Building>((int)EntityCacheKeys.HouseBuildings);
|
||||
}
|
||||
|
||||
public static List<Building> GetTentBuildings(this IEntityCache entityCache)
|
||||
{
|
||||
return entityCache.Get<Building>((int)EntityCacheKeys.TentBuildings);
|
||||
}
|
||||
|
||||
public static List<Building> GetStorageBuildings(this IEntityCache entityCache)
|
||||
{
|
||||
return entityCache.Get<Building>((int)EntityCacheKeys.StorageBuildings);
|
||||
}
|
||||
|
||||
public static List<Building> GetStorageRequestBuildings(this IEntityCache entityCache)
|
||||
{
|
||||
return entityCache.Get<Building>((int)EntityCacheKeys.StorageRequestBuildings);
|
||||
}
|
||||
|
||||
public static List<Agent> GetHunterAgents(this IEntityCache entityCache)
|
||||
{
|
||||
return entityCache.Get<Agent>((int)EntityCacheKeys.HunterAgents);
|
||||
}
|
||||
|
||||
public static List<Agent> GetCritterAgents(this IEntityCache entityCache)
|
||||
{
|
||||
return entityCache.Get<Agent>((int)EntityCacheKeys.CritterAgents);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public enum EntityCacheKeys
|
||||
{
|
||||
Invalid,
|
||||
|
||||
#region Buildings
|
||||
|
||||
HarvesterBuildings,
|
||||
|
||||
HunterBuildings,
|
||||
|
||||
FarmBuildings,
|
||||
|
||||
ProducerBuildings,
|
||||
|
||||
ProviderBuildings,
|
||||
|
||||
BuildingsWithWorkers,
|
||||
|
||||
HouseBuildings,
|
||||
|
||||
StorageBuildings,
|
||||
|
||||
StorageRequestBuildings,
|
||||
|
||||
TentBuildings,
|
||||
|
||||
#endregion
|
||||
|
||||
#region Agents
|
||||
|
||||
HunterAgents,
|
||||
|
||||
CritterAgents
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
[GameSystemGroup(typeof(EarlyGameSystemGroup))]
|
||||
[InitializeAfter(typeof(PreLoadAssetsSystem))]
|
||||
public class EntityCacheSystem : GameSystem, IInitializable, IDisposable
|
||||
{
|
||||
[InjectService]
|
||||
private IEntityCollection _entityCollection;
|
||||
|
||||
[InjectService]
|
||||
private IEntityCache _entityCache;
|
||||
|
||||
[InjectService]
|
||||
private GameConfig _config;
|
||||
|
||||
public EntityCacheSystem(IServiceLocator serviceLocator) : base(serviceLocator)
|
||||
{
|
||||
}
|
||||
|
||||
public UniTask InitializeAsync()
|
||||
{
|
||||
CreateCaches();
|
||||
|
||||
foreach (var entity in _entityCollection.GetInternalEntityList(typeof(Entity))) _entityCache.OnAdded(entity);
|
||||
|
||||
var callbacks = _entityCollection.On<Entity>();
|
||||
callbacks.Added += _entityCache.OnAdded;
|
||||
callbacks.Removed += _entityCache.OnRemoved;
|
||||
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
private void CreateCaches()
|
||||
{
|
||||
_entityCache.CreateCache<Building>((int)EntityCacheKeys.HarvesterBuildings, b => b.Definition.HarvestedResource);
|
||||
_entityCache.CreateCache<Building>((int)EntityCacheKeys.HunterBuildings, b => b.Definition.TargetCritter);
|
||||
_entityCache.CreateCache<Building>((int)EntityCacheKeys.FarmBuildings, b => b.Definition.IsFarm);
|
||||
_entityCache.CreateCache<Building>((int)EntityCacheKeys.ProducerBuildings, b => b.Definition.Recipe);
|
||||
_entityCache.CreateCache<Building>((int)EntityCacheKeys.ProviderBuildings, b => b.Definition.ProvidedProducts.Count > 0);
|
||||
_entityCache.CreateCache<Building>((int)EntityCacheKeys.BuildingsWithWorkers, b => b.Definition.WorkerCount > 0);
|
||||
_entityCache.CreateCache<Building>((int)EntityCacheKeys.HouseBuildings, b => b.Definition.IsHouse);
|
||||
_entityCache.CreateCache<Building>((int)EntityCacheKeys.StorageBuildings, b => b.Definition.IsStorage);
|
||||
_entityCache.CreateCache<Building>((int)EntityCacheKeys.StorageRequestBuildings, b => b.Definition.IsStorage);
|
||||
_entityCache.CreateCache<Building>((int)EntityCacheKeys.TentBuildings, b => b.Definition == _config.Population.TentBuilding.Asset);
|
||||
_entityCache.CreateCache<Agent>(
|
||||
(int)EntityCacheKeys.HunterAgents,
|
||||
a =>
|
||||
{
|
||||
ref var jobState = ref a.GetJobStateRW();
|
||||
return jobState.Job == AgentJob.Hunter;
|
||||
});
|
||||
_entityCache.CreateCache<Agent>(
|
||||
(int)EntityCacheKeys.CritterAgents,
|
||||
a =>
|
||||
{
|
||||
ref var critterState = ref a.GetCritterStateRW();
|
||||
return critterState.IsCritter;
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
var callbacks = _entityCollection.On<Entity>();
|
||||
callbacks.Added -= _entityCache.OnAdded;
|
||||
callbacks.Removed -= _entityCache.OnRemoved;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public interface IEntityCache
|
||||
{
|
||||
void CreateCache<T>(int key, Predicate<T> filter) where T : Entity;
|
||||
|
||||
List<T> Get<T>(int key) where T : Entity;
|
||||
|
||||
void OnAdded(Entity entity);
|
||||
|
||||
void OnRemoved(Entity entity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public class EntityCollection : IEntityCollection
|
||||
{
|
||||
private static readonly List<Entity> Empty = new();
|
||||
|
||||
private int _nextId = Entity.InvalidId + 1;
|
||||
|
||||
private Dictionary<int, Entity> _entitiesById = new();
|
||||
|
||||
private ListMultiDictionary<Type, Entity> _entitiesByType = new();
|
||||
|
||||
private Dictionary<Type, IEntityCollectionCallbacks> _callbacks = new();
|
||||
|
||||
public T Create<T>() where T : Entity, new()
|
||||
{
|
||||
return Entity.Create<T>(_nextId++);
|
||||
}
|
||||
|
||||
public void Add(Entity entity)
|
||||
{
|
||||
_entitiesById.Add(entity.Id, entity);
|
||||
OnAdded(entity.GetType(), entity);
|
||||
}
|
||||
|
||||
private void OnAdded(Type type, Entity entity)
|
||||
{
|
||||
_entitiesByType.Add(type, entity);
|
||||
|
||||
if (_callbacks.TryGetValue(type, out var callbacks)) callbacks.OnAdded(entity);
|
||||
|
||||
if (type == typeof(Entity)) return;
|
||||
|
||||
OnAdded(type.BaseType, entity);
|
||||
}
|
||||
|
||||
public Entity Remove(int id)
|
||||
{
|
||||
if (!_entitiesById.Remove(id, out var entity)) return null;
|
||||
|
||||
OnRemoved(entity.GetType(), entity);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
private void OnRemoved(Type type, Entity entity)
|
||||
{
|
||||
_entitiesByType.Remove(type, entity);
|
||||
|
||||
if (_callbacks.TryGetValue(type, out var callbacks)) callbacks.OnRemoved(entity);
|
||||
|
||||
if (type == typeof(Entity)) return;
|
||||
|
||||
OnRemoved(type.BaseType, entity);
|
||||
}
|
||||
|
||||
public bool Exists(int id)
|
||||
{
|
||||
return _entitiesById.ContainsKey(id);
|
||||
}
|
||||
|
||||
public Entity Get(int id)
|
||||
{
|
||||
return _entitiesById.TryGetValue(id, out var entity) ? entity : null;
|
||||
}
|
||||
|
||||
public Type GetEntityType(int id)
|
||||
{
|
||||
return Get(id)?.GetType();
|
||||
}
|
||||
|
||||
public List<Entity> GetInternalEntityList(Type type)
|
||||
{
|
||||
return _entitiesByType.TryGetValues(type, out var entityList) ? entityList : Empty;
|
||||
}
|
||||
|
||||
public IEntityCollectionCallbacks<T> On<T>() where T : Entity
|
||||
{
|
||||
if (!_callbacks.TryGetValue(typeof(T), out var callbacks))
|
||||
{
|
||||
callbacks = new EntityCollectionCallbacks<T>();
|
||||
_callbacks.Add(typeof(T), callbacks);
|
||||
}
|
||||
return (IEntityCollectionCallbacks<T>)callbacks;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public class EntityCollectionCallbacks<T> : IEntityCollectionCallbacks<T> where T : Entity
|
||||
{
|
||||
public event Action<T> Added;
|
||||
|
||||
public event Action<T> Removed;
|
||||
|
||||
public void OnAdded(Entity entity)
|
||||
{
|
||||
Added?.Invoke((T)entity);
|
||||
}
|
||||
|
||||
public void OnRemoved(Entity entity)
|
||||
{
|
||||
Removed?.Invoke((T)entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public static class EntityCollectionExtensions
|
||||
{
|
||||
public static T Get<T>(this IEntityCollection entityCollection, int id) where T : Entity
|
||||
{
|
||||
return (T)entityCollection.Get(id);
|
||||
}
|
||||
|
||||
public static T CreateAndAdd<T>(this IEntityCollection entityCollection) where T : Entity, new()
|
||||
{
|
||||
var entity = entityCollection.Create<T>();
|
||||
entityCollection.Add(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static bool TryGet<T>(this IEntityCollection entityCollection, int id, out T entity) where T : Entity
|
||||
{
|
||||
entity = entityCollection.Get(id) as T;
|
||||
return entity != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
7
Source/Riversong/Game/CommonServices/Entities/IEntity.cs
Normal file
7
Source/Riversong/Game/CommonServices/Entities/IEntity.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public interface IEntity
|
||||
{
|
||||
int Id { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public interface IEntityCollection
|
||||
{
|
||||
T Create<T>() where T : Entity, new();
|
||||
|
||||
void Add(Entity entity);
|
||||
|
||||
Entity Remove(int id);
|
||||
|
||||
bool Exists(int id);
|
||||
|
||||
Entity Get(int id);
|
||||
|
||||
Type GetEntityType(int id);
|
||||
|
||||
public List<Entity> GetInternalEntityList(Type type);
|
||||
|
||||
public IEntityCollectionCallbacks<T> On<T>() where T : Entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public interface IEntityCollectionCallbacks
|
||||
{
|
||||
public void OnAdded(Entity entity);
|
||||
|
||||
public void OnRemoved(Entity entity);
|
||||
}
|
||||
|
||||
public interface IEntityCollectionCallbacks<out T> : IEntityCollectionCallbacks
|
||||
{
|
||||
event Action<T> Added;
|
||||
|
||||
event Action<T> Removed;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user