Select Git revision
ScrollDetails.cs
MaZiFAU authored
BugFixes: + repaired Play/Create button in InGameEditor + null exception when deleting assigned fact / parsing null-hint + <no label> for defined and filtered facts from application + collission Player <> snapzone RenderedScrollFact.cs: + changed primary key from ID to ScrollFactURI ScrollDetails.cs: + fully enabled ScrollOverwrites + filtering null-"df" assignments from Scrolls + reduced number of dynamic scroll applications
ScrollDetails.cs 14.76 KiB
using Newtonsoft.Json;
using REST_JSON_API;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;
using static CommunicationEvents;
public class ScrollDetails : MonoBehaviour
{
public static ScrollDetails Instance
{
get => _Instance;
set
{
if (_Instance != value)
Destroy(_Instance);
_Instance = value;
}
}
private static ScrollDetails _Instance;
public WorldCursor cursor;
public GameObject parameterDisplayPrefab;
public static Scroll ActiveScroll { get; private set; }
public GameObject mmtAnswerPopUp;
private PopupBehavior Popup;
public static List<RenderedScrollFact> ParameterDisplays { get; private set; }
private static List<ScrollAssignment> LatestBackwartsCompletions;
private static List<Fact> LatestRenderedHints;
public string currentMmtAnswer;
public bool DynamicScrollDescriptionsActive = true;
public bool AutomaticHintGenerationActive = true;
private bool SendingViewDone = true;
private bool DynamicScrollInQue = false;
private bool MagicInQue = false;
private readonly IReadOnlyList<string> NoDynamicScroll = new List<string>()
{
// Insert ScrollURIS that are poorly optimized
};
void Awake()
{
Instance = this;
if (cursor == null)
cursor = FindObjectOfType<WorldCursor>();
Popup = mmtAnswerPopUp.GetComponent<PopupBehavior>();
Popup.gameObject.SetActive(true); // force Awake
}
private void OnEnable()
{
ScrollFactHintEvent.AddListener(animateHint);
NewAssignmentEvent.AddListener(NewAssignmentTrigger);
}
private void OnDisable()
{
ScrollFactHintEvent.RemoveListener(animateHint);
NewAssignmentEvent.RemoveListener(NewAssignmentTrigger);
}
private void OnDestroy()
{
_Instance = null;
}
public void SetScroll(Scroll scroll_to_set)
{
ActiveScroll = scroll_to_set;
Transform originalScroll = gameObject.transform.GetChild(1);
Transform originalScrollView = originalScroll.GetChild(1);
Transform originalViewport = originalScrollView.GetChild(0);
//Clear all current ScrollFacts
originalViewport.GetChild(0).gameObject.DestroyAllChildren();
originalScroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = ActiveScroll.description;
//ParameterDisplays.ForEach(gameObj => Destroy(gameObj));
ParameterDisplays = new();
for (int i = 0; i < ActiveScroll.requiredFacts.Count; i++)
{
GameObject originalObj =
Instantiate(parameterDisplayPrefab, originalViewport.GetChild(0));
RenderedScrollFact originalRSF =
originalObj.GetComponentInChildren<RenderedScrollFact>();
ParameterDisplays.Add(originalRSF);
originalRSF.Populate(ActiveScroll, ActiveScroll.requiredFacts[i].@ref.uri);
}
if (StageStatic.stage.solution.ScrollOverwrites
.TryGetValue(ActiveScroll.ScrollReference, out (string, int, bool)[] population)
&& population.Length > 0)
{
DynamicScrollInQue = true; // block update on population
foreach ((string Id, int index, bool show) in population)
{
ParameterDisplays[index].URI = Id;
ParameterDisplays[index].transform.parent.gameObject.SetActive(show);
}
DynamicScrollInQue = false; // unblock
NewAssignmentEvent.Invoke(); // init display
}
}
public bool SetNextEmptyTo(FactObjectUI activator)
{
RenderedScrollFact check = ParameterDisplays
.Find(RSF => RSF != null
&& RSF.Payload != null
&& RSF.Payload.Equals(activator));
if (check != null)
{
check.URI = null;
return false;
}
RenderedScrollFact empty = ParameterDisplays
.Find(RSF => !RSF.IsSet);
if (empty == null)
return false;
empty.SetByFactObject(activator);
return true;
}
public void MagicButtonTrigger()
{
StartCoroutine(_MagicButton());
IEnumerator _MagicButton()
{
if (MagicInQue)
yield break; // only need next in que to finish
MagicInQue = true;
while (!SendingViewDone || DynamicScrollInQue)
yield return null; // Wait for last assignment
MagicInQue = false;
yield return SendView("/scroll/apply");
if (currentMmtAnswer == null)
{
Debug.LogError("Magic FAILED");
ScrollApplicationCheckingErrorEvent.Invoke(null);
}
else
{
if (VerboseURI)
Debug.Log("Magic answers:\n" + currentMmtAnswer);
ScrollApplicationInfo pushout = JsonConvert.DeserializeObject<ScrollApplicationInfo>(currentMmtAnswer);
if (pushout.acquiredFacts.Count == 0
|| pushout.errors.Length > 0)
{
ScrollApplicationCheckingErrorEvent.Invoke(pushout.errors);
PushoutFactFailEvent.Invoke();
}
else
Popup.HidePopUp(); //close error Window
yield return __GeneratePushoutFacts(pushout.acquiredFacts);
}
}
IEnumerator __GeneratePushoutFacts(List<MMTFact> pushoutFacts)
{
List<Fact> new_facts = new();
Dictionary<string, string> old_to_new = new();
System.DateTime parseTime = System.DateTime.UtcNow;
bool samestep = false;
for (int i = 0; i < pushoutFacts.Count; i++)
{
List<Fact> new_list = Fact.MMTFactory(pushoutFacts[i].MapURIs(old_to_new));
if (new_list.Count == 0)
{
Debug.LogWarning("Parsing on pushout-fact returned empty List -> One of the dependent facts does not exist or parsing failed");
continue;
}
foreach (Fact new_fact in new_list)
{
Fact added = FactAdder.AddFactIfNotFound(new_fact, out bool exists, samestep, null, ActiveScroll.label);
if (!exists)
{
new_facts.Add(added);
AnimateExistingFactEvent.Invoke(added.Id, FactWrapper.FactMaterials.Solution);
samestep = true;
}
else
{
// AnimateExistingFactEvent.Invoke(_new, FactWrapper.FactMaterials.Hint); // Automaticly done in FactRecorder
old_to_new.Add(new_fact.Id, added.Id);
}
yield return null;
}
}
Debug.Log($"Facts parsed within {(System.DateTime.UtcNow - parseTime).TotalMilliseconds}ms");
yield break;
}
}
public void NewAssignmentTrigger()
{
if (ActiveScroll?.ScrollReference == null
|| NoDynamicScroll.Contains(ActiveScroll.ScrollReference))
return;
if (AutomaticHintGenerationActive || DynamicScrollDescriptionsActive)
StartCoroutine(_NewAssignment());
IEnumerator _NewAssignment()
{
if (DynamicScrollInQue)
yield break; // only need next in que to finish
DynamicScrollInQue = true;
while (!SendingViewDone)
yield return null; // if we dont wait => server will crash
DynamicScrollInQue = false;
yield return SendView("/scroll/dynamic");
if (currentMmtAnswer == null)
{
Debug.LogError("Dynamic Scroll FAILED");
}
else
{
ScrollDynamicInfo scrollDynamicInfo;
try
{
scrollDynamicInfo = JsonConvert.DeserializeObject<ScrollDynamicInfo>(currentMmtAnswer);
//scrollDynamicInfo = IJSONsavable<ScrollDynamicInfo>.postprocess(scrollDynamicInfo); // DON'T! will remove hints
}
catch (JsonSerializationException ex)
{
Debug.LogException(ex);
Debug.LogError("Could not Deserialize MMT aswer for /scroll/dynamic\n" + currentMmtAnswer);
yield break;
}
ScrollApplicationCheckingError[] errors = scrollDynamicInfo.errors
.Where(err => err.kind != "nonTotal") // expected
.ToArray();
if (errors.Length > 0)
ScrollApplicationCheckingErrorEvent.Invoke(errors);
processScrollDynamicInfo(scrollDynamicInfo);
}
}
}
private IEnumerator SendView(string endpoint)
{
SendingViewDone = false;
while (ParameterDisplays == null) // Wait for server
yield return null;
string body = prepareScrollAssignments();
using UnityWebRequest www = UnityWebRequest.Put(ServerAdress + endpoint, body);
www.method = UnityWebRequest.kHttpVerbPOST;
www.SetRequestHeader("Content-Type", "application/json");
System.DateTime sendTime = System.DateTime.UtcNow;
yield return www.SendWebRequest();
//if (VerboseURI)
Debug.LogFormat("Server answerd in : {0}ms"
, (System.DateTime.UtcNow - sendTime).TotalMilliseconds);
if (www.result == UnityWebRequest.Result.ConnectionError
|| www.result == UnityWebRequest.Result.ProtocolError)
{
Debug.LogWarning(www.error);
currentMmtAnswer = null;
}
else
{
while (!www.downloadHandler.isDone)
yield return null;
currentMmtAnswer = www.downloadHandler.text
.Replace("\"float\":null", "\"float\":0.0"); // cannot convert null to value type
}
SendingViewDone = true;
yield break;
string prepareScrollAssignments()
{
List<ScrollAssignment> assignmentList = new();
for (int i = 0; i < ParameterDisplays.Count; i++)
{
Fact tempFact = ParameterDisplays[i].Fact;
if (tempFact != null)
assignmentList.Add(new ScrollAssignment(ActiveScroll.requiredFacts[i].@ref.uri, tempFact.Id));
}
return JsonConvert.SerializeObject(new ScrollApplication(ActiveScroll.ScrollReference, assignmentList));
}
}
private void processScrollDynamicInfo(ScrollDynamicInfo scrollDynamicInfo)
{
LatestBackwartsCompletions = scrollDynamicInfo.backward_completions.Count > 0
? scrollDynamicInfo.backward_completions[0]
: new List<ScrollAssignment>();
List<string> hintUris = LatestBackwartsCompletions
.Select(completion => completion.fact.uri)
.ToList();
//Update Scroll, process data for later hints and update Uri-List for which hints are available
_processRenderedScroll(scrollDynamicInfo.rendered, hintUris);
if (AutomaticHintGenerationActive)
//Show that Hint is available for ScrollParameter
HintAvailableEvent.Invoke(hintUris);
return;
void _processRenderedScroll(Scroll rendered, List<string> hintUris)
{
if (DynamicScrollDescriptionsActive)
{ // Update scroll-description
Transform scroll = gameObject.transform.GetChild(1).transform;
scroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = rendered.description;
}
LatestRenderedHints = new();
for (int i = 0; i < rendered.requiredFacts.Count; i++)
{
RenderedScrollFact RenderedScrollFact = ParameterDisplays
.Find(RSF => RSF.ScrollFactURI == rendered.requiredFacts[i].@ref.uri);
if (RenderedScrollFact == null)
{ // e.g. FUNCs
//if(CommunicationEvents.VerboseScroll)
// Debug.Log($"Descrapancy between requiredFacts and displayed facts:" +
// $"Could not find display with ref: {rendered.requiredFacts[i].@ref.uri}");
continue;
}
if (DynamicScrollDescriptionsActive)
//Update ScrollParameter label
RenderedScrollFact.Scroll = rendered;
//If ScrollFact is assigned -> No Hint
if (!RenderedScrollFact.IsSet)
{
List<Fact> HintFactList = Fact.MMTFactory(rendered.requiredFacts[i]);
foreach (Fact HintFact in HintFactList)
{
hintUris.Add(HintFact.Id); // == rendered.requiredFacts[i].@ref.uri
LatestRenderedHints.Add(HintFact);
}
}
}
return;
}
}
public void animateHint(string scrollParameterUri)
{
if (FactRecorder.AllFacts.ContainsKey(scrollParameterUri))
AnimateExistingFactEvent.Invoke(
scrollParameterUri,
FactWrapper.FactMaterials.Hint
);
Fact hintFact = LatestRenderedHints.Find(x => x.Id == scrollParameterUri); // "Dictionary"
ScrollAssignment suitableCompletion =
LatestBackwartsCompletions.Find((ScrollAssignment x) => x.fact.uri == scrollParameterUri); // "Dictionary"
if (suitableCompletion != null && suitableCompletion.assignment is OMS assignment)
{
if (FactRecorder.AllFacts.ContainsKey(assignment.uri))
{
AnimateExistingFactEvent.Invoke(
assignment.uri,
FactWrapper.FactMaterials.Hint
);
}
}
else if (hintFact != null)
{
if (FactRecorder.FindEquivalent(StageStatic.stage.factState.MyFactSpace, hintFact, out string found_key, out Fact _, out bool _, false))
// existing fact -> Animate that
AnimateExistingFactEvent.Invoke(
found_key,
FactWrapper.FactMaterials.Hint
);
else
{ // Generate new FactRepresentation and animate it
AnimateNonExistingFactEvent.Invoke(hintFact);
AnimateExistingFactEvent.Invoke(
scrollParameterUri,
FactWrapper.FactMaterials.Hint
);
}
}
}
}