Skip to content

Commit ab2d06e

Browse files
authored
Merge pull request #89 from BentoBoxWorld/develop
Release 2.7.0
2 parents 93ce4b5 + d107dbf commit ab2d06e

File tree

5 files changed

+189
-64
lines changed

5 files changed

+189
-64
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
<!-- Do not change unless you want different name for local builds. -->
6666
<build.number>-LOCAL</build.number>
6767
<!-- This allows to change between versions. -->
68-
<build.version>2.6.1</build.version>
68+
<build.version>2.7.0</build.version>
6969

7070
<sonar.projectKey>BentoBoxWorld_Boxed</sonar.projectKey>
7171
<sonar.organization>bentobox-world</sonar.organization>

src/main/java/world/bentobox/boxed/BoxedPladdon.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@
77

88
public class BoxedPladdon extends Pladdon {
99

10+
private Boxed addon;
11+
1012
@Override
1113
public Addon getAddon() {
12-
return new Boxed();
14+
if (addon == null) {
15+
addon = new Boxed();
16+
}
17+
return addon;
1318
}
1419

1520
}

src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import world.bentobox.bentobox.util.Util;
2727
import world.bentobox.boxed.Boxed;
2828
import world.bentobox.boxed.listeners.NewAreaListener;
29-
import world.bentobox.boxed.listeners.NewAreaListener.StructureRecord;
29+
import world.bentobox.boxed.objects.ToBePlacedStructures.StructureRecord;
3030

