#[BUG] 1.3.9 - hash in internal map of `TypeManager.GetTypeIndexFromStableTypeHash` are different

1 messages · Page 1 of 1 (latest)

echo thunder
#
Dictionary<Type, ulong> hashCache = new Dictionary<Type, ulong>();
var newHash = TypeHash.CalculateStableTypeHash(typeof(GlobalRegisteredObjectComponent), hashCache);
Debug.Log($"new hash is {newHash}");

a lookup with var testIndex = TypeManager.GetTypeIndexFromStableTypeHash(10519832631075699071); fails to get the TypeIndex

#

in the internal map the hash for the same type is 14052980312872689470

echo thunder
#

@oblique magnet are you the right guy to ask about this?
i've dug into the changes. in HashTypeName the assembly hashing is gone. considering a simple double now has netstandard instead of mscorlib this one is breaking with no real fix.
fieldTypeHash = CombineFNV1A64(fieldTypeHash, fieldIndex); is also missing. easy fix though.

oblique magnet
#

what is GlobalRegisteredObjectComponent?

#

(yes this is me)

#

@echo thunder ^

echo thunder
#

oh just my struct i test with.

namespace Saving.Sample
{
  public struct GlobalRegisteredObjectComponent : IComponentData, ISavableObject
  {
    public double Time;
  }
}
oblique magnet
#

wow how does anything work

#

ok, trying that

#

and no attributes on the definition right

echo thunder
#

no that's it 🙂

#

i've been keeping notes in case that helps:

HashNamespace 11084440852496975332 - OK
Name 9222270895565106312 - OK
assemblyName 2137562232237579539 - MISSING on IL side

HashTypeName 2137562232237579539 
HashVersionAttribute 16526688664467706066 

System.Double Time 
HashNamespace 14703219465302457231
hash = CombineFNV1A64(hash, FNV1A64(type.Assembly.GetName().Name)); 5325887266552202061
hash = HashTypeName(type);  5325887266552202061

fieldTypeHash = HashType(fieldType, cache) - 5325887266552202061
fieldTypeHash = CombineFNV1A64(fieldTypeHash, fieldIndex) - 3911279803650923479 - MISSING on IL side

10519832631075699071 hash = CombineFNV1A64(hash, fieldTypeHash);
10519832631075699071

namespace is Saving.Sample if you wanna test the same thing

#

and basically, i want/need to know how stable, stableTypeHash is across versions. as i use it to make saving work, in case the hash changes, the type can't be resolved anymore which is problematic ^^

oblique magnet
#

ah no it is not expected to be stable across versions

#

that said, we don't mess with it that often

echo thunder
#

hm, ok, then i need to write some form of migration.

oblique magnet
#

ok so it looks like it works if you do TypeManager.GetTypeInfo<GlobalRegisteredObjectComponent >().StableTypeHash, which is how anything else works

#

but this is obviously a bug

#

i imagine that may help work around it, though?

#

basically what happened that caused this and the other two bugs is that i switched the entire typemanager to use information generated at compile time rather than at startup, to help with memory usage and startup time [edit: and gc perf]. but, for this one, i didn't realize that this function was still computing stuff on the fly instead of looking stuff up based on the typemanager db

echo thunder
#

ah right, TypeManager.GetTypeInfo<GlobalRegisteredObjectComponent >().StableTypeHash tertle told me to use it too. with that i can at least lookup the typeIndex again. but the bigger problem is that the hash changed and that's not fixable, right?

#

and another thing as i was looking over: var stableTypeHash = !hasCustomMemoryOrder ? memoryOrdering : TypeHash.CalculateStableTypeHash(typeRef);
seems like it should be hasCustomMemoryOrder instead?

oblique magnet
#

actually not, if you look at CalculateMemoryOrdering

#

but yeah, i doubt it will be fixable to make the hashes not change

#

the purpose of the stable type hash is primarily for serialization, so when the editor writes out scene files with entity data in them, the player knows what types are what data

echo thunder
#

with the attribute, HasCustomMemoryOrdering is true and custom hash is returned, otherwise CalculateStableTypeHash which means the ternary isn't even needed afterwards. but if HasCustomMemoryOrdering is true, the 2nd operation is returned which is TypeHash.CalculateStableTypeHash(typeRef)

#

so in all cases, TypeHash.CalculateStableTypeHash(typeRef) is returned

oblique magnet
#

huh. that's true. i assumed that we separately wanted to know whether it had a custom memory order for other purposes, but we seem not to use that anywhere

#

wait! we do use it! we write it down in the typeinfo

#

we don't use the bool hasCustomMemoryOrder, but we do use the memoryOrdering in the typeinfo struct

