#[BUG] 1.3.9 - hash in internal map of `TypeManager.GetTypeIndexFromStableTypeHash` are different
1 messages · Page 1 of 1 (latest)
@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.
oh just my struct i test with.
namespace Saving.Sample
{
public struct GlobalRegisteredObjectComponent : IComponentData, ISavableObject
{
public double Time;
}
}
wow how does anything work
ok, trying that
and no attributes on the definition right
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 ^^
ah no it is not expected to be stable across versions
that said, we don't mess with it that often
hm, ok, then i need to write some form of migration.
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
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?
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
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
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
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
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)
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
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
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
memoryOrdering existence is new to me. yeah, that confused me. or rather the ternary confused me var stableTypeHash = !hasCustomMemoryOrder ? memoryOrdering : TypeHash.CalculateStableTypeHash(typeRef);
on a side note, how did i not know TypeOverridesAttribute exists
in all cases it's TypeHash.CalculateStableTypeHash(typeRef)
and do i now need to add it to 90% of my components -_-
what would you have in mind?
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 ^_^'
memoryOrdering used down below is also just TypeHash.CalculateStableTypeHash(typeRef); and equal to STH
memoryOrdering == stableTypeHash unless you have the attribute
the only exception is with the custom attribute
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
do you know what it is used for?
/// <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
hm, yeah no idea how it could be useful for me 😄