Skip to content

Commit b17c8b0

Browse files
committed
Improvements related to thread-safety and some general cleanup
Also add duration a search took into the result message
1 parent 4a83247 commit b17c8b0

9 files changed

Lines changed: 69 additions & 58 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# <img src="assets/icon.png" height="32px" alt="Entity Detection Icon"/> EntityDetection
22

3-
EntityDetection is a Paper plugin with which you can quickly find chunks with a large amount of Monsters, Animals or even Tile Entities like Hoppers in it.
3+
EntityDetection is a Paper/Folia plugin with which you can quickly find chunks with a large amount of Monsters, Animals or even Tile Entities like Hoppers in it.
44

55
Very useful if you want to find XP-Farms that accumulate a large amount of mobs or that one infinite chicken or villager breeder that brings your server to its knees!
66

@@ -17,7 +17,7 @@ Very useful if you want to find XP-Farms that accumulate a large amount of mobs
1717

1818
## Dependencies
1919

20-
This plugin does not require any other plugin to run, but it requires [Paper](https://papermc.io/) to be used as the server software and can optionally integrate with [WorldGuard](https://worldguard.enginehub.org/) for region-based entity detection.
20+
This plugin does not require any other plugin to run, but it requires [Paper](https://papermc.io/downloads/paper) or [Folia](https://papermc.io/downloads/folia) to be used as the server software and can optionally integrate with [WorldGuard](https://worldguard.enginehub.org/) for region-based entity detection.
2121

2222
## Usage
2323

pom.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
<groupId>de.themoep</groupId>
88
<artifactId>entitydetection</artifactId>
99
<version>1.3.1-SNAPSHOT</version>
10-
<description>Paper plugin to find groups of mobs, animals or other (tile) entities.</description>
10+
<description>Paper/Folia plugin to find groups of mobs, animals or other (tile) entities.</description>
1111
<name>EntityDetection</name>
1212

1313
<properties>
1414
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1515
<minecraft.plugin.version>${project.version} ${buildDescription}</minecraft.plugin.version>
16-
<maven.compiler.source>1.8</maven.compiler.source>
17-
<maven.compiler.target>1.8</maven.compiler.target>
16+
<maven.compiler.source>11</maven.compiler.source>
17+
<maven.compiler.target>11</maven.compiler.target>
1818
</properties>
1919

2020
<repositories>
@@ -103,7 +103,7 @@
103103
</profiles>
104104

105105
<build>
106-
<finalName>${project.name}-${project.version}</finalName>
106+
<finalName>${project.name}</finalName>
107107
<resources>
108108
<resource>
109109
<filtering>true</filtering>

src/main/java/de/themoep/entitydetection/EntityDetection.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public Component getMessage(CommandSender sender, String key, String... replacem
7777
}
7878

7979
public boolean startSearch(EntitySearch search) {
80-
if(currentSearch != null && currentSearch.isRunning()) {
80+
if (currentSearch != null && currentSearch.isRunning()) {
8181
return false;
8282
}
8383
currentSearch = search;
@@ -86,7 +86,7 @@ public boolean startSearch(EntitySearch search) {
8686
}
8787

8888
public boolean stopSearch(String stopper) {
89-
if(currentSearch == null || !currentSearch.isRunning()) {
89+
if (currentSearch == null || !currentSearch.isRunning()) {
9090
return false;
9191
}
9292
currentSearch.stop(stopper);
@@ -95,7 +95,7 @@ public boolean stopSearch(String stopper) {
9595
}
9696

9797
public void addResult(SearchResult<?> result) {
98-
if(result.getType() == SearchType.CUSTOM && result.getSearched().size() == 1) {
98+
if (result.getType() == SearchType.CUSTOM && result.getSearched().size() == 1) {
9999
Set<String> searchedEntities = result.getSearched();
100100
customResults.put(searchedEntities.toArray(new String[searchedEntities.size()])[0], result);
101101
} else {
@@ -128,7 +128,7 @@ public void send(CommandSender sender, SearchResult<?> result, int page) {
128128
.append(getMessage(sender, "result.searched-types.entry", "type", Utils.enumToHumanName(typeIter.next())));
129129
}
130130

131-
Component message = getMessage(sender, "result.head", "type", Utils.enumToHumanName(result.getType()), "timestamp", dateStr);
131+
Component message = getMessage(sender, "result.head", "type", Utils.enumToHumanName(result.getType()), "timestamp", dateStr, "duration", String.valueOf(result.getDuration()));
132132
message = Replacer.replaceIn(message, "searchedtypes", searchedTypes);
133133

134134
List<? extends SearchResultEntry<?>> results = result.getSortedEntries();

src/main/java/de/themoep/entitydetection/searcher/ChunkSearchResult.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package de.themoep.entitydetection.searcher;
22

3-
import com.tcoded.folialib.impl.PlatformScheduler;
43
import de.themoep.entitydetection.ChunkLocation;
54
import de.themoep.entitydetection.Utils;
65
import org.bukkit.Bukkit;
@@ -14,11 +13,8 @@
1413

1514
public class ChunkSearchResult extends SearchResult<ChunkLocation> {
1615

17-
private final PlatformScheduler scheduler;
18-
1916
public ChunkSearchResult(EntitySearch search) {
2017
super(search);
21-
this.scheduler = search.getScheduler();
2218
}
2319

2420
@Override
@@ -35,7 +31,7 @@ public void addBlockState(BlockState blockState) {
3531
public void add(Location location, String type) {
3632
ChunkLocation chunkLocation = new ChunkLocation(location);
3733

38-
if(!resultEntryMap.containsKey(chunkLocation)) {
34+
if (!resultEntryMap.containsKey(chunkLocation)) {
3935
resultEntryMap.put(chunkLocation, new SearchResultEntry<>(chunkLocation));
4036
}
4137
resultEntryMap.get(chunkLocation).increment(type);
@@ -56,18 +52,18 @@ public void teleport(Player sender, SearchResultEntry<ChunkLocation> entry, int
5652
int anchorZ = (cz << 4) + 8;
5753
Location location = new Location(targetWorld, anchorX, 64, anchorZ);
5854

59-
targetWorld.getChunkAtAsync(cx, cz, true, chunk -> scheduler.runAtLocation(location, task -> {
55+
scheduler.runAtLocation(location, task -> targetWorld.getChunkAtAsync(cx, cz, false, chunk -> {
6056
Location loc = null;
6157

62-
for(Entity e : chunk.getEntities()) {
63-
if(e.getType().toString().equals(entry.getEntryCount().get(0).getKey())) {
58+
for (Entity e : chunk.getEntities()) {
59+
if (e.getType().toString().equals(entry.getEntryCount().get(0).getKey())) {
6460
loc = e.getLocation();
6561
break;
6662
}
6763
}
6864

6965
for (BlockState b : chunk.getTileEntities()) {
70-
if(b.getType().toString().equals(entry.getEntryCount().get(0).getKey())) {
66+
if (b.getType().toString().equals(entry.getEntryCount().get(0).getKey())) {
7167
loc = b.getLocation().add(0, 1, 0);
7268
break;
7369
}

src/main/java/de/themoep/entitydetection/searcher/EntitySearch.java

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
package de.themoep.entitydetection.searcher;
22

3+
import com.google.common.collect.Multimap;
4+
import com.google.common.collect.MultimapBuilder;
35
import com.tcoded.folialib.impl.PlatformScheduler;
46
import com.tcoded.folialib.wrapper.task.WrappedTask;
57
import de.themoep.entitydetection.EntityDetection;
68
import net.md_5.bungee.api.ChatColor;
79
import org.bukkit.Chunk;
10+
import org.bukkit.Location;
811
import org.bukkit.Material;
912
import org.bukkit.World;
1013
import org.bukkit.block.BlockState;
1114
import org.bukkit.command.CommandSender;
1215
import org.bukkit.entity.Entity;
1316
import org.bukkit.entity.EntityType;
1417

15-
import java.util.ArrayList;
16-
import java.util.Arrays;
1718
import java.util.Collections;
19+
import java.util.HashMap;
1820
import java.util.HashSet;
19-
import java.util.List;
21+
import java.util.Map;
2022
import java.util.Set;
2123
import java.util.concurrent.atomic.AtomicInteger;
24+
import java.util.function.Consumer;
2225

2326
/**
2427
* Copyright 2016 Max Lee (https://github.com/Phoenix616/)
@@ -35,7 +38,7 @@
3538
* You should have received a copy of the Mozilla Public License v2.0
3639
* along with this program. If not, see <http://mozilla.org/MPL/2.0/>.
3740
*/
38-
public class EntitySearch implements Runnable {
41+
public class EntitySearch implements Consumer<WrappedTask> {
3942
private final EntityDetection plugin;
4043
private final PlatformScheduler scheduler;
4144
private final CommandSender owner;
@@ -45,8 +48,8 @@ public class EntitySearch implements Runnable {
4548
private Set<Material> searchedMaterial = new HashSet<>();
4649
private long startTime;
4750
private boolean running = true;
48-
private List<Entity> entities = new ArrayList<>();
49-
private List<BlockState> blockStates = new ArrayList<>();
51+
private Multimap<EntityType, Location> entities = MultimapBuilder.hashKeys().arrayListValues().build();
52+
private Map<Material, Multimap<Class, Location>> blockStates = new HashMap<>();
5053

5154
private boolean isWorldGuardRegion = false;
5255
private final AtomicInteger pending = new AtomicInteger(0);
@@ -117,6 +120,7 @@ public boolean isWorldGuardRegion() {
117120
public void setWorldGuardRegion(boolean value) {
118121
this.isWorldGuardRegion = value;
119122
}
123+
120124
/**
121125
* Get the duration since this search started
122126
* @return The duration in seconds
@@ -125,18 +129,20 @@ public long getDuration() {
125129
return (System.currentTimeMillis() - getStartTime()) / 1000;
126130
}
127131

128-
public WrappedTask start() {
132+
public void start() {
129133
int scheduled = 0;
130134
if (searchedEntities.size() > 0) {
131135
for (World world : plugin.getServer().getWorlds()) {
132136
pending.incrementAndGet();
133137
scheduled++;
134138
scheduler.runAtLocation(world.getSpawnLocation(), task -> {
135139
try {
136-
entities.addAll(world.getEntities());
140+
for (Entity entity : world.getEntities()) {
141+
entities.put(entity.getType(), entity.getLocation());
142+
}
137143
} finally {
138144
if (pending.decrementAndGet() == 0) {
139-
scheduler.runLaterAsync(this, 1L);
145+
scheduler.runAsync(this);
140146
}
141147
}
142148
});
@@ -149,20 +155,26 @@ public WrappedTask start() {
149155
scheduled++;
150156
scheduler.runAtLocation(chunk.getBlock(0, 0, 0).getLocation(), task -> {
151157
try {
152-
blockStates.addAll(Arrays.asList(chunk.getTileEntities()));
158+
for (BlockState state : chunk.getTileEntities()) {
159+
Multimap<Class, Location> multiMap = blockStates.get(state.getType());
160+
if (multiMap == null) {
161+
multiMap = MultimapBuilder.hashKeys().arrayListValues().build();
162+
blockStates.put(state.getType(), multiMap);
163+
}
164+
multiMap.put(state.getClass(), state.getLocation());
165+
}
153166
} finally {
154167
if (pending.decrementAndGet() == 0) {
155-
scheduler.runLaterAsync(this, 1L);
168+
scheduler.runAsync(this);
156169
}
157170
}
158171
});
159172
}
160173
}
161174
}
162175
if (scheduled == 0) {
163-
return scheduler.runLaterAsync(this, 1L);
176+
scheduler.runAsync(this);
164177
}
165-
return null;
166178
}
167179

168180
public boolean isRunning() {
@@ -176,28 +188,28 @@ public void stop(String name) {
176188
}
177189
}
178190

179-
public void run() {
191+
public void accept(WrappedTask task) {
180192
startTime = System.currentTimeMillis();
181193
SearchResult<?> result;
182-
if(isWorldGuardRegion) result = new WGSearchResult(this);
194+
if (isWorldGuardRegion) result = new WGSearchResult(this);
183195
else result = new ChunkSearchResult(this);
184196

185-
for(Entity e : entities) {
186-
if(!running) {
187-
return;
197+
entities.forEach((type, location) -> {
198+
if (searchedEntities.contains(type)) {
199+
result.add(location, type.toString());
188200
}
189-
if(searchedEntities.contains(e.getType())) {
190-
result.addEntity(e);
191-
}
192-
}
201+
});
193202

194-
for (BlockState blockState : blockStates) {
195-
if (!running) {
196-
return;
197-
}
198-
if (searchedBlockStates.contains(BlockState.class) || searchedMaterial.contains(blockState.getType()) || searchedBlockStates.contains(blockState.getClass())) {
199-
result.addBlockState(blockState);
200-
}
203+
blockStates.forEach((type, blockLocations) -> {
204+
blockLocations.forEach((clazz, location) -> {
205+
if (searchedBlockStates.contains(BlockState.class) || searchedMaterial.contains(type) || searchedBlockStates.contains(clazz)) {
206+
result.add(location, type.toString());
207+
}
208+
});
209+
});
210+
211+
if (!running) {
212+
return;
201213
}
202214

203215
result.sort();

src/main/java/de/themoep/entitydetection/searcher/SearchResult.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package de.themoep.entitydetection.searcher;
22

3+
import com.tcoded.folialib.impl.PlatformScheduler;
34
import org.bukkit.Location;
45
import org.bukkit.Material;
56
import org.bukkit.block.BlockState;
@@ -31,6 +32,7 @@
3132
* along with this program. If not, see <http://mozilla.org/MPL/2.0/>.
3233
*/
3334
public abstract class SearchResult<T> {
35+
protected final PlatformScheduler scheduler;
3436
private SearchType type;
3537
private Set<String> searched;
3638
private long startTime;
@@ -47,6 +49,7 @@ public abstract class SearchResult<T> {
4749
protected List<SearchResultEntry<T>> resultEntryList = new ArrayList<>();
4850

4951
public SearchResult(EntitySearch search) {
52+
scheduler = search.getScheduler();
5053
type = search.getType();
5154
searched = new HashSet<>();
5255
for (EntityType e : search.getSearchedEntities()) {
@@ -87,6 +90,10 @@ public long getEndTime() {
8790
return endTime == 0 ? System.currentTimeMillis() : endTime;
8891
}
8992

93+
/**
94+
* Get the time this search took to finish
95+
* @return The duration in milliseconds
96+
*/
9097
public long getDuration() {
9198
return getEndTime() - getStartTime();
9299
}

src/main/java/de/themoep/entitydetection/searcher/SearchResultEntry.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public class SearchResultEntry<T> implements Comparable<SearchResultEntry<T>> {
3434

3535
public void increment(String type) {
3636
size++;
37-
if(!entryCount.containsKey(type)) {
37+
if (!entryCount.containsKey(type)) {
3838
entryCount.put(type, 1);
3939
} else {
4040
entryCount.put(type, entryCount.get(type) + 1);

src/main/java/de/themoep/entitydetection/searcher/WGSearchResult.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import com.sk89q.worldguard.protection.flags.Flags;
77
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
88
import com.sk89q.worldguard.protection.regions.RegionQuery;
9-
import com.tcoded.folialib.impl.PlatformScheduler;
109
import de.themoep.entitydetection.Utils;
1110
import org.bukkit.ChatColor;
1211
import org.bukkit.Location;
@@ -20,11 +19,9 @@
2019
import java.util.Objects;
2120

2221
public class WGSearchResult extends SearchResult<WGSearchResult.ProtectedRegionEntry> {
23-
private final PlatformScheduler scheduler;
2422

2523
public WGSearchResult(EntitySearch search) {
2624
super(search);
27-
this.scheduler = search.getScheduler();
2825
}
2926

3027
@Override
@@ -44,7 +41,7 @@ public void add(Location location, String type) {
4441

4542
applicableRegions.forEach(region -> {
4643
ProtectedRegionEntry protectedRegionEntry = new ProtectedRegionEntry(location.getWorld(), region);
47-
if(!resultEntryMap.containsKey(protectedRegionEntry)) {
44+
if (!resultEntryMap.containsKey(protectedRegionEntry)) {
4845
resultEntryMap.put(protectedRegionEntry, new SearchResultEntry<>(protectedRegionEntry));
4946
}
5047
resultEntryMap.get(protectedRegionEntry).increment(type);
@@ -56,18 +53,17 @@ public void teleport(Player sender, SearchResultEntry<WGSearchResult.ProtectedRe
5653
com.sk89q.worldedit.util.Location wgLocation = entry.getLocation().region.getFlag(Flags.TELE_LOC);
5754
try {
5855
World world = entry.getLocation().world.get();
59-
if(world == null) {
56+
if (world == null) {
6057
sender.sendMessage(ChatColor.RED + "World " + ChatColor.WHITE + entry.getLocation().worldName + ChatColor.RED + " is not loaded anymore.");
6158
return;
6259
}
6360
Location loc = wgLocation != null ? BukkitAdapter.adapt(wgLocation) : null;
64-
if(loc == null) {
61+
if (loc == null) {
6562
loc = BukkitAdapter.adapt(world, entry.getLocation().region.getMinimumPoint().add(
6663
entry.getLocation().region.getMaximumPoint().subtract(entry.getLocation().region.getMinimumPoint()).divide(2)));
6764
}
6865

69-
Location finalLoc = loc;
70-
scheduler.teleportAsync(sender, finalLoc, PlayerTeleportEvent.TeleportCause.PLUGIN);
66+
scheduler.teleportAsync(sender, loc, PlayerTeleportEvent.TeleportCause.PLUGIN);
7167

7268
sender.sendMessage(
7369
ChatColor.GREEN + "Teleported to entry " + ChatColor.WHITE + i + ": " +

src/main/resources/languages/lang.en.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ help:
1212
none: "&red&No sub commands for command %command% found."
1313
usage: "Usage: &yellow&%usage%"
1414
result:
15-
head: "[%type% search](green hover=%searchedtypes%) from %timestamp%:"
15+
head: "[%type% search](green hover=%searchedtypes%) from %timestamp% (%duration%ms):"
1616
searched-types:
1717
head: "&yellow&Entity Types:"
1818
entry: " &dark_purple&%type%"

0 commit comments

Comments
 (0)