Skip to content

Commit fd7f21e

Browse files
committed
More recipe search optimization (world data)
1 parent e21993f commit fd7f21e

File tree

11 files changed

+328
-107
lines changed

11 files changed

+328
-107
lines changed

src/main/java/com/robotgryphon/compactcrafting/blocks/FieldProjectorTile.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import com.robotgryphon.compactcrafting.field.FieldProjectionSize;
88
import com.robotgryphon.compactcrafting.field.ProjectorHelper;
99
import com.robotgryphon.compactcrafting.recipes.MiniaturizationRecipe;
10+
import com.robotgryphon.compactcrafting.world.ProjectionFieldSavedData;
11+
import com.robotgryphon.compactcrafting.world.ProjectorFieldData;
1012
import net.minecraft.block.BlockState;
1113
import net.minecraft.entity.item.ItemEntity;
1214
import net.minecraft.item.Item;
@@ -16,6 +18,7 @@
1618
import net.minecraft.util.Direction;
1719
import net.minecraft.util.math.AxisAlignedBB;
1820
import net.minecraft.util.math.BlockPos;
21+
import net.minecraft.world.server.ServerWorld;
1922
import net.minecraftforge.fml.RegistryObject;
2023

2124
import java.util.Collection;
@@ -95,8 +98,18 @@ public Optional<BlockPos> getMainProjectorPosition() {
9598
}
9699

97100
public void invalidateField() {
98-
this.field = Optional.empty();
99-
this.fieldCheckTimeout = 20;
101+
if(world == null || world.isRemote)
102+
return;
103+
104+
if(this.field.isPresent()) {
105+
BlockPos center = this.field.get().getCenterPosition();
106+
ProjectionFieldSavedData data = ProjectionFieldSavedData.get((ServerWorld) world);
107+
data.ACTIVE_FIELDS.remove(center);
108+
data.markDirty();
109+
110+
this.field = Optional.empty();
111+
this.fieldCheckTimeout = 20;
112+
}
100113
}
101114

