Skip to content

Commit 9575439

Browse files
skaldarnarketurn
andauthored
refactor(BlockItemFactory): remove code dupes; add doc; retain components (#4006)
* Remove code duplication * Update doc for RetainComponentsComponent * Keep components listed as _retain components_ when creating a block item * doc(BlockItemFactory): doc class and builder methods * doc(BlockItemFactory): doc for `BlockItemFactory::newInstance` methods * doc(BlockItemFactory): finalize documentation for class * Update engine/src/main/java/org/terasology/world/block/items/BlockItemFactory.java Co-authored-by: Kevin Turner <[email protected]> Co-authored-by: Kevin Turner <[email protected]>
2 parents f130438 + 9b47bc5 commit 9575439

File tree

2 files changed

+179
-57
lines changed

2 files changed

+179
-57
lines changed

engine/src/main/java/org/terasology/logic/common/RetainComponentsComponent.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@
2222
import java.util.Set;
2323

2424
/**
25-
* This component is intended to list component classes that are supposed to be retained when placing a block.
26-
*
27-
* If a block entity has a component that is not part of its prefab, retaining this component results in
28-
* the block entity still having this component afterwards. If not retained, it is likely to be removed instead.
25+
* This component is intended to list component classes that are supposed to be retained when converting between blocks
26+
* and block items.
27+
* <p>
28+
* If a block (item) entity has a component that is not part of its prefab, retaining this component results in the block
29+
* entity still having this component afterwards. If not retained, it is likely to be removed instead.
2930
*/
3031
public class RetainComponentsComponent implements Component {
3132
@Replicate

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

Lines changed: 174 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -15,68 +15,81 @@
1515
*/
1616
package org.terasology.world.block.items;
1717

18+
import com.google.common.base.Preconditions;
19+
import com.google.common.primitives.SignedBytes;
1820
import org.terasology.entitySystem.Component;
21+
import org.terasology.entitySystem.ComponentContainer;
1922
import org.terasology.entitySystem.entity.EntityBuilder;
2023
import org.terasology.entitySystem.entity.EntityManager;
2124
import org.terasology.entitySystem.entity.EntityRef;
22-
import org.terasology.entitySystem.prefab.Prefab;
2325
import org.terasology.logic.common.DisplayNameComponent;
26+
import org.terasology.logic.common.RetainComponentsComponent;
2427
import org.terasology.logic.inventory.ItemComponent;
2528
import org.terasology.rendering.logic.LightComponent;
2629
import org.terasology.world.block.family.BlockFamily;
2730

31+
import java.util.Collections;
2832
import java.util.Optional;
33+
import java.util.Set;
2934

3035
/**
36+
* A factory to create new <em>block items</em> for a {@link BlockFamily}.
37+
* <p>
38+
* A <strong>block item</strong> is an entity guaranteed to have the following components.
39+
* <ul>
40+
* <li>{@link BlockItemComponent}. the back-reference to the block family</li>
41+
* <li>{@link ItemComponent}. the item component with automatic stack id and quantity if block is stackable</li>
42+
* <li>{@link DisplayNameComponent}. the block family archetype display name</li>
43+
* </ul>
44+
* Components on the block prefab (or the reference block entity) will only be retained if the component class is
45+
* annotated with {@link AddToBlockBasedItem} or the component class is listed in {@link RetainComponentsComponent}.
46+
* <p>
47+
* The created entity (builder) is based on the block item base prefab ({@code engine:blockItemBase}).
48+
*
49+
* @see AddToBlockBasedItem
50+
* @see RetainComponentsComponent
3151
*/
3252
public class BlockItemFactory {
33-
private EntityManager entityManager;
53+
private final EntityManager entityManager;
3454

55+
/**
56+
* Instantiate new block item factory with the given entity manager.
57+
*
58+
* @param entityManager entity manager to create new {@link EntityBuilder}s and copy components from
59+
* reference entities or prefabs to the new block items
60+
*/
3561
public BlockItemFactory(EntityManager entityManager) {
3662
this.entityManager = entityManager;
3763
}
3864

65+
/**
66+
* Create a new block item for the given {@link BlockFamily}.
67+
* <p>
68+
* Attempts to resolve the corresponding block prefab to retrieve a list of potential components to add. The item
69+
* quantity defaults to 1.
70+
* <p>
71+
* Use {@link #newBuilder(BlockFamily, int)} if you want to modify the block item entity's properties before it gets
72+
* created.
73+
*
74+
* @param blockFamily block family to create the block item builder for
75+
* @return the block item entity
76+
*/
3977
public EntityRef newInstance(BlockFamily blockFamily) {
4078
return newInstance(blockFamily, 1);
4179
}
4280

4381
/**
44-
* Use this method instead of {@link #newInstance(BlockFamily)} to modify entity properties like the persistence
45-
* flag before it gets created.
82+
* Create a new block item for the given {@link BlockFamily} and item quantity.
83+
* <p>
84+
* Attempts to resolve the corresponding block prefab to retrieve a list of potential components to add.
85+
* <p>
86+
* Use {@link #newBuilder(BlockFamily, int)} if you want to modify the block item entity's properties before it gets
87+
* created.
4688
*
47-
* @param blockFamily must not be null
89+
* @param blockFamily block family to create the block item builder for
90+
* @param quantity item quantity (see {@link ItemComponent#stackCount}); constrained to [0...128)
91+
* @return the block item entity
4892
*/
49-
public EntityBuilder newBuilder(BlockFamily blockFamily, int quantity) {
50-
EntityBuilder builder = entityManager.newBuilder("engine:blockItemBase");
51-
if (blockFamily.getArchetypeBlock().getLuminance() > 0) {
52-
builder.addComponent(new LightComponent());
53-
}
54-
55-
// Copy the components from block prefab into the block item
56-
Optional<Prefab> prefab = blockFamily.getArchetypeBlock().getPrefab();
57-
if (prefab.isPresent()) {
58-
for (Component component : prefab.get().iterateComponents()) {
59-
if (component.getClass().getAnnotation(AddToBlockBasedItem.class) != null) {
60-
builder.addComponent(entityManager.getComponentLibrary().copy(component));
61-
}
62-
}
63-
}
64-
65-
DisplayNameComponent displayNameComponent = builder.getComponent(DisplayNameComponent.class);
66-
displayNameComponent.name = blockFamily.getDisplayName();
67-
68-
ItemComponent item = builder.getComponent(ItemComponent.class);
69-
if (blockFamily.getArchetypeBlock().isStackable()) {
70-
item.stackId = "block:" + blockFamily.getURI().toString();
71-
item.stackCount = (byte) quantity;
72-
}
73-
74-
BlockItemComponent blockItem = builder.getComponent(BlockItemComponent.class);
75-
blockItem.blockFamily = blockFamily;
76-
77-
return builder;
78-
}
79-
8093
public EntityRef newInstance(BlockFamily blockFamily, int quantity) {
8194
if (blockFamily == null) {
8295
return EntityRef.NULL;
@@ -85,37 +98,145 @@ public EntityRef newInstance(BlockFamily blockFamily, int quantity) {
8598
return builder.build();
8699
}
87100

101+
/**
102+
* Create a new block item for the given {@link BlockFamily}, with {@code blockEntity} as reference entity to retain
103+
* components from.
104+
* <p>
105+
* The item quantity defaults to 1.
106+
* <p>
107+
* Use {@link #newBuilder(BlockFamily, EntityRef, int)} if you want to modify the block item entity's properties
108+
* before it gets created.
109+
*
110+
* @param blockFamily block family to create the block item builder for
111+
* @param blockEntity reference block entity to retain components from
112+
* @return the block item entity
113+
*/
88114
public EntityRef newInstance(BlockFamily blockFamily, EntityRef blockEntity) {
89115
if (blockFamily == null) {
90116
return EntityRef.NULL;
91117
}
92118

119+
return createBuilder(blockFamily, blockEntity, (byte) 1).build();
120+
}
121+
122+
/**
123+
* Create a new block item builder for the given {@link BlockFamily} and item quantity.
124+
* <p>
125+
* Attempts to resolve the corresponding block prefab to retrieve a list of potential components to add.
126+
* <p>
127+
* Use this method if you want to modify the block item entity's properties before it gets created.
128+
*
129+
* @param blockFamily block family to create the block item builder for
130+
* @param quantity item quantity (see {@link ItemComponent#stackCount}); constrained to [0...128)
131+
* @return a pre-populated entity builder for a block item entity
132+
*/
133+
public EntityBuilder newBuilder(BlockFamily blockFamily, int quantity) {
134+
final ComponentContainer components =
135+
blockFamily.getArchetypeBlock().getPrefab()
136+
.map(p -> ((ComponentContainer) p))
137+
.orElse(EntityRef.NULL);
138+
139+
return createBuilder(blockFamily, components, SignedBytes.saturatedCast(quantity));
140+
}
141+
142+
/**
143+
* Create a new block item builder for the given {@link BlockFamily} and item quantity, with {@code blockEntity} as
144+
* reference entity to retain components from.
145+
* <p>
146+
* Use this method if you want to modify the block item entity's properties before it gets created.
147+
*
148+
* @param blockFamily block family to create the block item builder for
149+
* @param blockEntity reference block entity to retain components from
150+
* @param quantity item quantity (see {@link ItemComponent#stackCount}); constrained to [0...128)
151+
* @return a pre-populated entity builder for a block item entity
152+
*/
153+
public EntityBuilder newBuilder(BlockFamily blockFamily, EntityRef blockEntity, int quantity) {
154+
return createBuilder(blockFamily, blockEntity, SignedBytes.saturatedCast(quantity));
155+
}
156+
157+
/**
158+
* Create a new block item builder for the given {@link BlockFamily}.
159+
*
160+
* @param blockFamily block family to create the block item builder for
161+
* @param components potential components to add to the block item entity
162+
* @param quantity item quantity (see {@link ItemComponent#stackCount})
163+
* @return a pre-populated entity builder for a block item entity
164+
*/
165+
private EntityBuilder createBuilder(BlockFamily blockFamily, ComponentContainer components, byte quantity) {
166+
Preconditions.checkNotNull(blockFamily, "Block family must not be null when creating block item");
167+
93168
EntityBuilder builder = entityManager.newBuilder("engine:blockItemBase");
94-
if (blockFamily.getArchetypeBlock().getLuminance() > 0) {
95-
builder.addComponent(new LightComponent());
96-
}
169+
addComponents(builder, components);
97170

98-
// Copy the components from block prefab into the block item
99-
for (Component component : blockEntity.iterateComponents()) {
100-
if (component.getClass().getAnnotation(AddToBlockBasedItem.class) != null) {
171+
adjustLightComponent(builder, blockFamily);
172+
adjustDisplayNameComponent(builder, blockFamily);
173+
adjustItemComponent(builder, blockFamily, quantity);
174+
adjustBlockItemComponent(builder, blockFamily);
175+
176+
return builder;
177+
}
178+
179+
/**
180+
* Mutate the builder to add components from the given component container.
181+
* <p>
182+
* A component is only added to the builder if at least one of the following conditions holds:
183+
* <ul>
184+
* <li>the component class is annotated with {@link AddToBlockBasedItem}</li>
185+
* <li>the container contains a {@link RetainComponentsComponent} and the component class is listed as
186+
* retained</li>
187+
* </ul>
188+
* <p>
189+
* Components that should be added to the block item entity are <emph>copied</emph>.
190+
*
191+
* @param builder the builder to add the components to
192+
* @param components the container with potential components to add
193+
*/
194+
private void addComponents(EntityBuilder builder, ComponentContainer components) {
195+
final Set<Class<? extends Component>> retainComponents =
196+
Optional.ofNullable(components.getComponent(RetainComponentsComponent.class))
197+
.map(retain -> retain.components)
198+
.orElse(Collections.emptySet());
199+
200+
for (Component component : components.iterateComponents()) {
201+
if (keepByAnnotation(component) || retainComponents.contains(component.getClass())) {
101202
builder.addComponent(entityManager.getComponentLibrary().copy(component));
102203
}
103204
}
205+
}
104206

105-
DisplayNameComponent displayNameComponent = builder.getComponent(DisplayNameComponent.class);
106-
if (displayNameComponent != null) {
107-
displayNameComponent.name = blockFamily.getDisplayName();
108-
}
207+
private boolean keepByAnnotation(Component component) {
208+
return component.getClass().getAnnotation(AddToBlockBasedItem.class) != null;
209+
}
109210

110-
ItemComponent item = builder.getComponent(ItemComponent.class);
111-
if (blockFamily.getArchetypeBlock().isStackable()) {
112-
item.stackId = "block:" + blockFamily.getURI().toString();
113-
item.stackCount = (byte) 1;
211+
private void adjustLightComponent(EntityBuilder builder, BlockFamily blockFamily) {
212+
//TODO: set properties of the LightComponent based on the archetype block?
213+
if (blockFamily.getArchetypeBlock().getLuminance() > 0 && !builder.hasComponent(LightComponent.class)) {
214+
builder.addComponent(new LightComponent());
114215
}
216+
}
115217

116-
BlockItemComponent blockItem = builder.getComponent(BlockItemComponent.class);
117-
blockItem.blockFamily = blockFamily;
218+
private void adjustDisplayNameComponent(EntityBuilder builder, BlockFamily blockFamily) {
219+
builder.updateComponent(DisplayNameComponent.class, displayName -> {
220+
displayName.name = blockFamily.getDisplayName();
221+
return displayName;
222+
});
223+
}
118224

119-
return builder.build();
225+
private void adjustItemComponent(EntityBuilder builder, BlockFamily blockFamily, byte quantity) {
226+
builder.updateComponent(ItemComponent.class, item -> {
227+
if (blockFamily.getArchetypeBlock().isStackable()) {
228+
item.stackId = "block:" + blockFamily.getURI().toString();
229+
//TODO: quantity may be greater than item.maxStackSize
230+
item.stackCount = quantity;
231+
}
232+
return item;
233+
});
234+
}
235+
236+
private void adjustBlockItemComponent(EntityBuilder builder, BlockFamily blockFamily) {
237+
builder.updateComponent(BlockItemComponent.class, blockItem -> {
238+
blockItem.blockFamily = blockFamily;
239+
return blockItem;
240+
});
120241
}
121242
}

0 commit comments

Comments
 (0)