Skip to content

Commit 9a6f71d

Browse files
soloturnclaude
andcommitted
fix(world): skip prefab components with no registered metadata instead of NPEing
ComponentLibrary.copy() returns null for a component class with no registered metadata (e.g. an optional-dependency module not loaded); these three call sites passed that straight into addComponent() -> NPE. BlockPrefabManager.updateBlock() already guards this; applied the same skip-and-log pattern here. Found via a live crash: ManualLabor:ShapingTable referencing MachineDefinitionComponent with `machines` not loaded. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 8f97a54 commit 9a6f71d

3 files changed

Lines changed: 36 additions & 3 deletions

File tree

engine/src/main/java/org/terasology/engine/world/block/items/BlockItemFactory.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import com.google.common.base.Preconditions;
66
import com.google.common.primitives.SignedBytes;
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
79
import org.terasology.engine.entitySystem.ComponentContainer;
810
import org.terasology.engine.entitySystem.entity.EntityBuilder;
911
import org.terasology.engine.entitySystem.entity.EntityManager;
@@ -37,6 +39,8 @@
3739
* @see RetainComponentsComponent
3840
*/
3941
public class BlockItemFactory {
42+
private static final Logger logger = LoggerFactory.getLogger(BlockItemFactory.class);
43+
4044
private final EntityManager entityManager;
4145

4246
/**
@@ -186,7 +190,14 @@ private void addComponents(EntityBuilder builder, ComponentContainer components)
186190

187191
for (Component component : components.iterateComponents()) {
188192
if (keepByAnnotation(component) || retainComponents.contains(component.getClass())) {
189-
builder.addComponent(entityManager.getComponentLibrary().copy(component));
193+
Component copy = entityManager.getComponentLibrary().copy(component);
194+
if (copy == null) {
195+
// No registered ComponentMetadata for component's class, most likely because a
196+
// module it depends on isn't loaded - see BlockPrefabManager.updateBlock().
197+
logger.error("No ComponentMetadata for {} - skipping", component.getClass().getName());
198+
continue;
199+
}
200+
builder.addComponent(copy);
190201
}
191202
}
192203
}

engine/src/main/java/org/terasology/engine/world/block/typeEntity/BlockTypeEntityGenerator.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// SPDX-License-Identifier: Apache-2.0
33
package org.terasology.engine.world.block.typeEntity;
44

5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
57
import org.terasology.engine.entitySystem.entity.EntityBuilder;
68
import org.terasology.engine.entitySystem.entity.EntityManager;
79
import org.terasology.engine.entitySystem.entity.EntityRef;
@@ -18,6 +20,8 @@
1820

1921
public class BlockTypeEntityGenerator implements BlockRegistrationListener {
2022

23+
private static final Logger logger = LoggerFactory.getLogger(BlockTypeEntityGenerator.class);
24+
2125
private EntityManager entityManager;
2226
private PrefabManager prefabManager;
2327
private BlockManager blockManager;
@@ -69,7 +73,17 @@ private void generateBlockTypeEntity(Block block) {
6973
if (prefab.isPresent()) {
7074
for (Component comp : prefab.get().iterateComponents()) {
7175
if (!(comp instanceof NetworkComponent)) {
72-
builder.addComponent(entityManager.getComponentLibrary().copy(comp));
76+
Component copy = entityManager.getComponentLibrary().copy(comp);
77+
if (copy == null) {
78+
// No registered ComponentMetadata for comp's class, most likely because a
79+
// module it depends on isn't loaded - see the matching guard and explanation
80+
// in BlockPrefabManager.updateBlock(). Skip it rather than NPE and take the
81+
// whole block type entity down.
82+
logger.error("No ComponentMetadata for {}, referenced by block {}'s prefab {} - skipping",
83+
comp.getClass().getName(), block.getURI(), prefab.get().getUrn());
84+
continue;
85+
}
86+
builder.addComponent(copy);
7387
}
7488
}
7589
}

engine/src/main/java/org/terasology/engine/world/internal/EntityAwareWorldProvider.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,15 @@ private void cleanUpTemporaryEntity(EntityRef entity) {
474474
for (Component comp : prefab.iterateComponents()) {
475475
Component currentComp = entity.getComponent(comp.getClass());
476476
if (currentComp == null) {
477-
entity.addComponent(entityManager.getComponentLibrary().copy(comp));
477+
Component copy = entityManager.getComponentLibrary().copy(comp);
478+
if (copy == null) {
479+
// No registered ComponentMetadata for comp's class, most likely because a
480+
// module it depends on isn't loaded - see BlockPrefabManager.updateBlock().
481+
logger.error("No ComponentMetadata for {}, referenced by prefab {} - skipping",
482+
comp.getClass().getName(), prefab.getUrn());
483+
continue;
484+
}
485+
entity.addComponent(copy);
478486
} else {
479487
ComponentMetadata<?> metadata = entityManager.getComponentLibrary().getMetadata(comp.getClass());
480488
boolean changed = false;

0 commit comments

Comments
 (0)