148 lines
5.3 KiB
Java
148 lines
5.3 KiB
Java
package tc.oc.commons.bukkit.teleport;
|
|
|
|
import java.time.Duration;
|
|
import java.time.Instant;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.UUID;
|
|
import java.util.logging.Logger;
|
|
import javax.inject.Inject;
|
|
import javax.inject.Singleton;
|
|
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.event.EventHandler;
|
|
import org.bukkit.event.EventPriority;
|
|
import org.bukkit.event.Listener;
|
|
import org.bukkit.event.player.PlayerJoinEvent;
|
|
import tc.oc.api.bukkit.users.OnlinePlayers;
|
|
import tc.oc.api.message.MessageListener;
|
|
import tc.oc.api.message.MessageService;
|
|
import tc.oc.api.message.types.PlayerTeleportRequest;
|
|
import tc.oc.commons.bukkit.permissions.PermissionRegistry;
|
|
import tc.oc.commons.core.logging.Loggers;
|
|
import tc.oc.commons.core.plugin.PluginFacet;
|
|
import tc.oc.minecraft.api.scheduler.Scheduler;
|
|
import tc.oc.minecraft.api.scheduler.Tickable;
|
|
import tc.oc.minecraft.scheduler.SyncExecutor;
|
|
|
|
/**
|
|
* Listens for remote teleport requests and executes both ends of them.
|
|
*
|
|
* When the request target is a different server, this class sends the
|
|
* player there (via Bungee plugin channel). We do this in Bukkit so that
|
|
* it can be cancelled i.e. by PGM when the player is in a match.
|
|
*
|
|
* When the target is this server, and there is also a target player,
|
|
* this class waits for the teleporting player to arrive and then
|
|
* teleports them to the target.
|
|
*/
|
|
@Singleton
|
|
public class TeleportListener implements MessageListener, Listener, PluginFacet, Tickable {
|
|
|
|
private static final Duration TIMEOUT = Duration.ofSeconds(10);
|
|
|
|
static class Received {
|
|
final java.time.Instant timestamp = java.time.Instant.now();
|
|
final PlayerTeleportRequest request;
|
|
|
|
Received(PlayerTeleportRequest request) {
|
|
this.request = request;
|
|
}
|
|
}
|
|
|
|
private final Logger logger;
|
|
private final MessageService primaryQueue;
|
|
private final Teleporter teleporter;
|
|
private final SyncExecutor syncExecutor;
|
|
private final Scheduler scheduler;
|
|
private final PermissionRegistry permissionRegistry;
|
|
private final OnlinePlayers onlinePlayers;
|
|
|
|
private final Map<UUID, Received> requests = new HashMap<>();
|
|
|
|
@Inject TeleportListener(Loggers loggers, MessageService primaryQueue, Teleporter teleporter, SyncExecutor syncExecutor, Scheduler scheduler, PermissionRegistry permissionRegistry, OnlinePlayers onlinePlayers) {
|
|
this.logger = loggers.get(getClass());
|
|
this.primaryQueue = primaryQueue;
|
|
this.onlinePlayers = onlinePlayers;
|
|
this.syncExecutor = syncExecutor;
|
|
this.scheduler = scheduler;
|
|
this.permissionRegistry = permissionRegistry;
|
|
this.teleporter = teleporter;
|
|
}
|
|
|
|
@Override
|
|
public Duration tickPeriod() {
|
|
return TIMEOUT;
|
|
}
|
|
|
|
@Override
|
|
public void enable() {
|
|
permissionRegistry.addPermission(Teleporter.PERMISSION);
|
|
primaryQueue.subscribe(this, syncExecutor);
|
|
primaryQueue.bind(PlayerTeleportRequest.class);
|
|
}
|
|
|
|
@Override
|
|
public void disable() {
|
|
primaryQueue.unsubscribe(this);
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
final Instant now = Instant.now();
|
|
requests.values().removeIf(received -> received.timestamp.plus(TIMEOUT).isBefore(now));
|
|
}
|
|
|
|
@HandleMessage
|
|
public void onTeleport(PlayerTeleportRequest request) {
|
|
Player traveler = onlinePlayers.find(request.player_uuid);
|
|
|
|
if(!teleporter.isLocal(request.target_server())) {
|
|
// Send player to another server
|
|
if(traveler != null && traveler.hasPermission(Teleporter.PERMISSION)) {
|
|
if(request.target_server() == null) {
|
|
logger.info("Sending " + traveler.getName() + " to lobby");
|
|
} else {
|
|
logger.info("Sending " + traveler.getName() + " to server " + request.target_server().bungee_name());
|
|
}
|
|
teleporter.remoteTeleport(traveler, request.target_server());
|
|
}
|
|
} else if(request.target_player_uuid != null) {
|
|
// Teleport player to a target on this server
|
|
if(traveler != null) {
|
|
doTeleport(traveler, request);
|
|
} else {
|
|
queueTeleport(request);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Priority must be high enough so that whatever applies observer permissions
|
|
* runs before this, since those include the teleport permission. Lobby does it
|
|
* in PlayerListener and PGM does it in SpawnMatchModule.
|
|
*/
|
|
@EventHandler(priority = EventPriority.MONITOR)
|
|
public void onJoin(PlayerJoinEvent event) {
|
|
Received received = requests.remove(event.getPlayer().getUniqueId());
|
|
if(received != null) {
|
|
doTeleport(event.getPlayer(), received.request);
|
|
}
|
|
}
|
|
|
|
private void queueTeleport(final PlayerTeleportRequest request) {
|
|
logger.info("Queueing remote teleport for offline player " + request.player_uuid);
|
|
requests.put(request.player_uuid, new Received(request));
|
|
|
|
scheduler.delaySync(TIMEOUT, () -> {
|
|
if(requests.remove(request.player_uuid) != null) {
|
|
logger.warning("Expired remote teleport for " + request.player_uuid);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void doTeleport(Player traveler, PlayerTeleportRequest request) {
|
|
teleporter.localTeleport(traveler, request.target_player_uuid);
|
|
}
|
|
}
|