102115
@Override
@@ -124,9 +137,19 @@ public void tick() {
124137
* Invalidates the current field projection and attempts to rebuild it from this position as an initial.
125138
*/
126139
private void doFieldCheck() {
140+
141+
127142
Optional<FieldProjection> field = FieldProjection.tryCreateFromPosition(world, this.pos);
128143
if (field.isPresent()) {
129144
this.field = field;
145+
FieldProjection fp = field.get();
146+
147+
if(world != null && !world.isRemote) {
148+
ProjectionFieldSavedData data = ProjectionFieldSavedData.get((ServerWorld) world);
149+
data.ACTIVE_FIELDS.put(fp.getCenterPosition(), ProjectorFieldData.fromInstance(fp));
150+
data.markDirty();
151+
}
152+
130153
return;
131154
}
132155

@@ -149,7 +172,6 @@ private void doRecipeScan() {
149172
// Only the primary projector needs to worry about the recipe scan
150173
if(!isMainProjector())
151174
{
152-
// getCenterForSize(IWorldReader world, BlockPos initial, FieldProjectionSize size) {
153175
Optional<BlockPos> center = ProjectorHelper.getCenterForSize(world, pos, size);
154176
BlockPos masterPos = ProjectorHelper.getProjectorLocationForDirection(world, center.get(), Direction.NORTH, size);
155177

src/main/java/com/robotgryphon/compactcrafting/core/Registration.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,11 @@ public class Registration {
8484
MiniaturizationRecipe rec = new MiniaturizationRecipe();
8585

8686
Set<BlockPos> template = new HashSet<>();
87-
BlockPos[] layerBlocks = new BlockPos[] {
88-
new BlockPos(0, 0, 0),
89-
new BlockPos(2, 0, 0),
87+
BlockPos[] layerBlocks = new BlockPos[]{
88+
new BlockPos(1, 0, 0),
89+
new BlockPos(0, 0, 1),
90+
new BlockPos(2, 0, 1),
91+
new BlockPos(1, 0, 2)
9092
};
9193

9294
Collections.addAll(template, layerBlocks);

src/main/java/com/robotgryphon/compactcrafting/events/EventHandler.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,23 @@
1414
public class EventHandler {
1515

1616
@SubscribeEvent
17-
public static void onBlockPlaced(final BlockEvent.EntityPlaceEvent blockPlaced) {
17+
public static void onBlockDestroyed(final BlockEvent.EntityPlaceEvent blockPlaced) {
1818
// Check if block is in or around a projector field
1919

2020
IWorld world = blockPlaced.getWorld();
2121
BlockPos pos = blockPlaced.getPos();
2222

23-
// We don't care about client worlds RN
24-
if(world.isRemote())
25-
return;
26-
2723
// Send the event position over to the field helper, so any nearby projectors can be notified
2824
FieldHelper.checkBlockPlacement(world, pos, BlockUpdateType.PLACE);
2925
}
3026

3127
@SubscribeEvent
32-
public static void onBlockPlaced(final BlockEvent.BreakEvent blockDestroyed) {
28+
public static void onBlockDestroyed(final BlockEvent.BreakEvent blockDestroyed) {
3329
// Check if block is in or around a projector field
3430

3531
IWorld world = blockDestroyed.getWorld();
3632
BlockPos pos = blockDestroyed.getPos();
3733

38-
// We don't care about client worlds RN
39-
if(world.isRemote())
40-
return;
41-
4234
// Send the event position over to the field helper, so any nearby projectors can be notified
4335
FieldHelper.checkBlockPlacement(world, pos, BlockUpdateType.REMOVE);
4436
}

src/main/java/com/robotgryphon/compactcrafting/field/FieldHelper.java

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,44 @@
11
package com.robotgryphon.compactcrafting.field;
22

33
import com.robotgryphon.compactcrafting.CompactCrafting;
4-
import com.robotgryphon.compactcrafting.blocks.FieldProjectorBlock;
54
import com.robotgryphon.compactcrafting.blocks.FieldProjectorTile;
65
import com.robotgryphon.compactcrafting.core.BlockUpdateType;
6+
import com.robotgryphon.compactcrafting.world.ProjectionFieldSavedData;
7+
import com.robotgryphon.compactcrafting.world.ProjectorFieldData;
78
import net.minecraft.util.math.AxisAlignedBB;
89
import net.minecraft.util.math.BlockPos;
910
import net.minecraft.world.IWorld;
11+
import net.minecraft.world.server.ServerWorld;
1012

13+
import java.util.ArrayList;
14+
import java.util.List;
1115
import java.util.stream.Stream;
1216

1317
/**
1418
* Provides utilities to help with projector field management.
1519
*/
1620
public abstract class FieldHelper {
1721
public static void checkBlockPlacement(IWorld world, BlockPos pos, BlockUpdateType type) {
18-
// Scan all blocks in the maximum field projection range (trying to find projectors)
19-
AxisAlignedBB scanBlocks = new AxisAlignedBB(pos).grow(FieldProjectionSize.maximum().getProjectorDistance() + 1);
22+
// We don't care about client worlds RN
23+
if(world.isRemote())
24+
return;
2025

21-
BlockPos[] potentials = BlockPos.getAllInBox(scanBlocks)
22-
.map(BlockPos::toImmutable)
23-
.filter(p -> !world.isAirBlock(p))
24-
.filter(p -> world.getBlockState(p).getBlock() instanceof FieldProjectorBlock)
25-
.toArray(BlockPos[]::new);
26+
ProjectionFieldSavedData data = ProjectionFieldSavedData.get((ServerWorld) world);
27+
if(data.ACTIVE_FIELDS.isEmpty())
28+
return;
2629

27-
for (BlockPos p : potentials) {
30+
List<BlockPos> projectors = new ArrayList<>();
31+
int maxDimensions = FieldProjectionSize.maximum().getDimensions();
32+
for(BlockPos center : data.ACTIVE_FIELDS.keySet()) {
33+
// FieldProjectorTile mainTile = (FieldProjectorTile) world.getTileEntity(fielf)
34+
boolean closeEnough = center.withinDistance(pos, maxDimensions);
35+
if(closeEnough) {
36+
ProjectorFieldData field = data.ACTIVE_FIELDS.get(center);
37+
projectors.add(field.mainProjector);
38+
}
39+
}
40+
41+
for (BlockPos p : projectors) {
2842
FieldProjectorTile tile = (FieldProjectorTile) world.getTileEntity(p);
2943

3044
CompactCrafting.LOGGER.debug("Got a block placed near a projector: " + p.getCoordinatesAsString());

src/main/java/com/robotgryphon/compactcrafting/recipes/IRecipeLayer.java

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
package com.robotgryphon.compactcrafting.recipes;
22

33
import net.minecraft.util.math.AxisAlignedBB;
4-
import net.minecraft.util.math.vector.Vector3i;
4+
import net.minecraft.util.math.BlockPos;
55
import net.minecraft.world.IWorldReader;
66

77
import java.util.Map;
8+
import java.util.Set;
89

910
public interface IRecipeLayer {
1011

12+
/**
13+
* Specifies if the current layer needs to add padding spaces (air)
14+
* around a recipe template.
15+
*
16+
* @param world
17+
* @param recipe
18+
* @return
19+
*/
20+
boolean hasPadding(IWorldReader world, MiniaturizationRecipe recipe);
21+
1122
/**
1223
* Determines if this layer matches a field's layer, given the blocks in the field
1324
* and the definition of the recipe layer. This is expected to rotate and check all four
@@ -25,15 +36,23 @@ public interface IRecipeLayer {
2536
*
2637
* @return
2738
*/
28-
Vector3i getDimensions();
39+
AxisAlignedBB getDimensions();
40+
41+
Map<String, Integer> getComponentTotals();
2942

3043
/**
31-
* Relative offset from center; if BlockPos.ZERO, center of this is assumed to be the center
32-
* of the layer.
33-
*
44+
* Gets a component key for the given (normalized) position.
45+
* @param pos
3446
* @return
3547
*/
36-
Vector3i getRelativeOffset();
48+
String getRequiredComponentKeyForPosition(BlockPos pos);
3749

38-
Map<String, Integer> getComponentTotals();
50+
/**
51+
* Gets a set of non-air positions that are required for the layer to match.
52+
* This is expected to trim the air positions off the edges and return the positions with NW
53+
* in the 0, 0 position.
54+
*
55+
* @return
56+
*/
57+
Set<BlockPos> getNonAirPositions();
3958
}

src/main/java/com/robotgryphon/compactcrafting/recipes/MiniaturizationRecipe.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,13 @@ public Optional<BlockState> getRecipeComponent(String i) {
105105

106106
return Optional.empty();
107107
}
108+
109+
public Optional<String> getRecipeComponentKey(BlockState state) {
110+
for(String comp : this.components.keySet()) {
111+
if(components.get(comp) == state)
112+
return Optional.of(comp);
113+
}
114+
115+
return Optional.empty();
116+
}
108117
}

src/main/java/com/robotgryphon/compactcrafting/recipes/PatternRecipeLayer.java

Lines changed: 0 additions & 30 deletions
This file was deleted.
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,110 @@
11
package com.robotgryphon.compactcrafting.recipes;
22

3+
import net.minecraft.block.BlockState;
4+
import net.minecraft.util.math.AxisAlignedBB;
5+
import net.minecraft.util.math.BlockPos;
6+
import net.minecraft.util.math.MutableBoundingBox;
7+
import net.minecraft.util.math.vector.Vector3i;
8+
import net.minecraft.world.IWorldReader;
9+
10+
import java.util.*;
11+
import java.util.stream.Stream;
12+
313
public abstract class RecipeHelper {
414

15+
public static BlockState getBlockStateForNormalizedLocation(IWorldReader world, BlockPos normalizedPos, AxisAlignedBB layerBounds) {
16+
Vector3i offset = new Vector3i(layerBounds.minX, layerBounds.minY, layerBounds.minZ);
17+
BlockPos offsetPos = normalizedPos.add(offset);
18+
19+
return world.getBlockState(offsetPos);
20+
}
21+
22+
/**
23+
* Converts world-coordinate positions into relative field positions.
24+
*
25+
* @param layerBounds The boundaries of the crafting field layer.
26+
* @param fieldPositions The non-air block positions in the field (world coordinates).
27+
* @return
28+
*/
29+
public static BlockPos[] normalizeLayerPositions(AxisAlignedBB layerBounds, BlockPos[] fieldPositions) {
30+
// Normalize the block positions so the recipe can match easier
31+
return Stream.of(fieldPositions)
32+
.parallel()
33+
.map(p -> new BlockPos(
34+
p.getX() - layerBounds.minX,
35+
p.getY() - layerBounds.minY,
36+
p.getZ() - layerBounds.minZ
37+
))
38+
.map(BlockPos::toImmutable)
39+
.toArray(BlockPos[]::new);
40+
}
41+
42+
public static AxisAlignedBB getBoundsForBlocks(Collection<BlockPos> filled) {
43+
if(filled.size() == 0)
44+
return AxisAlignedBB.withSizeAtOrigin(0, 0, 0);
45+
46+
MutableBoundingBox trimmedBounds = null;
47+
for(BlockPos filledPos : filled) {
48+
if(trimmedBounds == null) {
49+
trimmedBounds = new MutableBoundingBox(filledPos, filledPos);
50+
continue;
51+
}
52+
53+
MutableBoundingBox checkPos = new MutableBoundingBox(filledPos, filledPos);
54+
if(!trimmedBounds.intersectsWith(checkPos))
55+
trimmedBounds.expandTo(checkPos);
56+
}
57+
58+
return AxisAlignedBB.toImmutable(trimmedBounds);
59+
}
60+
61+
public static String[][] getTrimmedTemplateForLayer(IRecipeLayer layer, AxisAlignedBB fieldBounds) {
62+
Set<BlockPos> nonAir = layer.getNonAirPositions();
63+
AxisAlignedBB bounds = getBoundsForBlocks(nonAir);
64+
65+
int dim = (int) fieldBounds.getXSize();
66+
67+
String[][] components = new String[dim][dim];
68+
69+
for(BlockPos filledPos : nonAir) {
70+
String layerRequirement = layer.getRequiredComponentKeyForPosition(filledPos);
71+
72+
if(layerRequirement != null)
73+
components[filledPos.getX()][filledPos.getZ()] = layerRequirement;
74+
}
75+
76+
return components;
77+
}
78+
/**
79+
* Checks if a layer matches a template by extracting information about components
80+
* from the various filled positions.
81+
*
82+
* @param world
83+
* @param layer
84+
* @param filledLocations A world-space mapping of non-air blocks.
85+
* @return
86+
*/
87+
public static boolean layerMatchesTemplate(IWorldReader world, MiniaturizationRecipe recipe, IRecipeLayer layer, AxisAlignedBB fieldBounds, BlockPos[] filledLocations) {
88+
AxisAlignedBB trimmedBounds = getBoundsForBlocks(Arrays.asList(filledLocations));
89+
BlockPos[] normalizedLocations = normalizeLayerPositions(trimmedBounds, filledLocations);
90+
91+
String[][] template = getTrimmedTemplateForLayer(layer, fieldBounds);
92+
93+
// Finally, simply check the normalized template
94+
for(BlockPos pos : normalizedLocations) {
95+
String key = layer.getRequiredComponentKeyForPosition(pos);
96+
if(key == null)
97+
return false;
98+
99+
BlockState state = getBlockStateForNormalizedLocation(world, pos, layer.getDimensions());
100+
Optional<String> worldKey = recipe.getRecipeComponentKey(state);
101+
102+
// Is the position the correct block?
103+
boolean keyCorrect = worldKey.filter(key::equals).isPresent();
104+
if(!keyCorrect)
105+
return false;
106+
}
5107

108+
return true;
109+
}
6110
}

0 commit comments

Comments
 (0)