Skip to content

Commit dd0f49d

Browse files
committed
Redesigned entity listener to be async
- Created SelectBlockListener class - Created SelectFakeItemFrameListener class - Created SelectBlockOrImageTask class > Fixes #163
1 parent 93f9893 commit dd0f49d

File tree

7 files changed

+511
-393
lines changed

7 files changed

+511
-393
lines changed

src/main/java/io/josemmo/bukkit/plugin/commands/ImageCommand.java

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package io.josemmo.bukkit.plugin.commands;
22

33
import io.josemmo.bukkit.plugin.YamipaPlugin;
4+
import io.josemmo.bukkit.plugin.interaction.SelectBlockOrImageTask;
45
import io.josemmo.bukkit.plugin.renderer.FakeImage;
56
import io.josemmo.bukkit.plugin.renderer.ImageRenderer;
67
import io.josemmo.bukkit.plugin.renderer.ItemService;
78
import io.josemmo.bukkit.plugin.storage.ImageFile;
89
import io.josemmo.bukkit.plugin.storage.ImageStorage;
910
import io.josemmo.bukkit.plugin.utils.Logger;
1011
import io.josemmo.bukkit.plugin.utils.Permissions;
11-
import io.josemmo.bukkit.plugin.utils.SelectBlockTask;
1212
import io.josemmo.bukkit.plugin.utils.ActionBar;
1313
import org.bukkit.*;
1414
import org.bukkit.block.BlockFace;
@@ -211,9 +211,10 @@ public static void placeImage(
211211
final int finalHeight = (height == 0) ? FakeImage.getProportionalHeight(sizeInPixels, player, width) : height;
212212

213213
// Ask player where to place image
214-
SelectBlockTask task = new SelectBlockTask(player);
215-
task.onSuccess((location, face) -> placeImage(player, image, width, finalHeight, flags, location, face));
216-
task.onFailure(() -> ActionBar.send(player, ChatColor.RED + "Image placing canceled"));
214+
SelectBlockOrImageTask task = new SelectBlockOrImageTask(player);
215+
task.onBlock((location, face) -> placeImage(player, image, width, finalHeight, flags, location, face));
216+
task.onImage(__ -> ActionBar.send(player, ChatColor.RED + "There's already an image there!"));
217+
task.onCancel(() -> ActionBar.send(player, ChatColor.RED + "Image placing canceled"));
217218
task.run("Right click a block to continue");
218219
}
219220

@@ -255,14 +256,8 @@ public static boolean placeImage(
255256
}
256257