3131
/**
3232
* Enables admins to place templates in a Box and have them recorded for future boxes.
@@ -149,7 +149,7 @@ public boolean execute(User user, String label, List<String> args) {
149149
int z = args.size() == 1 || args.get(3).equals("~") ? user.getLocation().getBlockZ() : Integer.parseInt(args.get(3).trim());
150150
Location spot = new Location(user.getWorld(), x, y, z);
151151
s.place(spot, true, sr, mirror, PALETTE, INTEGRITY, new Random());
152-
NewAreaListener.removeJigsaw(new StructureRecord(tag.getKey(), s, spot, sr, mirror, noMobs));
152+
NewAreaListener.removeJigsaw(new StructureRecord(tag.getKey(), tag.getKey(), spot, sr, mirror, noMobs));
153153
boolean result = saveStructure(spot, tag, user, sr, mirror);
154154
if (result) {
155155
user.sendMessage("boxed.commands.boxadmin.place.saved");

src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java

Lines changed: 102 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -58,28 +58,14 @@
5858
import world.bentobox.boxed.objects.BoxedJigsawBlock;
5959
import world.bentobox.boxed.objects.BoxedStructureBlock;
6060
import world.bentobox.boxed.objects.IslandStructures;
61+
import world.bentobox.boxed.objects.ToBePlacedStructures;
62+
import world.bentobox.boxed.objects.ToBePlacedStructures.StructureRecord;
6163

6264
/**
6365
* @author tastybento Place structures in areas after they are created
6466
*/
6567
public class NewAreaListener implements Listener {
6668

67-
/**
68-
* Structure record contains the name of the structure, the structure itself,
69-
* where it was placed and enums for rotation, mirror, and a flag to paste mobs
70-
* or not.
71-
*
72-
* @param name - name of structure
73-
* @param structure - Structure object
74-
* @param location - location where it has been placed
75-
* @param rot - rotation
76-
* @param mirror - mirror setting
77-
* @param noMobs - if false, mobs not pasted
78-
*/
79-
public record StructureRecord(String name, Structure structure, Location location, StructureRotation rot,
80-
Mirror mirror, Boolean noMobs) {
81-
}
82-
8369
private static final Map<Integer, EntityType> BUTCHER_ANIMALS = Map.of(0, EntityType.COW, 1, EntityType.SHEEP, 2,
8470
EntityType.PIG);
8571
private static final List<BlockFace> CARDINALS = List.of(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST,
@@ -95,16 +81,30 @@ public record StructureRecord(String name, Structure structure, Location locatio
9581
"village_snowy", "village_taiga");
9682
private final Boxed addon;
9783
private final File structureFile;
84+
/**
85+
* Queue for structures that have been determined to be built now
86+
*/
9887
private final Queue<StructureRecord> itemsToBuild = new LinkedList<>();
88+
89+
/**
90+
* Store for structures that are pending being built, e.g., waiting until the chunk they are is in loaded
91+
*/
92+
private final Map<Pair<Integer, Integer>, List<StructureRecord>> pending;
93+
94+
/**
95+
* A cache of all structures that have been placed. Used to determine if players have entered them
96+
*/
97+
private final Map<String, IslandStructures> islandStructureCache = new HashMap<>();
98+
9999
private static final Random rand = new Random();
100100
private boolean pasting = true;
101101
private static final Gson gson = new Gson();
102-
Pair<Integer, Integer> min = new Pair<>(0, 0);
103-
Pair<Integer, Integer> max = new Pair<>(0, 0);
102+
private static final String TODO = "ToDo";
103+
private static final String COULD_NOT_LOAD = "Could not load ";
104104
// Database handler for structure data
105105
private final Database<IslandStructures> handler;
106-
private final Map<String, IslandStructures> islandStructureCache = new HashMap<>();
107-
private Map<Pair<Integer, Integer>, List<StructureRecord>> readyToBuild = new HashMap<>();
106+
private final Database<ToBePlacedStructures> toPlace;
107+
108108
private static String bukkitVersion = "v" + Bukkit.getBukkitVersion().replace('.', '_').replace('-', '_');
109109
private static String pluginPackageName;
110110

@@ -120,12 +120,20 @@ public NewAreaListener(Boxed addon) {
120120
structureFile = new File(addon.getDataFolder(), "structures.yml");
121121
// Get database ready
122122
handler = new Database<>(addon, IslandStructures.class);
123-
// Try to build something every second
123+
// Load the pending structures
124+
toPlace = new Database<>(addon, ToBePlacedStructures.class);
125+
pending = this.loadToDos().getReadyToBuild();
126+
// Try to build something
124127
runStructurePrinter();
125128
}
126129

130+
/**
131+
* Runs a recurring task to build structures in the queue and register Jar structures.
132+
*/
127133
private void runStructurePrinter() {
134+
// Set up recurring task
128135
Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), this::buildStructure, 100, 60);
136+
// Run through all the structures in the Jar and register them with the server
129137
for (String js : JAR_STRUCTURES) {
130138
addon.saveResource("structures/" + js + ".nbt", false);
131139
File structureFile = new File(addon.getDataFolder(), "structures/" + js + ".nbt");
@@ -142,7 +150,7 @@ private void runStructurePrinter() {
142150
}
143151

144152
/**
145-
* Build something in the queue
153+
* Build something in the queue. Structures are built one by one
146154
*/
147155
private void buildStructure() {
148156
// Only kick off a build if there is something to build and something isn't
@@ -158,7 +166,12 @@ private void placeStructure(StructureRecord item) {
158166
// Set the semaphore - only paste one at a time
159167
pasting = true;
160168
// Place the structure - this cannot be done async
161-
item.structure().place(item.location(), true, item.rot(), item.mirror(), -1, 1, rand);
169+
Structure structure = Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString(item.structure()));
170+
if (structure == null) {
171+
BentoBox.getInstance().logError(COULD_NOT_LOAD + item.structure());
172+
return;
173+
}
174+
structure.place(item.location(), true, item.rot(), item.mirror(), -1, 1, rand);
162175
addon.log(item.name() + " placed at " + item.location().getWorld().getName() + " "
163176
+ Util.xyz(item.location().toVector()));
164177
// Remove any jigsaw artifacts
@@ -214,15 +227,19 @@ public void onChunkLoad(ChunkLoadEvent e) {
214227
return;
215228
}
216229
Pair<Integer, Integer> chunkCoords = new Pair<Integer, Integer>(chunk.getX(), chunk.getZ());
217-
if (this.readyToBuild.containsKey(chunkCoords)) {
218-
Iterator<StructureRecord> it = this.readyToBuild.get(chunkCoords).iterator();
230+
if (pending.containsKey(chunkCoords)) {
231+
Iterator<StructureRecord> it = pending.get(chunkCoords).iterator();
219232
while (it.hasNext()) {
220233
StructureRecord item = it.next();
221234
if (item.location().getWorld().equals(e.getWorld())) {
222235
this.itemsToBuild.add(item);
223236
it.remove();
224237
}
225238
}
239+
// Save to latest to the database
240+
ToBePlacedStructures tbd = new ToBePlacedStructures();
241+
tbd.setReadyToBuild(pending);
242+
toPlace.saveObjectAsync(tbd);
226243
}
227244
}
228245

@@ -336,46 +353,53 @@ private void place(ConfigurationSection section, Location center, Environment en
336353
if (world == null) {
337354
return;
338355
}
339-
// Loop through the structures in the file - there could be more than one
356+
357+
Map<Pair<Integer, Integer>, List<StructureRecord>> readyToBuild = new HashMap<>();
358+
340359
for (String vector : section.getKeys(false)) {
341-
StructureRotation rot = StructureRotation.NONE;
342-
Mirror mirror = Mirror.NONE;
343-
boolean noMobs = false;
344-
String name = section.getString(vector);
345-
// Check for rotation
346-
String[] split = name.split(",");
347-
if (split.length > 1) {
348-
// Rotation
349-
rot = Enums.getIfPresent(StructureRotation.class, split[1].strip().toUpperCase(Locale.ENGLISH))
350-
.or(StructureRotation.NONE);
351-
name = split[0];
352-
}
353-
if (split.length == 3) {
354-
// Mirror
355-
mirror = Enums.getIfPresent(Mirror.class, split[2].strip().toUpperCase(Locale.ENGLISH)).or(Mirror.NONE);
356-
}
357-
if (split.length == 4) {
358-
noMobs = split[3].strip().toUpperCase(Locale.ENGLISH).equals("NO_MOBS");
359-
}
360-
// Load Structure
361-
Structure s = Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString("minecraft:" + name));
362-
if (s == null) {
363-
BentoBox.getInstance().logError("Could not load " + name);
360+
String[] nameParts = section.getString(vector).split(",");
361+
String name = nameParts[0].strip();
362+
StructureRotation rotation = nameParts.length > 1
363+
? Enums.getIfPresent(StructureRotation.class, nameParts[1].strip().toUpperCase(Locale.ENGLISH)).or(
364+
StructureRotation.NONE)
365+
: StructureRotation.NONE;
366+
Mirror mirror = nameParts.length > 2
367+
? Enums.getIfPresent(Mirror.class, nameParts[2].strip().toUpperCase(Locale.ENGLISH)).or(Mirror.NONE)
368+
: Mirror.NONE;
369+
boolean noMobs = nameParts.length > 3 && "NO_MOBS".equalsIgnoreCase(nameParts[3].strip());
370+
371+
// Check the structure exists
372+
Structure structure = Bukkit.getStructureManager()
373+
.loadStructure(NamespacedKey.fromString("minecraft:" + name));
374+
if (structure == null) {
375+
BentoBox.getInstance().logError(COULD_NOT_LOAD + name);
364376
return;
365377
}
366-
// Extract coords
367-
String[] value = vector.split(",");
368-
if (value.length > 2) {
369-
int x = Integer.parseInt(value[0].strip()) + center.getBlockX();
370-
int y = Integer.parseInt(value[1].strip());
371-
int z = Integer.parseInt(value[2].strip()) + center.getBlockZ();
372-
Location l = new Location(world, x, y, z);
373-
readyToBuild.computeIfAbsent(new Pair<Integer, Integer>(x >> 4, z >> 4), k -> new ArrayList<>())
374-
.add(new StructureRecord(name, s, l, rot, mirror, noMobs));
378+
379+
String[] coords = vector.split(",");
380+
if (coords.length > 2) {
381+
int x = Integer.parseInt(coords[0].strip()) + center.getBlockX();
382+
int y = Integer.parseInt(coords[1].strip());
383+
int z = Integer.parseInt(coords[2].strip()) + center.getBlockZ();
384+
Location location = new Location(world, x, y, z);
385+
386+
readyToBuild.computeIfAbsent(new Pair<>(x >> 4, z >> 4), k -> new ArrayList<>())
387+
.add(new StructureRecord(name, "minecraft:" + name, location,
388+
rotation, mirror, noMobs));
375389
} else {
376-
addon.logError("Structure file syntax error: " + vector + ": " + Arrays.toString(value));
390+
addon.logError("Structure file syntax error: " + vector + ": " + Arrays.toString(coords));
377391
}
378392
}
393+
394+
ToBePlacedStructures tbd = this.loadToDos();
395+
Map<Pair<Integer, Integer>, List<StructureRecord>> mergedMap = tbd.getReadyToBuild();
396+
readyToBuild.forEach((key, value) -> mergedMap.merge(key, value, (list1, list2) -> {
397+
list1.addAll(list2);
398+
return list1;
399+
}));
400+
401+
tbd.setReadyToBuild(readyToBuild);
402+
toPlace.saveObjectAsync(tbd);
379403
}
380404

381405
/**
@@ -387,7 +411,11 @@ private void place(ConfigurationSection section, Location center, Environment en
387411
*/
388412
public static BoundingBox removeJigsaw(StructureRecord item) {
389413
Location loc = item.location();
390-
Structure structure = item.structure();
414+
Structure structure = Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString(item.structure()));
415+
if (structure == null) {
416+
BentoBox.getInstance().logError(COULD_NOT_LOAD + item.structure());
417+
return new BoundingBox();
418+
}
391419
StructureRotation structureRotation = item.rot();
392420
String key = item.name();
393421

@@ -600,4 +628,18 @@ private static String nmsData(Block block) {
600628
return handler.nmsData(block);
601629
}
602630

631+
private ToBePlacedStructures loadToDos() {
632+
if (!toPlace.objectExists(TODO)) {
633+
return new ToBePlacedStructures();
634+
}
635+
ToBePlacedStructures list = toPlace.loadObject(TODO);
636+
if (list == null) {
637+
return new ToBePlacedStructures();
638+
}
639+
if (!list.getReadyToBuild().isEmpty()) {
640+
addon.log("Loaded " + list.getReadyToBuild().size() + " structure todos.");
641+
}
642+
return list;
643+
}
644+
603645
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package world.bentobox.boxed.objects;
2+
3+
import java.util.HashMap;
4+
import java.util.List;
5+
import java.util.Map;
6+
7+
import org.bukkit.Location;
8+
import org.bukkit.block.structure.Mirror;
9+
import org.bukkit.block.structure.StructureRotation;
10+
11+
import com.google.gson.annotations.Expose;
12+
13+
import world.bentobox.bentobox.database.objects.DataObject;
14+
import world.bentobox.bentobox.database.objects.Table;
15+
import world.bentobox.bentobox.util.Pair;
16+
17+
/**
18+
* Stores all the structures to be placed in the world. This is a queue that is done over
19+
* time to avoid lag and if the server is stopped then the pending list is saved here
20+
* @author tastybento
21+
*
22+
*/
23+
@Table(name = "ToBePlacedStructures")
24+
public class ToBePlacedStructures implements DataObject {
25+
26+
/**
27+
* Structure record contains the name of the structure, the structure itself,
28+
* where it was placed and enums for rotation, mirror, and a flag to paste mobs
29+
* or not.
30+
*
31+
* @param name - name of structure
32+
* @param structure - Structure namespaced key
33+
* @param location - location where it has been placed
34+
* @param rot - rotation
35+
* @param mirror - mirror setting
36+
* @param noMobs - if false, mobs not pasted
37+
*/
38+
public record StructureRecord(@Expose String name, @Expose String structure, @Expose Location location,
39+
@Expose StructureRotation rot, @Expose Mirror mirror, @Expose Boolean noMobs) {
40+
}
41+
42+
@Expose
43+
String uniqueId = "ToDo";
44+
@Expose
45+
private Map<Pair<Integer, Integer>, List<StructureRecord>> readyToBuild = new HashMap<>();
46+
47+
/**
48+
* @return the uniqueId
49+
*/
50+
public String getUniqueId() {
51+
return uniqueId;
52+
}
53+
54+
/**
55+
* @param uniqueId the uniqueId to set
56+
*/
57+
public void setUniqueId(String uniqueId) {
58+
this.uniqueId = uniqueId;
59+
}
60+
61+
/**
62+
* @return the readyToBuild
63+
*/
64+
public Map<Pair<Integer, Integer>, List<StructureRecord>> getReadyToBuild() {
65+
if (readyToBuild == null) {
66+
readyToBuild = new HashMap<>();
67+
}
68+
return readyToBuild;
69+
}
70+
71+
/**
72+
* @param readyToBuild the readyToBuild to set
73+
*/
74+
public void setReadyToBuild(Map<Pair<Integer, Integer>, List<StructureRecord>> readyToBuild) {
75+
this.readyToBuild = readyToBuild;
76+
}
77+
78+
}

0 commit comments

Comments
 (0)