Ported over more code from FSO

- Renamed VMInstruction.cs to VMThread.cs
- Created README.md
- Added Github CI
This commit is contained in:
Anthony Foxclaw 2020-04-15 21:17:50 -04:00
parent 5620d11d95
commit 6c0d63d4bb
25 changed files with 1180 additions and 50 deletions

View file

@ -8,9 +8,13 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.2.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"><IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="1.2.1"><IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>

View file

@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using Xunit;
namespace SimAntics.Tests
@ -8,6 +9,10 @@ namespace SimAntics.Tests
[Fact]
public void Test1()
{
var clock = new VMClock();
Console.WriteLine(clock.Ticks);
clock.Tick();
Console.WriteLine(clock.Ticks);
}
}
}

View file

@ -0,0 +1,12 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
namespace SimAntics.Tests
{
public class VMTest
{
public VMTest()
{
}
}
}

View file

@ -1,5 +1,6 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimAntics", "SimAntics\SimAntics.csproj", "{6B758449-9D5A-456A-A733-31B7841E538A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimAntics.Tests", "SimAntics.Tests\SimAntics.Tests.csproj", "{4B7461A4-982A-4D89-92E3-E4D4A3EC85FB}"
@ -19,4 +20,9 @@ Global
{4B7461A4-982A-4D89-92E3-E4D4A3EC85FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B7461A4-982A-4D89-92E3-E4D4A3EC85FB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0
$0.StandardHeader = $1
$1.Text = @This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.\nIf a copy of the MPL was not distributed with this file, You can obtain one at\nhttp://mozilla.org/MPL/2.0/.
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,18 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
namespace SimAntics
{
public enum Direction
{
NORTH,
NORTHEAST,
EAST,
SOUTHEAST,
SOUTH,
SOUTHWEST,
WEST,
NORTHWEST
}
}

View file

@ -1,19 +0,0 @@
namespace SimAntics.Engine
{
/// <summary>
/// Compatibility class
/// </summary>
public class VMThread : VMInstruction { }
/// <summary>
/// Handles instruction sets
/// </summary>
public class VMInstruction
{
public static int MAX_USER_ACTIONS = 20;
public VMContext Context;
public bool IsCheck;
}
}

View file

@ -0,0 +1,12 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
namespace SimAntics.Engine
{
public class VMMemory
{
public VMMemory()
{
}
}
}

View file

@ -0,0 +1,32 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
using System.Collections.Generic;
using SimAntics.Engine.Entities;
namespace SimAntics.Engine
{
public class VMScheduler
{
VM VM { get; set; }
Dictionary<uint, List<VMEntity>> _tickScheduler = new Dictionary<uint, List<VMEntity>>();
List<VMEntity> _tickThisFrame;
public HashSet<VMEntity> PendingDeletion { get; set; } = new HashSet<VMEntity>();
public uint CurrentTickID { get; set; }
public short CurrentObjectID { get; set; }
public bool RunningNow { get; set; }
public VMScheduler(VM vm)
{
VM = vm;
}
public void ScheduleTickIn(VMEntity _ent, uint delay)
{
}
}
}

View file

@ -0,0 +1,178 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
using SimAntics.Engine.Entities;
using SimAntics.Marshals;
namespace SimAntics.Engine
{
/// <summary>
/// Holds information about the execution of a routine
/// </summary>
public class VMStackFrame
{
public VMStackFrame() { }
/** Thread executing this routine **/
public VMThread Thread;
/** Routine that this context relates to **/
// public VMRoutine Routine;
/** Current instruction **/
public ushort InstructionPointer;
/** The object who executed this behavior **/
public VMEntity Caller;
/** The object the code is running on **/
public VMEntity Callee;
/** An object selected by the code to perform operations on. **/
public VMEntity StackObject
{
get { return _StackObject; }
set
{
_StackObject = value;
_StackObjectID = value?.ObjectID ?? 0;
}
}
public short StackObjectID
{
get { return _StackObjectID; }
set
{
_StackObjectID = value;
_StackObject = VM.GetObjectById(value);
}
}
VMEntity _StackObject;
public short _StackObjectID;
/** If true, this stack frame is not a subroutine. Return with a continue. **/
public bool DiscardResult;
/** Indicates that the current stack frame is part of an action tree.
** Set by "idle for input, allow push", when an interaction is selected.
** Used to stop recursive interactions, is only false when within "main".
**/
public bool ActionTree;
/** Used to get strings and other resources (for primitives) from the code owner, as it may not be the callee but instead a semiglobal or global. **/
//public GameIffResource ScopeResource
//{
// get
// {
// return CodeOwner.Resource;
// }
//}
//public GameObject CodeOwner;
/**
* Routine locals
*/
public short[] Locals;
/**
* Arguments
*/
public short[] Args;
//public GameObjectResource CallerPrivate
//{
// get
// {
// return Caller.Object.Resource;
// }
//}
//public GameObjectResource CalleePrivate
//{
// get
// {
// return Callee.Object.Resource;
// }
//}
//public GameObjectResource StackObjPrivate
//{
// get
// {
// return StackObject.Object.Resource;
// }
//}
//public GameGlobal Global
//{
// get
// {
// return Thread.Context.Globals;
// }
//}
public VM VM
{
get
{
return Thread.Context.VM;
}
}
/** Utilities **/
//public VMInstruction GetCurrentInstruction()
//{
// return Routine.Instructions[InstructionPointer];
//}
//public T GetCurrentOperand<T>()
//{
// return (T)GetCurrentInstruction().Operand;
//}
#region VM Marshalling Functions
//public virtual VMStackFrameMarshal Save()
//{
// return new VMStackFrameMarshal
// {
// RoutineID = Routine?.ID ?? 0,
// InstructionPointer = InstructionPointer,
// Caller = (Caller == null) ? (short)0 : Caller.ObjectID,
// Callee = (Callee == null) ? (short)0 : Callee.ObjectID,
// StackObject = StackObjectID,
// CodeOwnerGUID = CodeOwner.OBJ.GUID,
// Locals = (short[])Locals?.Clone(),
// Args = (short[])Args?.Clone(),
// DiscardResult = DiscardResult,
// ActionTree = ActionTree,
// };
//}
public virtual void Load(VMStackFrameMarshal input, VMContext context)
{
// CodeOwner = GameContent.Get.WorldObjects.Get(input.CodeOwnerGUID);
// Routine = null;
//if (input.RoutineID >= 8192) Routine = (VMRoutine)ScopeResource.SemiGlobal.GetRoutine(input.RoutineID);
//else if (input.RoutineID >= 4096) Routine = (VMRoutine)ScopeResource.GetRoutine(input.RoutineID);
//else Routine = (VMRoutine)Global.Resource.GetRoutine(input.RoutineID);
InstructionPointer = input.InstructionPointer;
Caller = context.VM.GetObjectById(input.Caller);
Callee = context.VM.GetObjectById(input.Callee);
StackObjectID = input.StackObject;
Locals = input.Locals;
Args = input.Args;
DiscardResult = input.DiscardResult;
ActionTree = input.ActionTree;
}
public VMStackFrame(VMStackFrameMarshal input, VMContext context, VMThread thread)
{
Thread = thread;
// Load(input, context);
}
#endregion
}
}

View file

@ -0,0 +1,56 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
#if !Server
#define IDE_COMPAT
#endif
using System.Collections.Generic;
using SimAntics.Engine.Entities;
namespace SimAntics.Engine
{
/// <summary>
/// Compatibility class
/// </summary>
public class VMThread : VMInstruction { }
/// <summary>
/// Handles instruction sets
/// </summary>
public class VMInstruction
{
public static int MAX_USER_ACTIONS = 20;
public VMContext Context;
//check tree only vars
public bool IsCheck;
VMEntity Entity;
public List<VMStackFrame> Stack;
bool ContinueExecution;
public string ThreadBreakString;
public int BreakFrame; //frame the last breakpoint was performed on
public bool RoutineDirty;
public bool Interrupt;
ushort ActionUID;
// Exception handling variables
// Don't need to be serialized.
public int DialogCooldown = 0;
// the number of ticks that have executed so far this frame. If this exceeds the allowed max,
// the thread resets, and a SimAntics Error pops up.
public int TicksThisFrame = 0;
// the maximum number of primitives a thread can execute in one frame. Tweak appropriately.
// variables for internal scheduler
public uint ScheduleIdleStart; // keep track of tick when we started idling for an object. must be synced!
public uint ScheduleIdleEnd;
}
}

View file

@ -0,0 +1,158 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
using System.Collections.Generic;
namespace SimAntics.Engine.Entities
{
public class VMEntityRTTI
{
public string[] AttributeLabels;
}
public abstract class VMEntity
{
public static bool UseWorld = true;
public VMEntityRTTI RTTI;
public bool GhostImage;
public short ObjectID;
public uint PersistID;
public short[] ObjectData;
public LinkedList<short> MyList = new LinkedList<short>();
// public List<VMSoundEntry> SoundThreads;
// public VMRuntimeHeadline Headline;
/// <summary>
/// IS NOT serialized, but rather regenerated on deserialize.
/// </summary>
// public VMHeadlineRenderer HeadlineRenderer;
// public GameObject Object;
public VMThread Thread;
// public VMMultitileGroup MultitileGroup;
public short MainParam; //parameters passed to main on creation.
public short MainStackOBJ;
public VMEntity[] Contained = new VMEntity[0];
public VMEntity Container;
public short ContainerSlot;
/// <summary>
/// set when the entity is removed, threads owned by this object or with this object as callee will be cancelled/have their stack emptied
/// </summary>
public bool Dead;
/** Relationship variables **/
public Dictionary<ushort, List<short>> MeToObject;
public Dictionary<uint, List<short>> MeToPersist;
//a runtime cache for objects that have relationships to us. Used to get a quick reference to objects
//that may need to delete a relationship to us.
//note this can point to false positives, but the worst case is a slow deletion if somehow every object is added.
public HashSet<ushort> MayHaveRelToMe = new HashSet<ushort>();
//signals which relationships have changed since the last time this was reset
//used to partial update relationships when doing an avatar save to db
public HashSet<uint> ChangedRels = new HashSet<uint>();
public ulong DynamicSpriteFlags; /** Used to show/hide dynamic sprites **/
public ulong DynamicSpriteFlags2;
//public VMObstacle Footprint;
//LotTilePos _Position = new LotTilePos(LotTilePos.OUT_OF_WORLD);
//public EntityComponent WorldUI;
public uint TimestampLockoutCount = 0;
//public Color LightColor = Color.White;
//inferred properties (from object resource)
//public GameGlobalResource SemiGlobal;
//public TTAB TreeTable;
//public TTAs TreeTableStrings;
//public Dictionary<string, VMTreeByNameTableEntry> TreeByName;
//public SLOT Slots;
//public OBJD MasterDefinition; //if this object is multitile, its master definition will be stored here.
//public OBJfFunctionEntry[] EntryPoints; /** Entry points for specific events, eg. init, main, clean... **/
//public virtual bool MovesOften
//{
// get
// {
// if (Container != null) return true;
// if (Slots == null) return false;
// if (!Slots.Slots.ContainsKey(3)) return false;
// var slots = Slots.Slots[3];
// return (slots.Count > 7);
// }
//}
//public string Name
//{
// get
// {
// if (MultitileGroup.Name != "") return MultitileGroup.Name;
// else return this.ToString();
// }
// set
// {
// MultitileGroup.Name = value;
// }
//}
//bool DynamicMultitile
//{
// get
// {
// return EntryPoints[8].ActionFunction >= 256;
// }
//}
//public override string ToString()
//{
// if (MultitileGroup.Name != "") return MultitileGroup.Name;
// var strings = Object.Resource.Get<CTSS>(Object.OBJ.CatalogStringsID);
// if (strings != null)
// {
// return strings.GetString(0);
// }
// var label = Object.OBJ.ChunkLabel;
// if (label != null && label.Length > 0)
// {
// return label;
// }
// return Object.OBJ.GUID.ToString("X");
//}
//positioning properties
protected static Direction[] DirectionNotches = new Direction[]
{
Direction.NORTH,
Direction.NORTHEAST,
Direction.EAST,
Direction.SOUTHEAST,
Direction.SOUTH,
Direction.SOUTHWEST,
Direction.WEST,
Direction.NORTHWEST
};
//public LotTilePos Position
//{
// get { return _Position; }
// set
// {
// _Position = value;
// if (UseWorld) WorldUI.Level = Position.Level;
// if (this is VMAvatar) ((VMAvatar)this).VisualPositionStart = null;
// VisualPosition = new Vector3(_Position.x / 16.0f, _Position.y / 16.0f, (_Position.Level - 1) * 2.95f);
// }
// }
// public abstract Vector3 VisualPosition { get; set; }
public abstract Direction Direction { get; set; }
public abstract float RadianDirection { get; set; }
}
}

View file

@ -1,4 +1,8 @@
namespace SimAntics
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
namespace SimAntics
{
public interface IVM
{

View file

@ -0,0 +1,10 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
namespace SimAntics.Interfaces
{
public interface VMIMotiveDecay : VMSerializable
{
// void Tick(VMAvatar avatar, VMContext context);
}
}

View file

@ -0,0 +1,9 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
namespace SimAntics.Interfaces
{
public interface VMSerializable
{
}
}

View file

@ -0,0 +1,13 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
using System;
namespace SimAntics.Marshals
{
public class VMMarshal
{
public VMMarshal()
{
}
}
}

View file

@ -0,0 +1,70 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
using System;
using System.IO;
namespace SimAntics.Marshals
{
public class VMStackFrameMarshal
{
public ushort RoutineID { get; set; }
public ushort InstructionPointer { get; set; }
public short Caller { get; set; }
public short Callee { get; set; }
public short StackObject { get; set; }
public uint CodeOwnerGUID { get; set; }
public short[] Locals { get; set; }
public short[] Args { get; set; }
public bool DiscardResult { get; set; }
public bool ActionTree { get; set; }
public int Version { get; set; }
public VMStackFrameMarshal() { }
public VMStackFrameMarshal(int version) { Version = version; }
public virtual void Deserialize(BinaryReader reader)
{
RoutineID = reader.ReadUInt16();
InstructionPointer = reader.ReadUInt16();
Caller = reader.ReadInt16();
Callee = reader.ReadInt16();
StackObject = reader.ReadInt16();
CodeOwnerGUID = reader.ReadUInt32();
var localN = reader.ReadInt32();
if (localN > -1)
{
Locals = new short[localN];
for (var i = 0; i < localN; i++) Locals[i] = reader.ReadInt16();
}
var argsN = reader.ReadInt32();
if (argsN > -1)
{
Args = new short[argsN];
for (var i = 0; i < argsN; i++) Args[i] = reader.ReadInt16();
}
if (Version > 3) DiscardResult = reader.ReadBoolean();
ActionTree = reader.ReadBoolean();
}
public virtual void SerializeInto(BinaryWriter writer)
{
writer.Write(RoutineID);
writer.Write(InstructionPointer);
writer.Write(Caller);
writer.Write(Callee);
writer.Write(StackObject);
writer.Write(CodeOwnerGUID);
writer.Write((Locals == null) ? -1 : Locals.Length);
//if (Locals != null) writer.Write(VMSerializableUtils.ToByteArray(Locals));
//writer.Write((Args == null) ? -1 : Args.Length);
//if (Args != null) writer.Write(VMSerializableUtils.ToByteArray(Args));
writer.Write(DiscardResult);
writer.Write(ActionTree);
}
}
}

View file

@ -4,4 +4,9 @@
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="Entities\" />
<Folder Include="Interfaces\" />
<Folder Include="Marshals\" />
</ItemGroup>
</Project>

View file

@ -1,4 +1,10 @@
using System;
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
using System;
using System.Collections.Generic;
using SimAntics.Engine;
using SimAntics.Engine.Entities;
namespace SimAntics
{
@ -10,32 +16,129 @@ namespace SimAntics
public bool IsTS1 { get; set; }
public bool Ready { get; set; }
public bool BHAVDirty { get; set; }
public VMContext Context { get; internal set; }
public VMScheduler Scheduler { set; get; }
public delegate void VMRefreshHandler();
public delegate void VMLotSwitchHandler(uint lotId);
public void Init()
Dictionary<short, VMEntity> ObjectsById = new Dictionary<short, VMEntity>();
short ObjectId = 1;
public List<VMEntity> Entities = new List<VMEntity>();
public HashSet<VMEntity> SoundEntities = new HashSet<VMEntity>();
public short[] GlobalState;
int GameTickRate = 60;
int GameTickNum = 0;
public int SpeedMultiplier = 1;
public int LastSpeedMultiplier;
int LastFrameSpeed = 1;
float Fraction;
public VMEntity GlobalBlockingDialog;
/// <summary>
/// Gets an entity from this VM.
/// </summary>
/// <param name="id">The entity's ID.</param>
/// <returns>A VMEntity instance associated with the ID.</returns>
public VMEntity GetObjectById(short id)
{
return ObjectsById.ContainsKey(id) ? ObjectsById[id] : null;
}
/// <summary>
/// Constructs a new Virtual Machine instance.
/// </summary>
/// <param name="context">The VMContext instance to use.</param>
public VM(VMContext context)
{
context.VM = this;
Context = context;
Scheduler = new VMScheduler(this);
}
public VMEntity GetObjectByPersist(uint id)
{
// return Entities.FirstOrDefault(x => x.PersistID == id);
throw new VMSimanticsException();
}
public virtual void Init()
{
// PlatformState = (TS1)?(VMAbstractLotState)new VMTS1LotState():new VMTSOLotState();
GlobalState = new short[38];
GlobalState[20] = 255; //Game Edition. Basically, what "expansion packs" are running. Let's just say all of them.
GlobalState[25] = 4; //as seen in EA-Land edith's simulator globals, this needs to be set for people to do their idle interactions.
GlobalState[17] = 4; //Runtime Code Version, is this in EA-Land.
// if (Driver is VMServerDriver) EODHost = new VMEODHost();
}
public virtual void Reset()
{
throw new NotImplementedException();
}
public virtual void Update()
{
throw new NotImplementedException();
}
public void Reset()
{
throw new NotImplementedException();
}
public void Update()
public virtual void Tick()
{
throw new NotImplementedException();
}
public void Tick()
public virtual void InternalTick(uint tickId)
{
throw new NotImplementedException();
}
public void InternalTick(uint tickId)
/// <summary>
/// Adds an entity to this Virtual Machine.
/// </summary>
/// <param name="entity">The entity to add.</param>
public void AddEntity(VMEntity entity)
{
throw new NotImplementedException();
entity.ObjectID = ObjectId;
ObjectsById.Add(entity.ObjectID, entity);
// AddToObjList(this.Entities, entity);
// if (!entity.GhostImage) Context.ObjectQueries.NewObject(entity);
// ObjectId = NextObjID();
}
public static void AddToObjList(List<VMEntity> list, VMEntity entity)
{
if (list.Count == 0) { list.Add(entity); return; }
int id = entity.ObjectID;
var max = list.Count;
var min = 0;
while (max>min)
{
var mid = (max+min) / 2;
int nid = list[mid].ObjectID;
if (id < nid) max = mid;
else if (id == nid) return; //do not add dupes
else min = mid+1;
}
list.Insert(min, entity);
// list.Insert((list[min].ObjectID>id)?min:((list[max].ObjectID > id)?max:max+1), entity);
}
/// <summary>
/// Removes an entity from this Virtual Machine.
/// </summary>
/// <param name="entity">The entity to remove.</param>
public void RemoveEntity(VMEntity entity)
{
if (Entities.Contains(entity))
{
// Context.ObjectQueries.RemoveObject(entity);
this.Entities.Remove(entity);
ObjectsById.Remove(entity.ObjectID);
// Scheduler.DescheduleTick(entity);
if (entity.ObjectID < ObjectId) ObjectId = entity.ObjectID; //this id is now the smallest free object id.
}
entity.Dead = true;
}
}
}

View file

@ -1,3 +1,6 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
using System;
namespace SimAntics
@ -20,7 +23,7 @@ namespace SimAntics
public int TimeOfDay => (Hours >= 6 && Hours < 18) ? 0 : 1;
public int Seconds => MinuteFractions * 60 / TicksPerMinute;
public DateTime UTCNow => (new DateTime(UTCStart)).AddSeconds(Ticks / 30.0);
public DateTime UTCNow => new DateTime(UTCStart).AddSeconds(Ticks / 30.0);
public VMClock() {}

View file

@ -1,8 +1,11 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
namespace SimAntics
{
public class VMContext
{
public static bool useWorld = true;
public VM Vm;
}
public VM VM { get; set; }
}
}

View file

@ -0,0 +1,98 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
using System;
using System.Collections.Generic;
using System.Text;
using SimAntics.Engine;
namespace SimAntics
{
public class VMSimanticsException : Exception
{
readonly string _message;
VMStackFrame _context;
public VMSimanticsException() { }
public VMSimanticsException(string message, VMStackFrame context) : base(message)
{
_context = context;
_message = message;
}
public override string ToString()
{
var output = new StringBuilder();
output.Append(_message);
output.AppendLine();
output.AppendLine();
output.Append(GetStackTrace());
return output.ToString();
}
public string GetStackTrace()
{
if (_context == null) return "No Stack Info.";
var stack = _context.Thread.Stack;
return GetStackTrace(stack);
}
public static string GetStackTrace(List<VMStackFrame> stack)
{
var output = new StringBuilder();
var prevEE = "";
var prevER = "";
for (var i = stack.Count - 1; i >= 0; i--)
{
if (i == 9 && i <= stack.Count - 8)
{
output.Append("...");
output.AppendLine();
}
if (i > 8 && i <= stack.Count - 8) continue;
var frame = stack[i];
//run in tree:76
var callerStr = frame.Caller.ToString();
var calleeStr = frame.Callee?.ToString();
if (callerStr != prevER || calleeStr != prevEE)
{
output.Append('(');
output.Append(callerStr);
output.Append(':');
output.Append(calleeStr);
output.Append(") ");
output.AppendLine();
prevEE = calleeStr;
prevER = callerStr;
}
output.Append(" > ");
/*if (frame is VMRoutingFrame)
{
output.Append("VMRoutingFrame with state: ");
output.Append(((VMRoutingFrame)frame).State.ToString());
}
else
{
output.Append(frame.Routine.Rti.Name.TrimEnd('\0'));
output.Append(':');
output.Append(frame.InstructionPointer);
output.Append(" (");
var opcode = frame.GetCurrentInstruction().Opcode;
var primitive = (opcode > 255) ? null : VMContext.Primitives[opcode];
output.Append((primitive == null) ? opcode.ToString() : primitive.Name);
output.Append(")");
}*/
output.AppendLine();
}
return output.ToString();
}
}
}