CodeJunkie.Repositories
1.0.0
dotnet add package CodeJunkie.Repositories --version 1.0.0
NuGet\Install-Package CodeJunkie.Repositories -Version 1.0.0
<PackageReference Include="CodeJunkie.Repositories" Version="1.0.0" />
<PackageVersion Include="CodeJunkie.Repositories" Version="1.0.0" />
<PackageReference Include="CodeJunkie.Repositories" />
paket add CodeJunkie.Repositories --version 1.0.0
#r "nuget: CodeJunkie.Repositories, 1.0.0"
#:package CodeJunkie.Repositories@1.0.0
#addin nuget:?package=CodeJunkie.Repositories&version=1.0.0
#tool nuget:?package=CodeJunkie.Repositories&version=1.0.0
CodeJunkie Messaging & Repository System
A high-performance messaging and repository system for Unity and .NET applications, providing publish-subscribe patterns, property change notifications, and thread-safe operations.
🎯 Features
- Thread-Safe Messaging - High-performance concurrent messaging with weak reference management
- Channel-Based Communication - Isolated message delivery through named channels
- Repository Pattern - Base classes for data management with built-in messaging
- Property Change Broadcasting - Automatic message broadcasting for property changes
- Synchronization Context Support - UI thread-safe message delivery
- Memory Efficient - Weak references prevent memory leaks from unsubscribed handlers
📦 Core Components
IMessenger Interface
Central messaging contract supporting both typed and untyped message delivery:
public interface IMessenger
{
ISubscription<T> Subscribe<T>(Action<T> action);
ISubscription<T> Subscribe<T>(string channel, Action<T> action);
void Publish<T>(T message);
void Publish<T>(string channel, T message);
}
Messenger Class
High-performance implementation using concurrent dictionaries and weak references:
var messenger = new Messenger();
// Subscribe to messages
var subscription = messenger.Subscribe<PlayerDiedMessage>(msg => {
Console.WriteLine($"Player {msg.PlayerId} died!");
});
// Publish messages
messenger.Publish(new PlayerDiedMessage { PlayerId = 123 });
// Clean up
subscription.Dispose();
RepositoryBase Class
Base class for repositories with built-in messaging and property change notifications:
public class PlayerRepository : RepositoryBase
{
private int _health;
private string _name;
public int Health
{
get => _health;
set => Set(ref _health, value, nameof(Health), broadcast: true);
}
public string Name
{
get => _name;
set => Set(ref _name, value, nameof(Name), broadcast: true);
}
}
🎮 Unity Game Development Examples
Player Health System
public class PlayerHealthSystem : MonoBehaviour
{
private PlayerRepository _playerRepo;
private IMessenger _messenger;
private ISubscription<PropertyChangedMessage<int>> _healthSubscription;
void Start()
{
_messenger = new Messenger();
_playerRepo = new PlayerRepository(_messenger);
// Subscribe to health changes
_healthSubscription = _messenger.Subscribe<PropertyChangedMessage<int>>(msg => {
if (msg.PropertyName == nameof(PlayerRepository.Health))
{
UpdateHealthUI(msg.NewValue);
if (msg.NewValue <= 0)
{
_messenger.Publish(new PlayerDiedMessage(_playerRepo));
}
}
});
// Subscribe to death events
_messenger.Subscribe<PlayerDiedMessage>(msg => {
ShowGameOverScreen();
});
}
void OnDestroy()
{
_healthSubscription?.Dispose();
_playerRepo?.Dispose();
}
void UpdateHealthUI(int health)
{
var healthBar = FindObjectOfType<Slider>();
healthBar.value = health / 100f;
}
}
public class PlayerDiedMessage : MessageBase
{
public PlayerDiedMessage(object sender) : base(sender) { }
}
Multi-System Communication
public class GameManager : MonoBehaviour
{
private IMessenger _messenger;
private PlayerRepository _playerRepo;
private InventoryRepository _inventoryRepo;
private ScoreRepository _scoreRepo;
void Start()
{
_messenger = new Messenger();
// Create repositories with shared messenger
_playerRepo = new PlayerRepository(_messenger);
_inventoryRepo = new InventoryRepository(_messenger);
_scoreRepo = new ScoreRepository(_messenger);
// Set up cross-system communication
SetupEnemyDefeatedHandling();
SetupItemCollectionHandling();
SetupLevelUpHandling();
}
void SetupEnemyDefeatedHandling()
{
_messenger.Subscribe<EnemyDefeatedMessage>(msg => {
_scoreRepo.AddScore(msg.ScoreValue);
_playerRepo.AddExperience(msg.ExpValue);
});
}
void SetupItemCollectionHandling()
{
_messenger.Subscribe<ItemCollectedMessage>(msg => {
_inventoryRepo.AddItem(msg.Item);
if (msg.Item.Type == ItemType.HealthPotion)
{
_playerRepo.Heal(msg.Item.Value);
}
});
}
void SetupLevelUpHandling()
{
_messenger.Subscribe<PropertyChangedMessage<int>>(msg => {
if (msg.PropertyName == nameof(PlayerRepository.Level))
{
_messenger.Publish(new LevelUpMessage(msg.NewValue));
}
});
}
}
Channel-Based Communication
public class UIManager : MonoBehaviour
{
private IMessenger _messenger;
void Start()
{
_messenger = new Messenger();
// Subscribe to UI-specific messages
_messenger.Subscribe<ShowDialogMessage>("UI", msg => {
ShowDialog(msg.Title, msg.Content);
});
_messenger.Subscribe<UpdateHUDMessage>("UI", msg => {
UpdateHUD(msg.Data);
});
// Subscribe to game events on different channel
_messenger.Subscribe<GameStateChangedMessage>("Game", msg => {
UpdateGameStateUI(msg.NewState);
});
}
public void ShowDialog(string title, string content)
{
// Show dialog logic
var dialog = Instantiate(dialogPrefab);
dialog.Setup(title, content);
}
}
// Publish to specific channels
_messenger.Publish("UI", new ShowDialogMessage("Warning", "Low Health!"));
_messenger.Publish("Game", new GameStateChangedMessage(GameState.Paused));
Thread-Safe UI Updates
public class NetworkManager : MonoBehaviour
{
private IMessenger _messenger;
private SynchronizationContext _mainThreadContext;
void Start()
{
_messenger = new Messenger();
_mainThreadContext = SynchronizationContext.Current;
// Subscribe with UI thread context
_messenger.Subscribe<NetworkDataMessage>(msg => {
UpdateUI(msg.Data); // This will run on main thread
}).ObserveOn(_mainThreadContext);
// Start background network operations
Task.Run(async () => await ProcessNetworkData());
}
async Task ProcessNetworkData()
{
while (true)
{
var data = await ReceiveNetworkData();
// Publish from background thread - will be marshaled to main thread
_messenger.Publish(new NetworkDataMessage(data));
await Task.Delay(100);
}
}
}
🔧 Advanced Usage
Custom Repository with Complex Logic
public class InventoryRepository : RepositoryBase
{
private ObservableList<Item> _items = new ObservableList<Item>();
private int _maxCapacity = 50;
public ObservableList<Item> Items => _items;
public int MaxCapacity
{
get => _maxCapacity;
set => Set(ref _maxCapacity, value, nameof(MaxCapacity), broadcast: true);
}
public bool IsFull => _items.Count >= _maxCapacity;
public bool TryAddItem(Item item)
{
if (IsFull)
{
Messenger?.Publish(new InventoryFullMessage(this));
return false;
}
_items.Add(item);
Messenger?.Publish(new ItemAddedMessage(this, item));
return true;
}
public bool TryRemoveItem(Item item)
{
if (_items.Remove(item))
{
Messenger?.Publish(new ItemRemovedMessage(this, item));
return true;
}
return false;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_items?.Clear();
}
base.Dispose(disposing);
}
}
Message Inheritance and Polymorphism
public abstract class GameEventMessage : MessageBase
{
public DateTime Timestamp { get; }
protected GameEventMessage(object sender) : base(sender)
{
Timestamp = DateTime.UtcNow;
}
}
public class PlayerActionMessage : GameEventMessage
{
public string Action { get; }
public PlayerActionMessage(object sender, string action) : base(sender)
{
Action = action;
}
}
public class CombatMessage : GameEventMessage
{
public int Damage { get; }
public string Target { get; }
public CombatMessage(object sender, int damage, string target) : base(sender)
{
Damage = damage;
Target = target;
}
}
// Subscribe to base class to receive all derived messages
_messenger.Subscribe<GameEventMessage>(msg => {
LogGameEvent(msg.Timestamp, msg.GetType().Name);
});
Performance Monitoring
public class PerformanceMonitor
{
private readonly IMessenger _messenger;
private readonly Dictionary<Type, int> _messageStats = new Dictionary<Type, int>();
public PerformanceMonitor(IMessenger messenger)
{
_messenger = messenger;
// Monitor all messages using base type
_messenger.Subscribe<object>(msg => {
var type = msg.GetType();
_messageStats[type] = _messageStats.GetValueOrDefault(type) + 1;
});
}
public void LogStatistics()
{
foreach (var kvp in _messageStats)
{
Debug.Log($"Message Type: {kvp.Key.Name}, Count: {kvp.Value}");
}
}
}
🛠️ Installation
Unity Package Manager
Add the following to your manifest.json:
{
"dependencies": {
"com.codejunkie.messaging": "https://github.com/yourrepo/CodeJunkie.Messaging.git"
}
}
Manual Installation
- Download the source files
- Copy to your Unity project's
Assets/Scripts/CodeJunkie/folder - Add the namespace:
using CodeJunkie.Observables;andusing CodeJunkie.Repositories;
📋 Platform Support
- Unity 2019.4+ (All platforms including WebGL)
- .NET Framework 4.6+
- .NET Core 2.0+
- .NET 5/6/7/8
🔄 Thread Safety
All messaging operations are thread-safe using concurrent collections:
// Safe to call from any thread
Parallel.For(0, 1000, i => {
messenger.Publish(new TestMessage(i));
});
// Safe concurrent subscriptions
Parallel.For(0, 100, i => {
messenger.Subscribe<TestMessage>(msg => ProcessMessage(msg));
});
⚡ Performance Characteristics
- O(1) message publishing for direct type matches
- O(n) for inheritance-based message matching (where n = number of subscribed types)
- Memory Efficient - Weak references automatically clean up dead subscriptions
- Lock-Free Reads - Message publishing uses lock-free concurrent data structures
- Minimal Allocations - Reuses internal structures where possible
🏗️ Architecture Patterns
Repository Pattern Implementation
public interface IPlayerRepository : IRepository
{
int Health { get; set; }
string Name { get; set; }
void Heal(int amount);
void TakeDamage(int damage);
}
public class PlayerRepository : RepositoryBase, IPlayerRepository
{
// Implementation with automatic messaging
}
Event Sourcing Support
public class EventSourcedRepository : RepositoryBase
{
private readonly List<GameEventMessage> _events = new List<GameEventMessage>();
protected void ApplyEvent(GameEventMessage evt)
{
_events.Add(evt);
Messenger?.Publish(evt);
}
public IReadOnlyList<GameEventMessage> GetEvents() => _events.AsReadOnly();
}
🤝 Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- Inspired by ReactiveX patterns and modern messaging frameworks
- Optimized for Unity game development workflows
- Performance improvements based on community feedback
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- CodeJunkie.Log (>= 1.0.0)
- CodeJunkie.Observables (>= 1.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 174 | 6/7/2025 |