mirror of
https://github.com/LazyDuchess/OpenTS2.git
synced 2025-01-23 08:41:47 -05:00
Merge pull request #53 from LazyDuchess/vm
Initial SimAntics VM Implementation
This commit is contained in:
commit
757eb53777
48 changed files with 1640 additions and 1 deletions
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using OpenTS2.Common;
|
||||
using OpenTS2.Files.Formats.DBPF;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
@ -8,6 +10,8 @@ namespace OpenTS2.Content.DBPF
|
|||
{
|
||||
public class ObjectDefinitionAsset : AbstractAsset
|
||||
{
|
||||
// TODO: Person and Template object types seem to use 0x80 as the SG instance id for some reason.
|
||||
public SemiGlobalAsset SemiGlobal => ContentProvider.Get().GetAsset<SemiGlobalAsset>(new ResourceKey(1, GlobalTGI.GroupID, TypeIDs.SEMIGLOBAL));
|
||||
public string FileName;
|
||||
|
||||
public enum FieldNames
|
||||
|
|
|
@ -5,6 +5,7 @@ using OpenTS2.Diagnostic;
|
|||
using OpenTS2.Files;
|
||||
using OpenTS2.Files.Formats.DBPF;
|
||||
using OpenTS2.Rendering;
|
||||
using OpenTS2.SimAntics.Primitives;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OpenTS2.Engine
|
||||
|
@ -34,6 +35,7 @@ namespace OpenTS2.Engine
|
|||
Filesystem.Initialize(new JSONPathProvider(), epManager);
|
||||
CodecAttribute.Initialize();
|
||||
CheatSystem.Initialize();
|
||||
VMPrimitiveRegistry.Initialize();
|
||||
//Initialize the game assembly, do all reflection things.
|
||||
AssemblyHelper.InitializeLoadedAssemblies();
|
||||
s_initialized = true;
|
||||
|
|
|
@ -49,6 +49,7 @@ namespace OpenTS2.Files.Formats.DBPF
|
|||
public const uint DLL = 0x7582DEC6;
|
||||
public const uint CTSS = 0x43545353;
|
||||
public const uint UI = 0x0;
|
||||
public const uint BHAV = 0x42484156;
|
||||
public const uint SEMIGLOBAL = 0x7F8D70BF;
|
||||
}
|
||||
public static class GroupIDs
|
||||
|
@ -60,5 +61,6 @@ namespace OpenTS2.Files.Formats.DBPF
|
|||
public const uint DIR = 0xE86B1EEF;
|
||||
public const uint Scenegraph = 0x1C0532FA;
|
||||
public const uint Effects = 0xEA5118B1;
|
||||
public const uint Global = 0x7FD46CD0;
|
||||
}
|
||||
}
|
||||
|
|
8
Assets/Scripts/OpenTS2/SimAntics.meta
generated
Normal file
8
Assets/Scripts/OpenTS2/SimAntics.meta
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e31a40caa8341a846bd8243595805e3d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
76
Assets/Scripts/OpenTS2/SimAntics/BHAVAsset.cs
Normal file
76
Assets/Scripts/OpenTS2/SimAntics/BHAVAsset.cs
Normal file
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using OpenTS2.Content;
|
||||
|
||||
namespace OpenTS2.SimAntics
|
||||
{
|
||||
/// <summary>
|
||||
/// Script in the SimAntics language. Handles most of the simulation.
|
||||
/// </summary>
|
||||
public class BHAVAsset : AbstractAsset
|
||||
{
|
||||
public string FileName = "";
|
||||
public int ArgumentCount = 0;
|
||||
public int LocalCount = 0;
|
||||
public List<Node> Nodes = new List<Node>();
|
||||
|
||||
public class Node
|
||||
{
|
||||
public const ushort ErrorReturnValue = 0xFFFC;
|
||||
public const ushort TrueReturnValue = 0xFFFD;
|
||||
public const ushort FalseReturnValue = 0xFFFE;
|
||||
public ushort OpCode;
|
||||
public ushort TrueTarget;
|
||||
public ushort FalseTarget;
|
||||
public byte[] Operands;
|
||||
public byte Version;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the operand at index.
|
||||
/// </summary>
|
||||
/// <returns>Operand byte value, or 0 if the index is out of range.</returns>
|
||||
public byte GetOperand(int index)
|
||||
{
|
||||
if (index < 0)
|
||||
return 0;
|
||||
if (index >= Operands.Length)
|
||||
return 0;
|
||||
return Operands[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an operand byte array starting at index and of the specified length.
|
||||
/// </summary>
|
||||
/// <returns>Operand bytes, bytes out of range return 0.</returns>
|
||||
public byte[] GetOperands(int index, int length)
|
||||
{
|
||||
var array = new byte[length];
|
||||
for(var i=0;i<length;i++)
|
||||
{
|
||||
var ind = index + i;
|
||||
array[i] = GetOperand(ind);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an ushort from the operand array, starting at index.
|
||||
/// </summary>
|
||||
public ushort GetUInt16Operand(int index)
|
||||
{
|
||||
return BitConverter.ToUInt16(GetOperands(index,2), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a short from the operand array, starting at index.
|
||||
/// </summary>
|
||||
public short GetInt16Operand(int index)
|
||||
{
|
||||
return BitConverter.ToInt16(GetOperands(index, 2), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/BHAVAsset.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/BHAVAsset.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1bc064226f63fda41bec72257f2ffc1c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
123
Assets/Scripts/OpenTS2/SimAntics/BHAVCodec.cs
Normal file
123
Assets/Scripts/OpenTS2/SimAntics/BHAVCodec.cs
Normal file
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using OpenTS2.Common;
|
||||
using OpenTS2.Content;
|
||||
using OpenTS2.Files.Formats.DBPF;
|
||||
using OpenTS2.Files.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OpenTS2.SimAntics
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses a BHAV resource into an asset.
|
||||
/// </summary>
|
||||
/// https://modthesims.info/wiki.php?title=42484156
|
||||
[Codec(TypeIDs.BHAV)]
|
||||
public class BHAVCodec : AbstractCodec
|
||||
{
|
||||
public override AbstractAsset Deserialize(byte[] bytes, ResourceKey tgi, DBPFFile sourceFile)
|
||||
{
|
||||
var asset = new BHAVAsset();
|
||||
var stream = new MemoryStream(bytes);
|
||||
var reader = IoBuffer.FromStream(stream, ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
// Header
|
||||
asset.FileName = reader.ReadNullTerminatedUTF8();
|
||||
reader.Seek(SeekOrigin.Begin, 64);
|
||||
var magic = reader.ReadUInt16();
|
||||
Debug.Assert(magic <= 0x8009);
|
||||
var instructionCount = reader.ReadUInt16();
|
||||
var type = reader.ReadByte();
|
||||
asset.ArgumentCount = reader.ReadByte();
|
||||
asset.LocalCount = reader.ReadByte();
|
||||
var flags = reader.ReadByte();
|
||||
var treeVersion = reader.ReadUInt32();
|
||||
|
||||
byte cacheFlags = 0;
|
||||
// Sims wiki says cacheflags are at the end of each node, not in the header, which isn't correct.
|
||||
if (magic >= 0x8009)
|
||||
cacheFlags = reader.ReadByte();
|
||||
|
||||
// Nodes
|
||||
for (var i=0;i<instructionCount;i++)
|
||||
{
|
||||
byte nodeVersion = 0;
|
||||
ushort opcode, trueTarget, falseTarget;
|
||||
byte[] operands;
|
||||
|
||||
if (magic <= 0x8002)
|
||||
{
|
||||
opcode = reader.ReadUInt16();
|
||||
trueTarget = reader.ReadByte();
|
||||
falseTarget = reader.ReadByte();
|
||||
operands = reader.ReadBytes(8);
|
||||
}
|
||||
else if (magic <= 0x8004)
|
||||
{
|
||||
opcode = reader.ReadUInt16();
|
||||
trueTarget = reader.ReadByte();
|
||||
falseTarget = reader.ReadByte();
|
||||
operands = reader.ReadBytes(16);
|
||||
}
|
||||
else if (magic <= 0x8006)
|
||||
{
|
||||
opcode = reader.ReadUInt16();
|
||||
trueTarget = reader.ReadByte();
|
||||
falseTarget = reader.ReadByte();
|
||||
nodeVersion = reader.ReadByte();
|
||||
operands = reader.ReadBytes(16);
|
||||
}
|
||||
else
|
||||
{
|
||||
opcode = reader.ReadUInt16();
|
||||
trueTarget = reader.ReadUInt16();
|
||||
falseTarget = reader.ReadUInt16();
|
||||
nodeVersion = reader.ReadByte();
|
||||
operands = reader.ReadBytes(16);
|
||||
}
|
||||
|
||||
// Convert from TS1 to TS2 format if necessary.
|
||||
trueTarget = ParseTarget(trueTarget);
|
||||
falseTarget = ParseTarget(falseTarget);
|
||||
|
||||
var node = new BHAVAsset.Node
|
||||
{
|
||||
OpCode = opcode,
|
||||
TrueTarget = trueTarget,
|
||||
FalseTarget = falseTarget,
|
||||
Operands = operands,
|
||||
Version = nodeVersion
|
||||
};
|
||||
|
||||
asset.Nodes.Add(node);
|
||||
}
|
||||
|
||||
stream.Dispose();
|
||||
reader.Dispose();
|
||||
return asset;
|
||||
|
||||
ushort ParseTarget(ushort target)
|
||||
{
|
||||
switch(target)
|
||||
{
|
||||
// None / Error
|
||||
case 0xFD:
|
||||
return BHAVAsset.Node.ErrorReturnValue;
|
||||
// True
|
||||
case 0xFE:
|
||||
return BHAVAsset.Node.TrueReturnValue;
|
||||
// False
|
||||
case 0xFF:
|
||||
return BHAVAsset.Node.FalseReturnValue;
|
||||
// probably already an okay value
|
||||
default:
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/BHAVCodec.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/BHAVCodec.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1991f3f661f4f854096da0e28958370c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Scripts/OpenTS2/SimAntics/Primitives.meta
generated
Normal file
8
Assets/Scripts/OpenTS2/SimAntics/Primitives.meta
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 456765861d2c2ab4384fc8a8de928bac
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
166
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMExpression.cs
Normal file
166
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMExpression.cs
Normal file
|
@ -0,0 +1,166 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenTS2.SimAntics.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// Expression Primitive, handles variable assignation, retrieval, math.
|
||||
/// </summary>
|
||||
public class VMExpression : VMPrimitive
|
||||
{
|
||||
public enum Operator : byte
|
||||
{
|
||||
GreaterThan,
|
||||
LessThan,
|
||||
EqualTo,
|
||||
Add,
|
||||
Subtract,
|
||||
Assign,
|
||||
Multiply,
|
||||
Divide,
|
||||
IsFlagSet,
|
||||
SetFlag,
|
||||
ClearFlag,
|
||||
IncThenLessThan,
|
||||
Modulo,
|
||||
And,
|
||||
GreaterThanOrEqualTo,
|
||||
LessThanOrEqualTo,
|
||||
NotEqualTo,
|
||||
DecThenGreaterThan,
|
||||
Or,
|
||||
Xor,
|
||||
Abs,
|
||||
Assign32BitValue
|
||||
}
|
||||
public override VMReturnValue Execute(VMContext ctx)
|
||||
{
|
||||
var lhsData = ctx.Node.GetInt16Operand(0);
|
||||
var rhsData = ctx.Node.GetInt16Operand(2);
|
||||
var signedFlag = ctx.Node.Operands[4];
|
||||
var op = (Operator)ctx.Node.Operands[5];
|
||||
var lhsSource = (VMDataSource)ctx.Node.Operands[6];
|
||||
var rhsSource = (VMDataSource)ctx.Node.Operands[7];
|
||||
|
||||
short lhs, rhs;
|
||||
|
||||
switch(op)
|
||||
{
|
||||
case Operator.GreaterThan:
|
||||
return ctx.GetData(lhsSource, lhsData) > ctx.GetData(rhsSource, rhsData) ?
|
||||
VMReturnValue.ReturnTrue : VMReturnValue.ReturnFalse;
|
||||
case Operator.LessThan:
|
||||
return ctx.GetData(lhsSource, lhsData) < ctx.GetData(rhsSource, rhsData) ?
|
||||
VMReturnValue.ReturnTrue : VMReturnValue.ReturnFalse;
|
||||
case Operator.EqualTo:
|
||||
return ctx.GetData(lhsSource, lhsData) == ctx.GetData(rhsSource, rhsData) ?
|
||||
VMReturnValue.ReturnTrue : VMReturnValue.ReturnFalse;
|
||||
case Operator.Add:
|
||||
lhs = ctx.GetData(lhsSource, lhsData);
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
ctx.SetData(lhsSource, lhsData, (short)(lhs + rhs));
|
||||
return VMReturnValue.ReturnTrue;
|
||||
case Operator.Subtract:
|
||||
lhs = ctx.GetData(lhsSource, lhsData);
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
ctx.SetData(lhsSource, lhsData, (short)(lhs - rhs));
|
||||
return VMReturnValue.ReturnTrue;
|
||||
case Operator.Assign:
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
ctx.SetData(lhsSource, lhsData, rhs);
|
||||
return VMReturnValue.ReturnTrue;
|
||||
case Operator.Multiply:
|
||||
lhs = ctx.GetData(lhsSource, lhsData);
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
ctx.SetData(lhsSource, lhsData, (short)(lhs * rhs));
|
||||
return VMReturnValue.ReturnTrue;
|
||||
case Operator.Divide:
|
||||
lhs = ctx.GetData(lhsSource, lhsData);
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
ctx.SetData(lhsSource, lhsData, (short)(lhs / rhs));
|
||||
return VMReturnValue.ReturnTrue;
|
||||
case Operator.IsFlagSet:
|
||||
lhs = ctx.GetData(lhsSource, lhsData);
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
return ((lhs & (1 << (rhs - 1))) > 0) ?
|
||||
VMReturnValue.ReturnTrue : VMReturnValue.ReturnFalse;
|
||||
case Operator.SetFlag:
|
||||
lhs = ctx.GetData(lhsSource, lhsData);
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
var bitval = 1 << (rhs - 1);
|
||||
var finalSet = (int)lhs | bitval;
|
||||
ctx.SetData(lhsSource, lhsData, (short)(finalSet));
|
||||
return VMReturnValue.ReturnTrue;
|
||||
case Operator.ClearFlag:
|
||||
lhs = ctx.GetData(lhsSource, lhsData);
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
var clearBitVal = ~(1 << (rhs - 1));
|
||||
var finalClear = (int)lhs & clearBitVal;
|
||||
ctx.SetData(lhsSource, lhsData, (short)(finalClear));
|
||||
return VMReturnValue.ReturnTrue;
|
||||
case Operator.IncThenLessThan:
|
||||
lhs = ctx.GetData(lhsSource, lhsData);
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
var lhsAdded = lhs + 1;
|
||||
ctx.SetData(lhsSource, lhsData, (short)lhsAdded);
|
||||
return lhsAdded < rhs ?
|
||||
VMReturnValue.ReturnTrue : VMReturnValue.ReturnFalse;
|
||||
case Operator.Modulo:
|
||||
lhs = ctx.GetData(lhsSource, lhsData);
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
var lhsMod = lhs % rhs;
|
||||
ctx.SetData(lhsSource, lhsData, (short)lhsMod);
|
||||
return VMReturnValue.ReturnTrue;
|
||||
case Operator.And:
|
||||
lhs = ctx.GetData(lhsSource, lhsData);
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
var lhsAnd = lhs & rhs;
|
||||
ctx.SetData(lhsSource, lhsData, (short)lhsAnd);
|
||||
return VMReturnValue.ReturnTrue;
|
||||
case Operator.GreaterThanOrEqualTo:
|
||||
return ctx.GetData(lhsSource, lhsData) >= ctx.GetData(rhsSource, rhsData) ?
|
||||
VMReturnValue.ReturnTrue : VMReturnValue.ReturnFalse;
|
||||
case Operator.LessThanOrEqualTo:
|
||||
return ctx.GetData(lhsSource, lhsData) <= ctx.GetData(rhsSource, rhsData) ?
|
||||
VMReturnValue.ReturnTrue : VMReturnValue.ReturnFalse;
|
||||
case Operator.NotEqualTo:
|
||||
return ctx.GetData(lhsSource, lhsData) != ctx.GetData(rhsSource, rhsData) ?
|
||||
VMReturnValue.ReturnTrue : VMReturnValue.ReturnFalse;
|
||||
case Operator.DecThenGreaterThan:
|
||||
lhs = ctx.GetData(lhsSource, lhsData);
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
var lhsSubbed = lhs - 1;
|
||||
ctx.SetData(lhsSource, lhsData, (short)lhsSubbed);
|
||||
return lhsSubbed > rhs ?
|
||||
VMReturnValue.ReturnTrue : VMReturnValue.ReturnFalse;
|
||||
case Operator.Or:
|
||||
lhs = ctx.GetData(lhsSource, lhsData);
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
var lhsOr = lhs | rhs;
|
||||
ctx.SetData(lhsSource, lhsData, (short)lhsOr);
|
||||
return VMReturnValue.ReturnTrue;
|
||||
case Operator.Xor:
|
||||
lhs = ctx.GetData(lhsSource, lhsData);
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
var lhsXor = lhs ^ rhs;
|
||||
ctx.SetData(lhsSource, lhsData, (short)lhsXor);
|
||||
return VMReturnValue.ReturnTrue;
|
||||
case Operator.Abs:
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
ctx.SetData(lhsSource, lhsData, Math.Abs(rhs));
|
||||
return VMReturnValue.ReturnTrue;
|
||||
// TODO: This is new in TS2 so I'm not sure if this is correct.
|
||||
case Operator.Assign32BitValue:
|
||||
rhs = ctx.GetData(rhsSource, rhsData);
|
||||
var rhs2 = ctx.GetData(rhsSource, (short)(rhsData+1));
|
||||
ctx.SetData(lhsSource, lhsData, rhs);
|
||||
ctx.SetData(lhsSource, (short)(lhsData+1), rhs2);
|
||||
return VMReturnValue.ReturnTrue;
|
||||
}
|
||||
return VMReturnValue.ReturnFalse;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMExpression.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMExpression.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 66c1d2b5188323d4ebb04f6fc5f499e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenTS2.SimAntics.Primitives
|
||||
{
|
||||
public class VMNotifyStackObjectOutOfIdle : VMPrimitive
|
||||
{
|
||||
public override VMReturnValue Execute(VMContext ctx)
|
||||
{
|
||||
var stackObject = ctx.VM.GetEntityByID(ctx.StackFrame.StackObjectID);
|
||||
if (stackObject == null)
|
||||
throw new KeyNotFoundException($"Couldn't find Object with ID {ctx.StackFrame.StackObjectID}");
|
||||
ctx.VM.Scheduler.ScheduleInterrupt(ctx.StackObjectEntity.Stack);
|
||||
return VMReturnValue.ReturnTrue;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMNotifyStackObjectOutOfIdle.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMNotifyStackObjectOutOfIdle.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9cde7a92de8623c40a7dbf0f9527174b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenTS2.SimAntics.Primitives
|
||||
{
|
||||
public static class VMPrimitiveRegistry
|
||||
{
|
||||
private static Dictionary<ushort, VMPrimitive> s_primitiveByOpCode = new Dictionary<ushort, VMPrimitive>();
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
RegisterPrimitive<VMSleep>(0x0);
|
||||
RegisterPrimitive<VMExpression>(0x2);
|
||||
RegisterPrimitive<VMRandomNumber>(0x8);
|
||||
RegisterPrimitive<VMRemoveObjectInstance>(0x12);
|
||||
RegisterPrimitive<VMNotifyStackObjectOutOfIdle>(0x31);
|
||||
}
|
||||
|
||||
public static void RegisterPrimitive<T>(ushort opcode) where T : VMPrimitive
|
||||
{
|
||||
s_primitiveByOpCode[opcode] = Activator.CreateInstance(typeof(T)) as VMPrimitive;
|
||||
}
|
||||
|
||||
public static T GetPrimitive<T>(ushort opcode) where T : VMPrimitive
|
||||
{
|
||||
return GetPrimitive(opcode) as T;
|
||||
}
|
||||
|
||||
public static VMPrimitive GetPrimitive(ushort opcode)
|
||||
{
|
||||
if (s_primitiveByOpCode.TryGetValue(opcode, out VMPrimitive returnPrim))
|
||||
return returnPrim;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMPrimitiveRegistry.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMPrimitiveRegistry.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2251550a13386fc4ca77428e52e11359
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenTS2.SimAntics.Primitives
|
||||
{
|
||||
public class VMRandomNumber : VMPrimitive
|
||||
{
|
||||
public override VMReturnValue Execute(VMContext ctx)
|
||||
{
|
||||
var lhsSource = (VMDataSource)ctx.Node.GetOperand(2);
|
||||
var lhsData = ctx.Node.GetInt16Operand(0);
|
||||
|
||||
var rhsSource = (VMDataSource)ctx.Node.GetOperand(6);
|
||||
var rhsData = ctx.Node.GetInt16Operand(4);
|
||||
|
||||
var randomMaxValue = ctx.GetData(rhsSource, rhsData);
|
||||
var randomFinalValue = (short)UnityEngine.Random.Range(0, randomMaxValue);
|
||||
|
||||
ctx.SetData(lhsSource, lhsData, randomFinalValue);
|
||||
|
||||
return VMReturnValue.ReturnTrue;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMRandomNumber.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMRandomNumber.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f67753d3b8a97c442b581b3a03d97f53
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using OpenTS2.Common;
|
||||
|
||||
namespace OpenTS2.SimAntics.Primitives
|
||||
{
|
||||
public class VMRemoveObjectInstance : VMPrimitive
|
||||
{
|
||||
public override VMReturnValue Execute(VMContext ctx)
|
||||
{
|
||||
// These two seem to be a mystery according to FreeSO src
|
||||
var returnImmediately = ((ctx.Node.GetOperand(2) & 1) == 1);
|
||||
var cleanUpAll = ((ctx.Node.GetOperand(2) & 2) != 2);
|
||||
|
||||
var entityToRemove = ctx.Node.GetOperand(0) > 0 ? ctx.StackObjectEntity : ctx.Entity;
|
||||
entityToRemove.Delete();
|
||||
|
||||
// FreeSO yields 1 tick in the case of self deletion, preventing further execution of the script, so we also do that.
|
||||
// TS2 BHAVs tend to remove themselves -> idle for a few ticks -> loop back to remove prim.
|
||||
if (entityToRemove == ctx.Entity)
|
||||
return VMReturnValue.ReturnTrueNextTick;
|
||||
|
||||
return VMReturnValue.ReturnTrue;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMRemoveObjectInstance.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMRemoveObjectInstance.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f4f2d62be2b411b43b4fb2ab0347a435
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
50
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMSleep.cs
Normal file
50
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMSleep.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenTS2.SimAntics.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// Sleeps the current thread for a number of ticks.
|
||||
/// </summary>
|
||||
public class VMSleep : VMPrimitive
|
||||
{
|
||||
public override VMReturnValue Execute(VMContext ctx)
|
||||
{
|
||||
var argumentIndex = ctx.Node.GetUInt16Operand(0);
|
||||
var sleepTicks = (uint)Math.Max(0,(int)ctx.StackFrame.Arguments[argumentIndex]);
|
||||
return new VMReturnValue(new ContinueHandler(ctx.Stack, sleepTicks));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles VM thread blocking for the Sleep prim.
|
||||
/// </summary>
|
||||
public class ContinueHandler : VMContinueHandler
|
||||
{
|
||||
public uint TargetTick = 0;
|
||||
VMStack _stack;
|
||||
|
||||
public ContinueHandler(VMStack stack, uint ticks)
|
||||
{
|
||||
_stack = stack;
|
||||
var vm = _stack.Entity.VM;
|
||||
TargetTick = vm.CurrentTick + ticks;
|
||||
}
|
||||
|
||||
public override VMExitCode Tick()
|
||||
{
|
||||
if (_stack.Interrupt)
|
||||
{
|
||||
// Handled!
|
||||
_stack.Interrupt = false;
|
||||
return VMExitCode.True;
|
||||
}
|
||||
if (_stack.Entity.VM.CurrentTick >= TargetTick)
|
||||
return VMExitCode.True;
|
||||
return VMExitCode.Continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMSleep.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/Primitives/VMSleep.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4b295e337c8206e43bfa5c8ba0fc3fb5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
82
Assets/Scripts/OpenTS2/SimAntics/VM.cs
Normal file
82
Assets/Scripts/OpenTS2/SimAntics/VM.cs
Normal file
|
@ -0,0 +1,82 @@
|
|||
using OpenTS2.Common;
|
||||
using OpenTS2.Content;
|
||||
using OpenTS2.Files.Formats.DBPF;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OpenTS2.SimAntics
|
||||
{
|
||||
/// <summary>
|
||||
/// SimAntics virtual machine.
|
||||
/// </summary>
|
||||
public class VM
|
||||
{
|
||||
public VMScheduler Scheduler = new VMScheduler();
|
||||
public List<VMEntity> Entities = new List<VMEntity>();
|
||||
public uint CurrentTick = 0;
|
||||
|
||||
private Dictionary<short, VMEntity> _entitiesByID = new Dictionary<short, VMEntity>();
|
||||
|
||||
/// <summary>
|
||||
/// Ticks all entities and advances the Simulation by 1 tick.
|
||||
/// </summary>
|
||||
public void Tick()
|
||||
{
|
||||
Scheduler.OnBeginTick(this);
|
||||
foreach(var entity in Entities)
|
||||
{
|
||||
entity.Tick();
|
||||
}
|
||||
Scheduler.OnEndTick(this);
|
||||
CurrentTick++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a BHAV Asset from the content system.
|
||||
/// </summary>
|
||||
public static BHAVAsset GetBHAV(ushort id, uint groupID)
|
||||
{
|
||||
return ContentProvider.Get().GetAsset<BHAVAsset>(new ResourceKey(id, groupID, TypeIDs.BHAV));
|
||||
}
|
||||
|
||||
public VMEntity GetEntityByID(short id)
|
||||
{
|
||||
if (_entitiesByID.TryGetValue(id, out VMEntity result))
|
||||
return result;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an entity to the simulator, and assigns a unique ID to it.
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
public void AddEntity(VMEntity entity)
|
||||
{
|
||||
entity.VM = this;
|
||||
entity.ID = GetUniqueID();
|
||||
Entities.Add(entity);
|
||||
_entitiesByID[entity.ID] = entity;
|
||||
}
|
||||
|
||||
public void RemoveEntity(short id)
|
||||
{
|
||||
if (!_entitiesByID.TryGetValue(id, out VMEntity result))
|
||||
return;
|
||||
_entitiesByID.Remove(id);
|
||||
Entities.Remove(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a free unused entity ID.
|
||||
/// </summary>
|
||||
public short GetUniqueID()
|
||||
{
|
||||
short resultID = 1;
|
||||
while (_entitiesByID.TryGetValue(resultID, out VMEntity _))
|
||||
resultID++;
|
||||
return resultID;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/VM.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/VM.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 41af7919700444842831300feb1548fa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
64
Assets/Scripts/OpenTS2/SimAntics/VMContext.cs
Normal file
64
Assets/Scripts/OpenTS2/SimAntics/VMContext.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenTS2.SimAntics
|
||||
{
|
||||
/// <summary>
|
||||
/// SimAntics virtual machine context to be sent to primitives and such.
|
||||
/// </summary>
|
||||
public struct VMContext
|
||||
{
|
||||
public VMStackFrame StackFrame;
|
||||
public BHAVAsset.Node Node;
|
||||
public VMStack Stack => StackFrame.Stack;
|
||||
public VMEntity Entity => StackFrame.Stack.Entity;
|
||||
public VMEntity StackObjectEntity => VM.GetEntityByID(StackFrame.StackObjectID);
|
||||
public VM VM => Entity.VM;
|
||||
|
||||
// TODO - still super incomplete, just enough to run basic scripts.
|
||||
public short GetData(VMDataSource source, short dataIndex)
|
||||
{
|
||||
return source switch
|
||||
{
|
||||
VMDataSource.Literal => dataIndex,
|
||||
VMDataSource.Temps => Entity.Temps[dataIndex],
|
||||
VMDataSource.Params => StackFrame.Arguments[dataIndex],
|
||||
VMDataSource.StackObjectID => StackFrame.StackObjectID,
|
||||
VMDataSource.TempByTempIndex => Entity.Temps[Entity.Temps[dataIndex]],
|
||||
VMDataSource.StackObjectsTemp => StackObjectEntity.Temps[dataIndex],
|
||||
VMDataSource.Local => StackFrame.Locals[dataIndex],
|
||||
VMDataSource.StackObjectsDefinition => (short)StackObjectEntity.ObjectDefinition.Fields[dataIndex],
|
||||
_ => throw new ArgumentOutOfRangeException("SimAntics data source out of range!")
|
||||
};
|
||||
}
|
||||
|
||||
public void SetData(VMDataSource source, short dataIndex, short value)
|
||||
{
|
||||
switch(source)
|
||||
{
|
||||
case VMDataSource.Temps:
|
||||
Entity.Temps[dataIndex] = value;
|
||||
return;
|
||||
case VMDataSource.Params:
|
||||
StackFrame.Arguments[dataIndex] = value;
|
||||
return;
|
||||
case VMDataSource.StackObjectID:
|
||||
StackFrame.StackObjectID = value;
|
||||
return;
|
||||
case VMDataSource.TempByTempIndex:
|
||||
Entity.Temps[Entity.Temps[dataIndex]] = value;
|
||||
return;
|
||||
case VMDataSource.StackObjectsTemp:
|
||||
StackObjectEntity.Temps[dataIndex] = value;
|
||||
return;
|
||||
case VMDataSource.Local:
|
||||
StackFrame.Locals[dataIndex] = value;
|
||||
return;
|
||||
}
|
||||
throw new ArgumentOutOfRangeException("SimAntics data source out of range!");
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/VMContext.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/VMContext.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 002b800405232b04eb76e97756962bd1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
13
Assets/Scripts/OpenTS2/SimAntics/VMContinueHandler.cs
Normal file
13
Assets/Scripts/OpenTS2/SimAntics/VMContinueHandler.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenTS2.SimAntics
|
||||
{
|
||||
public abstract class VMContinueHandler
|
||||
{
|
||||
public abstract VMExitCode Tick();
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/VMContinueHandler.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/VMContinueHandler.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 80b0d4061c5924a4f86ed92e1b1d4886
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
81
Assets/Scripts/OpenTS2/SimAntics/VMDataSource.cs
Normal file
81
Assets/Scripts/OpenTS2/SimAntics/VMDataSource.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenTS2.SimAntics
|
||||
{
|
||||
public enum VMDataSource : byte
|
||||
{
|
||||
MyObjectsAttributes,
|
||||
StackObjectsAttributes,
|
||||
MyObjectsSemiAttributes,
|
||||
MyObject,
|
||||
StackObject,
|
||||
StackObjectsSemiAttributes,
|
||||
Globals,
|
||||
Literal,
|
||||
Temps,
|
||||
Params,
|
||||
StackObjectID,
|
||||
// Temp[Temp]
|
||||
TempByTempIndex,
|
||||
CheckTreeAdRange,
|
||||
StackObjectsTemp,
|
||||
MyMotives,
|
||||
StackObjectsMotives,
|
||||
StackObjectsSlot,
|
||||
// Stack obj's motive[temp]
|
||||
StackObjectsMotiveByTemp,
|
||||
MyPersonData,
|
||||
StackObjectsPersonData,
|
||||
MySlot,
|
||||
StackObjectsDefinition,
|
||||
// Stack Objs Attribute[Param]
|
||||
StackObjectsAttributeByParam,
|
||||
// Room[Temp0]
|
||||
RoomInTemp0,
|
||||
NeighborInStackObject,
|
||||
Local,
|
||||
Constant,
|
||||
Unused,
|
||||
CheckTreeAdPersonalityVar,
|
||||
CheckTreeAdMin,
|
||||
// My Person Data[Temp]
|
||||
MyPersonDataByTemp,
|
||||
// Stack Obj's person data [Temp]
|
||||
StackObjectsPersonDataByTemp,
|
||||
NeighborsPersonData,
|
||||
// Job data [temp0,1]
|
||||
JobDataByTemp0And1,
|
||||
NeighborhoodDataReadOnly,
|
||||
StackObjectsFunction,
|
||||
MyTypeAttribute,
|
||||
StackObjectsTypeAttribute,
|
||||
NeighborsDefinition,
|
||||
MyTempToken,
|
||||
StackObjectsTempToken,
|
||||
// My object array [array] Iterator Index
|
||||
MyObjectsArrayByArrayIteratorIndex,
|
||||
// Stack Object's object array [array] iterator Index
|
||||
StackObjectsArrayByArrayIteratorIndex,
|
||||
// My object array [array] iterator data
|
||||
MyObjectsArrayByArrayIteratorData,
|
||||
// Stack Object's object array [array] iterator Data
|
||||
StackObjectsArrayByArrayIteratorData,
|
||||
// My object array [array] element at Temp0
|
||||
MyObjectsArrayByArrayElementAtTemp0,
|
||||
// Stack Object's object array [array] element at Temp0
|
||||
StackObjectsArrayByArrayElementAtTemp0,
|
||||
// Const[Temp]
|
||||
ConstByTemp,
|
||||
// My slot[Temp]
|
||||
MySlotByTemp,
|
||||
// Stack Object's slot[Temp]
|
||||
StackObjectsSlotByTemp,
|
||||
// Stack Object's semi attr[Param]
|
||||
StackObjectsSemiAttributeByParam,
|
||||
StackObjectsMasterDefinition
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/VMDataSource.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/VMDataSource.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b936f65f70e77874f8c424e5363af9f6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
60
Assets/Scripts/OpenTS2/SimAntics/VMEntity.cs
Normal file
60
Assets/Scripts/OpenTS2/SimAntics/VMEntity.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
using OpenTS2.Content.DBPF;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenTS2.SimAntics
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a process or thread running in the SimAntics virtual machine, with its own stack and temp variables.
|
||||
/// </summary>
|
||||
public class VMEntity
|
||||
{
|
||||
public bool PendingDeletion = false;
|
||||
public short ID = 1;
|
||||
public short[] Temps = new short[20];
|
||||
public VMStack Stack;
|
||||
public VM VM;
|
||||
public ObjectDefinitionAsset ObjectDefinition;
|
||||
public uint PrivateGroupID => ObjectDefinition.GlobalTGI.GroupID;
|
||||
public uint SemiGlobalGroupID
|
||||
{
|
||||
get
|
||||
{
|
||||
var semiGlobal = ObjectDefinition.SemiGlobal;
|
||||
if (semiGlobal == null)
|
||||
return 0;
|
||||
return semiGlobal.SemiGlobalGroupID;
|
||||
}
|
||||
}
|
||||
|
||||
protected VMEntity()
|
||||
{
|
||||
Stack = new VMStack(this);
|
||||
}
|
||||
|
||||
public VMEntity(ObjectDefinitionAsset objectDefinition) : this()
|
||||
{
|
||||
ObjectDefinition = objectDefinition;
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
Stack.Tick();
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
if (VM.Scheduler.RunningTick)
|
||||
{
|
||||
if (PendingDeletion)
|
||||
return;
|
||||
VM.Scheduler.ScheduleDeletion(this);
|
||||
return;
|
||||
}
|
||||
VM.RemoveEntity(ID);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/VMEntity.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/VMEntity.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 17d0bfd1bb2c4ab48a5e03c6c55d4c3d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
9
Assets/Scripts/OpenTS2/SimAntics/VMExitCode.cs
Normal file
9
Assets/Scripts/OpenTS2/SimAntics/VMExitCode.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace OpenTS2.SimAntics
|
||||
{
|
||||
public enum VMExitCode : byte
|
||||
{
|
||||
True,
|
||||
False,
|
||||
Continue
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/VMExitCode.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/VMExitCode.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3e8ea9071ef11a043b50e2f12f622914
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
13
Assets/Scripts/OpenTS2/SimAntics/VMPrimitive.cs
Normal file
13
Assets/Scripts/OpenTS2/SimAntics/VMPrimitive.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenTS2.SimAntics
|
||||
{
|
||||
public abstract class VMPrimitive
|
||||
{
|
||||
public abstract VMReturnValue Execute(VMContext ctx);
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/VMPrimitive.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/VMPrimitive.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 636b30d1f9f76fa4b8ff19785fe663f8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
51
Assets/Scripts/OpenTS2/SimAntics/VMReturnValue.cs
Normal file
51
Assets/Scripts/OpenTS2/SimAntics/VMReturnValue.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenTS2.SimAntics
|
||||
{
|
||||
public struct VMReturnValue
|
||||
{
|
||||
public VMExitCode Code;
|
||||
public VMContinueHandler ContinueHandler;
|
||||
public static VMReturnValue ReturnTrue = new VMReturnValue(VMExitCode.True);
|
||||
public static VMReturnValue ReturnFalse = new VMReturnValue(VMExitCode.False);
|
||||
public static VMReturnValue ReturnTrueNextTick = new VMReturnValue(new YieldOneTickContinueHandler(VMExitCode.True));
|
||||
public static VMReturnValue ReturnFalseNextTick = new VMReturnValue(new YieldOneTickContinueHandler(VMExitCode.False));
|
||||
|
||||
public VMReturnValue(VMExitCode exitCode)
|
||||
{
|
||||
Code = exitCode;
|
||||
ContinueHandler = null;
|
||||
}
|
||||
|
||||
public VMReturnValue(VMContinueHandler continueHandler)
|
||||
{
|
||||
Code = VMExitCode.Continue;
|
||||
ContinueHandler = continueHandler;
|
||||
}
|
||||
|
||||
public class YieldOneTickContinueHandler : VMContinueHandler
|
||||
{
|
||||
bool _ticked = false;
|
||||
VMExitCode _exitCode;
|
||||
|
||||
public YieldOneTickContinueHandler(VMExitCode exitCode)
|
||||
{
|
||||
_exitCode = exitCode;
|
||||
}
|
||||
|
||||
public override VMExitCode Tick()
|
||||
{
|
||||
if (!_ticked)
|
||||
{
|
||||
_ticked = true;
|
||||
return VMExitCode.Continue;
|
||||
}
|
||||
return _exitCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/VMReturnValue.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/VMReturnValue.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f827d2fe75bd932488d54abccaf9fd28
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
112
Assets/Scripts/OpenTS2/SimAntics/VMScheduler.cs
Normal file
112
Assets/Scripts/OpenTS2/SimAntics/VMScheduler.cs
Normal file
|
@ -0,0 +1,112 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenTS2.SimAntics
|
||||
{
|
||||
public class VMScheduler
|
||||
{
|
||||
public bool RunningTick => _runningTick;
|
||||
private bool _runningTick = false;
|
||||
|
||||
private VMScheduledEvents _preTickEvents = new VMScheduledEvents();
|
||||
private VMScheduledEvents _postTickEvents = new VMScheduledEvents();
|
||||
|
||||
/// <summary>
|
||||
/// Runs a function immediately if the VM is not currently in a tick, otherwise schedules it to run at the end of the current tick.
|
||||
/// </summary>
|
||||
public void ScheduleWhenPossible(Action func)
|
||||
{
|
||||
if (!RunningTick)
|
||||
{
|
||||
func.Invoke();
|
||||
return;
|
||||
}
|
||||
ScheduleOnEndTick(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schedules a function to run once the VM begins a tick.
|
||||
/// </summary>
|
||||
public void ScheduleOnBeginTick(Action func, uint targetTick = 0)
|
||||
{
|
||||
_preTickEvents.AddEvent(new VMEvent(func, targetTick));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schedules a function to run once the VM is done with a tick.
|
||||
/// </summary>
|
||||
public void ScheduleOnEndTick(Action func, uint targetTick = 0)
|
||||
{
|
||||
_postTickEvents.AddEvent(new VMEvent(func, targetTick));
|
||||
}
|
||||
|
||||
// TODO - Right now the way this works is that once an entity has been notified out of idle/interrupted, the first idle that gets executed on the next tick (or that continues to run if there is one already running) won't actually sleep. If no idles are ran then the interrupt is just discarded. Verify this is okay!
|
||||
public void ScheduleInterrupt(VMStack stack)
|
||||
{
|
||||
ScheduleWhenPossible(() =>
|
||||
{
|
||||
stack.Interrupt = true;
|
||||
});
|
||||
}
|
||||
|
||||
public void ScheduleDeletion(VMEntity entity)
|
||||
{
|
||||
ScheduleWhenPossible(() =>
|
||||
{
|
||||
entity.Delete();
|
||||
});
|
||||
entity.PendingDeletion = true;
|
||||
}
|
||||
|
||||
public void OnBeginTick(VM vm)
|
||||
{
|
||||
_preTickEvents.Run(vm);
|
||||
_runningTick = true;
|
||||
}
|
||||
|
||||
public void OnEndTick(VM vm)
|
||||
{
|
||||
_runningTick = false;
|
||||
_postTickEvents.Run(vm);
|
||||
}
|
||||
|
||||
public class VMEvent
|
||||
{
|
||||
public Action Event;
|
||||
public uint TargetTick = 0;
|
||||
|
||||
public VMEvent(Action func, uint targetTick = 0)
|
||||
{
|
||||
Event = func;
|
||||
TargetTick = targetTick;
|
||||
}
|
||||
}
|
||||
|
||||
public class VMScheduledEvents
|
||||
{
|
||||
private List<VMEvent> _events = new List<VMEvent>();
|
||||
public void Run(VM vm)
|
||||
{
|
||||
var newEvList = new List<VMEvent>();
|
||||
foreach(var ev in _events)
|
||||
{
|
||||
if (vm.CurrentTick >= ev.TargetTick)
|
||||
{
|
||||
ev.Event?.Invoke();
|
||||
}
|
||||
else
|
||||
newEvList.Add(ev);
|
||||
}
|
||||
_events = newEvList;
|
||||
}
|
||||
|
||||
public void AddEvent(VMEvent ev)
|
||||
{
|
||||
_events.Add(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/VMScheduler.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/VMScheduler.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 86354629246dece4fac8ccc5660ae5f3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
64
Assets/Scripts/OpenTS2/SimAntics/VMStack.cs
Normal file
64
Assets/Scripts/OpenTS2/SimAntics/VMStack.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenTS2.SimAntics
|
||||
{
|
||||
/// <summary>
|
||||
/// Stack of scripts to run on a SimAntics entity/thread.
|
||||
/// </summary>
|
||||
public class VMStack
|
||||
{
|
||||
public bool Interrupt = false;
|
||||
// For check trees and other things that should execute and return immediately we should set this to false.
|
||||
public bool CanYield = true;
|
||||
public VMEntity Entity;
|
||||
public Stack<VMStackFrame> Frames = new Stack<VMStackFrame>();
|
||||
public VMStack(VMEntity entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
public VMStackFrame GetCurrentFrame()
|
||||
{
|
||||
if (Frames.Count == 0)
|
||||
return null;
|
||||
return Frames.Peek();
|
||||
}
|
||||
|
||||
VMExitCode TickInternal()
|
||||
{
|
||||
var currentFrame = GetCurrentFrame();
|
||||
if (currentFrame == null)
|
||||
return VMExitCode.False;
|
||||
var returnValue = currentFrame.Tick();
|
||||
if (returnValue == VMExitCode.Continue && !CanYield)
|
||||
throw new Exception("Attempted to yield in a non-yielding VMStack.");
|
||||
while (returnValue != VMExitCode.Continue)
|
||||
{
|
||||
Frames.Pop();
|
||||
currentFrame = GetCurrentFrame();
|
||||
if (currentFrame == null)
|
||||
return returnValue;
|
||||
var currentNode = currentFrame.GetCurrentNode();
|
||||
|
||||
if (returnValue == VMExitCode.True)
|
||||
currentFrame.CurrentNode = currentNode.TrueTarget;
|
||||
else
|
||||
currentFrame.CurrentNode = currentNode.FalseTarget;
|
||||
|
||||
returnValue = currentFrame.Tick();
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public VMExitCode Tick()
|
||||
{
|
||||
var returnValue = TickInternal();
|
||||
Interrupt = false;
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/VMStack.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/VMStack.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d66f3e25b8c58944789a10675ddaa524
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
230
Assets/Scripts/OpenTS2/SimAntics/VMStackFrame.cs
Normal file
230
Assets/Scripts/OpenTS2/SimAntics/VMStackFrame.cs
Normal file
|
@ -0,0 +1,230 @@
|
|||
using OpenTS2.Files.Formats.DBPF;
|
||||
using OpenTS2.SimAntics.Primitives;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenTS2.SimAntics
|
||||
{
|
||||
/// <summary>
|
||||
/// A frame in a SimAntics stack.
|
||||
/// </summary>
|
||||
public class VMStackFrame
|
||||
{
|
||||
public VMStack Stack;
|
||||
public BHAVAsset BHAV;
|
||||
public short StackObjectID = 0;
|
||||
public int CurrentNode = 0;
|
||||
/// <summary>
|
||||
/// Current blocking behavior. As long as this variable's Tick() returns Continue this thread won't move.
|
||||
/// </summary>
|
||||
public VMContinueHandler CurrentContinueHandler = null;
|
||||
public short[] Locals;
|
||||
public short[] Arguments;
|
||||
|
||||
public VMStackFrame(BHAVAsset bhav, VMStack stack)
|
||||
{
|
||||
BHAV = bhav;
|
||||
Stack = stack;
|
||||
Locals = new short[BHAV.LocalCount];
|
||||
Arguments = new short[BHAV.ArgumentCount];
|
||||
}
|
||||
|
||||
public VMExitCode Tick()
|
||||
{
|
||||
if (CurrentContinueHandler != null)
|
||||
{
|
||||
var returnCode = CurrentContinueHandler.Tick();
|
||||
if (returnCode == VMExitCode.Continue)
|
||||
return returnCode;
|
||||
else
|
||||
{
|
||||
return AdvanceNodeAndRunTick(returnCode);
|
||||
}
|
||||
}
|
||||
return RunCurrentTick();
|
||||
}
|
||||
|
||||
VMExitCode AdvanceNodeAndRunTick(VMExitCode exitCode)
|
||||
{
|
||||
var currentNode = GetCurrentNode();
|
||||
ushort returnTarget;
|
||||
if (exitCode == VMExitCode.True)
|
||||
returnTarget = currentNode.TrueTarget;
|
||||
else
|
||||
returnTarget = currentNode.FalseTarget;
|
||||
switch (returnTarget)
|
||||
{
|
||||
case BHAVAsset.Node.FalseReturnValue:
|
||||
return VMExitCode.False;
|
||||
case BHAVAsset.Node.TrueReturnValue:
|
||||
return VMExitCode.True;
|
||||
case BHAVAsset.Node.ErrorReturnValue:
|
||||
throw new Exception("Jumped to Error.");
|
||||
default:
|
||||
SetCurrentNode(returnTarget);
|
||||
return RunCurrentTick();
|
||||
}
|
||||
}
|
||||
|
||||
VMExitCode RunCurrentTick()
|
||||
{
|
||||
var currentNode = GetCurrentNode();
|
||||
if (currentNode != null)
|
||||
{
|
||||
var context = new VMContext
|
||||
{
|
||||
StackFrame = this,
|
||||
Node = currentNode
|
||||
};
|
||||
var opcode = currentNode.OpCode;
|
||||
var prim = VMPrimitiveRegistry.GetPrimitive(opcode);
|
||||
if (prim != null)
|
||||
{
|
||||
var primReturn = prim.Execute(context);
|
||||
|
||||
if (primReturn.Code == VMExitCode.Continue)
|
||||
primReturn.Code = primReturn.ContinueHandler.Tick();
|
||||
|
||||
if (primReturn.Code == VMExitCode.Continue)
|
||||
{
|
||||
CurrentContinueHandler = primReturn.ContinueHandler;
|
||||
return primReturn.Code;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentContinueHandler = null;
|
||||
return AdvanceNodeAndRunTick(primReturn.Code);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var newStackFrame = CreateStackFrameForNode(context);
|
||||
if (newStackFrame != null)
|
||||
{
|
||||
Stack.Frames.Push(newStackFrame);
|
||||
return newStackFrame.Tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
return VMExitCode.False;
|
||||
}
|
||||
|
||||
enum GoSubFormat
|
||||
{
|
||||
PassTemps,
|
||||
TS1,
|
||||
TS2,
|
||||
CallerParams
|
||||
}
|
||||
|
||||
VMStackFrame CreateStackFrameForNode(VMContext ctx)
|
||||
{
|
||||
var bhav = GetBHAVForOpCode(ctx.Node.OpCode);
|
||||
|
||||
if (bhav == null)
|
||||
return null;
|
||||
|
||||
var newStackFrame = new VMStackFrame(bhav, Stack);
|
||||
newStackFrame.StackObjectID = ctx.StackFrame.StackObjectID;
|
||||
|
||||
GoSubFormat format = GoSubFormat.PassTemps;
|
||||
|
||||
if (ctx.Node.GetOperand(12) > 0)
|
||||
{
|
||||
format = GoSubFormat.TS2;
|
||||
if (ctx.Node.GetOperand(12) == 2 && ctx.Node.Version > 0)
|
||||
format = GoSubFormat.CallerParams;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
if (ctx.Node.Operands[i] != 0xFF)
|
||||
{
|
||||
format = GoSubFormat.TS1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var argAmount = 0;
|
||||
|
||||
switch(format)
|
||||
{
|
||||
case GoSubFormat.PassTemps:
|
||||
argAmount = Math.Min(newStackFrame.Arguments.Length, Stack.Entity.Temps.Length);
|
||||
for (var i=0;i<argAmount;i++)
|
||||
{
|
||||
newStackFrame.TrySetArgument(i, Stack.Entity.Temps[i]);
|
||||
}
|
||||
break;
|
||||
case GoSubFormat.TS1:
|
||||
argAmount = Math.Min(newStackFrame.Arguments.Length, 8);
|
||||
for(var i=0;i<argAmount;i++)
|
||||
{
|
||||
newStackFrame.TrySetArgument(i, ctx.Node.GetInt16Operand(i*2));
|
||||
}
|
||||
break;
|
||||
case GoSubFormat.TS2:
|
||||
argAmount = Math.Min(newStackFrame.Arguments.Length, 4);
|
||||
for(var i=0;i<argAmount;i++)
|
||||
{
|
||||
var dataSourceIndex = i * 3;
|
||||
var dataValueIndex = dataSourceIndex + 1;
|
||||
|
||||
var dataSource = (VMDataSource)ctx.Node.GetOperand(dataSourceIndex);
|
||||
var dataValue = ctx.Node.GetInt16Operand(dataValueIndex);
|
||||
|
||||
var data = ctx.GetData(dataSource, dataValue);
|
||||
|
||||
newStackFrame.TrySetArgument(i, data);
|
||||
}
|
||||
break;
|
||||
case GoSubFormat.CallerParams:
|
||||
argAmount = Math.Min(newStackFrame.Arguments.Length, ctx.StackFrame.Arguments.Length);
|
||||
for (var i=0;i<argAmount;i++)
|
||||
{
|
||||
newStackFrame.TrySetArgument(i, ctx.StackFrame.Arguments[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return newStackFrame;
|
||||
}
|
||||
|
||||
void TrySetArgument(int index, short value)
|
||||
{
|
||||
if (index < 0)
|
||||
return;
|
||||
if (Arguments.Length <= index)
|
||||
return;
|
||||
Arguments[index] = value;
|
||||
}
|
||||
|
||||
BHAVAsset GetBHAVForOpCode(ushort opCode)
|
||||
{
|
||||
// 0x0XXX is global scope, 0x1XXX is private scope and 0x2XXX is semiglobal scope.
|
||||
var groupid = Stack.Entity.SemiGlobalGroupID;
|
||||
|
||||
if (opCode < 0x1000)
|
||||
groupid = GroupIDs.Global;
|
||||
else if (opCode < 0x2000)
|
||||
groupid = Stack.Entity.PrivateGroupID;
|
||||
|
||||
return VM.GetBHAV(opCode, groupid);
|
||||
}
|
||||
|
||||
public void SetCurrentNode(int nodeIndex)
|
||||
{
|
||||
CurrentNode = nodeIndex;
|
||||
CurrentContinueHandler = null;
|
||||
}
|
||||
public BHAVAsset.Node GetCurrentNode()
|
||||
{
|
||||
return BHAV.Nodes[CurrentNode];
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/OpenTS2/SimAntics/VMStackFrame.cs.meta
generated
Normal file
11
Assets/Scripts/OpenTS2/SimAntics/VMStackFrame.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6f0131025319ce348a70d3ee3ee46cb7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Tests/OpenTS2/SimAntics.meta
generated
Normal file
8
Assets/Tests/OpenTS2/SimAntics.meta
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9fc25c06c3b7e794c9c8694d4dc67ff3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
76
Assets/Tests/OpenTS2/SimAntics/SimAnticsTest.cs
Normal file
76
Assets/Tests/OpenTS2/SimAntics/SimAnticsTest.cs
Normal file
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using OpenTS2.Content;
|
||||
using OpenTS2.SimAntics;
|
||||
using OpenTS2.Common;
|
||||
using OpenTS2.Files.Formats.DBPF;
|
||||
using OpenTS2.SimAntics.Primitives;
|
||||
using OpenTS2.Content.DBPF;
|
||||
|
||||
public class SimAnticsTest
|
||||
{
|
||||
private uint _groupID;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
TestMain.Initialize();
|
||||
_groupID = ContentProvider.Get().AddPackage("TestAssets/SimAntics/bhav.package").GroupID;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLoadsBHAV()
|
||||
{
|
||||
var bhav = VM.GetBHAV(0x1001, _groupID);
|
||||
|
||||
Assert.That(bhav.FileName, Is.EqualTo("OpenTS2 BHAV Test"));
|
||||
Assert.That(bhav.ArgumentCount, Is.EqualTo(1));
|
||||
Assert.That(bhav.LocalCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRunBHAV()
|
||||
{
|
||||
// VM Entities need to be attached to an OBJD to be aware of private/semiglobal scope.
|
||||
var testObjectDefinition = new ObjectDefinitionAsset();
|
||||
testObjectDefinition.TGI = new ResourceKey(1, _groupID, TypeIDs.OBJD);
|
||||
|
||||
var bhav = VM.GetBHAV(0x1001, _groupID);
|
||||
|
||||
var vm = new VM();
|
||||
var entity = new VMEntity(testObjectDefinition);
|
||||
vm.AddEntity(entity);
|
||||
|
||||
var stackFrame = new VMStackFrame(bhav, entity.Stack);
|
||||
entity.Stack.Frames.Push(stackFrame);
|
||||
|
||||
// Test BHAV:
|
||||
// Multiplies Param0 by 2, stores it in Temp0
|
||||
// Sleeps for 1 Tick
|
||||
// Sets Temp0 to 1200
|
||||
// Sleeps for 20000 Ticks
|
||||
// Sets Temp0 to 0
|
||||
stackFrame.Arguments[0] = 10;
|
||||
|
||||
vm.Tick();
|
||||
Assert.That(entity.Temps[0], Is.EqualTo(20));
|
||||
vm.Tick();
|
||||
Assert.That(entity.Temps[0], Is.EqualTo(1200));
|
||||
// Interrupt idle here, so that it doesn't sleep for 20000 ticks.
|
||||
vm.Scheduler.ScheduleInterrupt(entity.Stack);
|
||||
vm.Tick();
|
||||
Assert.That(entity.Temps[0], Is.EqualTo(0));
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPrimitiveRegistry()
|
||||
{
|
||||
var vmExpressionPrim = VMPrimitiveRegistry.GetPrimitive(0x2);
|
||||
Assert.That(vmExpressionPrim, Is.TypeOf(typeof(VMExpression)));
|
||||
}
|
||||
}
|
11
Assets/Tests/OpenTS2/SimAntics/SimAnticsTest.cs.meta
generated
Normal file
11
Assets/Tests/OpenTS2/SimAntics/SimAnticsTest.cs.meta
generated
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dff9cc352fdef4a4fa14c911aa68b29a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -3,6 +3,7 @@ using OpenTS2.Client;
|
|||
using OpenTS2.Content;
|
||||
using OpenTS2.Files;
|
||||
using OpenTS2.Files.Formats.DBPF;
|
||||
using OpenTS2.SimAntics.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Main initialization class for OpenTS2 unit testing.
|
||||
|
@ -28,6 +29,7 @@ public static class TestMain
|
|||
Filesystem.Initialize(new TestPathProvider(), epManager);
|
||||
CodecAttribute.Initialize();
|
||||
AssemblyHelper.InitializeLoadedAssemblies();
|
||||
VMPrimitiveRegistry.Initialize();
|
||||
s_initialized = true;
|
||||
}
|
||||
|
||||
|
|
BIN
TestAssets/SimAntics/bhav.package
Normal file
BIN
TestAssets/SimAntics/bhav.package
Normal file
Binary file not shown.
Loading…
Reference in a new issue