Last active
April 16, 2024 16:53
-
-
Save Nenkai/ac5b0009551c3b57b6a599450857c680 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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