This repository has been archived by the owner on Jan 18, 2022. It is now read-only.
/
RequiredSubscriptionsInjector.cs
122 lines (101 loc) · 3.85 KB
/
RequiredSubscriptionsInjector.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
using System;
using System.Collections.Generic;
using Improbable.Gdk.Core;
namespace Improbable.Gdk.Subscriptions
{
// todo this should probably be an interface to be able to remove reflection via baking
// or to reduce allocation on monobehaviours
public class RequiredSubscriptionsInjector
{
private readonly SubscriptionAggregate subscriptions;
private readonly RequiredSubscriptionsInfo info;
private readonly object target;
private Action onEnable;
private Action onDisable;
// todo should either special case monobehaviours or not use callbacks
// for non monobehaviours we could use functors
public RequiredSubscriptionsInjector(object target, EntityId entityId, SubscriptionSystem subscriptionSystem,
Action onEnable = null, Action onDisable = null)
{
info = RequiredSubscriptionsDatabase.GetOrCreateRequiredSubscriptionsInfo(target.GetType());
if (info.RequiredTypes.Length == 0)
{
return;
}
this.target = target;
this.onEnable = onEnable;
this.onDisable = onDisable;
subscriptions = new SubscriptionAggregate(subscriptionSystem, entityId, info.RequiredTypes);
subscriptions.SetAvailabilityHandler(Handler.Pool.Rent(this));
}
// todo the disposal pattern for this is currently awful and needs to be improved
public void CancelSubscriptions()
{
Handler.Pool.Return((Handler) subscriptions.GetAvailabilityHandler());
subscriptions.Cancel();
onDisable?.Invoke();
if (target == null)
{
return;
}
foreach (var field in info.RequiredFields)
{
if (!field.FieldType.IsValueType)
{
field.SetValue(target, null);
}
}
}
private void HandleSubscriptionsSatisfied()
{
foreach (var field in info.RequiredFields)
{
// todo should really do this as they become available rather than all at once
field.SetValue(target, subscriptions.GetErasedValue(field.FieldType));
}
onEnable?.Invoke();
}
private void HandleSubscriptionsNoLongerSatisfied()
{
onDisable?.Invoke();
}
private class Handler : ISubscriptionAvailabilityHandler
{
private RequiredSubscriptionsInjector injector;
public void OnAvailable()
{
if (injector == null)
{
throw new InvalidOperationException("Not a valid subscription");
}
injector.HandleSubscriptionsSatisfied();
}
public void OnUnavailable()
{
if (injector == null)
{
throw new InvalidOperationException("Not a valid subscription");
}
injector.HandleSubscriptionsNoLongerSatisfied();
}
public class Pool
{
private static readonly Stack<Handler> HandlerPool = new Stack<Handler>();
public static Handler Rent(RequiredSubscriptionsInjector injector)
{
Handler handler = HandlerPool.Count == 0
? new Handler()
: HandlerPool.Pop();
handler.injector = injector;
return handler;
}
public static void Return(Handler handler)
{
// todo this should be bounded
handler.injector = null;
HandlerPool.Push(handler);
}
}
}
}
}