package net.hywave.survival.services.locations.LastLocationService;
import net.hywave.db.HywaveDB;
import net.hywave.db.queries.Query;
import net.hywave.db.handlers.Table;
import net.hywave.db.config.DatabaseConfig;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.Optional;
import java.util.logging.Level;
public class LastLocationService {
private final JavaPlugin plugin;
private final HywaveDB database;
private final Table playerLocationsTable;
private final Map<UUID, Long> lastTeleportTime;
private static final long TELEPORT_COOLDOWN = 30000; // 30 saniye
public LastLocationService(JavaPlugin plugin) {
this.plugin = plugin;
this.lastTeleportTime = new HashMap<>();
DatabaseConfig config = DatabaseConfig.builder()
.path(plugin.getDataFolder().getAbsolutePath() + "/locations.db")
.maxConnections(10)
.enableWAL(true) // Comment<1etu>: If you're about to run a developer server, please make it false. You hardcore fucking the current threads.
.build();
this.database = HywaveDB.connect(config);
this.playerLocationsTable = initializeTable(); // test?
}
private Table initializeTable() {
// Comment<1etu>: We may want to move this under "players" table.
return database.createTableIfNotExists("player_locations")
.addColumn("uuid", "TEXT PRIMARY KEY")
.addColumn("world", "TEXT NOT NULL")
.addColumn("x", "DOUBLE NOT NULL")
.addColumn("y", "DOUBLE NOT NULL")
.addColumn("z", "DOUBLE NOT NULL")
.addColumn("yaw", "FLOAT NOT NULL")
.addColumn("pitch", "FLOAT NOT NULL")
.addColumn("is_safe", "BOOLEAN NOT NULL")
.addColumn("is_stuck", "BOOLEAN NOT NULL")
.addColumn("last_updated", "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
.addColumn("can_teleport_again", "BOOLEAN DEFAULT TRUE")
.addIndex("idx_uuid", "uuid")
.execute();
}
public void savePlayerLocation(Player player, boolean isSafe, boolean isStuck) {
Location loc = player.getLocation();
Query query = database.query()
.insertOrReplace()
.into(playerLocationsTable)
.values(
Map.of(
"uuid", player.getUniqueId().toString(),
"world", loc.getWorld().getName(),
"x", loc.getX(),
"y", loc.getY(),
"z", loc.getZ(),
"yaw", loc.getYaw(),
"pitch", loc.getPitch(),
"is_safe", isSafe,
"is_stuck", isStuck,
"can_teleport_again", canTeleportAgain(player.getUniqueId())
)
);
try {
query.execute();
} catch (Exception e) {}
}
public Optional<LocationData> getLastLocation(UUID playerUUID) {
try {
return database.query()
.select()
.from(playerLocationsTable)
.where("uuid = ?", playerUUID.toString())
.mapTo(rs -> new LocationData(
plugin.getServer().getWorld(rs.getString("world")),
rs.getDouble("x"),
rs.getDouble("y"),
rs.getDouble("z"),
rs.getFloat("yaw"),
rs.getFloat("pitch"),
rs.getBoolean("is_safe"),
rs.getBoolean("is_stuck"),
rs.getBoolean("can_teleport_again")
))
.findFirst();
} catch (Exception e) {
return Optional.empty();
}
}
public boolean isSafeLocation(Location location) {
return location != null &&
location.getBlock().getType().isAir() &&
location.clone().add(0, 1, 0).getBlock().getType().isAir() &&
!location.clone().subtract(0, 1, 0).getBlock().getType().isAir();
}
public boolean isPlayerStuck(Player player) {
Location loc = player.getLocation();
for (int x = -1; x <= 1; x++) {
for (int y = 0; y <= 1; y++) {
for (int z = -1; z <= 1; z++) {
if (!loc.clone().add(x, y, z).getBlock().getType().isAir()) {
return true;
}
}
}
}
return false;
}
public boolean canTeleportAgain(UUID playerUUID) {
return !lastTeleportTime.containsKey(playerUUID) ||
System.currentTimeMillis() - lastTeleportTime.get(playerUUID) >= TELEPORT_COOLDOWN;
}
public void updateTeleportTime(UUID playerUUID) {
lastTeleportTime.put(playerUUID, System.currentTimeMillis());
}
public void teleportToLastLocation(Player player) {
Optional<LocationData> lastLocOpt = getLastLocation(player.getUniqueId());
if (lastLocOpt.isEmpty()) {
player.sendMessage("§c§lWARNING! §cNo previous location found!");
return;
}
LocationData lastLoc = lastLocOpt.get();
if (!lastLoc.canTeleportAgain()) {
player.sendMessage("§e§lCOOLDOWN! §7You must wait before teleporting again!");
return;
}
if (!lastLoc.isSafe()) {
player.sendMessage("§c§lRISK! §cLast location might not be safe!");
return;
}
if (lastLoc.isStuck()) {
player.sendMessage("§d§lREMINDER §7You might get stuck at this location!");
return;
}
Location location = new Location(
lastLoc.world(),
lastLoc.x(),
lastLoc.y(),
lastLoc.z(),
lastLoc.yaw(),
lastLoc.pitch()
);
player.teleport(location);
updateTeleportTime(player.getUniqueId());
}
public void cleanup() {
try {
database.shutdown();
} catch (Exception e) {
}
}
public record LocationData(
org.bukkit.World world,
double x,
double y,
double z,
float yaw,
float pitch,
boolean isSafe,
boolean isStuck,
boolean canTeleportAgain
) {}
}