|
1 | 1 | using System.Text;
|
| 2 | +using Gazillion; |
2 | 3 | using Google.ProtocolBuffers;
|
3 |
| -using MHServerEmu.Core.Extensions; |
| 4 | +using MHServerEmu.Core.Logging; |
4 | 5 | using MHServerEmu.Core.Serialization;
|
5 | 6 | using MHServerEmu.Games.Common;
|
6 | 7 | using MHServerEmu.Games.GameData;
|
7 | 8 | using MHServerEmu.Games.GameData.Prototypes;
|
| 9 | +using MHServerEmu.Games.Regions; |
8 | 10 | using MHServerEmu.Games.UI.Widgets;
|
9 | 11 |
|
10 | 12 | namespace MHServerEmu.Games.UI
|
11 | 13 | {
|
12 | 14 | public class UIDataProvider
|
13 | 15 | {
|
14 |
| - public UISyncData[] UISyncData { get; set; } |
| 16 | + private static readonly Logger Logger = LogManager.CreateLogger(); |
15 | 17 |
|
16 |
| - public UIDataProvider(UISyncData[] uiSyncData) |
| 18 | + private readonly Dictionary<(PrototypeId, PrototypeId), UISyncData> _dataDict = new(); |
| 19 | + |
| 20 | + public Game Game { get; } |
| 21 | + public Region Region { get; } |
| 22 | + |
| 23 | + public UIDataProvider() { } |
| 24 | + |
| 25 | + public UIDataProvider(CodedInputStream stream, BoolDecoder boolDecoder) |
17 | 26 | {
|
18 |
| - UISyncData = uiSyncData; |
| 27 | + ulong numWidgets = stream.ReadRawVarint64(); |
| 28 | + |
| 29 | + for (ulong i = 0; i < numWidgets; i++) |
| 30 | + { |
| 31 | + PrototypeId widgetRef = stream.ReadPrototypeRef<Prototype>(); |
| 32 | + PrototypeId contextRef = stream.ReadPrototypeRef<Prototype>(); |
| 33 | + UpdateOrCreateUIWidget(widgetRef, contextRef, stream, boolDecoder); |
| 34 | + } |
19 | 35 | }
|
20 | 36 |
|
21 |
| - public UIDataProvider(CodedInputStream stream, BoolDecoder boolDecoder) |
| 37 | + public void Encode(CodedOutputStream stream, BoolEncoder boolEncoder) |
22 | 38 | {
|
23 |
| - UISyncData = new UISyncData[stream.ReadRawVarint32()]; |
24 |
| - for (int i = 0; i < UISyncData.Length; i++) |
| 39 | + stream.WriteRawVarint64((ulong)_dataDict.Count); |
| 40 | + foreach (var kvp in _dataDict) |
25 | 41 | {
|
26 |
| - PrototypeId widgetR = stream.ReadPrototypeRef<Prototype>(); |
27 |
| - PrototypeId contextR = stream.ReadPrototypeRef<Prototype>(); |
| 42 | + stream.WritePrototypeRef<Prototype>(kvp.Key.Item1); |
| 43 | + stream.WritePrototypeRef<Prototype>(kvp.Key.Item2); |
| 44 | + kvp.Value.Encode(stream, boolEncoder); |
| 45 | + } |
| 46 | + } |
28 | 47 |
|
29 |
| - PrototypeId[] areas = new PrototypeId[stream.ReadRawInt32()]; |
30 |
| - for (int j = 0; j < areas.Length; j++) |
31 |
| - areas[j] = stream.ReadPrototypeRef<Prototype>(); |
| 48 | + public void EncodeBools(BoolEncoder boolEncoder) |
| 49 | + { |
| 50 | + foreach (UISyncData uiData in _dataDict.Values) |
| 51 | + uiData.EncodeBools(boolEncoder); |
| 52 | + } |
32 | 53 |
|
33 |
| - string className = GameDatabase.DataDirectory.GetPrototypeBlueprint(widgetR).RuntimeBindingClassType.Name; |
| 54 | + public override string ToString() |
| 55 | + { |
| 56 | + StringBuilder sb = new(); |
34 | 57 |
|
35 |
| - switch (className) |
36 |
| - { |
37 |
| - case "UIWidgetButtonPrototype": |
38 |
| - UISyncData[i] = new UIWidgetButton(widgetR, contextR, areas, stream); |
39 |
| - break; |
| 58 | + foreach (var kvp in _dataDict) |
| 59 | + { |
| 60 | + string widgetName = GameDatabase.GetFormattedPrototypeName(kvp.Key.Item1); |
| 61 | + string contextName = GameDatabase.GetFormattedPrototypeName(kvp.Key.Item2); |
| 62 | + sb.AppendLine($"_dataDict[{widgetName}][{contextName}]: {kvp.Value}"); |
| 63 | + } |
40 | 64 |
|
41 |
| - case "UIWidgetEntityIconsSyncDataPrototype": |
42 |
| - UISyncData[i] = new UIWidgetEntityIconsSyncData(widgetR, contextR, areas, stream, boolDecoder); |
43 |
| - break; |
| 65 | + return sb.ToString(); |
| 66 | + } |
44 | 67 |
|
45 |
| - case "UIWidgetGenericFractionPrototype": |
46 |
| - UISyncData[i] = new UIWidgetGenericFraction(widgetR, contextR, areas, stream, boolDecoder); |
47 |
| - break; |
| 68 | + public T GetWidget<T>(PrototypeId widgetRef, PrototypeId contextRef) where T: UISyncData |
| 69 | + { |
| 70 | + if (_dataDict.TryGetValue((widgetRef, contextRef), out UISyncData widget) == false) |
| 71 | + widget = AllocateUIWidget(widgetRef, contextRef); |
48 | 72 |
|
49 |
| - case "UIWidgetMissionTextPrototype": |
50 |
| - UISyncData[i] = new UIWidgetMissionText(widgetR, contextR, areas, stream); |
51 |
| - break; |
| 73 | + return widget as T; |
| 74 | + } |
52 | 75 |
|
53 |
| - case "UIWidgetReadyCheckPrototype": |
54 |
| - UISyncData[i] = new UIWidgetReadyCheck(widgetR, contextR, areas, stream); |
55 |
| - break; |
| 76 | + public void DeleteWidget(PrototypeId widgetRef, PrototypeId contextRef) |
| 77 | + { |
| 78 | + if (_dataDict.Remove((widgetRef, contextRef)) == false) |
| 79 | + Logger.Warn($"DeleteWidget(): Widget not found, widgetRef={widgetRef}, contextRef={contextRef}"); |
56 | 80 |
|
57 |
| - default: |
58 |
| - throw new($"Unsupported UISyncData type {className}."); |
59 |
| - } |
60 |
| - } |
| 81 | + // todo: send a NetMessageUISyncDataRemove to clients when a widget is removed server-side |
61 | 82 | }
|
62 | 83 |
|
63 |
| - public void Encode(CodedOutputStream stream, BoolEncoder boolEncoder) |
| 84 | + public void DeleteWidget(NetMessageUISyncDataRemove removeMessage) |
64 | 85 | {
|
65 |
| - stream.WriteRawVarint32((uint)UISyncData.Length); |
66 |
| - foreach (UISyncData data in UISyncData) |
67 |
| - data.Encode(stream, boolEncoder); |
| 86 | + throw new NotImplementedException(); |
68 | 87 | }
|
69 | 88 |
|
70 |
| - public void EncodeBools(BoolEncoder boolEncoder) |
| 89 | + /// <summary> |
| 90 | + /// Creates a <see cref="UISyncData"/> inst |
| 91 | + /// </summary> |
| 92 | + private UISyncData AllocateUIWidget(PrototypeId widgetRef, PrototypeId contextRef) |
71 | 93 | {
|
72 |
| - foreach (UISyncData data in UISyncData) |
73 |
| - data.EncodeBools(boolEncoder); |
| 94 | + if (widgetRef == PrototypeId.Invalid) |
| 95 | + return Logger.WarnReturn<UISyncData>(null, "AllocateUIWidget(): widgetRef == PrototypeId.Invalid"); |
| 96 | + |
| 97 | + if (_dataDict.ContainsKey((widgetRef, contextRef))) |
| 98 | + return Logger.WarnReturn<UISyncData>(null, $"AllocateUIWidget(): Widget already exists, widgetRef={widgetRef}, contextRef={contextRef}"); |
| 99 | + |
| 100 | + MetaGameDataPrototype metaGameDataPrototype = GameDatabase.GetPrototype<MetaGameDataPrototype>(widgetRef); |
| 101 | + |
| 102 | + if (metaGameDataPrototype == null) |
| 103 | + return Logger.WarnReturn<UISyncData>(null, "AllocateUIWidget(): metaGameDataPrototype == null"); |
| 104 | + |
| 105 | + UISyncData uiSyncData = metaGameDataPrototype switch |
| 106 | + { |
| 107 | + UIWidgetButtonPrototype => new UIWidgetButton(this, widgetRef, contextRef), |
| 108 | + UIWidgetEntityIconsPrototype => new UIWidgetEntityIconsSyncData(this, widgetRef, contextRef), |
| 109 | + UIWidgetGenericFractionPrototype => new UIWidgetGenericFraction(this, widgetRef, contextRef), |
| 110 | + UIWidgetMissionTextPrototype => new UIWidgetMissionText(this, widgetRef, contextRef), |
| 111 | + UIWidgetReadyCheckPrototype => new UIWidgetReadyCheck(this, widgetRef, contextRef), |
| 112 | + _ => null, |
| 113 | + }; |
| 114 | + |
| 115 | + if (uiSyncData == null) |
| 116 | + return Logger.WarnReturn<UISyncData>(null, "AllocateUIWidget(): Trying to allocate widget of the base type"); |
| 117 | + |
| 118 | + _dataDict.Add((widgetRef, contextRef), uiSyncData); |
| 119 | + return uiSyncData; |
74 | 120 | }
|
75 | 121 |
|
76 |
| - public override string ToString() |
| 122 | + // TODO: stream + decoder -> archive |
| 123 | + private UISyncData UpdateOrCreateUIWidget(PrototypeId widgetRef, PrototypeId contextRef, CodedInputStream stream, BoolDecoder boolDecoder) |
77 | 124 | {
|
78 |
| - StringBuilder sb = new(); |
79 |
| - for (int i = 0; i < UISyncData.Length; i++) sb.AppendLine($"UISyncData{i}: {UISyncData[i]}"); |
80 |
| - return sb.ToString(); |
| 125 | + if (_dataDict.TryGetValue((widgetRef, contextRef), out UISyncData uiData) == false) |
| 126 | + uiData = AllocateUIWidget(widgetRef, contextRef); |
| 127 | + |
| 128 | + uiData.Decode(stream, boolDecoder); |
| 129 | + uiData.UpdateUI(); |
| 130 | + |
| 131 | + return uiData; |
81 | 132 | }
|
82 | 133 | }
|
83 | 134 | }
|
0 commit comments