riversong code showcase

This commit is contained in:
Daniele Marotta
2026-05-21 15:52:18 +02:00
commit 4c9eea1c02
462 changed files with 23406 additions and 0 deletions

View 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 };
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,7 @@
namespace DanieleMarotta.RiversongCodeShowcase
{
public interface IEntity
{
int Id { get; }
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}