Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -98,23 +98,45 @@ private boolean isTpllCommand(@NotNull PlayerCommandPreprocessEvent event) {
* @return A CompletableFuture representing whether teleportation interception is required.
*/
private @NotNull CompletableFuture<Boolean> shouldIntercept() {
return OpenStreetMapAPI.getCountryFromLocationAsync(coordinate)
.thenComposeAsync(address -> {
if (address == null) return CompletableFuture.completedFuture(false);

String countryName = address[0];
Region region = Region.getByName(countryName);
return getRegionFromLocation()
.thenApplyAsync(region -> {
if (region == null) return false;

BuildTeam regionBuildTeam = region.getBuildTeam();
BuildTeam currentTeam = networkModule.getBuildTeam();
if (region != null && region.getBuildTeam() != null && currentTeam != null
&& !Objects.equals(region.getBuildTeam().getID(), currentTeam.getID())) {
targetBuildTeam = region.getBuildTeam();
return CompletableFuture.completedFuture(true);

if (regionBuildTeam == null || currentTeam == null) return false;

if (!Objects.equals(regionBuildTeam.getID(), currentTeam.getID())) {
targetBuildTeam = regionBuildTeam;
return true;
}

return CompletableFuture.completedFuture(false);
return false;
});
}

/**
* Resolves the region for the current coordinate, falling back to the alternative
* OpenStreetMap lookup if the first lookup does not produce a known region.
*/
private @NotNull CompletableFuture<Region> getRegionFromLocation() {
return getRegionFromLocation(false)
.thenCompose(region -> {
if (region != null) {
return CompletableFuture.completedFuture(region);
}

return getRegionFromLocation(true);
});
}

/**
* Resolves the region for the current coordinate using the requested lookup mode.
*/
private @NotNull CompletableFuture<Region> getRegionFromLocation(boolean fallbackLookup) {
return OpenStreetMapAPI.getCountryFromLocationAsync(coordinate, fallbackLookup)
.thenApply(address -> Region.getByName(address.regionName()));
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import net.buildtheearth.buildteamtools.modules.navigation.components.warps.model.WarpGroup;
import net.buildtheearth.buildteamtools.modules.network.NetworkModule;
import net.buildtheearth.buildteamtools.modules.network.api.OpenStreetMapAPI;
import net.buildtheearth.buildteamtools.modules.network.api.RegionLookupResult;
import net.buildtheearth.buildteamtools.modules.network.model.BuildTeam;
import net.buildtheearth.buildteamtools.utils.menus.AbstractMenu;
import net.buildtheearth.model.GeographicalCoordinate;
Expand All @@ -34,6 +35,7 @@
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;

public class WarpsComponent extends ModuleComponent {

Expand Down Expand Up @@ -169,28 +171,14 @@ public static void createWarp(@NonNull Player creator, WarpGroup group) {
try {
GeographicalCoordinate coordinate = Projection.toGeo(location.getX(), location.getZ());

//Get the country belonging to the coordinates
CompletableFuture<String[]> future = OpenStreetMapAPI.getCountryFromLocationAsync(coordinate);

future.thenAccept(result -> {
String regionName = result[0];
String countryCodeCCA2 = result[1].toUpperCase();

if (countryCodeCCA2.isEmpty()) countryCodeCCA2 = NavUtils.getCCA2FromCountryName(regionName, creator);

//Check if the team owns this region/country
boolean ownsRegion = NetworkModule.getInstance().ownsRegion(regionName, countryCodeCCA2);

if (!ownsRegion) {
creator.sendMessage(ChatHelper.getErrorString("This team does not own the country %s!", result[0]));
return;
}

WarpsComponent.getOwnedRegionFromLocation(coordinate, creator)
.thenAccept(result -> {
// Create a default name for the warp
String name = creator.getName() + "'s Warp";

// Create an instance of the warp POJO
Warp warp = new Warp(group, name, countryCodeCCA2, "cca2", null, null, null, location.getWorld().getName(),
Warp warp = new Warp(group, name, result.countryCodeCCA2(), "cca2", null, null, null,
location.getWorld().getName(),
coordinate, location.getY(), location.getYaw(), location.getPitch(), false);

Bukkit.getScheduler().runTask(BuildTeamTools.getInstance(), () ->
Expand All @@ -211,29 +199,15 @@ public static void createWarp(@NonNull Player creator, WarpGroup group) {
* Creates a warp at the given location.
*/
public static void createWarp(@NonNull Location location, String name, WarpGroup group, Player creator) throws OutOfProjectionBoundsException {
GeographicalCoordinate coordinates = Projection.toGeo(location.getX(), location.getZ());

//Get the country belonging to the coordinates
CompletableFuture<String[]> future = OpenStreetMapAPI.getCountryFromLocationAsync(coordinates);

future.thenAccept(result -> {
String regionName = result[0];
String countryCodeCCA2 = result[1].toUpperCase();

BuildTeamTools.getInstance().getComponentLogger().debug("Creating warp at {}", location);
GeographicalCoordinate coordinate = Projection.toGeo(location.getX(), location.getZ());

//Check if the team owns this region/country
boolean ownsRegion = NetworkModule.getInstance().ownsRegion(regionName, countryCodeCCA2);

if (!ownsRegion) {
creator.sendMessage(ChatHelper.getErrorString("Warp %s cannot be created. This team does not own the country %s" +
" (Country code %s)!", name, regionName, countryCodeCCA2));
return;
}
getOwnedRegionFromLocation(coordinate, creator)
.thenAccept(result -> {

// Create an instance of the warp POJO
Warp warp = new Warp(group, name, countryCodeCCA2, "cca2", null, null, null, location.getWorld().getName(),
coordinates, location.getY(), location.getYaw(), location.getPitch(), false);
Warp warp = new Warp(group, name, result.countryCodeCCA2(), "cca2", null, null, null,
location.getWorld().getName(),
coordinate, location.getY(), location.getYaw(), location.getPitch(), false);

Objects.requireNonNull(NetworkModule.getInstance().getBuildTeam()).createWarp(creator, warp, true);
}).exceptionally(e -> {
Expand Down Expand Up @@ -304,4 +278,49 @@ public static void openWarpMenu(@NotNull Player player, @NotNull BuildTeam build
default -> new WarpGroupMenu(player, buildTeam, menu != null, true, menu);
}
}

public static @NotNull CompletableFuture<RegionLookupResult> getOwnedRegionFromLocation(
GeographicalCoordinate coordinate,
Player player
) {
return getRegionLookupResult(coordinate, false, player)
.thenCompose(result -> {
if (ownsRegion(result)) {
return CompletableFuture.completedFuture(result);
}

return getRegionLookupResult(coordinate, true, player)
.thenApply(fallbackResult -> {
if (!ownsRegion(fallbackResult)) {
throw new CompletionException(new IllegalStateException(
"This team does not own the country " + fallbackResult.regionName() + "!"
));
}

return fallbackResult;
});
});
}

private static @NotNull CompletableFuture<RegionLookupResult> getRegionLookupResult(
GeographicalCoordinate coordinate,
boolean fallbackLookup,
Player player
) {
return OpenStreetMapAPI.getCountryFromLocationAsync(coordinate, fallbackLookup)
.thenApply(result -> {
if (result.countryCodeCCA2() == null) {
return new RegionLookupResult(result.regionName(), NavUtils.getCCA2FromCountryName(result.regionName(),
player));
}
return result;
});
}

private static boolean ownsRegion(@NonNull RegionLookupResult result) {
return NetworkModule.getInstance().ownsRegion(
result.regionName(),
result.countryCodeCCA2()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
import net.buildtheearth.OutOfProjectionBoundsException;
import net.buildtheearth.Projection;
import net.buildtheearth.buildteamtools.BuildTeamTools;
import net.buildtheearth.buildteamtools.modules.navigation.NavUtils;
import net.buildtheearth.buildteamtools.modules.navigation.components.warps.WarpsComponent;
import net.buildtheearth.buildteamtools.modules.navigation.components.warps.model.Warp;
import net.buildtheearth.buildteamtools.modules.network.NetworkModule;
import net.buildtheearth.buildteamtools.modules.network.api.OpenStreetMapAPI;
import net.buildtheearth.buildteamtools.modules.network.model.BuildTeam;
import net.buildtheearth.buildteamtools.modules.network.model.Permissions;
import net.buildtheearth.buildteamtools.utils.ListUtil;
Expand All @@ -30,7 +29,6 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;

public class WarpEditMenu extends AbstractMenu {

Expand Down Expand Up @@ -140,45 +138,40 @@ protected void setItemClickEventsAsync() {
getMenu().getSlot(LOCATION_SLOT).setClickHandler((clickPlayer, clickInformation) -> {
clickPlayer.playSound(clickPlayer.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1.0F, 1.0F);

// Get the geographic coordinates of the player's location.
Location location = clickPlayer.getLocation();

try {
GeographicalCoordinate coordinate = Projection.toGeo(location.getX(), location.getZ());

//Get the country belonging to the coordinates
CompletableFuture<String[]> future = OpenStreetMapAPI.getCountryFromLocationAsync(coordinate);
WarpsComponent.getOwnedRegionFromLocation(coordinate, clickPlayer)
.thenAccept(result -> {
warp.setCountryCode(result.countryCodeCCA2());
warp.setWorldName(location.getWorld().getName());
warp.setY(location.getY());
warp.setCoordinate(coordinate);
warp.setYaw(location.getYaw());
warp.setPitch(location.getPitch());

future.thenAccept(result -> {
String regionName = result[0];
String countryCodeCCA2 = result[1].toUpperCase();
clickPlayer.playSound(clickPlayer.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1.0F, 1.0F);

if (countryCodeCCA2.isEmpty())
countryCodeCCA2 = NavUtils.getCCA2FromCountryName(regionName, clickPlayer);
new WarpEditMenu(clickPlayer, warp, alreadyExists, true);
})
.exceptionally(e -> {
Throwable cause = e.getCause() != null ? e.getCause() : e;

//Check if the team owns this region/country
boolean ownsRegion = NetworkModule.getInstance().ownsRegion(regionName, countryCodeCCA2);
clickPlayer.sendMessage(ChatHelper.getErrorString(
"Failed to change location: %s",
cause.getMessage()
));

if (!ownsRegion) {
clickPlayer.sendMessage(ChatHelper.getErrorString("This team does not own the country %s!", result[0]));
return;
}
BuildTeamTools.getInstance().getComponentLogger().error(
"An error occurred while changing the location of the warp!",
e
);

return null;
});

warp.setCountryCode(countryCodeCCA2);
warp.setWorldName(location.getWorld().getName());
warp.setY(location.getY());
warp.setCoordinate(coordinate);
warp.setYaw(location.getYaw());
warp.setPitch(location.getPitch());

clickPlayer.playSound(clickPlayer.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1.0F, 1.0F);

new WarpEditMenu(clickPlayer, warp, alreadyExists, true);
}).exceptionally(e -> {
Throwable cause = e.getCause() != null ? e.getCause() : e;
clickPlayer.sendMessage(ChatHelper.getErrorString("Failed to change location: %s", cause.getMessage()));
BuildTeamTools.getInstance().getComponentLogger().error("An error occurred while changing the location of the warp!", e);
return null;
});
} catch (OutOfProjectionBoundsException e) {
clickPlayer.sendMessage(ChatHelper.getErrorString("Cannot set location here: %s", e.getMessage()));
}
Expand Down Expand Up @@ -274,4 +267,5 @@ protected Mask getMask() {
.pattern("111111110")
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,19 @@
import org.jspecify.annotations.NonNull;

import java.io.IOException;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;

public class OpenStreetMapAPI extends API {

/**
* @param coordinate The latitude & longitude coordinates to get the country, region & city/town from
* @param forcePhoton If true, the photon API will be used instead of the RGC API. This is useful when the RGC API is not
* working properly.
* @return The country name and country code belonging to this location
*/
public static @NotNull CompletableFuture<String[]> getCountryFromLocationAsync(@NotNull GeographicalCoordinate coordinate) {
if (canUseRgcHandler()) {
public static @NotNull CompletableFuture<RegionLookupResult> getCountryFromLocationAsync(@NotNull GeographicalCoordinate coordinate, boolean forcePhoton) {
if (!forcePhoton && canUseRgcHandler()) {
return getCountryFromRgcAsync(coordinate);
}
return getCountryFromPhotonAsync(coordinate);
Expand All @@ -32,8 +35,8 @@ private static boolean canUseRgcHandler() {
&& NavigationModule.getInstance().getRgcHandler() != null;
}

private static @NotNull CompletableFuture<String[]> getCountryFromRgcAsync(@NotNull GeographicalCoordinate coordinates) {
CompletableFuture<String[]> future = new CompletableFuture<>();
private static @NotNull CompletableFuture<RegionLookupResult> getCountryFromRgcAsync(@NotNull GeographicalCoordinate coordinates) {
CompletableFuture<RegionLookupResult> future = new CompletableFuture<>();
ChatHelper.logDebug("Using custom file API to get country from location: %s, %s", coordinates.latitude(), coordinates.longitude());

if (!Bukkit.isPrimaryThread()) {
Expand All @@ -46,20 +49,21 @@ private static boolean canUseRgcHandler() {
return future;
}

private static void completeRgcLookup(@NotNull GeographicalCoordinate coordinates, CompletableFuture<String[]> future) {
private static void completeRgcLookup(@NotNull GeographicalCoordinate coordinates,
CompletableFuture<RegionLookupResult> future) {
try {
if (NavigationModule.getInstance().getRgcHandler() == null) throw new AssertionError("RgcHandler have to be initialized first");
var location = NavigationModule.getInstance().getRgcHandler()
.locationFromCoordinates((float) coordinates.latitude(), (float) coordinates.longitude());
ChatHelper.logDebug("RGC lookup successful: %s", location);
future.complete(new String[]{location.get(AdminLevel.COUNTRY), ""});
future.complete(new RegionLookupResult(location.get(AdminLevel.COUNTRY), null));
} catch (Exception ex) {
future.completeExceptionally(ex);
}
}

private static @NotNull CompletableFuture<String[]> getCountryFromPhotonAsync(@NotNull GeographicalCoordinate coordinates) {
CompletableFuture<String[]> future = new CompletableFuture<>();
private static @NotNull CompletableFuture<RegionLookupResult> getCountryFromPhotonAsync(@NotNull GeographicalCoordinate coordinates) {
CompletableFuture<RegionLookupResult> future = new CompletableFuture<>();
String url = "https://photon.komoot.io/reverse?lat=" + coordinates.latitude() + "&lon=" + coordinates.longitude() + "&lang=en";

ChatHelper.logDebug("Requesting country from location: %s", url);
Expand All @@ -78,7 +82,7 @@ public void onFailure(IOException e) {
return future;
}

private static void completePhotonLookup(String response, @NonNull CompletableFuture<String[]> future) {
private static void completePhotonLookup(String response, @NonNull CompletableFuture<RegionLookupResult> future) {
JSONObject jsonObject = API.createJSONObject(response);
ChatHelper.logDebug("Response from OpenStreetMap: %s", jsonObject);

Expand All @@ -97,6 +101,7 @@ private static void completePhotonLookup(String response, @NonNull CompletableFu
String countryCodeCca2 = (String) propertiesObject.get("countrycode");
String countryName = (String) propertiesObject.get("country");

future.complete(new String[]{countryName, countryCodeCca2});
future.complete(new RegionLookupResult(countryName, countryCodeCca2.toUpperCase(Locale.ROOT)));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package net.buildtheearth.buildteamtools.modules.network.api;

import org.jetbrains.annotations.Nullable;

public record RegionLookupResult(String regionName, @Nullable String countryCodeCCA2) {
}
Loading