1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. Hey Guest, is it this your first time on the forums?

    Visit the Beginner's Box

    Introduce yourself, read some of the ins and outs of the community, access to useful links and information.

    Dismiss Notice

Obscure CShape::RemoveShape method

Discussion in 'Modding Help' started by Asu, May 28, 2017.

  1. Asu

    Asu THD Team THD Team Forum Moderator

    Messages:
    1,580
    Hi, for the needs of my mod I gave a look at the undocumented RemoveShape method of CShape.
    It takes an int index argument, but no matter what I pass to it (0, 1, that with a couple quads already appended with AddShape) it seems to trigger a null dereference crash in Juxta:
    Unhandled exception: page fault on read access to 0x00000064 in 32-bit code (0x007e7bdd).
    It seems completely unused in KAG, so I'm curious if it's just untested and broken (and then supposed to erase vertices appended by AddShape), not meant to be exposed or if I've been using incorrectly, but it doesn't look quite safe.
     
    Biurza and TFlippy like this.
  2. Geti

    Geti Please avoid PMing me (poke a mod instead) THD Team Administrator Global Moderator

    Messages:
    3,730
    I think this might have been used in TR? Could be worth searching for it there.

    Additionally you can't remove the base shape, only shapes added with AddShape afair. RemoveShape will also shift down the shape array so if you're doing anything "interesting" with multiple shapes you might have a bad time.

    Kinda surprised that it crashes, will look into why that might be happening now.
    (E: looked into it, standard "physics code is a big ball of knots" reasons, will put it on the todo - if you're doing anything with it in a physics related callback, don't do that. It's unsafe to destroy most box2d structures while box2d is handling them for hopefully obvious reasons - we could defer these operations until afterwards but currently pretty much all of them are instantaneous)

    Let me know if i you have any more specific issues.
     
    Last edited: May 29, 2017
    Asu likes this.
  3. Asu

    Asu THD Team THD Team Forum Moderator

    Messages:
    1,580
    You were right, Trench Run uses this - Base/Entities/Soldier/SoldierHit.as: data.shape.RemoveShape(1);
    That shape is created by void AddTopShape(CBlob@ this) in SoldierRevive.as, which is called when initializing the soldier or when reviving him.

    I am currently doing the ShapeAdd and RemoveShape calls in onInit(CBlob@). I have a quad already defined in the .cfg vertices.
    This is the current test code I get which reproduces the crash :
    Code:
    void onInit(CBlob@ blob)
    {
        CShape@ shape = blob.getShape();
        Vec2f[] someRect =
        {
            Vec2f(0.0, -4.0),
            Vec2f(8.0, -4.0),
           Vec2f(8.0,  4.0),
            Vec2f(0.0,  4.0)
        };
        shape.AddShape(someRect);
        shape.RemoveShape(1); // unsurprisingly same result with 0
    }
    Code:
    # Vehiblock config file
    
    $sprite_factory = generic_sprite
    
    @$sprite_scripts = VehiblockAnim.as;
    $sprite_texture = VehiblockCore.png
    s32_sprite_frame_width = 8
    s32_sprite_frame_height = 8
    f32 sprite_offset_x = 0
    f32 sprite_offset_y = 0
    
    $sprite_gibs_start = *start*
    $sprite_gibs_end = *end*
    
    $sprite_animation_start = *start*
        $sprite_animation_default_name = default
        u16 sprite_animation_default_time = 0
        u8_sprite_animation_default_loop = 0
        @u16 sprite_animation_default_frames = 0
    $sprite_animation_start = *end*
    
    $shape_factory = box2d_shape
    @$shape_scripts =
    f32 shape_mass = 6000.0 # determined and changed at run time
    f32 shape_radius = 10.0 # pickup radius
    f32 shape_friction = 0.2
    f32 shape_elasticity = 0.0
    f32 shape_buoyancy = 0.20
    f32 shape_drag = 2.0
    bool shape_collides = yes
    bool shape_ladder = no
    bool shape_platform = no
    
    @f32 verticesXY = -4.0; -4.0;
                      4.0; -4.0;
                      4.0;  4.0;
                     -4.0;  4.0;
    u8 block_support = 0
    bool block_background = no
    bool block_lightpasses = no
    bool block_snaptogrid = no
    
    $movement_factory =
    $brain_factory =
    
    $attachment_factory = box2d_attachment
    @$attachment_scripts =
    # name; pixel offset (from center) X; offset Y; socket/plug 0/1; controller; radius
    @$attachment_points = PICKUP; 0; 0; 1; 0; 10;
    
    $inventory_factory = generic_inventory
    @$inventory_scripts =
    u8 inventory_slots_width = 3
    u8 inventory_slots_height = 3
    $inventory_name = Core modules
    
    $name = vehiblock
    @$scripts = Vehiblock.as;
    f32 health = 25.0
    $inventory_name = Vehiblock core
    

    This is the crash I get along with a part of the backtrace (it might be worth noting I'm running it under Wine, I'll do the test on the KAG Linux test build to be sure and to get a somewhat more relevant stacktrace if the symbols are left there) :
    I can reproduce the issue on the linux build as well - the error happens directly in RemoveShape.
    Code:
    #0  0xf667cc75 in CBox2dShape::RemoveShape(int) () from /home/sdelang/KingArthursGold/BuildLinuxDesktop/libJuxta.so
    Edit : I have tested for a bit, and it looks like it is necessary to wait for one tick between the shape add and remove. The crash occurs when I add and remove the shape directly onInit. If I do those two things (once) in onTick, the shape that was created was not destroyed. If I wait a tick before destroying the shape, everything is performed correctly.
     
    Last edited: May 29, 2017
    joshua12131415, Geti and Osmal like this.
  4. Geti

    Geti Please avoid PMing me (poke a mod instead) THD Team Administrator Global Moderator

    Messages:
    3,730
    Hrm. Good to know - I'd advise against adding and then removing a shape in the short term, I'll see what can be done about it long term. Keeping this thread in the todo as a reference for exactly what goes wrong.
     
    Asu likes this.
  5. Asu

    Asu THD Team THD Team Forum Moderator

    Messages:
    1,580
    Looks like things are more broken than I thought...
    Whenever you use RemoveShape weird things might happen to other shapes - as if something was out of sync between the engine and box2D, or another awkward issue. It looks like both bugs are due to the same thing.
    The reason it doesn't cause a bug in TR is likely that it only has one extra shape to handle at a maximum.
    This is pretty bad because it is breaking for me, since I need obligatory separate shapes that can be added and removed on the fly, and I can't think of a workaround here :|

    I recorded a video of the ways I could reproduce the issue in my mod:

    It seemingly happens when changing the facing of the blob/shape (I assume the engine reverses the x of every vertice using an outdated vertice shape array) or apparently even when adding a new shape with AddShape.

    (Github)
     
  6. Geti

    Geti Please avoid PMing me (poke a mod instead) THD Team Administrator Global Moderator

    Messages:
    3,730
    Well, I can think of plenty but none of them are very nice :D Using separate blobs and enforcing constraints with a rules script is probably the simplest. The AddShape API was never meant to be used for dynamic shapes, it's just to allow simple concave shapes like the war boat.

    It "might" be a simple fix (it rarely is though) but I'm not going to think about it before we get a release out I'm afraid.
     
    Asu likes this.
  7. Asu

    Asu THD Team THD Team Forum Moderator

    Messages:
    1,580
    I thought about this solution as well but it would be clearly too heavy to implement and would be quite bad to maintain properly.
    I'd rather use blobs and attachments only for blobs that needs those (i.e. special blocks such as drills or wheels).

    Thanks anyway, there is quite a lot of stuff that I can implement in the mean time though, so I can wait to know if a fix might be pushed in the future.

    Edit: Actually rewriting to use blob attachments. I hope it doesn't get too slow with massive vehicles.
    --- Double Post Merged, Jun 30, 2017, Original Post Date: Jun 6, 2017 ---
    Now I started implementing it the blob attachment way, but I'm facing issues with attachments. This is the code that sets up the attachments.
    1. The attached blob (yellow block) angle is off the core block, which I don't want:
      [​IMG]
      I worked around it by doing setAngleDegrees each tick but I wonder if there's a better way.
    2. To enable collisions I set ShapeConsts::collideWhenAttached to true in the attached blob, but this doesn't work with tile collisions, and I need tile collision for the attached blob.
    Any ideas?
     
    Last edited: Jun 30, 2017
  8. Geti

    Geti Please avoid PMing me (poke a mod instead) THD Team Administrator Global Moderator

    Messages:
    3,730
    Don't bother with blob attachments, it's not very flexible. Use a single rules script (single point of update, only one script context and call required), list of connections (parent, child, local-space offset, local space angle difference), iterate through that list and apply the offset
    1. rotate the offset by the parent angle
    2. figure out the mid-point between the objects
    3. set the child and parent positions from that midpoint by half the rotated offset vector each - this handles collisions as if the child moves, the parent will be pushed
    4. set the child angle to the parent angle plus the angle offset
    This can probably (?) be done serverside only and rely on syncing, try and see at least (less to run on client).

    Should be reasonably fast (it's just burning through an array) and will be no more expensive bandwidth-wise than the CAttachment solution while letting you customise the behaviour completely.

    If the joints seem "spongy" you'll need to either do repeated iterations over the list (expensive, though not terrible; cost is still linear on the number of connections, just steeper cost per connection) or somehow figure out "more distorted" connections and re-iterate those (might be more expensive than just re-iterating tbh).
     
    Asu likes this.