Fix error accessing deleted objects after an object is deleted from a scene (#7612)

This commit is contained in:
Clément Pasteau
2025-05-15 18:18:09 +02:00
committed by GitHub
parent b219d50fd8
commit d7d17400dd
14 changed files with 91 additions and 7 deletions

View File

@@ -227,7 +227,7 @@ module.exports = {
behaviorProperties
.getOrCreate('object3D')
.setValue(behaviorContent.getChild('Object3D').getStringValue())
.setValue(behaviorContent.getChild('object3D').getStringValue())
.setType('Behavior')
.setLabel('3D capability')
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden)

View File

@@ -59,6 +59,7 @@ export type AskAiEditorInterface = {|
scene: gdLayout,
objectWithContext: ObjectWithContext
) => void,
onSceneObjectsDeleted: (scene: gdLayout) => void,
|};
const noop = () => {};
@@ -109,6 +110,7 @@ export const AskAi = React.memo<Props>(
forceUpdateEditor: noop,
onEventsBasedObjectChildrenEdited: noop,
onSceneObjectEdited: noop,
onSceneObjectsDeleted: noop,
}));
const aiRequestChatRef = React.useRef<AiRequestChatInterface | null>(

View File

@@ -151,6 +151,7 @@ export type RenderEditorContainerProps = {|
scene: gdLayout,
objectWithContext: ObjectWithContext
) => void,
onSceneObjectsDeleted: (scene: gdLayout) => void,
onExtractAsExternalLayout: (name: string) => void,
onExtractAsEventBasedObject: (

View File

@@ -237,9 +237,15 @@ export class CustomObjectEditorContainer extends React.Component<RenderEditorCon
onObjectEdited={() =>
this.props.onEventsBasedObjectChildrenEdited(eventsBasedObject)
}
onObjectsDeleted={() =>
this.props.onEventsBasedObjectChildrenEdited(eventsBasedObject)
}
onObjectGroupEdited={() =>
this.props.onEventsBasedObjectChildrenEdited(eventsBasedObject)
}
onObjectGroupsDeleted={() =>
this.props.onEventsBasedObjectChildrenEdited(eventsBasedObject)
}
onEventsBasedObjectChildrenEdited={
this.props.onEventsBasedObjectChildrenEdited
}

View File

@@ -57,6 +57,10 @@ export class DebuggerEditorContainer extends React.Component<
// No thing to be done.
}
onSceneObjectsDeleted(scene: gdLayout) {
// No thing to be done.
}
// To be updated, see https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops.
UNSAFE_componentWillReceiveProps() {
this._checkUserHasSubscription();

View File

@@ -53,6 +53,10 @@ export class EventsEditorContainer extends React.Component<RenderEditorContainer
// No thing to be done.
}
onSceneObjectsDeleted(scene: gdLayout) {
// No thing to be done.
}
getLayout(): ?gdLayout {
const { project, projectItemName } = this.props;
if (

View File

@@ -46,6 +46,10 @@ export class EventsFunctionsExtensionEditorContainer extends React.Component<Ren
// No thing to be done.
}
onSceneObjectsDeleted(scene: gdLayout) {
// No thing to be done.
}
shouldComponentUpdate(nextProps: RenderEditorContainerProps) {
// We stop updates when the component is inactive.
// If it's active, was active or becoming active again we let update propagate.

View File

@@ -90,6 +90,10 @@ export class ExternalEventsEditorContainer extends React.Component<
// No thing to be done.
}
onSceneObjectsDeleted(scene: gdLayout) {
// No thing to be done.
}
getExternalEvents(): ?gdExternalEvents {
const { project, projectItemName } = this.props;
if (!project || !projectItemName) return null;

View File

@@ -138,6 +138,20 @@ export class ExternalLayoutEditorContainer extends React.Component<
}
}
onSceneObjectsDeleted(scene: gdLayout) {
const { editor } = this;
const externalLayout = this.getExternalLayout();
if (!externalLayout) {
return;
}
if (externalLayout.getAssociatedLayout() !== scene.getName()) {
return;
}
if (editor) {
editor.forceUpdateObjectsList();
}
}
getExternalLayout(): ?gdExternalLayout {
const { project, projectItemName } = this.props;
if (!project || !projectItemName) return null;
@@ -266,8 +280,10 @@ export class ExternalLayoutEditorContainer extends React.Component<
onObjectEdited={objectWithContext =>
this.props.onSceneObjectEdited(layout, objectWithContext)
}
onObjectsDeleted={() => this.props.onSceneObjectsDeleted(layout)}
// It's only used to refresh events-based object variants.
onObjectGroupEdited={() => {}}
onObjectGroupsDeleted={() => {}}
// Nothing to do as events-based objects can't have external layout.
onEventsBasedObjectChildrenEdited={() => {}}
onExtensionInstalled={this.props.onExtensionInstalled}

View File

@@ -169,6 +169,7 @@ export type HomePageEditorInterface = {|
scene: gdLayout,
objectWithContext: ObjectWithContext
) => void,
onSceneObjectsDeleted: (scene: gdLayout) => void,
|};
export const HomePage = React.memo<Props>(
@@ -482,12 +483,17 @@ export const HomePage = React.memo<Props>(
[]
);
const onSceneObjectsDeleted = React.useCallback((scene: gdLayout) => {
// No thing to be done.
}, []);
React.useImperativeHandle(ref, () => ({
getProject,
updateToolbar,
forceUpdateEditor,
onEventsBasedObjectChildrenEdited,
onSceneObjectEdited,
onSceneObjectsDeleted,
}));
const onUserSurveyStarted = React.useCallback(() => {

View File

@@ -41,6 +41,10 @@ export class ResourcesEditorContainer extends React.Component<RenderEditorContai
// No thing to be done.
}
onSceneObjectsDeleted(scene: gdLayout) {
// No thing to be done.
}
componentDidUpdate(prevProps: RenderEditorContainerProps) {
if (
this.editor &&

View File

@@ -79,6 +79,20 @@ export class SceneEditorContainer extends React.Component<RenderEditorContainerP
}
}
onSceneObjectsDeleted(scene: gdLayout) {
const layout = this.getLayout();
if (!layout) {
return;
}
if (layout !== scene) {
return;
}
const { editor } = this;
if (editor) {
editor.forceUpdateObjectsList();
}
}
getLayout(): ?gdLayout {
const { project, projectItemName } = this.props;
if (
@@ -160,8 +174,10 @@ export class SceneEditorContainer extends React.Component<RenderEditorContainerP
onObjectEdited={objectWithContext =>
this.props.onSceneObjectEdited(layout, objectWithContext)
}
onObjectsDeleted={() => this.props.onSceneObjectsDeleted(layout)}
// It's only used to refresh events-based object variants.
onObjectGroupEdited={() => {}}
onObjectGroupsDeleted={() => {}}
// Nothing to do as scenes are not events-based objects.
onEventsBasedObjectChildrenEdited={() => {}}
/>

View File

@@ -2350,6 +2350,18 @@ const MainFrame = (props: Props) => {
[state.editorTabs]
);
const onSceneObjectsDeleted = React.useCallback(
(scene: gdLayout) => {
for (const editor of state.editorTabs.editors) {
const { editorRef } = editor;
if (editorRef) {
editorRef.onSceneObjectsDeleted(scene);
}
}
},
[state.editorTabs]
);
const _onProjectItemModified = () => {
triggerUnsavedChanges();
forceUpdate();
@@ -4048,6 +4060,7 @@ const MainFrame = (props: Props) => {
onDeleteEventsBasedObjectVariant: deleteEventsBasedObjectVariant,
onEventsBasedObjectChildrenEdited: onEventsBasedObjectChildrenEdited,
onSceneObjectEdited: onSceneObjectEdited,
onSceneObjectsDeleted: onSceneObjectsDeleted,
onExtensionInstalled: onExtensionInstalled,
gamesList,
gamesPlatformFrameTools,

View File

@@ -119,6 +119,9 @@ type Props = {|
eventsBasedObject: gdEventsBasedObject
) => void,
onObjectsDeleted: () => void,
onObjectGroupsDeleted: () => void,
setToolbar: (?React.Node) => void,
resourceManagementProps: ResourceManagementProps,
isActive: boolean,
@@ -973,7 +976,7 @@ export default class SceneEditor extends React.Component<Props, State> {
objectsWithContext: ObjectWithContext[],
done: boolean => void
) => {
const { project, layout, eventsBasedObject, onObjectEdited } = this.props;
const { project, layout, eventsBasedObject, onObjectsDeleted } = this.props;
objectsWithContext.forEach(objectWithContext => {
const { object, global } = objectWithContext;
@@ -1005,12 +1008,11 @@ export default class SceneEditor extends React.Component<Props, State> {
}
});
// Note: done() actually does the deletion of the objects,
// so ensure objectsWithContext are not used after this call.
done(true);
onObjectsDeleted();
objectsWithContext.forEach(objectWithContext => {
// TODO Avoid to do this N times.
onObjectEdited(objectWithContext);
});
// We modified the selection, so force an update of editors dealing with it.
this.forceUpdatePropertiesEditor();
this.updateToolbar();
@@ -1207,8 +1209,10 @@ export default class SceneEditor extends React.Component<Props, State> {
groupWithContext: GroupWithContext,
done: boolean => void
) => {
// done() actually does the deletion of the object group,
// so ensure groupWithContext is not used after this call.
done(true);
this.props.onObjectGroupEdited(groupWithContext);
this.props.onObjectGroupsDeleted();
};
_onRenameObjectGroup = (