-
Notifications
You must be signed in to change notification settings - Fork 746
Expand file tree
/
Copy pathUserInterfaceComponent.cs
More file actions
270 lines (232 loc) · 9.07 KB
/
Copy pathUserInterfaceComponent.cs
File metadata and controls
270 lines (232 loc) · 9.07 KB
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
using System;
using System.Collections.Generic;
using Robust.Shared.GameStates;
using Robust.Shared.Player;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
using Robust.Shared.ViewVariables;
namespace Robust.Shared.GameObjects
{
[RegisterComponent, NetworkedComponent, Access(typeof(SharedUserInterfaceSystem))]
public sealed partial class UserInterfaceComponent : Component, IComponentDelta
{
/// <inheritdoc/>
public GameTick LastUnclassifiedDirty { get; set; }
/// <inheritdoc />
public GameTick[] LastModifiedFields { get; set; } = default!;
/// <summary>
/// The currently open interfaces. Used clientside to store the UI.
/// </summary>
[ViewVariables, Access(Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.ReadWriteExecute)]
public readonly Dictionary<Enum, BoundUserInterface> ClientOpenInterfaces = new();
[DataField, AlwaysPushInheritance]
internal Dictionary<Enum, InterfaceData> Interfaces = new();
/// <summary>
/// Actors that currently have interfaces open.
/// </summary>
[DataField]
public Dictionary<Enum, HashSet<EntityUid>> Actors = new();
/// <summary>
/// Legacy data, new BUIs should be using comp states.
/// </summary>
public Dictionary<Enum, BoundUserInterfaceState> States = new();
}
[Serializable, NetSerializable]
internal sealed class UserInterfaceComponentState(
Dictionary<Enum, List<NetEntity>> actors,
Dictionary<Enum, BoundUserInterfaceState> states,
Dictionary<Enum, InterfaceData> data)
: IComponentState
{
public Dictionary<Enum, List<NetEntity>> Actors = actors;
public Dictionary<Enum, BoundUserInterfaceState> States = states;
public Dictionary<Enum, InterfaceData> Data = data;
}
[Serializable, NetSerializable]
internal sealed class UserInterfaceActorsDeltaState : IComponentDeltaState<UserInterfaceComponentState>
{
public Dictionary<Enum, List<NetEntity>> Actors = new();
public void ApplyToFullState(UserInterfaceComponentState fullState)
{
fullState.Actors.Clear();
foreach (var (key, value) in Actors)
{
fullState.Actors.Add(key, value);
}
}
public UserInterfaceComponentState CreateNewFullState(UserInterfaceComponentState fullState)
{
var newState = new UserInterfaceComponentState(
new Dictionary<Enum, List<NetEntity>>(Actors),
new(fullState.States),
new(fullState.Data));
return newState;
}
}
[Serializable, NetSerializable]
internal sealed class UserInterfaceStatesDeltaState : IComponentDeltaState<UserInterfaceComponentState>
{
public Dictionary<Enum, BoundUserInterfaceState> States = new();
public void ApplyToFullState(UserInterfaceComponentState fullState)
{
fullState.States.Clear();
foreach (var (key, value) in States)
{
fullState.States.Add(key, value);
}
}
public UserInterfaceComponentState CreateNewFullState(UserInterfaceComponentState fullState)
{
var newState = new UserInterfaceComponentState(
new Dictionary<Enum, List<NetEntity>>(fullState.Actors),
new(States),
new(fullState.Data));
return newState;
}
}
[DataDefinition, Serializable, NetSerializable]
public sealed partial class InterfaceData
{
[DataField("type", required: true)]
public string ClientType { get; private set; } = default!;
/// <summary>
/// Maximum range before a BUI auto-closes. A non-positive number means there is no limit.
/// </summary>
[DataField]
public float InteractionRange = 2f;
// TODO BUI move to content?
// I've tried to keep the name general, but really this is a bool for: can ghosts/stunned/dead people press buttons on this UI?
/// <summary>
/// Determines whether the server should verify that a client is capable of performing generic UI interactions when receiving UI messages.
/// </summary>
/// <remarks>
/// Avoids requiring each system to individually validate client inputs. However, perhaps some BUIs are supposed to be bypass accessibility checks
/// </remarks>
[DataField]
public bool RequireInputValidation = true;
public InterfaceData(string clientType, float interactionRange = 2f, bool requireInputValidation = true)
{
ClientType = clientType;
InteractionRange = interactionRange;
RequireInputValidation = requireInputValidation;
}
public InterfaceData(InterfaceData data)
{
ClientType = data.ClientType;
InteractionRange = data.InteractionRange;
RequireInputValidation = data.RequireInputValidation;
}
}
/// <summary>
/// Raised whenever the server receives a BUI message from a client relating to a UI that requires input validation.
/// This is first broadcast then, if not cancelled already, raised directly on the BUI's owner.
/// </summary>
[ByRefEvent]
public record struct BoundUserInterfaceMessageAttempt(EntityUid Actor, EntityUid Target, Enum UiKey, BoundUserInterfaceMessage Message)
{
public bool Cancelled { get; private set; }
public void Cancel()
{
Cancelled = true;
}
public void Uncancel()
{
Cancelled = false;
}
}
/// <summary>
/// Raised whenever the server receives a BUI wrap message from a client for a valid interface.
/// </summary>
[ByRefEvent]
public readonly record struct BoundUserInterfaceMessageReceivedEvent(
EntityUid Actor,
EntityUid Target,
Enum UiKey);
[NetSerializable, Serializable]
public abstract class BoundUserInterfaceState
{
}
/// <summary>
/// Abstract class for local BUI events.
/// </summary>
public abstract class BaseLocalBoundUserInterfaceEvent : BaseBoundUserInterfaceEvent
{
/// <summary>
/// The Entity receiving the message.
/// Only set when the message is raised as a directed event.
/// </summary>
public EntityUid Entity = EntityUid.Invalid;
}
/// <summary>
/// Abstract class for all BUI events.
/// </summary>
[Serializable, NetSerializable]
public abstract class BaseBoundUserInterfaceEvent : EntityEventArgs
{
/// <summary>
/// The UI of this message.
/// Only set when the message is raised as a directed event.
/// </summary>
public Enum UiKey = default!;
/// <summary>
/// The session sending or receiving this message.
/// Only set when the message is raised as a directed event.
/// </summary>
[NonSerialized]
public EntityUid Actor = default!;
}
/// <summary>
/// Abstract class for networked BUI events.
/// </summary>
[NetSerializable, Serializable]
public abstract class BoundUserInterfaceMessage : BaseBoundUserInterfaceEvent
{
/// <summary>
/// The Entity receiving the message.
/// Only set when the message is raised as a directed event.
/// </summary>
public NetEntity Entity { get; set; } = NetEntity.Invalid;
}
[NetSerializable, Serializable]
public sealed class OpenBoundInterfaceMessage : BoundUserInterfaceMessage
{
}
[NetSerializable, Serializable]
public sealed class CloseBoundInterfaceMessage : BoundUserInterfaceMessage
{
}
[Serializable, NetSerializable]
internal abstract class BaseBoundUIWrapMessage(NetEntity entity, BoundUserInterfaceMessage message, Enum uiKey)
: EntityEventArgs
{
public readonly NetEntity Entity = entity;
public readonly BoundUserInterfaceMessage Message = message;
public readonly Enum UiKey = uiKey;
}
/// <summary>
/// Helper message raised from client to server.
/// </summary>
[Serializable, NetSerializable]
internal sealed class BoundUIWrapMessage(NetEntity entity, BoundUserInterfaceMessage message, Enum uiKey)
: BaseBoundUIWrapMessage(entity, message, uiKey);
public sealed class BoundUIOpenedEvent : BaseLocalBoundUserInterfaceEvent
{
public BoundUIOpenedEvent(Enum uiKey, EntityUid uid, EntityUid actor)
{
UiKey = uiKey;
Entity = uid;
Actor = actor;
}
}
public sealed class BoundUIClosedEvent : BaseLocalBoundUserInterfaceEvent
{
public BoundUIClosedEvent(Enum uiKey, EntityUid uid, EntityUid actor)
{
UiKey = uiKey;
Entity = uid;
Actor = actor;
}
}
}