diff --git a/source/blender/animrig/intern/action.cc b/source/blender/animrig/intern/action.cc index 0d307fddf8b..08cb02f8633 100644 --- a/source/blender/animrig/intern/action.cc +++ b/source/blender/animrig/intern/action.cc @@ -697,6 +697,13 @@ void Action::slot_identifier_ensure_prefix(Slot &slot) void Action::slot_setup_for_id(Slot &slot, const ID &animated_id) { + if (!ID_IS_EDITABLE(this) || ID_IS_OVERRIDE_LIBRARY(this)) { + /* Do not write to linked data. For now, also avoid changing the slot identifier on an + * override. Actions cannot have library overrides at the moment, and when they do, this should + * actually get designed. For now, it's better to avoid editing data than editing too much. */ + return; + } + if (slot.has_idtype()) { BLI_assert(slot.idtype == GS(animated_id.name)); return; diff --git a/tests/data b/tests/data index 02bedf6aa5b..7330388f9a7 160000 --- a/tests/data +++ b/tests/data @@ -1 +1 @@ -Subproject commit 02bedf6aa5bf8ece10006a07b380edd3f2843191 +Subproject commit 7330388f9a7a457f9031b26fbf18bced9afc8430 diff --git a/tests/python/bl_animation_action.py b/tests/python/bl_animation_action.py index 261cd2489a7..3ba20a59397 100644 --- a/tests/python/bl_animation_action.py +++ b/tests/python/bl_animation_action.py @@ -162,6 +162,59 @@ class ActionSlotAssignmentTest(unittest.TestCase): cube_adt.action_slot = slot self.assertEqual(cube_adt.action_slot, slot_cube, "The slot should not have changed") + def test_untyped_slot_assignment_local(self): + """Test untyped slot assignment, with a local Action.""" + + action = self._load_legacy_action(link=False) + + # Assign the Action to a Mesh data-block. This should set the ID type of the Slot to 'MESH'. + mesh = bpy.data.meshes['Cube'] + mesh.animation_data_create().action = action + + slot = action.slots[0] + self.assertEqual('MESH', slot.target_id_type, "After assignment, the ID type should be specified.") + self.assertEqual("MELegacy Slot", slot.identifier) + + def test_untyped_slot_assignment_linked(self): + """Test untyped slot assignment, with a linked Action.""" + + action = self._load_legacy_action(link=True) + + # Assign the Action to a Mesh data-block. This should set the ID type of the Slot to 'MESH'. + mesh = bpy.data.meshes['Cube'] + mesh.animation_data_create().action = action + + slot = action.slots[0] + self.assertEqual( + 'UNSPECIFIED', + slot.target_id_type, + "After assignment, the ID type should remain UNSPECIFIED when the Action is linked.") + self.assertEqual("XXLegacy Slot", slot.identifier) + + @staticmethod + def _load_legacy_action(*, link: bool) -> bpy.types.Action: + # At the moment of writing, the only way to create an untyped slot is to + # load a legacy Action that has `id_root=0` and let the versioning code + # create the untyped slot. + blendpath = args.testdir / "legacy-action-without-idroot.blend" + + # Append or link the one Action from the legacy file. + with bpy.data.libraries.load(str(blendpath), link=link) as (data_in, data_out): + data_out.actions = data_in.actions + + # Using plain asserts here, because these are not part of the unit test. + # They're here to test that the test code itself is doing the right thing. + assert len(data_out.actions) == 1 + assert isinstance(data_out.actions[0], bpy.types.Action) + + # Check that the state of things is as expected. + action = data_out.actions[0] + slot = action.slots[0] + assert slot.target_id_type == 'UNSPECIFIED' + assert slot.identifier == "XXLegacy Slot" + + return action + class LimitationsTest(unittest.TestCase): """Test artificial limitations for the layered Action.