Skip to content

Instantly share code, notes, and snippets.

@Nenkai
Last active April 16, 2024 16:53
Show Gist options
  • Save Nenkai/ac5b0009551c3b57b6a599450857c680 to your computer and use it in GitHub Desktop.
Save Nenkai/ac5b0009551c3b57b6a599450857c680 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.Json;
using MessagePack;
namespace gbfr.fix.matchmaking.fsm;
// This class attempts to parse the FSM Tree like the way the original code does (reverse-engineered)
public class FSMParser
{
public List<int> LayerIndices { get; set; } = new List<int>();
public List<List<Node>> LayersToNodes { get; set; } = new List<List<Node>>();
public List<Transition> TransitionList1 { get; set; } = new List<Transition>();
public List<Transition> TransitionList2 { get; set; } = new List<Transition>();
public List<BehaviorTreeComponent> Components { get; set; } = new List<BehaviorTreeComponent>();
public Node RootNode { get; set; }
public void Parse(string fileName)
{
byte[] data = File.ReadAllBytes(fileName);
string json = MessagePackSerializer.ConvertToJson(data);
JsonDocument doc = JsonDocument.Parse(json);
Node lastNode = null;
int layerIndex = -1;
foreach (var elem in doc.RootElement.EnumerateObject())
{
switch (elem.Name)
{
case "layerNo":
{
if (!elem.Value.TryGetInt32(out int layerNo))
throw new InvalidDataException("layerNo has invalid integer value.");
LayerIndices.Add(layerNo);
layerIndex++;
}
break;
case "FSMNode":
{
if (LayersToNodes.Count <= layerIndex)
LayersToNodes.Add(new List<Node>());
if (!elem.Value.TryGetProperty("guid_", out JsonElement guid_) || !guid_.TryGetInt32(out int guid))
throw new InvalidDataException("FSMNode is missing or invalid mandatory 'guid_' property.");
if (!elem.Value.TryGetProperty("childLayerId_", out JsonElement childLayerId_) || !childLayerId_.TryGetInt32(out int childLayerId))
throw new InvalidDataException("Transition is missing or invalid mandatory 'childLayerId_' property.");
var node = new Node()
{
Guid = guid,
ChildLayerId = childLayerId
};
LayersToNodes[layerIndex].Add(node);
lastNode = node;
}
break;
case "Transition":
{
if (!elem.Value.TryGetProperty("toNodeGuid_", out JsonElement toNodeGuid_) || !toNodeGuid_.TryGetInt32(out int toNodeGuid))
throw new InvalidDataException("Transition is missing or invalid mandatory 'toNodeGuid_' property.");
if (!elem.Value.TryGetProperty("fromNodeGuid_", out JsonElement fromNodeGuid_) || !fromNodeGuid_.TryGetInt32(out int fromNodeGuid))
throw new InvalidDataException("Transition is missing or invalid mandatory 'fromNodeGuid_' property.");
Transition transition = new Transition()
{
ToNodeGuid = toNodeGuid,
FromNodeGuid = fromNodeGuid
};
if (!elem.Value.TryGetProperty("conditionGuids_", out JsonElement conditionGuids_))
throw new InvalidDataException("Conditional Transition is missing mandatory 'conditionGuids_' property.");
foreach (JsonElement element in conditionGuids_.EnumerateArray())
{
int conditionGuid = element.GetProperty("Element").GetInt32();
transition.ConditionGuids.Add(conditionGuid);
}
if (transition.ToNodeGuid != 0)
{
TransitionList1.Add(transition);
lastNode.TransitionList1.Add(transition);
}
else
{
TransitionList2.Add(transition);
lastNode.TransitionList2.Add(transition);
}
}
break;
case "addAllTransition":
case "addTransition":
case "EnableBaseAllTransition":
case "EnableBaseTransition":
case "EnableFalseComponent":
case "className":
case "fsmName":
// TODO
break;
default:
// Anything else is a component
{
if (!elem.Value.TryGetProperty("guid_", out JsonElement guid_) || !guid_.TryGetUInt32(out uint guid))
throw new InvalidDataException($"Component '{elem.Name}' is missing or invalid mandatory 'guid_' property.");
if (!elem.Value.TryGetProperty("parentGuid_", out JsonElement parentGuid_) || !parentGuid_.TryGetUInt32(out uint parentGuid))
throw new InvalidDataException($"Component '{elem.Name}' is missing or invalid mandatory 'parentGuid_' property.");
var component = new BehaviorTreeComponent()
{
Guid = guid,
ParentGuid = parentGuid,
};
Components.Add(component);
}
break;
}
}
// Link transition condition guids to their components directly
foreach (Transition transition in TransitionList2)
{
foreach (int conditionGuid in transition.ConditionGuids)
{
foreach (BehaviorTreeComponent component in Components)
{
if (conditionGuid == component.Guid)
{
transition.ConditionComponents.Add(component);
break;
}
}
}
}
RootNode = LayersToNodes[0][0];
int nIndex = 1;
BuildTree(RootNode, ref nIndex, 0, LayersToNodes, LayerIndices);
}
// Reversed - 41 57 41 56 41 55 41 54 56 57 55 53 48 83 EC ? 4C 89 CE 45 89 C6
public void BuildTree(Node node, ref int nodeIndex, int layerIndex, List<List<Node>> layersToNodes, List<int> layerIndices)
{
int numNodesThisLayer = nodeIndex == 1 ? layersToNodes[layerIndex].Count - 1 : 0;
if (node.ChildLayerId != -1)
{
if (layersToNodes.Count > 0)
{
int max = layerIndices.Count > 1 ? layerIndices.Count : 1;
for (int i = 0; i < max; i++)
{
if (node.ChildLayerId == layerIndices[i])
{
int nIndex = 1;
node.Child.Add(layersToNodes[i][0]);
BuildTree(layersToNodes[i][0], ref nIndex, i, layersToNodes, layerIndices);
if (numNodesThisLayer == 0)
return;
else
break;
}
}
}
}
for (int i = 0; i < numNodesThisLayer; i++)
{
List<Node> nodesThisLayer = layersToNodes[layerIndex];
if (i >= nodesThisLayer.Count)
return;
Node childNode = nodesThisLayer[nodeIndex];
node.Child.Add(childNode);
nodeIndex++;
BuildTree(childNode, ref nodeIndex, layerIndex, layersToNodes, layerIndices);
}
}
// BTInGame::FSMNode
public class Node
{
public List<Node> Child { get; set; } = new List<Node>();
public int Guid; // exposed as guid_
public int ChildLayerId { get; set; } // exposed as childLayerId_
public List<Transition> TransitionList1 = new List<Transition>();
public List<Transition> TransitionList2 = new List<Transition>();
}
// BTInGame::Transition
public class Transition
{
public int ToNodeGuid { get; set; } // exposed as toNodeGuid_
public int FromNodeGuid { get; set; } // exposed as fromNodeGuid_
public List<int> ConditionGuids { get; set; } = new List<int>(); // exposed as conditionGuids_
public List<BehaviorTreeComponent> ConditionComponents { get; set; } = new List<BehaviorTreeComponent>();
}
// BT::BehaviorTreeComponent
public class BehaviorTreeComponent
{
public uint Guid { get; set; } // exposed as guid_
public uint ParentGuid { get; set; } // exposed as parentGuid_
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment