diff --git a/OpenDreamRuntime/ByondApi/ByondApi.Functions.cs b/OpenDreamRuntime/ByondApi/ByondApi.Functions.cs index 8acd002ca0..996ee75d6b 100644 --- a/OpenDreamRuntime/ByondApi/ByondApi.Functions.cs +++ b/OpenDreamRuntime/ByondApi/ByondApi.Functions.cs @@ -112,10 +112,10 @@ private static uint Byond_GetStrId(byte* cstr) { return RunOnMainThread(() => { var strId = _dreamManager!.FindString(str); if (strId != null) { - return (uint)RefType.String | strId.Value; - } else { - return NONE; + return strId.Value; } + + return NONE; }); } @@ -138,7 +138,7 @@ private static uint Byond_AddGetStrId(byte* cstr) { return RunOnMainThread(() => { var strIdx = _dreamManager!.FindOrAddString(str); - return (uint)RefType.String | strIdx; + return strIdx; }); } @@ -195,9 +195,9 @@ private static byte Byond_ReadVarByStrId(CByondValue* loc, uint varname, CByondV return RunOnMainThread(() => { try { - DreamValue varNameVal = _dreamManager!.RefIdToValue((int)varname); + DreamValue varNameVal = _dreamManager!.RefToValue(RefType.String, (int)varname); if (!varNameVal.TryGetValueAsString(out var varName)) - return SetLastError("varname argument was not a reference to a string"); + return SetLastError("varname argument was an invalid string ID"); DreamValue srcValue = ValueFromDreamApi(*loc); if (!srcValue.TryGetValueAsDreamObject(out var srcObj)) @@ -264,9 +264,9 @@ private static byte Byond_WriteVar(CByondValue* loc, byte* varname, CByondValue* private static byte Byond_WriteVarByStrId(CByondValue* loc, uint varname, CByondValue* val) { return RunOnMainThread(() => { try { - DreamValue varNameVal = _dreamManager!.RefIdToValue((int)varname); + DreamValue varNameVal = _dreamManager!.RefToValue(RefType.String, (int)varname); if (!varNameVal.TryGetValueAsString(out var varName)) - return SetLastError("varname argument was not a ref to a string"); + return SetLastError("varname argument was an invalid string ID"); DreamValue srcValue = ValueFromDreamApi(*val); DreamValue dstValue = ValueFromDreamApi(*loc); @@ -590,9 +590,9 @@ private static byte Byond_CallProcByStrId(CByondValue* cSrc, uint name, CByondVa return RunOnMainThread(() => { try { - DreamValue procNameVal = _dreamManager!.RefIdToValue((int)name); + DreamValue procNameVal = _dreamManager!.RefToValue(RefType.String, (int)name); if (!procNameVal.TryGetValueAsString(out var procName)) - return SetLastError("name argument was not a ref to a string"); + return SetLastError("name argument was an invalid string ID"); DreamValue src = ValueFromDreamApi(*cSrc); if (!src.TryGetValueAsDreamObject(out var srcObj)) @@ -659,9 +659,9 @@ private static byte Byond_CallGlobalProcByStrId(uint name, CByondValue* cArgs, u return RunOnMainThread(() => { try { - DreamValue procNameVal = _dreamManager!.RefIdToValue((int)name); + DreamValue procNameVal = _dreamManager!.RefToValue(RefType.String, (int)name); if (!procNameVal.TryGetValueAsString(out var procName)) - return SetLastError("name argument was not a ref to a string"); + return SetLastError("name argument was an invalid string ID"); if (!_dreamManager.TryGetGlobalProc(procName, out var proc)) return SetLastError($"no global proc named \"{procName}\""); @@ -1068,13 +1068,13 @@ private static void ByondValue_DecTempRef(CByondValue* src) { // This only applies to ref types, not null/num/string which are always valid. [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] private static byte Byond_TestRef(CByondValue* src) { - if (src == null) return SetLastError("src argument was a null pointer"); - if (src->type == ByondValueType.Null) { + if (src == null) + return SetLastError("src argument was a null pointer"); + if (src->type == ByondValueType.Null) return 1; - } return RunOnMainThread(() => { - var srcValue = _dreamManager!.RefIdToValue((int)src->data.@ref); + var srcValue = ValueFromDreamApi(*src); if (srcValue == DreamValue.Null) { src->type = 0; diff --git a/OpenDreamRuntime/ByondApi/ByondApi.cs b/OpenDreamRuntime/ByondApi/ByondApi.cs index b9da529f54..bf1ca5ab87 100644 --- a/OpenDreamRuntime/ByondApi/ByondApi.cs +++ b/OpenDreamRuntime/ByondApi/ByondApi.cs @@ -23,7 +23,7 @@ public static partial class ByondApi { /// A failed ByondApi call will set this string. It can be retrieved with . /// private static string _lastError = string.Empty; - + private static IntPtr _lastErrorPtr = IntPtr.Zero; public static void Initialize(DreamManager dreamManager, AtomManager atomManager, IDreamMapManager dreamMapManager, DreamObjectTree objectTree) { @@ -90,8 +90,32 @@ public static DreamValue ValueFromDreamApi(CByondValue value) { case ByondValueType.Appearance: case ByondValueType.World: case ByondValueType.Proc: + var refType = ctype switch { + ByondValueType.Turf => RefType.DreamObjectTurf, + ByondValueType.Obj => RefType.DreamObject, + ByondValueType.Mob => RefType.DreamObjectMob, + ByondValueType.Area => RefType.DreamObjectArea, + ByondValueType.Client => RefType.DreamObjectClient, + ByondValueType.Image => RefType.DreamObjectImage, + ByondValueType.List => RefType.DreamObjectList, + ByondValueType.Datum => RefType.DreamObjectDatum, + ByondValueType.World => RefType.DreamObjectDatum, + ByondValueType.String => RefType.String, + ByondValueType.Resource => RefType.DreamResource, + ByondValueType.Appearance => RefType.DreamAppearance, + ByondValueType.Proc => RefType.Proc, + + ByondValueType.ObjTypePath or + ByondValueType.MobTypePath or + ByondValueType.TurfTypePath or + ByondValueType.DatumTypePath or + ByondValueType.AreaTypePath => RefType.DreamType, + + _ => throw new Exception($"Invalid reference type for type {ctype.ToString()}") + }; + int refId = (int)cdata.@ref; - return _dreamManager!.RefIdToValue(refId); + return _dreamManager!.RefToValue(refType, refId); } } @@ -111,9 +135,9 @@ public static CByondValue ValueToByondApi(DreamValue value) { case DreamValue.DreamValueType.DreamType: case DreamValue.DreamValueType.Appearance: case DreamValue.DreamValueType.DreamProc: - var refid = _dreamManager!.CreateRefInt(value, out RefType refType); + var refId = _dreamManager!.GetRefId(value, out var refType); ByondValueType type; - ByondValueData data = new ByondValueData { @ref = refid }; + ByondValueData data = new ByondValueData { @ref = refId }; switch (refType) { default: diff --git a/OpenDreamRuntime/DreamManager.cs b/OpenDreamRuntime/DreamManager.cs index 753131ea2c..be10fd4d2e 100644 --- a/OpenDreamRuntime/DreamManager.cs +++ b/OpenDreamRuntime/DreamManager.cs @@ -235,10 +235,14 @@ public uint FindOrAddString(string str) { } public string CreateRef(DreamValue value) { - return $"[0x{CreateRefInt(value, out _):x}]"; + var refId = GetRefId(value, out var refType); + + // refType combines with the highest byte. This could produce an invalid ref, but that's BYOND behavior too. + var combined = (uint)refType | refId; + return $"[0x{combined:x}]"; } - public uint CreateRefInt(DreamValue value, out RefType refType) { + public uint GetRefId(DreamValue value, out RefType refType) { int idx; if (value.TryGetValueAsDreamObject(out var refObject)) { @@ -309,8 +313,7 @@ public uint CreateRefInt(DreamValue value, out RefType refType) { throw new NotImplementedException($"Ref for {value} is unimplemented"); } - // The highest byte is the type - return (uint)refType | (uint)idx; + return (uint)idx; } /// @@ -333,12 +336,16 @@ public IEnumerable IterateDatums() { } } - public DreamValue RefIdToValue(int rawRefId) { + public DreamValue RefToValue(int rawRef) { // The first one/two digits give the type, the last 6 give the index - var typeId = (RefType)(rawRefId & 0xFF000000); - var refId = (rawRefId & 0x00FFFFFF); // The ref minus its ref type prefix + var refType = (RefType)(rawRef & 0xFF000000); + var refId = (rawRef & 0x00FFFFFF); // The ref minus its ref type prefix + + return RefToValue(refType, refId); + } - switch (typeId) { + public DreamValue RefToValue(RefType refType, int refId) { + switch (refType) { case RefType.Null: return DreamValue.Null; case RefType.DreamObjectArea: @@ -380,7 +387,7 @@ public DreamValue RefIdToValue(int rawRefId) { case RefType.Number: // For the oh so few numbers this works with (most numbers clobber the ref type) return new(BitConverter.Int32BitsToSingle(refId)); default: - throw new Exception($"Invalid reference type for ref [0x{rawRefId:x}]"); + throw new Exception($"Invalid reference type for ref [0x{refId:x}]"); } } @@ -396,7 +403,7 @@ public DreamValue LocateRef(string refString) { } if (canBePointer && int.TryParse(refString.Substring(2), System.Globalization.NumberStyles.HexNumber, null, out var refId)) { - return RefIdToValue(refId); + return RefToValue(refId); } // Search for an object with this ref as its tag @@ -475,7 +482,6 @@ public enum RefType : uint { DreamObjectMob = 0x3000000, DreamObjectArea = 0x4000000, DreamObjectClient = 0x5000000, - DreamObjectFilter = 0x5300000, DreamResourceIcon = 0xC000000, DreamObjectImage = 0xD000000, DreamObjectList = 0xF000000, @@ -485,5 +491,6 @@ public enum RefType : uint { DreamResource = 0x27000000, //Equivalent to file DreamAppearance = 0x3A000000, Proc = 0x26000000, - Number = 0x2A000000 + Number = 0x2A000000, + DreamObjectFilter = 0x53000000 }