A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Reflection;
4using System.Text;
5using UnityEditor.Rendering;
6using UnityEngine;
7using UnityEngine.Rendering;
8using UnityEditor.ShaderGraph.Internal;
9using UnityEditor.Graphing;
10using UnityEngine.UIElements;
11using UnityEditor.ShaderGraph.Drawing;
12
13namespace UnityEditor.ShaderGraph.Serialization
14{
15 static class MultiJsonInternal
16 {
17 #region Unknown Data Handling
18 public class UnknownJsonObject : JsonObject
19 {
20 public string typeInfo;
21 public string jsonData;
22 public JsonData<JsonObject> castedObject;
23
24 public UnknownJsonObject(string typeInfo)
25 {
26 this.typeInfo = typeInfo;
27 }
28
29 public override void Deserailize(string typeInfo, string jsonData)
30 {
31 this.jsonData = jsonData;
32 }
33
34 public override string Serialize()
35 {
36 return jsonData;
37 }
38
39 public override void OnAfterDeserialize(string json)
40 {
41 if (castedObject.value != null)
42 {
43 Enqueue(castedObject, json.Trim());
44 }
45 }
46
47 public override void OnAfterMultiDeserialize(string json)
48 {
49 if (castedObject.value == null)
50 {
51 //Never got casted so nothing ever reffed this object
52 //likely that some other unknown json object had a ref
53 //to this thing. Need to include it in the serialization
54 //step of the object still.
55 if (jsonBlobs.TryGetValue(currentRoot.objectId, out var blobs))
56 {
57 blobs[objectId] = jsonData.Trim();
58 }
59 else
60 {
61 var lookup = new Dictionary<string, string>();
62 lookup[objectId] = jsonData.Trim();
63 jsonBlobs.Add(currentRoot.objectId, lookup);
64 }
65 }
66 }
67
68 public override T CastTo<T>()
69 {
70 if (castedObject.value != null)
71 return castedObject.value.CastTo<T>();
72
73 Type t = typeof(T);
74 if (t == typeof(AbstractMaterialNode) || t.IsSubclassOf(typeof(AbstractMaterialNode)))
75 {
76 UnknownNodeType unt = new UnknownNodeType(jsonData);
77 valueMap[objectId] = unt;
78 s_ObjectIdField.SetValue(unt, objectId);
79 castedObject = unt;
80 return unt.CastTo<T>();
81 }
82 else if (t == typeof(Target) || t.IsSubclassOf(typeof(Target)))
83 {
84 UnknownTargetType utt = new UnknownTargetType(typeInfo, jsonData);
85 valueMap[objectId] = utt;
86 s_ObjectIdField.SetValue(utt, objectId);
87 castedObject = utt;
88 return utt.CastTo<T>();
89 }
90 else if (t == typeof(SubTarget) || t.IsSubclassOf(typeof(SubTarget)))
91 {
92 UnknownSubTargetType ustt = new UnknownSubTargetType(typeInfo, jsonData);
93 valueMap[objectId] = ustt;
94 s_ObjectIdField.SetValue(ustt, objectId);
95 castedObject = ustt;
96 return ustt.CastTo<T>();
97 }
98 else if (t == typeof(ShaderInput) || t.IsSubclassOf(typeof(ShaderInput)))
99 {
100 UnknownShaderPropertyType usp = new UnknownShaderPropertyType(typeInfo, jsonData);
101 valueMap[objectId] = usp;
102 s_ObjectIdField.SetValue(usp, objectId);
103 castedObject = usp;
104 return usp.CastTo<T>();
105 }
106 else if (t == typeof(MaterialSlot) || t.IsSubclassOf(typeof(MaterialSlot)))
107 {
108 UnknownMaterialSlotType umst = new UnknownMaterialSlotType(typeInfo, jsonData);
109 valueMap[objectId] = umst;
110 s_ObjectIdField.SetValue(umst, objectId);
111 castedObject = umst;
112 return umst.CastTo<T>();
113 }
114 else if (t == typeof(AbstractShaderGraphDataExtension) || t.IsSubclassOf(typeof(AbstractShaderGraphDataExtension)))
115 {
116 UnknownGraphDataExtension usge = new UnknownGraphDataExtension(typeInfo, jsonData);
117 valueMap[objectId] = usge;
118 s_ObjectIdField.SetValue(usge, objectId);
119 castedObject = usge;
120 return usge.CastTo<T>();
121 }
122 else
123 {
124 Debug.LogError($"Unable to evaluate type {typeInfo} : {jsonData}");
125 }
126 return null;
127 }
128 }
129
130 public class UnknownTargetType : Target
131 {
132 public string jsonData;
133 public UnknownTargetType() : base()
134 {
135 isHidden = true;
136 }
137
138 private List<BlockFieldDescriptor> m_activeBlocks = null;
139
140 public UnknownTargetType(string displayName, string jsonData)
141 {
142 var split = displayName.Split('.');
143 var last = split[split.Length - 1];
144 this.displayName = last.Replace("Target", "") + " (Unknown)";
145 isHidden = false;
146 this.jsonData = jsonData;
147 }
148
149 public override void Deserailize(string typeInfo, string jsonData)
150 {
151 this.jsonData = jsonData;
152 base.Deserailize(typeInfo, jsonData);
153 }
154
155 public override string Serialize()
156 {
157 return jsonData.Trim();
158 }
159
160 //When we first call GetActiveBlocks, we assume any unknown blockfielddescriptors are owned by this target
161 public override void GetActiveBlocks(ref TargetActiveBlockContext context)
162 {
163 if (m_activeBlocks == null)
164 {
165 m_activeBlocks = new List<BlockFieldDescriptor>();
166 foreach (var cur in context.currentBlocks)
167 {
168 if (cur.isUnknown && !string.IsNullOrEmpty(cur.displayName))
169 {
170 m_activeBlocks.Add(cur);
171 }
172 }
173 }
174
175 foreach (var block in m_activeBlocks)
176 {
177 context.AddBlock(block);
178 }
179 }
180
181 public override void GetFields(ref TargetFieldContext context)
182 {
183 }
184
185 public override void GetPropertiesGUI(ref TargetPropertyGUIContext context, Action onChange, Action<string> registerUndo)
186 {
187 context.AddHelpBox(MessageType.Warning, "Cannot find the code for this Target, a package may be missing.");
188 }
189
190 public override bool IsActive() => false;
191
192 public override void Setup(ref TargetSetupContext context)
193 {
194 }
195
196 public override bool WorksWithSRP(RenderPipelineAsset scriptableRenderPipeline) => false;
197 }
198
199 private class UnknownSubTargetType : SubTarget
200 {
201 public string jsonData;
202 public UnknownSubTargetType() : base()
203 {
204 isHidden = true;
205 }
206
207 public UnknownSubTargetType(string displayName, string jsonData) : base()
208 {
209 isHidden = false;
210 this.displayName = displayName;
211 this.jsonData = jsonData;
212 }
213
214 public override void Deserailize(string typeInfo, string jsonData)
215 {
216 this.jsonData = jsonData;
217 base.Deserailize(typeInfo, jsonData);
218 }
219
220 public override string Serialize()
221 {
222 return jsonData.Trim();
223 }
224
225 internal override Type targetType => typeof(UnknownTargetType);
226
227 public override void GetActiveBlocks(ref TargetActiveBlockContext context)
228 {
229 }
230
231 public override void GetFields(ref TargetFieldContext context)
232 {
233 }
234
235 public override void GetPropertiesGUI(ref TargetPropertyGUIContext context, Action onChange, Action<string> registerUndo)
236 {
237 context.AddHelpBox(MessageType.Warning, "Cannot find the code for this SubTarget, a package may be missing.");
238 }
239
240 public override bool IsActive() => false;
241
242 public override void Setup(ref TargetSetupContext context)
243 {
244 }
245 }
246
247 internal class UnknownShaderPropertyType : AbstractShaderProperty
248 {
249 public string jsonData;
250
251 public UnknownShaderPropertyType(string displayName, string jsonData) : base()
252 {
253 this.displayName = displayName;
254 this.jsonData = jsonData;
255 }
256
257 public override void Deserailize(string typeInfo, string jsonData)
258 {
259 this.jsonData = jsonData;
260 base.Deserailize(typeInfo, jsonData);
261 }
262
263 public override string Serialize()
264 {
265 return jsonData.Trim();
266 }
267
268 internal override ConcreteSlotValueType concreteShaderValueType => ConcreteSlotValueType.Vector1;
269 internal override bool isExposable => false;
270 internal override bool isRenamable => false;
271 internal override ShaderInput Copy()
272 {
273 // we CANNOT copy ourselves, as the serialized GUID in the jsonData would not match the json GUID
274 return null;
275 }
276
277 public override PropertyType propertyType => PropertyType.Float;
278 internal override void GetPropertyReferenceNames(List<string> result) { }
279 internal override void GetPropertyDisplayNames(List<string> result) { }
280 internal override string GetPropertyBlockString() { return ""; }
281 internal override void AppendPropertyBlockStrings(ShaderStringBuilder builder)
282 {
283 builder.AppendLine("/* UNKNOWN PROPERTY: " + referenceName + " */");
284 }
285
286 internal override bool AllowHLSLDeclaration(HLSLDeclaration decl) => false;
287 internal override void ForeachHLSLProperty(Action<HLSLProperty> action)
288 {
289 action(new HLSLProperty(HLSLType._float, referenceName, HLSLDeclaration.Global, concretePrecision));
290 }
291
292 internal override string GetPropertyAsArgumentString(string precisionString) { return ""; }
293 internal override AbstractMaterialNode ToConcreteNode() { return null; }
294
295 internal override PreviewProperty GetPreviewMaterialProperty()
296 {
297 return new PreviewProperty(propertyType)
298 {
299 name = referenceName,
300 floatValue = 0.0f
301 };
302 }
303
304 public override string GetPropertyTypeString() { return ""; }
305 }
306
307 internal class UnknownMaterialSlotType : MaterialSlot
308 {
309 // used to deserialize some data out of an unknown MaterialSlot
310 class SerializerHelper
311 {
312 [SerializeField]
313 public string m_DisplayName = null;
314
315 [SerializeField]
316 public SlotType m_SlotType = SlotType.Input;
317
318 [SerializeField]
319 public bool m_Hidden = false;
320
321 [SerializeField]
322 public string m_ShaderOutputName = null;
323
324 [SerializeField]
325 public ShaderStageCapability m_StageCapability = ShaderStageCapability.All;
326 }
327
328 public string jsonData;
329
330 public UnknownMaterialSlotType(string displayName, string jsonData) : base()
331 {
332 // copy some minimal information to try to keep the UI as similar as possible
333 var helper = new SerializerHelper();
334 JsonUtility.FromJsonOverwrite(jsonData, helper);
335 this.displayName = helper.m_DisplayName;
336 this.hidden = helper.m_Hidden;
337 this.stageCapability = helper.m_StageCapability;
338 this.SetInternalData(helper.m_SlotType, helper.m_ShaderOutputName);
339
340 // save the original json for saving
341 this.jsonData = jsonData;
342 }
343
344 public override void Deserailize(string typeInfo, string jsonData)
345 {
346 this.jsonData = jsonData;
347 base.Deserailize(typeInfo, jsonData);
348 }
349
350 public override string Serialize()
351 {
352 return jsonData.Trim();
353 }
354
355 public override bool isDefaultValue => true;
356
357 public override SlotValueType valueType => SlotValueType.Vector1;
358
359 public override ConcreteSlotValueType concreteValueType => ConcreteSlotValueType.Vector1;
360
361 public override void AddDefaultProperty(PropertyCollector properties, GenerationMode generationMode) { }
362
363 public override void CopyValuesFrom(MaterialSlot foundSlot)
364 {
365 // we CANNOT copy data from another slot, as the GUID in the serialized jsonData would not match our real GUID
366 throw new NotSupportedException();
367 }
368 }
369
370 [NeverAllowedByTarget]
371 internal class UnknownNodeType : AbstractMaterialNode
372 {
373 public string jsonData;
374
375 public UnknownNodeType() : base()
376 {
377 jsonData = null;
378 isValid = false;
379 SetOverrideActiveState(ActiveState.ExplicitInactive, false);
380 SetActive(false, false);
381 }
382
383 public UnknownNodeType(string jsonData)
384 {
385 this.jsonData = jsonData;
386 isValid = false;
387 SetOverrideActiveState(ActiveState.ExplicitInactive, false);
388 SetActive(false, false);
389 }
390
391 public override void OnAfterDeserialize(string json)
392 {
393 jsonData = json;
394 base.OnAfterDeserialize(json);
395 }
396
397 public override string Serialize()
398 {
399 EnqueSlotsForSerialization();
400 return jsonData.Trim();
401 }
402
403 public override void ValidateNode()
404 {
405 base.ValidateNode();
406 owner.AddValidationError(objectId, "This node type could not be found. No function will be generated in the shader.", ShaderCompilerMessageSeverity.Warning);
407 }
408
409 // unknown node types cannot be copied, or else their GUID would not match the GUID in the serialized jsonDAta
410 public override bool canCutNode => false;
411 public override bool canCopyNode => false;
412 }
413
414 internal class UnknownGraphDataExtension : AbstractShaderGraphDataExtension
415 {
416 public string name;
417 public string jsonData;
418 internal override string displayName => name;
419
420 internal UnknownGraphDataExtension() : base() { }
421
422 internal UnknownGraphDataExtension(string displayName, string jsonData)
423 {
424 name = displayName;
425 this.jsonData = jsonData;
426 }
427
428 public override void Deserailize(string typeInfo, string jsonData)
429 {
430 this.jsonData = jsonData;
431 base.Deserailize(typeInfo, jsonData);
432 }
433
434 public override string Serialize()
435 {
436 return jsonData.Trim();
437 }
438
439 internal override void OnPropertiesGUI(VisualElement context, Action onChange, Action<string> registerUndo, GraphData owner)
440 {
441 var helpBox = new HelpBoxRow(MessageType.Info);
442 helpBox.Add(new Label("Cannot find the code for this data extension, a package may be missing."));
443 context.hierarchy.Add(helpBox);
444 }
445 }
446 #endregion //Unknown Data Handling
447
448 static readonly Dictionary<string, Type> k_TypeMap = CreateTypeMap();
449
450 internal static bool isDeserializing;
451
452 internal static readonly Dictionary<string, JsonObject> valueMap = new Dictionary<string, JsonObject>();
453
454 static List<MultiJsonEntry> s_Entries;
455
456 internal static bool isSerializing;
457
458 internal static readonly List<JsonObject> serializationQueue = new List<JsonObject>();
459
460 internal static readonly HashSet<string> serializedSet = new HashSet<string>();
461
462 static JsonObject currentRoot = null;
463
464 static Dictionary<string, Dictionary<string, string>> jsonBlobs = new Dictionary<string, Dictionary<string, string>>();
465
466 static Dictionary<string, Type> CreateTypeMap()
467 {
468 var map = new Dictionary<string, Type>();
469 foreach (var type in TypeCache.GetTypesDerivedFrom<JsonObject>())
470 {
471 if (type.FullName != null)
472 {
473 map[type.FullName] = type;
474 }
475 }
476
477 foreach (var type in TypeCache.GetTypesWithAttribute(typeof(FormerNameAttribute)))
478 {
479 if (type.IsAbstract || !typeof(JsonObject).IsAssignableFrom(type))
480 {
481 continue;
482 }
483
484 foreach (var attribute in type.GetCustomAttributes(typeof(FormerNameAttribute), false))
485 {
486 var legacyAttribute = (FormerNameAttribute)attribute;
487 map[legacyAttribute.fullName] = type;
488 }
489 }
490
491 return map;
492 }
493
494 public static Type ParseType(string typeString)
495 {
496 k_TypeMap.TryGetValue(typeString, out var type);
497 return type;
498 }
499
500 public static List<MultiJsonEntry> Parse(string str)
501 {
502 var result = new List<MultiJsonEntry>();
503 const string separatorStr = "\n\n";
504 var startIndex = 0;
505 var raw = new FakeJsonObject();
506
507 while (startIndex < str.Length)
508 {
509 var jsonBegin = str.IndexOf("{", startIndex, StringComparison.Ordinal);
510 if (jsonBegin == -1)
511 {
512 break;
513 }
514
515 var jsonEnd = str.IndexOf(separatorStr, jsonBegin, StringComparison.Ordinal);
516 if (jsonEnd == -1)
517 {
518 jsonEnd = str.IndexOf("\n\r\n", jsonBegin, StringComparison.Ordinal);
519 if (jsonEnd == -1)
520 {
521 jsonEnd = str.LastIndexOf("}", StringComparison.Ordinal) + 1;
522 }
523 }
524
525 var json = str.Substring(jsonBegin, jsonEnd - jsonBegin);
526
527 JsonUtility.FromJsonOverwrite(json, raw);
528 if (startIndex != 0 && string.IsNullOrWhiteSpace(raw.type))
529 {
530 throw new InvalidOperationException($"Type is null or whitespace in JSON:\n{json}");
531 }
532
533 result.Add(new MultiJsonEntry(raw.type, raw.id, json));
534 raw.Reset();
535
536 startIndex = jsonEnd + separatorStr.Length;
537 }
538
539 return result;
540 }
541
542 public static void Enqueue(JsonObject jsonObject, string json)
543 {
544 if (s_Entries == null)
545 {
546 throw new InvalidOperationException("Can only Enqueue during JsonObject.OnAfterDeserialize.");
547 }
548
549 valueMap.Add(jsonObject.objectId, jsonObject);
550 s_Entries.Add(new MultiJsonEntry(jsonObject.GetType().FullName, jsonObject.objectId, json));
551 }
552
553 public static JsonObject CreateInstanceForDeserialization(string typeString)
554 {
555 if (!k_TypeMap.TryGetValue(typeString, out var type))
556 {
557 return new UnknownJsonObject(typeString);
558 }
559 var output = (JsonObject)Activator.CreateInstance(type, true);
560 //This CreateInstance function is supposed to essentially create a blank copy of whatever class we end up deserializing into.
561 //when we typically create new JsonObjects in all other cases, we want that object to be assumed to be the latest version.
562 //This doesn't work if any json object was serialized before we had the idea of version, as the blank copy would have the
563 //latest version on creation and since the serialized version wouldn't have a version member, it would not get overwritten
564 //and we would automatically upgrade all previously serialized json objects incorrectly and without user action. To avoid this,
565 //we default jsonObject version to 0, and if the serialized value has a different saved version it gets changed and if the serialized
566 //version does not have a different saved value it remains 0 (earliest version)
567 output.ChangeVersion(0);
568 output.OnBeforeDeserialize();
569 return output;
570 }
571
572 private static FieldInfo s_ObjectIdField =
573 typeof(JsonObject).GetField("m_ObjectId", BindingFlags.Instance | BindingFlags.NonPublic);
574
575 public static void Deserialize(JsonObject root, List<MultiJsonEntry> entries, bool rewriteIds)
576 {
577 if (isDeserializing)
578 {
579 throw new InvalidOperationException("Nested MultiJson deserialization is not supported.");
580 }
581
582 try
583 {
584 isDeserializing = true;
585 currentRoot = root;
586 root.ChangeVersion(0); //Same issue as described in CreateInstance
587 for (var index = 0; index < entries.Count; index++)
588 {
589 var entry = entries[index];
590 try
591 {
592 JsonObject value = null;
593 if (index == 0)
594 {
595 value = root;
596 }
597 else
598 {
599 value = CreateInstanceForDeserialization(entry.type);
600 }
601
602 var id = entry.id;
603
604 if (id != null)
605 {
606 // Need to make sure that references looking for the old ID will find it in spite of
607 // ID rewriting.
608 valueMap[id] = value;
609 }
610
611 if (rewriteIds || entry.id == null)
612 {
613 id = value.objectId;
614 entries[index] = new MultiJsonEntry(entry.type, id, entry.json);
615 valueMap[id] = value;
616 }
617
618 s_ObjectIdField.SetValue(value, id);
619 }
620 catch (Exception e)
621 {
622 // External code could throw exceptions, but we don't want that to fail the whole thing.
623 // Potentially, the fallback type should also be used here.
624 Debug.LogException(e);
625 }
626 }
627
628 s_Entries = entries;
629
630 // Not a foreach because `entries` can be populated by calls to `Enqueue` as we go.
631 for (var i = 0; i < entries.Count; i++)
632 {
633 var entry = entries[i];
634 try
635 {
636 var value = valueMap[entry.id];
637 value.Deserailize(entry.type, entry.json);
638 // Set ID again as it could be overwritten from JSON.
639 s_ObjectIdField.SetValue(value, entry.id);
640 value.OnAfterDeserialize(entry.json);
641 }
642 catch (Exception e)
643 {
644 if (!String.IsNullOrEmpty(entry.id))
645 {
646 var value = valueMap[entry.id];
647 if (value != null)
648 {
649 Debug.LogError($"Exception thrown while deserialize object of type {entry.type}: {e.Message}");
650 }
651 }
652 Debug.LogException(e);
653 }
654 }
655
656 s_Entries = null;
657
658 foreach (var entry in entries)
659 {
660 try
661 {
662 var value = valueMap[entry.id];
663 value.OnAfterMultiDeserialize(entry.json);
664 }
665 catch (Exception e)
666 {
667 Debug.LogException(e);
668 }
669 }
670 }
671 finally
672 {
673 valueMap.Clear();
674 currentRoot = null;
675 isDeserializing = false;
676 }
677 }
678
679 public static string Serialize(JsonObject mainObject)
680 {
681 if (isSerializing)
682 {
683 throw new InvalidOperationException("Nested MultiJson serialization is not supported.");
684 }
685
686 try
687 {
688 isSerializing = true;
689
690 serializedSet.Add(mainObject.objectId);
691 serializationQueue.Add(mainObject);
692
693 var idJsonList = new List<(string, string)>();
694
695 // Not a foreach because the queue is populated by `JsonData<T>`s as we go.
696 for (var i = 0; i < serializationQueue.Count; i++)
697 {
698 var value = serializationQueue[i];
699 var json = value.Serialize();
700 idJsonList.Add((value.objectId, json));
701 }
702
703 if (jsonBlobs.TryGetValue(mainObject.objectId, out var blobs))
704 {
705 foreach (var blob in blobs)
706 {
707 if (!idJsonList.Contains((blob.Key, blob.Value)))
708 idJsonList.Add((blob.Key, blob.Value));
709 }
710 }
711
712
713 idJsonList.Sort((x, y) =>
714 // Main object needs to be placed first
715 x.Item1 == mainObject.objectId ? -1 :
716 y.Item1 == mainObject.objectId ? 1 :
717 // We sort everything else by ID to consistently maintain positions in the output
718 x.Item1.CompareTo(y.Item1));
719
720
721 const string k_NewLineString = "\n";
722 var sb = new StringBuilder();
723 foreach (var (id, json) in idJsonList)
724 {
725 sb.Append(json);
726 sb.Append(k_NewLineString);
727 sb.Append(k_NewLineString);
728 }
729
730 return sb.ToString();
731 }
732 finally
733 {
734 serializationQueue.Clear();
735 serializedSet.Clear();
736 isSerializing = false;
737 }
738 }
739
740 public static void PopulateValueMap(JsonObject mainObject)
741 {
742 if (isSerializing)
743 {
744 throw new InvalidOperationException("Nested MultiJson serialization is not supported.");
745 }
746
747 try
748 {
749 isSerializing = true;
750
751 serializedSet.Add(mainObject.objectId);
752 serializationQueue.Add(mainObject);
753
754 // Not a foreach because the queue is populated by `JsonRef<T>`s as we go.
755 for (var i = 0; i < serializationQueue.Count; i++)
756 {
757 var value = serializationQueue[i];
758 value.Serialize();
759 valueMap[value.objectId] = value;
760 }
761 }
762 finally
763 {
764 serializationQueue.Clear();
765 serializedSet.Clear();
766 isSerializing = false;
767 }
768 }
769 }
770}