karmic ridge
#

i remember looking at this ages ago and thinking a similar thing

#

but i actually think the logic is correct atm, it's just weirdly written and confusing
in CalculateMemoryOrdering when hasCustomMemoryOrder returns false it actually returns the stable hash already
return CalculateStableTypeHash(type, customAttributes, hashCache);

#

which is written to memoryOrdering

#

so it's already been computed (or if entity is pass in returns 0) and stored in memoryOrdering

#

so when !hasCustomMemoryOrder
memoryOrdering == stableTypeHash

echo thunder
#

but the purpose of the ForcedMemoryOrderingAttribute attribute is to override any calculated stableTypeHash. the memoryOrder value of the attribute is never used because hasCustomMemoryOrder would be true in that case which sets TypeHash.CalculateStableTypeHash(typeRef)

karmic ridge
#

but the purpose of the ForcedMemoryOrderingAttribute attribute is to override any calculated stableTypeHash

        /// <summary>
        /// Attribute to force the <see cref="TypeInfo.MemoryOrdering"/> for a component to a specific value.
        /// </summary>
        [AttributeUsage(AttributeTargets.Struct)]
        public class ForcedMemoryOrderingAttribute : Attribute```
#

it overrides the MemoryOrdering field not stable type hash?

#

which it is doing

#

and it then calculates the stabletypehash as if this attribute doesn't exist

echo thunder
#

hm, seems like a cool attribute if i get its purpose right. (maybe i don't) from the code and it being assigned directly to the stableTypeHash field i thought i'd understand its purpose

[TypeManager.ForcedMemoryOrdering(23423423423)]
    public struct GlobalRegisteredObjectComponent : IComponentData, ISavableObject

having this on my struct would give it this exact STH, right? at least in theory

karmic ridge
#

no?

#

it would give the MemoryOrdering value of 23423423423 but the same stable type hash

#

oh wait sorry i misstook your question

#

yeah i think so

echo thunder
#

memoryOrdering existence is new to me. yeah, that confused me. or rather the ternary confused me var stableTypeHash = !hasCustomMemoryOrder ? memoryOrdering : TypeHash.CalculateStableTypeHash(typeRef);

karmic ridge
#

on a side note, how did i not know TypeOverridesAttribute exists

echo thunder
#

in all cases it's TypeHash.CalculateStableTypeHash(typeRef)

karmic ridge
#

and do i now need to add it to 90% of my components -_-

echo thunder
#

what would you have in mind?

karmic ridge
#

oh wait it only matters for managed components i think

#
/// <summary>
/// [TypeOverrides] can be applied to a component that is known to never contain Entity and/or Blob references,
/// in order to reduce time taken during serialization operations.
/// </summary>
/// <remarks>
/// For example, a managed component containing a base class type field can only be determined to have entity or blob
/// references at runtime since the runtime instance might hold a child type instance which does contain Entity and/or
/// BlobAssetReferences. As such, serializing operations for managed components needs to walk runtime type instances
/// which might be unnecessary. Use this attribute to prevent this walking to improve managed component serialization
/// operations when you know the component type will never contain Entity and/or Blob references.
/// </remarks>```
i didn't even know you could remap on managed components ^_^'
echo thunder
#

memoryOrdering used down below is also just TypeHash.CalculateStableTypeHash(typeRef); and equal to STH

karmic ridge
#

memoryOrdering == stableTypeHash unless you have the attribute

echo thunder
#

the only exception is with the custom attribute

karmic ridge
#

we used this component for something at previous job once like 3 years ago

#

but i don't actually know the use for it tbh

echo thunder
#

do you know what it is used for?

karmic ridge
#
            /// <summary>
            /// Sort order for component types in <see cref="Chunk"/> storage. By default this is equivalent to <seealso cref="StableTypeHash"/>.
            /// Order is sorted from lowest to highest.
            /// </summary>
            public   readonly ulong         MemoryOrdering;```
#

i can't see any uses for it in entities

#
            // For serialization a stable ordering of the components in the
            // chunk is desired. The type index is not stable, since it depends
            // on the order in which types are added to the TypeManager.
            // A permutation of the types ordered by a TypeManager-generated
            // memory ordering is used instead.```
#

i guess this is the reasoning

echo thunder
#

hm, yeah no idea how it could be useful for me 😄

karmic ridge
#

let's you reorder the order types are serialized - why you'd need to customize this is beyond me

#

(actually this could have been useful for my dynamic chunk work i did a while ago - intentionally grouping certain components in the same chunk)

#

(yeah that could have been cool)

echo thunder
#

oh true

#

nice thinking 😄