Welcome to Keen Software House Forums! Log in or Sign up to interact with the KSH community.
  1. You are currently browsing our forum as a guest. Create your own forum account to access all forum functionality.

Uniquely identify a block without the use of CustomName?

Discussion in 'Programming Questions and Suggestions' started by Malware, Jan 29, 2015.

Thread Status:
This last post in this thread was made more than 31 days old.
  1. Malware Master Engineer

    Messages:
    9,549
    I have a power mode toggler (a mains switch, if you will) which switches between fully powered and a low-power mode keeping a single reactor running in order to keep a ship's connector working, switching batteries to charge mode etc.

    This script stores the state of each device as it is triggered and restores the device state when triggered the next time.

    The problem is to identify each single block without needing the user to keep their block names unique - I am using nameless antennae to decrease my screen clutter. Also I would like to safely be able to rename stuff on either state.

    I was hoping to use the EntityId property, but of course this property is unavailable (figures).

    Does anyone have a good idea? I have had much luck asking you guys for help earlier, so I thought I would rely on you again :)
     
  2. Phoera Senior Engineer

    Messages:
    1,713
    you can use Position to store grid X,Y,Z and use it, but it can broke when merging.
     
  3. Immersive Apprentice Engineer

    Messages:
    122
    Can you not just store the (memory) reference to the object?

    Code:
    public class RestoreState
    {
       IMyTerminalBlock block;
       bool isPowered;
    }
    
    private readonly List<RestoreState> States = new List<RestoreState>();
    void Main()
    {
    // Do Stuff
    }
    
     
  4. Phoera Senior Engineer

    Messages:
    1,713
    bad solution, will fail after restart and also can fail if block was deleted.
     
  5. Malware Master Engineer

    Messages:
    9,549
    @phoenixcorp13 that was my thought as well, right after I posted this. Unfortunately something seems off in the values - I am getting multiple blocks with the same coords, and X Y and Z are all the same value for each block, though they differ between blocks. I am hoping I am simply messing something up but it does not look that way at the moment. Could I bother you with testing whether you get the same result? I am going to dig further, still hoping this is my own fault (as usual ;))

    @Immersive Because this needs to work beyond save/load. But thanks anyway :)
     
  6. Immersive Apprentice Engineer

    Messages:
    122
    It's not a full solution, it's a hint.

    Fair enough, let me try something here and I'll get back to you.
     
    Last edited by a moderator: Jan 29, 2015
  7. Malware Master Engineer

    Messages:
    9,549
    I was right, there's something wrong with the Position property. I added a bug report.

    I'll just have to wait with this one then.
     
  8. Immersive Apprentice Engineer

    Messages:
    122
    Righto, how about this:

    Code:
    public class RestoreState
    {
       VRageMath.Vector3I blockPosition;
       bool isPowered;
    }
    
    private readonly List<RestoreState> States = new List<RestoreState>();
    void Main()
    {
    // Do Stuff
    
    // Fetch block with:
    IMyTerminalBlock anyBlock;
    var retrievedBlock = anyBlock.CubeGrid.GetCubeBlock( restoreState.Position );
    }
    
    The key problem with Vec3* (int, single and double) is that the X, Y and Z fields/properties all return the X value. You need to transform it to a Vec3 or Vec3D and use GetDim() to access the actual values. Note that the object does store the correct value internally.

    After that, just de/serialize the RestoreState from/to Storage.

    Ed: Ahh, and then I see I've come full-circle to Phoenix' original answer...
     
    Last edited by a moderator: Jan 29, 2015
  9. CommanderGizmo Apprentice Engineer

    Messages:
    133
    Isn't there a NumberInGrid or something per block? I'm not sure how it's numbered though; probably in the order added or by index for the grid's array. I haven't had a chance to research it yet.

    [EDIT]
    I found it: IMyCubeBlock.NumberInGrid
     
    Last edited by a moderator: Jan 29, 2015
  10. hellokeith Apprentice Engineer

    Messages:
    335
    block.ToString() looks like an object reference, should be unique?
     
  11. Phoera Senior Engineer

    Messages:
    1,713
    NumberInGrid is number you see after default name.
    it's unuque if not merging.
     
  12. Malware Master Engineer

    Messages:
    9,549
    Fantastic, GetDim/Vector3 casting was the missing piece of the puzzle. That one was new for me. Hope they fix the vector classes though.

    Thanks!
     
  13. Malware Master Engineer

    Messages:
    9,549
    If I'm not mistaken the NumberInGrid is only unique per block type.
     
  14. Malware Master Engineer

    Messages:
    9,549
    Oh and block.ToString() returns the name of an obfuscated type.
     
  15. MrMatthews Trainee Engineer

    Messages:
    14
    I have been doing block.tostring() and taking the value in curly brackets as an id

    This has appeared to be unique so far.
     
  16. Immersive Apprentice Engineer

    Messages:
    122
    How many objects of a given type do you have? ToString() by default returns the type name, unless overridden. If you only have one reactor, for example, then this wont present a problem.
     
  17. swixel Trainee Engineer

    Messages:
    42
    The way I handle it, usually, is using grid coords with a stored grid size, knowing the type and its number (all serialised). I was using HashCode until I found it to be resetting when I reloaded.

    This is the script I use to identify things (for the purposes of "cleaning up" builds), with the "ToString" tacked on.

    Code:
    void Main()
    {
        for (int idx = 0; idx < GridTerminalSystem.Blocks.Count; idx++)
        {
            IMyTerminalBlock Block = GridTerminalSystem.Blocks[idx];
    
            Block.SetCustomName(String.Format("{0} {1} [Local Grid: {2},{3},{4} | Hash: {5} | ToString: {6}]",
    
                // Obviously this is worthless, just for the purposes of showing what this block is ;)
                Block.DefinitionDisplayNameText,
                Block.NumberInGrid,
    
                // Local grid coordinates?
                // Reload from X,Y,Z with: GridTerminalSystem.Blocks[0].CubeGrid.GetCubeBlock(new VRageMath.Vector3I(x, y, z));
                Block.Position.AxisValue(VRageMath.Base6Directions.Axis.LeftRight),
                Block.Position.AxisValue(VRageMath.Base6Directions.Axis.UpDown),
                Block.Position.AxisValue(VRageMath.Base6Directions.Axis.ForwardBackward),
    
                // And what about hash code?  It only resets on server restart/map reload.
                Block.GetHashCode(),
    
                // I used this before to show block information somewhere else ...
                // So I already know it's useless.
                Block.ToString()
            ));
        }
    }
    
    How I handle it:
    1. Check Storage for my block information;
    2. Attempt to load the block using location;
    3. Use HashCode as checksum;
    4. Attempt failure routine (no hashcode matches), check grid size/location, realise it was a reload, store the new hashcode.
    How I start:
    1. Check Storage for block information (it fails);
    2. Attempt failure routine, knowing the initial name OR location (depending on the craft), set location and hashcode.
    How I handle damage/loss:
    1. Observe grid resize (IMyCubeGrid.Min and IMyCubeGrid.Max);
    2. Update using block type (SubType strings) and last known good location to attempt to find it.
    What this means I'm storing:
    • Part of the SubType information ("LargeContainer");
    • Coords as Int16 (because my ship won't get bigger and why waste space?): x,y,z;
    • Hashcode
    I've also played with NumberInGrid for safety (instead of hashcode), and found it to be more reliable and lower overhead. However, the HashCode has been unique in my experience, but is unreliable with restarts.

    EDIT: The only reason I'm NOT using Number + Coords + Type storage is I haven't updated my old code yet; I found HashCode first, and thus decided to use it.
     
    Last edited by a moderator: Jan 29, 2015
  18. Malware Master Engineer

    Messages:
    9,549
    Immersive is right. This is a proven fact, the blocks return their type name as .net does by default. It is easily verifiable.
     
  19. Phoera Senior Engineer

    Messages:
    1,713
    how did you read them?
    convert to which type?
     
    Last edited by a moderator: Jan 29, 2015
  20. Malware Master Engineer

    Messages:
    9,549
    Like Immersive said, if you cast the vectors to Vector3 you can access their values by calling GetDim({0-2}) on them.

    Code:
    var min = (Vector3)block.CubeGrid.Min;
    var x = min.GetDim(0);
    
    [Edit] Missed the Min property
     
    Last edited by a moderator: Jan 29, 2015
  21. swixel Trainee Engineer

    Messages:
    42
    Early proof of concept I still have kicking around from whenever it was I wrote this:

    Code:
    struct CraftSize
    {
        public VRageMath.Vector3D MinPos;
        public VRageMath.Vector3D MaxPos;
        public VRageMath.Vector3D Dimensions;
    }
    
    // To save on size
    double GetDistanceAxis(VRageMath.Vector3D V0, VRageMath.Vector3D V1, int idx)
    {
        double A = Math.Max(V0.GetDim(idx), V1.GetDim(idx)) - Math.Min(V0.GetDim(idx), V1.GetDim(idx));
        return Math.Sqrt(A*A);
    }
    
    CraftSize GetCraftSize(IMyCubeGrid Grid)
    {
        CraftSize CS = new CraftSize();
    
        // Slim Min/Max
        IMySlimBlock minBlock = Grid.GetCubeBlock(Grid.Min);
        IMySlimBlock maxBlock = Grid.GetCubeBlock(Grid.Max);
    
        // Fat Min/Max
        IMyCubeBlock MinBlock = minBlock.FatBlock;
        IMyCubeBlock MaxBlock = maxBlock.FatBlock;
    
        // Corners!
        CS.MinPos = new VRageMath.Vector3D(MinBlock.GetPosition());
        CS.MaxPos = new VRageMath.Vector3D(MaxBlock.GetPosition());
    
        // Get dimensions using distance
        CS.Dimensions = new VRageMath.Vector3D(
            GetDistanceAxis(CS.MinPos, CS.MaxPos, 0),
            GetDistanceAxis(CS.MinPos, CS.MaxPos, 1),
            GetDistanceAxis(CS.MinPos, CS.MaxPos, 2)
        );
    
        // Success?
        return CS;
    }
    
    Edit: Notably now I only get the size if the min/max pos I have serialised differ from the grid. If your script is too complex (long, large, whatever), you can use a single block to process any damage, etc., prior to running others, and use something else (in my case usually interior lights or a cargo container) to update changes for block calls. I'd also recommend the NumberInGrid over HashCode, though I realise having said that the HashCode's limited reliability window is probably great for the purposes of merging/connecting, at which point you'd need to handle swapping between the two.
     
    Last edited by a moderator: Jan 29, 2015
  22. Immersive Apprentice Engineer

    Messages:
    122
    HashCode is a function of the object's address in memory and as such doesn't survive across map reloads.
    NumberInGrid doesn't always survive across Grid merges.

    Checking against grid size isn't a bad idea, but I don't see any concrete benefit to it. You're still going to need to check if the individual blocks are still accessible.

    IMHO, the best idea is to try to get a block from that position and then see if it's the same type of block that was stored. You can do this from the BlockDefinition if you treat the BlockDefinition as a string.

    Code:
    var fullyQualifiedType = (string)block.BlockDefinition
    So, how I would do it
    Code:
     *** Pseudocode ***
    
    var block = CubeGrid.BlockAt( StoredBlock.Position )
    
    if ( block != null && block.BlockDefinition == StoredBlock.BlockDefinition )
        block.SetState( StoredBlock.State )
    
    
     
  23. hellokeith Apprentice Engineer

    Messages:
    335
    We really need EntityId. :(
     
  24. swixel Trainee Engineer

    Messages:
    42
    Which is why I'm stuck using an evil combination of the two :( I just have to hope things aren't merged pre-save (though I don't usually leave things connected for long unless they're a ship with named units anyway, so that isn't a huge deal for me).

    Agreed, and failing that for merged/connected grids to be treated differently in the in-game code (so we can have an array of Merged/Connected Grid terminals).
     
  25. Malware Master Engineer

    Messages:
    9,549
    I agree as well, this would give us the most flexible and durable way to uniquely identify a block. For the grid too, actually.
     
  26. Phoera Senior Engineer

    Messages:
    1,713
    they overloaded ToString.
    and it return Type+EntityId in brackets.
    Code:
    return string.Concat(this.GetType().Name, " {", _EntityId.ToString("X8"), "}")
    
     
  27. Malware Master Engineer

    Messages:
    9,549
    I stand corrected, and apologies to MrMatthews. In this case the ToString should be usable as a unique id! Sweet!
     
    Last edited by a moderator: Jan 30, 2015
  28. Immersive Apprentice Engineer

    Messages:
    122
    I believe we've solved all the problems.

    When script runs first time after load (test using global IsInited bool), find the blocks using Position and store it (or a reference to it, rather) in a global list.

    In subsequent runs, you should have direct access to the block via the list.
    In theory, you should be able to check if a block has been destroyed from its IMySlimBlock but I can't work out how to get the IMySlimBlock from IMyCubeBlock/IMyTerminalBlock... or you could just check for IsFunctional (build completion above red line)

    This should work because:
    1. Position shouldn't change while server is offline (ie, no grid merges)
    2. Reference shouldn't change even if grids merge, while server remains online
    3. Only thing you now need to test for is block destruction
     
  29. Phoera Senior Engineer

    Messages:
    1,713
    i have some other idea, but i need test it before, cuz if devs don't fix bug with CubeGrid this method useless.
     
  30. Immersive Apprentice Engineer

    Messages:
    122
    Which bug?
     
Thread Status:
This last post in this thread was made more than 31 days old.