257258
public static void removeImage(@NotNull Player player) {
258-
SelectBlockTask task = new SelectBlockTask(player);
259-
task.onSuccess((location, face) -> {
260-
FakeImage image = YamipaPlugin.getInstance().getRenderer().getImage(location, face);
261-
if (image == null) {
262-
ActionBar.send(player, ChatColor.RED + "That is not a valid image!");
263-
return;
264-
}
265-
259+
SelectBlockOrImageTask task = new SelectBlockOrImageTask(player);
260+
task.onImage(image -> {
266261
// Check player's command permissions
267262
if (
268263
!player.getUniqueId().equals(image.getPlacedBy().getUniqueId()) &&
@@ -276,7 +271,8 @@ public static void removeImage(@NotNull Player player) {
276271
// Attempt to remove image
277272
removeImage(player, image);
278273
});
279-
task.onFailure(() -> ActionBar.send(player, ChatColor.RED + "Image removing canceled"));
274+
task.onBlock((location, face) -> ActionBar.send(player, ChatColor.RED + "That is not a valid image!"));
275+
task.onCancel(() -> ActionBar.send(player, ChatColor.RED + "Image removing canceled"));
280276
task.run("Right click an image to continue");
281277
}
282278

@@ -338,17 +334,8 @@ public static void clearImages(
338334
}
339335

340336
public static void describeImage(@NotNull Player player) {
341-
ImageRenderer renderer = YamipaPlugin.getInstance().getRenderer();
342-
343-
// Ask user to select fake image
344-
SelectBlockTask task = new SelectBlockTask(player);
345-
task.onSuccess((location, face) -> {
346-
FakeImage image = renderer.getImage(location, face);
347-
if (image == null) {
348-
ActionBar.send(player, ChatColor.RED + "That is not a valid image!");
349-
return;
350-
}
351-
337+
SelectBlockOrImageTask task = new SelectBlockOrImageTask(player);
338+
task.onImage(image -> {
352339
// Separate previous messages
353340
player.sendMessage("");
354341

@@ -405,7 +392,8 @@ public static void describeImage(@NotNull Player player) {
405392
}
406393
player.sendMessage(ChatColor.GOLD + "Flags: " + ChatColor.RESET + flagsStr);
407394
});
408-
task.onFailure(() -> ActionBar.send(player, ChatColor.RED + "Image describing canceled"));
395+
task.onBlock((location, face) -> ActionBar.send(player, ChatColor.RED + "That is not a valid image!"));
396+
task.onCancel(() -> ActionBar.send(player, ChatColor.RED + "Image describing canceled"));
409397
task.run("Right click the image to describe");
410398
}
411399

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package io.josemmo.bukkit.plugin.interaction;
2+
3+
import io.josemmo.bukkit.plugin.YamipaPlugin;
4+
import org.bukkit.block.Block;
5+
import org.bukkit.block.BlockFace;
6+
import org.bukkit.entity.Player;
7+
import org.bukkit.event.EventHandler;
8+
import org.bukkit.event.EventPriority;
9+
import org.bukkit.event.HandlerList;
10+
import org.bukkit.event.Listener;
11+
import org.bukkit.event.block.Action;
12+
import org.bukkit.event.player.PlayerInteractEvent;
13+
import org.bukkit.event.player.PlayerQuitEvent;
14+
import org.jetbrains.annotations.NotNull;
15+
16+
public abstract class SelectBlockListener implements Listener {
17+
/**
18+
* On left click (attack)
19+
* @param player Initiating player
20+
* @param block Targeted block
21+
* @param face Targeted block face
22+
* @return Whether to allow original event or not
23+
*/
24+
protected abstract boolean onLeftClick(@NotNull Player player, @NotNull Block block, @NotNull BlockFace face);
25+
26+
/**
27+
* On right click (interact)
28+
* @param player Initiating player
29+
* @param block Targeted block
30+
* @param face Targeted block face
31+
* @return Whether to allow original event or not
32+
*/
33+
protected abstract boolean onRightClick(@NotNull Player player, @NotNull Block block, @NotNull BlockFace face);
34+
35+
/**
36+
* On player quit
37+
* @param player Initiating player
38+
*/
39+
protected abstract void onPlayerQuit(@NotNull Player player);
40+
41+
/**
42+
* Register listener
43+
*/
44+
public final void register() {
45+
YamipaPlugin plugin = YamipaPlugin.getInstance();
46+
plugin.getServer().getPluginManager().registerEvents(this, plugin);
47+
}
48+
49+
/**
50+
* Unregister listener
51+
*/
52+
public final void unregister() {
53+
HandlerList.unregisterAll(this);
54+
}
55+
56+
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
57+
public final void onBlockInteraction(@NotNull PlayerInteractEvent event) {
58+
Player player = event.getPlayer();
59+
60+
// Get targeted block
61+
Block block = event.getClickedBlock();
62+
if (block == null) {
63+
return;
64+
}
65+
BlockFace face = event.getBlockFace();
66+
67+
// Handle event
68+
boolean allowEvent = true;
69+
Action action = event.getAction();
70+
if (action == Action.LEFT_CLICK_AIR || action == Action.LEFT_CLICK_BLOCK) {
71+
allowEvent = onLeftClick(player, block, face);
72+
} else if (action == Action.RIGHT_CLICK_BLOCK) {
73+
allowEvent = onRightClick(player, block, face);
74+
}
75+
76+
// Cancel event (if needed)
77+
if (!allowEvent) {
78+
event.setCancelled(true);
79+
}
80+
}
81+
82+
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
83+
public final void onPlayerQuit(@NotNull PlayerQuitEvent event) {
84+
onPlayerQuit(event.getPlayer());
85+
}
86+
}
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
package io.josemmo.bukkit.plugin.interaction;
2+
3+
import com.comphenix.protocol.events.ListenerPriority;
4+
import io.josemmo.bukkit.plugin.YamipaPlugin;
5+
import io.josemmo.bukkit.plugin.renderer.FakeImage;
6+
import io.josemmo.bukkit.plugin.utils.ActionBar;
7+
import io.josemmo.bukkit.plugin.utils.Logger;
8+
import org.bukkit.ChatColor;
9+
import org.bukkit.Location;
10+
import org.bukkit.block.Block;
11+
import org.bukkit.block.BlockFace;
12+
import org.bukkit.entity.Player;
13+
import org.jetbrains.annotations.NotNull;
14+
import org.jetbrains.annotations.Nullable;
15+
import java.util.HashMap;
16+
import java.util.Map;
17+
import java.util.UUID;
18+
import java.util.function.BiConsumer;
19+
import java.util.function.Consumer;
20+
21+
public class SelectBlockOrImageTask {
22+
private static final Logger LOGGER = Logger.getLogger("SelectBlockOrImageTask");
23+
private static final Map<UUID, SelectBlockOrImageTask> INSTANCES = new HashMap<>();
24+
private static @Nullable SelectBlockListener BLOCK_LISTENER;
25+
private static @Nullable SelectFakeItemFrameListener ITEM_FRAME_LISTENER;
26+
private final @NotNull Player player;
27+
private @Nullable BiConsumer<@NotNull Location, @NotNull BlockFace> blockCallback;
28+
private @Nullable Runnable cancelCallback;
29+
private @Nullable Consumer<@NotNull FakeImage> imageCallback;
30+
private @Nullable ActionBar actionBar;
31+
32+
/**
33+
* Class constructor
34+
* @param player Target player instance
35+
*/
36+
public SelectBlockOrImageTask(@NotNull Player player) {
37+
this.player = player;
38+
}
39+
40+
/**
41+
* Set on block callback
42+
* @param callback Block callback
43+
*/
44+
public void onBlock(@Nullable BiConsumer<@NotNull Location, @NotNull BlockFace> callback) {
45+
this.blockCallback = callback;
46+
}
47+
48+
/**
49+
* Set on fake image callback
50+
* @param callback Fake image callback
51+
*/
52+
public void onImage(@Nullable Consumer<@NotNull FakeImage> callback) {
53+
this.imageCallback = callback;
54+
}
55+
56+
/**
57+
* Set on cancel callback
58+
* @param callback Cancel callback
59+
*/
60+
public void onCancel(@Nullable Runnable callback) {
61+
this.cancelCallback = callback;
62+
}
63+
64+
/**
65+
* Run task
66+
* @param helpMessage Help message for the player
67+
*/
68+
@SuppressWarnings("deprecation")
69+
public synchronized void run(@NotNull String helpMessage) {
70+
// Keep track of this instance
71+
UUID uuid = player.getUniqueId();
72+
if (INSTANCES.containsKey(uuid)) {
73+
player.sendMessage(ChatColor.RED + "You already have a pending action!");
74+
return;
75+
}
76+
INSTANCES.put(uuid, this);
77+
78+
// Create block listener singleton (if needed)
79+
if (BLOCK_LISTENER == null) {
80+
BLOCK_LISTENER = new SelectBlockListener() {
81+
@Override
82+
protected boolean onLeftClick(@NotNull Player player, @NotNull Block block, @NotNull BlockFace face) {
83+
SelectBlockOrImageTask task = getInstance(player);
84+
if (task == null) {
85+
return true;
86+
}
87+
task.stop();
88+
if (task.cancelCallback != null) {
89+
task.cancelCallback.run();
90+
}
91+
return false;
92+
}
93+
94+
@Override
95+
protected boolean onRightClick(@NotNull Player player, @NotNull Block block, @NotNull BlockFace face) {
96+
SelectBlockOrImageTask task = getInstance(player);
97+
if (task == null) {
98+
return true;
99+
}
100+
task.stop();
101+
if (task.blockCallback != null) {
102+
task.blockCallback.accept(block.getLocation(), face);
103+
}
104+
return false;
105+
}
106+
107+
@Override
108+
protected void onPlayerQuit(@NotNull Player player) {
109+
SelectBlockOrImageTask task = getInstance(player);
110+
if (task == null) {
111+
return;
112+
}
113+
task.stop();
114+
if (task.cancelCallback != null) {
115+
task.cancelCallback.run();
116+
}
117+
}
118+
};
119+
BLOCK_LISTENER.register();
120+
LOGGER.fine("Created SelectBlockListener singleton");
121+
}
122+
123+
// Create fake item frame listener singleton (if needed)
124+
if (ITEM_FRAME_LISTENER == null) {
125+
ITEM_FRAME_LISTENER = new SelectFakeItemFrameListener() {
126+
@Override
127+
protected boolean onLeftClick(@NotNull Player player, int entityId) {
128+
SelectBlockOrImageTask task = getInstance(player);
129+
if (task == null) {
130+
return true;
131+
}
132+
task.stop();
133+
if (task.cancelCallback != null) {
134+
task.cancelCallback.run();
135+
}
136+
return false;
137+
}
138+
139+
@Override
140+
protected boolean onRightClick(@NotNull Player player, int entityId) {
141+
SelectBlockOrImageTask task = getInstance(player);
142+
if (task == null) {
143+
return true;
144+
}
145+
task.stop();
146+
if (task.imageCallback != null) {
147+
notifyImage(player, task.imageCallback);
148+
}
149+
return false;
150+
}
151+
152+
@Override
153+
protected @NotNull ListenerPriority getPriority() {
154+
return ListenerPriority.LOW;
155+
}
156+
};
157+
ITEM_FRAME_LISTENER.register();
158+
LOGGER.fine("Created SelectFakeItemFrameListener singleton");
159+
}
160+
161+
// Start action bar
162+
actionBar = ActionBar.repeat(
163+
player,
164+
ChatColor.GREEN + helpMessage + ChatColor.RESET + " - " + ChatColor.RED + "Left click to cancel"
165+
);
166+
}
167+
168+
/**
169+
* Stop task
170+
*/
171+
public synchronized void stop() {
172+
// Stop action bar
173+
if (actionBar != null) {
174+
actionBar.clear();
175+
actionBar = null;
176+
}
177+
178+
// Remove reference to this instance
179+
INSTANCES.remove(player.getUniqueId());
180+
if (!INSTANCES.isEmpty()) {
181+
return;
182+
}
183+
184+
// Destroy block listener singleton
185+
if (BLOCK_LISTENER != null) {
186+
BLOCK_LISTENER.unregister();
187+
BLOCK_LISTENER = null;
188+
LOGGER.fine("Destroyed SelectBlockListener singleton");
189+
}
190+
191+
// Destroy fake item frame listener singleton
192+
if (ITEM_FRAME_LISTENER != null) {
193+
ITEM_FRAME_LISTENER.unregister();
194+
ITEM_FRAME_LISTENER = null;
195+
LOGGER.fine("Destroyed SelectFakeItemFrameListener singleton");
196+
}
197+
}
198+
199+
/**
200+
* Get task instance for player
201+
* @param player Targeted player
202+
* @return Task instance
203+
*/
204+
private synchronized @Nullable SelectBlockOrImageTask getInstance(@NotNull Player player) {
205+
return INSTANCES.get(player.getUniqueId());
206+
}
207+
208+
/**
209+
* Notify image
210+
* @param player Player looking at the image
211+
* @param callback Image callback
212+
*/
213+
private void notifyImage(@NotNull Player player, @NotNull Consumer<@NotNull FakeImage> callback) {
214+
YamipaPlugin.getInstance().getScheduler().runInGame(() -> {
215+
FakeImage image = SelectFakeItemFrameListener.getFakeImage(player);
216+
if (image == null) {
217+
LOGGER.warning("Failed to get image selected by Player#" + player.getName());
218+
return;
219+
}
220+
callback.accept(image);
221+
}, player.getLocation(), 0);
222+
}
223+
}

0 commit comments

Comments
 (0)