Skip to content

Commit dfc00f1

Browse files
Jakubk15vLuckyyygemini-code-assist[bot]
authored
GH-1133 Extend /back functionality to per-death and per-teleport (#1192)
* WIP permission for back on death. * Improve back logic * Update eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migration_0010_Move_back_to_dedicated_section.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Adjust to others' suggestions * Document tpaTimer to also work for /back command * Simplify if-else statement * Add missing comma * wip * Update messages * Adjust to others' suggestions (WIP) * Remove unused imports * Create a default executor for /back command * Add proper permission to BackCommand * Reduce nesting level * Properly send a message to player when teleporting back * Fix ClassCastException * Revert accidental world name change * Encapsulate classes * Delete deprecated translations in favor for the new dedicated back section * Fix comment formatting in TeleportRequestConfig * Rename `backTeleportTimer` to `backTeleportTime` in BackSettings, BackConfig, and BackService for clarity and consistency. --------- Co-authored-by: Martin Sulikowski <[email protected]> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent cb24876 commit dfc00f1

File tree

16 files changed

+402
-91
lines changed

16 files changed

+402
-91
lines changed

eternalcore-core/src/main/java/com/eternalcode/core/configuration/implementation/PluginConfiguration.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import com.eternalcode.core.feature.afk.AfkSettings;
88
import com.eternalcode.core.feature.automessage.AutoMessageConfig;
99
import com.eternalcode.core.feature.automessage.AutoMessageSettings;
10+
import com.eternalcode.core.feature.back.BackConfig;
11+
import com.eternalcode.core.feature.back.BackSettings;
1012
import com.eternalcode.core.feature.broadcast.BroadcastConfig;
1113
import com.eternalcode.core.feature.broadcast.BroadcastSettings;
1214
import com.eternalcode.core.feature.butcher.ButcherConfig;
@@ -50,8 +52,6 @@
5052
import eu.okaeri.configs.OkaeriConfig;
5153
import eu.okaeri.configs.annotation.Comment;
5254
import eu.okaeri.configs.annotation.Header;
53-
import org.bukkit.Sound;
54-
5555
import java.io.File;
5656

5757
@Header({
@@ -213,6 +213,12 @@ public static class Format extends OkaeriConfig {
213213
@Comment("# Settings responsible for player vanish functionality")
214214
VanishConfig vanish = new VanishConfig();
215215

216+
@Bean(proxied = BackSettings.class)
217+
@Comment("")
218+
@Comment("# Back Configuration")
219+
@Comment("# Settings for the /back command functionality")
220+
BackConfig back = new BackConfig();
221+
216222
@Override
217223
public File getConfigFile(File dataFolder) {
218224
return new File(dataFolder, "config.yml");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.eternalcode.core.configuration.migrations;
2+
3+
import static eu.okaeri.configs.migrate.ConfigMigrationDsl.move;
4+
5+
import eu.okaeri.configs.migrate.builtin.NamedMigration;
6+
7+
public class Migration_0030_Move_back_to_dedicated_section extends NamedMigration {
8+
Migration_0030_Move_back_to_dedicated_section() {
9+
super(
10+
"Move back to dedicated section",
11+
move("teleport.teleportedToLastLocation", "back.teleportedToLastTeleportLocation"),
12+
move("teleport.teleportedSpecifiedPlayerLastLocation", "back.teleportedTargetPlayerToLastTeleportLocation"),
13+
move("teleport.lastLocationNoExist", "back.lastLocationNotFound")
14+
);
15+
}
16+
}
17+

eternalcore-core/src/main/java/com/eternalcode/core/configuration/migrations/Migrations.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class Migrations {
3232
new Migration_0028_Move_skull_messages_to_dedicated_section(),
3333
new Migration_0029_Move_enchant_messages_to_dedicated_section(),
3434
new Migration_0015_Move_ignore_messages_to_dedicated_section(),
35+
new Migration_0030_Move_back_to_dedicated_section(),
3536
new Migration_0031_Move_death_messages_to_dedicated_section(),
3637
new Migration_0032_Move_join_quit_messages_to_dedicated_section(),
3738
new Migration_0033_Move_disposal_messages_to_dedicated_section(),
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package com.eternalcode.core.feature.back;
2+
3+
import com.eternalcode.annotations.scan.command.DescriptionDocs;
4+
import com.eternalcode.commons.bukkit.position.Position;
5+
import com.eternalcode.core.injector.annotations.Inject;
6+
import com.eternalcode.core.notice.NoticeService;
7+
import com.eternalcode.core.viewer.Viewer;
8+
import dev.rollczi.litecommands.annotations.argument.Arg;
9+
import dev.rollczi.litecommands.annotations.command.Command;
10+
import dev.rollczi.litecommands.annotations.context.Sender;
11+
import dev.rollczi.litecommands.annotations.execute.Execute;
12+
import dev.rollczi.litecommands.annotations.permission.Permission;
13+
import java.util.Optional;
14+
import org.bukkit.entity.Player;
15+
16+
@Command(name = "back")
17+
class BackCommand {
18+
19+
private final BackService backService;
20+
private final NoticeService noticeService;
21+
22+
@Inject
23+
public BackCommand(BackService backService, NoticeService noticeService) {
24+
this.backService = backService;
25+
this.noticeService = noticeService;
26+
}
27+
28+
29+
@Execute
30+
@Permission("eternalcore.back")
31+
@DescriptionDocs(description = "Teleport to your last teleport/death location, depending which one is most recent")
32+
public void executeBack(@Sender Player player) {
33+
Optional<Position> latestPositionOptional = this.backService.getLatestLocation(player.getUniqueId());
34+
if (latestPositionOptional.isEmpty()) {
35+
this.noticeService.player(player.getUniqueId(), translation -> translation.back().lastLocationNotFound());
36+
return;
37+
}
38+
Position latestPosition = latestPositionOptional.get();
39+
Position deathPosition = this.backService.getDeathLocation(player.getUniqueId()).orElse(null);
40+
41+
if (latestPosition.equals(deathPosition)) {
42+
this.executeBackDeath(player);
43+
return;
44+
}
45+
this.executeBackTeleport(player);
46+
}
47+
48+
49+
@Execute(name = "teleport")
50+
@Permission("eternalcore.back.teleport")
51+
@DescriptionDocs(description = "Teleport to your last teleport location")
52+
public void executeBackTeleport(@Sender Player player) {
53+
if (this.backService.teleportBack(player)) {
54+
this.noticeService.player(player.getUniqueId(), translation -> translation.back().teleportedToLastTeleportLocation());
55+
return;
56+
}
57+
this.noticeService.player(player.getUniqueId(), translation -> translation.back().lastLocationNotFound());
58+
}
59+
60+
@Execute(name = "death")
61+
@Permission("eternalcore.back.death")
62+
@DescriptionDocs(description = "Teleport to your last death location")
63+
public void executeBackDeath(@Sender Player player) {
64+
if (this.backService.teleportBackDeath(player)) {
65+
this.noticeService.player(player.getUniqueId(), translation -> translation.back().teleportedToLastDeathLocation());
66+
return;
67+
}
68+
this.noticeService.player(player.getUniqueId(), translation -> translation.back().lastLocationNotFound());
69+
}
70+
71+
@Execute(name = "teleport")
72+
@Permission("eternalcore.back.teleport.other")
73+
@DescriptionDocs(description = "Teleport specified player to their last teleport location", arguments = "<player>")
74+
public void executeBackTeleportOther(@Sender Viewer viewer, @Arg Player target) {
75+
if (!this.backService.teleportBack(target)) {
76+
this.noticeService.player(viewer.getUniqueId(), translation -> translation.back().lastLocationNotFound());
77+
return;
78+
}
79+
80+
this.noticeService.player(target.getUniqueId(), translation -> translation.back().teleportedToLastTeleportLocationByAdmin());
81+
this.noticeService.create()
82+
.viewer(viewer)
83+
.notice(translation -> translation.back().teleportedTargetPlayerToLastTeleportLocation())
84+
.placeholder("{PLAYER}", target.getName())
85+
.send();
86+
}
87+
88+
@Execute(name = "death")
89+
@Permission("eternalcore.back.death.other")
90+
@DescriptionDocs(description = "Teleport specified player to their last death location", arguments = "<player>")
91+
public void executeBackDeathOther(@Sender Viewer viewer, @Arg Player target) {
92+
if (!this.backService.teleportBackDeath(target)) {
93+
this.noticeService.player(viewer.getUniqueId(), translation -> translation.back().lastLocationNotFound());
94+
return;
95+
}
96+
97+
this.noticeService.player(target.getUniqueId(), translation -> translation.back().teleportedToLastDeathLocationByAdmin());
98+
this.noticeService.create()
99+
.viewer(viewer)
100+
.notice(translation -> translation.back().teleportedTargetPlayerToLastDeathLocation())
101+
.placeholder("{PLAYER}", target.getName())
102+
.send();
103+
104+
}
105+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.eternalcode.core.feature.back;
2+
3+
import eu.okaeri.configs.OkaeriConfig;
4+
import eu.okaeri.configs.annotation.Comment;
5+
import java.time.Duration;
6+
import lombok.Getter;
7+
import lombok.experimental.Accessors;
8+
9+
@Getter
10+
@Accessors(fluent = true)
11+
public class BackConfig extends OkaeriConfig implements BackSettings {
12+
13+
@Comment("# Time of teleportation time in /back command.")
14+
public Duration backTeleportTime = Duration.ofSeconds(5);
15+
16+
@Comment("# Duration of caching last locations for /back command.")
17+
public Duration backLocationCacheDuration = Duration.ofHours(1);
18+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.eternalcode.core.feature.back;
2+
3+
import com.eternalcode.commons.bukkit.position.PositionAdapter;
4+
import com.eternalcode.core.injector.annotations.Inject;
5+
import com.eternalcode.core.injector.annotations.component.Controller;
6+
import org.bukkit.entity.Player;
7+
import org.bukkit.event.EventHandler;
8+
import org.bukkit.event.EventPriority;
9+
import org.bukkit.event.Listener;
10+
import org.bukkit.event.entity.PlayerDeathEvent;
11+
import org.bukkit.event.player.PlayerTeleportEvent;
12+
13+
@Controller
14+
class BackController implements Listener {
15+
16+
private final BackService backService;
17+
18+
@Inject
19+
BackController(BackService backService) {
20+
this.backService = backService;
21+
}
22+
23+
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
24+
public void onPlayerDeath(PlayerDeathEvent event) {
25+
Player entity = event.getEntity();
26+
27+
this.backService.markDeathLocation(entity.getUniqueId(), PositionAdapter.convert(entity.getLocation()));
28+
}
29+
30+
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
31+
public void onPlayerTeleport(PlayerTeleportEvent event) {
32+
if (event.getCause() == PlayerTeleportEvent.TeleportCause.PLUGIN) {
33+
return;
34+
}
35+
36+
this.backService.markTeleportLocation(event.getPlayer().getUniqueId(), PositionAdapter.convert(event.getFrom()));
37+
}
38+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package com.eternalcode.core.feature.back;
2+
3+
import com.eternalcode.commons.bukkit.position.Position;
4+
import com.eternalcode.commons.bukkit.position.PositionAdapter;
5+
import com.eternalcode.core.feature.teleport.TeleportService;
6+
import com.eternalcode.core.feature.teleport.TeleportTaskService;
7+
import com.eternalcode.core.injector.annotations.Inject;
8+
import com.eternalcode.core.injector.annotations.component.Service;
9+
import com.github.benmanes.caffeine.cache.Cache;
10+
import com.github.benmanes.caffeine.cache.Caffeine;
11+
import java.util.Optional;
12+
import java.util.UUID;
13+
import org.bukkit.Location;
14+
import org.bukkit.entity.Player;
15+
16+
@Service
17+
class BackService {
18+
19+
private static final String BYPASS_PERMISSION = "eternalcore.teleport.bypass";
20+
21+
private final TeleportService teleportService;
22+
private final TeleportTaskService teleportTaskService;
23+
private final BackSettings settings;
24+
25+
private final Cache<UUID, Position> deathLocations;
26+
private final Cache<UUID, Position> teleportLocations;
27+
private final Cache<UUID, Position> latestLocations;
28+
29+
@Inject
30+
BackService(
31+
TeleportService teleportService,
32+
TeleportTaskService teleportTaskService,
33+
BackSettings settings
34+
) {
35+
this.teleportService = teleportService;
36+
this.teleportTaskService = teleportTaskService;
37+
this.settings = settings;
38+
39+
this.deathLocations = Caffeine.newBuilder()
40+
.expireAfterWrite(settings.backLocationCacheDuration())
41+
.build();
42+
this.teleportLocations = Caffeine.newBuilder()
43+
.expireAfterWrite(settings.backLocationCacheDuration())
44+
.build();
45+
this.latestLocations = Caffeine.newBuilder()
46+
.expireAfterWrite(settings.backLocationCacheDuration())
47+
.build();
48+
}
49+
50+
public Optional<Position> getDeathLocation(UUID playerId) {
51+
return Optional.ofNullable(deathLocations.getIfPresent(playerId));
52+
}
53+
54+
public Optional<Position> getTeleportLocation(UUID playerId) {
55+
return Optional.ofNullable(teleportLocations.getIfPresent(playerId));
56+
}
57+
58+
public Optional<Position> getLatestLocation(UUID playerId) {
59+
return Optional.ofNullable(latestLocations.getIfPresent(playerId));
60+
}
61+
62+
public void markDeathLocation(UUID player, Position position) {
63+
this.deathLocations.put(player, position);
64+
this.latestLocations.put(player, position);
65+
}
66+
67+
public void markTeleportLocation(UUID player, Position position) {
68+
this.teleportLocations.put(player, position);
69+
this.latestLocations.put(player, position);
70+
}
71+
72+
public boolean teleportBack(Player target) {
73+
Optional<Position> teleportLocation = this.getTeleportLocation(target.getUniqueId());
74+
75+
if (teleportLocation.isPresent()) {
76+
this.teleportBack(target, PositionAdapter.convert(teleportLocation.get()));
77+
78+
return true;
79+
}
80+
return false;
81+
}
82+
83+
public boolean teleportBackDeath(Player target) {
84+
Optional<Position> deathLocation = this.getDeathLocation(target.getUniqueId());
85+
86+
if (deathLocation.isPresent()) {
87+
this.teleportBack(target, PositionAdapter.convert(deathLocation.get()));
88+
89+
return true;
90+
}
91+
return false;
92+
}
93+
94+
private void teleportBack(Player player, Location location) {
95+
if (player.hasPermission(BYPASS_PERMISSION)) {
96+
teleportService.teleport(player, location);
97+
return;
98+
}
99+
teleportTaskService.createTeleport(
100+
player.getUniqueId(),
101+
PositionAdapter.convert(player.getLocation()),
102+
PositionAdapter.convert(location),
103+
settings.backTeleportTime()
104+
);
105+
}
106+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.eternalcode.core.feature.back;
2+
3+
import java.time.Duration;
4+
5+
public interface BackSettings {
6+
7+
Duration backTeleportTime();
8+
9+
Duration backLocationCacheDuration();
10+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.eternalcode.core.feature.back.messages;
2+
3+
import com.eternalcode.multification.notice.Notice;
4+
5+
public interface BackMessages {
6+
7+
Notice lastLocationNotFound();
8+
9+
Notice teleportedToLastTeleportLocation();
10+
Notice teleportedTargetPlayerToLastTeleportLocation();
11+
Notice teleportedToLastTeleportLocationByAdmin();
12+
13+
Notice teleportedToLastDeathLocation();
14+
Notice teleportedTargetPlayerToLastDeathLocation();
15+
Notice teleportedToLastDeathLocationByAdmin();
16+
17+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.eternalcode.core.feature.back.messages;
2+
3+
import com.eternalcode.multification.notice.Notice;
4+
import eu.okaeri.configs.OkaeriConfig;
5+
import lombok.Getter;
6+
import lombok.experimental.Accessors;
7+
8+
@Getter
9+
@Accessors(fluent = true)
10+
public class ENBackMessages extends OkaeriConfig implements BackMessages {
11+
12+
public Notice lastLocationNotFound = Notice.chat(
13+
"<red>► <white>You don't have any last location to teleport to!");
14+
15+
public Notice teleportedToLastTeleportLocation = Notice.chat(
16+
"<green>► <white>You have been teleported to your last location!");
17+
public Notice teleportedTargetPlayerToLastTeleportLocation = Notice.chat(
18+
"<green>► <white>Player <green>{PLAYER} <white>has been teleported to their last location!");
19+
public Notice teleportedToLastTeleportLocationByAdmin = Notice.chat(
20+
"<green>► <white>You have been teleported to your last location by an administrator!");
21+
22+
public Notice teleportedToLastDeathLocation = Notice.chat(
23+
"<green>► <white>You have been teleported to your last death location!");
24+
public Notice teleportedTargetPlayerToLastDeathLocation = Notice.chat(
25+
"<green>► <white>Player <green>{PLAYER} <white>has been teleported to their last death location!");
26+
public Notice teleportedToLastDeathLocationByAdmin = Notice.chat(
27+
"<green>► <white>You have been teleported to your last death location by an administrator!");
28+
}

0 commit comments

Comments
 (0)