diff --git a/sponge/src/main/java/com/griefdefender/GriefDefenderPlugin.java b/sponge/src/main/java/com/griefdefender/GriefDefenderPlugin.java index cb972a5..6b19562 100644 --- a/sponge/src/main/java/com/griefdefender/GriefDefenderPlugin.java +++ b/sponge/src/main/java/com/griefdefender/GriefDefenderPlugin.java @@ -161,6 +161,7 @@ import com.griefdefender.permission.flag.GDFlagData; import com.griefdefender.permission.flag.GDFlagDefinition; import com.griefdefender.permission.flag.GDFlags; +import com.griefdefender.provider.DynmapProvider; import com.griefdefender.provider.LuckPermsProvider; import com.griefdefender.provider.MCClansProvider; import com.griefdefender.provider.NucleusProvider; @@ -273,10 +274,11 @@ public class GriefDefenderPlugin { private SpongeCommandManager commandManager; public BaseStorage dataStore; + private DynmapProvider dynmapProvider; + private PermissionProvider permissionProvider; public MCClansProvider clanApiProvider; public NucleusProvider nucleusApiProvider; public WorldEditProvider worldEditProvider; - private PermissionProvider permissionProvider; public PermissionService permissionService; public Optional economyService; @@ -523,6 +525,9 @@ public void onServerAboutToStart(GameAboutToStartServerEvent event) { return; } + if (Sponge.getPluginManager().getPlugin("dynmap").isPresent()) { + this.dynmapProvider = new DynmapProvider(); + } if (Sponge.getPluginManager().getPlugin("mcclans").isPresent()) { this.clanApiProvider = new MCClansProvider(); } @@ -1156,6 +1161,10 @@ public static boolean isTargetIdBlacklisted(String flag, Object target, UUID wor return false; } + public DynmapProvider getDynmapProvider() { + return this.dynmapProvider; + } + public boolean isEconomyModeEnabled() { return GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode; } diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/DynmapCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/DynmapCategory.java new file mode 100644 index 0000000..319b331 --- /dev/null +++ b/sponge/src/main/java/com/griefdefender/configuration/category/DynmapCategory.java @@ -0,0 +1,74 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.configuration.category; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import ninja.leaping.configurate.objectmapping.Setting; +import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable; + +@ConfigSerializable +public class DynmapCategory { + + @Setting("owner-styles") + public Map ownerStyles = new HashMap<>(); + + @Setting + public int minzoom = 0; + + @Setting("layer-priority") + public int layerPriority = 10; + + @Setting("layer-hide-by-default") + public boolean layerHideByDefault = false; + + @Setting("use-3d-regions") + public boolean use3dRegions = false; + + @Setting("hidden-regions") + public List hiddenRegions = new ArrayList<>(); + + @Setting("info-window-basic") + public String infoWindowBasic = "
" + + "Name: %claimname%
" + + "Owner: %owner%
" + + "Type: %gdtype%
" + + "Last Seen: %lastseen%
" + + "Permission Trust: %managers%
" + + "Trust: %builders%
" + + "Container Trust: %containers%
" + + "Access Trust: %accessors%
"; + + @Setting("info-window-admin") + public String infoWindowAdmin = "
" + + "Administrator Claim
" + + "Permission Trust: %managers%
" + + "Trust: %builders%
" + + "Container Trust: %containers%
" + + "Access Trust: %accessors%
"; +} diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/DynmapOwnerStyleCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/DynmapOwnerStyleCategory.java new file mode 100644 index 0000000..5ae16f0 --- /dev/null +++ b/sponge/src/main/java/com/griefdefender/configuration/category/DynmapOwnerStyleCategory.java @@ -0,0 +1,50 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.configuration.category; + +import ninja.leaping.configurate.objectmapping.Setting; +import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable; + +@ConfigSerializable +public class DynmapOwnerStyleCategory { + + @Setting("stroke-color") + public String strokeColor = "#FF0000"; + + @Setting("stroke-opacity") + public double strokeOpacity = 0.8d; + + @Setting("stroke-weight") + public int strokeWeight = 3; + + @Setting("fill-color") + public String fillColor = "#FF0000"; + + @Setting("fill-opacity") + public double fillOpacity = 0.35d; + + @Setting("label") + public String label = "none"; +} diff --git a/sponge/src/main/java/com/griefdefender/configuration/type/GlobalConfig.java b/sponge/src/main/java/com/griefdefender/configuration/type/GlobalConfig.java index 038627c..009ea69 100644 --- a/sponge/src/main/java/com/griefdefender/configuration/type/GlobalConfig.java +++ b/sponge/src/main/java/com/griefdefender/configuration/type/GlobalConfig.java @@ -26,6 +26,7 @@ import com.griefdefender.configuration.category.CustomFlagGroupDefinitionCategory; import com.griefdefender.configuration.category.DefaultPermissionCategory; +import com.griefdefender.configuration.category.DynmapCategory; import com.griefdefender.configuration.category.EconomyCategory; import com.griefdefender.configuration.category.MessageCategory; import com.griefdefender.configuration.category.MigratorCategory; @@ -64,6 +65,8 @@ public class GlobalConfig extends ConfigBase { + "\nThese contexts may change, See https://github.com/bloodmc/GriefDefender/wiki for latest information.") public CustomFlagGroupDefinitionCategory customFlags = new CustomFlagGroupDefinitionCategory(); @Setting + public DynmapCategory dynmap = new DynmapCategory(); + @Setting public EconomyCategory economy = new EconomyCategory(); @Setting public PlayerDataCategory playerdata = new PlayerDataCategory(); diff --git a/sponge/src/main/java/com/griefdefender/provider/DynmapProvider.java b/sponge/src/main/java/com/griefdefender/provider/DynmapProvider.java new file mode 100644 index 0000000..482f44a --- /dev/null +++ b/sponge/src/main/java/com/griefdefender/provider/DynmapProvider.java @@ -0,0 +1,358 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) Dockter + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.provider; + +import com.flowpowered.math.vector.Vector3i; +import com.griefdefender.GDBootstrap; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.GriefDefender; +import com.griefdefender.api.claim.Claim; +import com.griefdefender.api.claim.ClaimManager; +import com.griefdefender.api.claim.ClaimTypes; +import com.griefdefender.api.claim.TrustTypes; +import com.griefdefender.api.event.ChangeClaimEvent; +import com.griefdefender.api.event.CreateClaimEvent; +import com.griefdefender.api.event.RemoveClaimEvent; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.configuration.category.DynmapOwnerStyleCategory; +import com.griefdefender.configuration.category.DynmapCategory; +import com.griefdefender.util.PlayerUtil; + +import net.kyori.text.serializer.plain.PlainComponentSerializer; + +import org.dynmap.DynmapCommonAPI; +import org.dynmap.DynmapCommonAPIListener; +import org.dynmap.markers.AreaMarker; +import org.dynmap.markers.MarkerAPI; +import org.dynmap.markers.MarkerSet; +import org.slf4j.Logger; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.Order; +import org.spongepowered.api.scheduler.Task; +import org.spongepowered.api.world.World; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; + +public class DynmapProvider { + + private final Logger logger; + private DynmapCommonAPI dynmap; + private MarkerAPI markerapi; + private DynmapCategory cfg; + private DynmapOwnerStyleCategory defaultStyle = new DynmapOwnerStyleCategory(); + private MarkerSet set; + private boolean disabled = false; + private boolean reload = false; + + public DynmapProvider() { + this.logger = GriefDefenderPlugin.getInstance().getLogger(); + logger.info("Initializing GriefDefender Dynmap provider..."); + DynmapCommonAPIListener.register(new DynmapCommonAPIListener() { + @Override + public void apiEnabled(DynmapCommonAPI api) { + dynmap = api; + cfg = GriefDefenderPlugin.getGlobalConfig().getConfig().dynmap; + activate(); + } + }); + if (this.markerapi == null) { + this.logger.error("Error loading Dynmap Provider! Could not locate Marker API."); + return; + } + GriefDefender.getEventManager().register(this); + } + + private Map areaMarkers = new HashMap(); + + private String getWindowInfo(Claim claim, AreaMarker marker) { + String info; + if (claim.isAdminClaim()) { + info = "
" + this.cfg.infoWindowAdmin + "
"; + } else { + info = "
" + this.cfg.infoWindowBasic + "
"; + } + info = info.replace("%owner%", ((GDClaim) claim).getOwnerFriendlyName()); + info = info.replace("%area%", Integer.toString(claim.getArea())); + info = info.replace("%claimname%", + claim.getData().getName().isPresent() + ? PlainComponentSerializer.INSTANCE.serialize(claim.getName().get()) + : "none"); + info = info.replace("%lastseen%", claim.getData().getDateLastActive().toString()); + info = info.replace("%gdtype%", claim.getType().toString()); + + final List builderList = claim.getUserTrusts(TrustTypes.BUILDER); + final List containerList = claim.getUserTrusts(TrustTypes.CONTAINER); + final List accessorList = claim.getUserTrusts(TrustTypes.ACCESSOR); + final List managerList = claim.getUserTrusts(TrustTypes.MANAGER); + + String trusted = ""; + for (int i = 0; i < builderList.size(); i++) { + if (i > 0) { + trusted += ", "; + } + final UUID uuid = builderList.get(i); + final String userName = PlayerUtil.getInstance().getUserName(uuid); + if (userName.equalsIgnoreCase("unknown")) { + trusted += uuid.toString(); + } else { + trusted += userName; + } + } + info = info.replace("%builders%", trusted); + + trusted = ""; + for (int i = 0; i < containerList.size(); i++) { + if (i > 0) { + trusted += ", "; + } + final UUID uuid = containerList.get(i); + final String userName = PlayerUtil.getInstance().getUserName(uuid); + if (userName.equalsIgnoreCase("unknown")) { + trusted += uuid.toString(); + } else { + trusted += userName; + } + } + info = info.replace("%containers%", trusted); + + trusted = ""; + for (int i = 0; i < accessorList.size(); i++) { + if (i > 0) { + trusted += ", "; + } + final UUID uuid = accessorList.get(i); + final String userName = PlayerUtil.getInstance().getUserName(uuid); + if (userName.equalsIgnoreCase("unknown")) { + trusted += uuid.toString(); + } else { + trusted += userName; + } + } + info = info.replace("%accessors%", trusted); + + trusted = ""; + for (int i = 0; i < managerList.size(); i++) { + if (i > 0) { + trusted += ", "; + } + final UUID uuid = managerList.get(i); + final String userName = PlayerUtil.getInstance().getUserName(uuid); + if (userName.equalsIgnoreCase("unknown")) { + trusted += uuid.toString(); + } else { + trusted += userName; + } + } + info = info.replace("%managers%", trusted); + + return info; + } + + private boolean isVisible(GDClaim claim, String owner, String worldname) { + if (!this.cfg.hiddenRegions.isEmpty()) { + if (this.cfg.hiddenRegions.contains(claim.getUniqueId().toString()) || this.cfg.hiddenRegions.contains(owner) || + this.cfg.hiddenRegions.contains("world:" + worldname) || this.cfg.hiddenRegions.contains(worldname + "/" + owner)) + return false; + } + return true; + } + + private void addOwnerStyle(String owner, String worldid, AreaMarker marker, Claim claim) { + DynmapOwnerStyleCategory ownerStyle = null; + + if (!this.cfg.ownerStyles.isEmpty()) { + ownerStyle = this.cfg.ownerStyles.get(owner.toLowerCase()); + } + + if (ownerStyle == null) { + ownerStyle = this.defaultStyle; + } + + int sc = 0xFF0000; + int fc = 0xFF0000; + + if (claim.getType().equals(ClaimTypes.ADMIN)) { + sc = 0xFF0000; + fc = 0xFF0000; + } else if (claim.getType().equals(ClaimTypes.BASIC)) { + sc = 0xFFFF00; + fc = 0xFFFF00; + } else if (claim.getType().equals(ClaimTypes.TOWN)) { + sc = 0x00FF00; + fc = 0x00FF00; + } else if (claim.getType().equals(ClaimTypes.SUBDIVISION)) { + sc = 0xFF9C00; + fc = 0xFF9C00; + } + + marker.setLineStyle(ownerStyle.strokeWeight, ownerStyle.strokeOpacity, sc); + marker.setFillStyle(ownerStyle.fillOpacity, fc); + if (ownerStyle.label != null) { + marker.setLabel(ownerStyle.label); + } + } + + private void updateClaimMarker(Claim claim, Map markerMap) { + final World world = Sponge.getServer().getWorld(claim.getWorldUniqueId()).orElse(null); + if (world == null) { + return; + } + final String worldName = world.getName(); + final String owner = ((GDClaim) claim).getOwnerFriendlyName(); + if (isVisible((GDClaim) claim, owner, worldName)) { + final Vector3i lesserPos = claim.getLesserBoundaryCorner(); + final Vector3i greaterPos = claim.getGreaterBoundaryCorner(); + final double[] x = new double[4]; + final double[] z = new double[4]; + x[0] = lesserPos.getX(); + z[0] = lesserPos.getZ(); + x[1] = lesserPos.getX(); + z[1] = greaterPos.getZ() + 1.0; + x[2] = greaterPos.getX() + 1.0; + z[2] = greaterPos.getZ() + 1.0; + x[3] = greaterPos.getX() + 1.0; + z[3] = lesserPos.getZ(); + final UUID id = claim.getUniqueId(); + final String markerid = "GD_" + id; + AreaMarker marker = this.areaMarkers.remove(markerid); + if (marker == null) { + marker = this.set.createAreaMarker(markerid, owner, false, worldName, x, z, false); + if (marker == null) { + return; + } + } else { + marker.setCornerLocations(x, z); + marker.setLabel(owner); + } + if (this.cfg.use3dRegions) { + marker.setRangeY(greaterPos.getY() + 1.0, lesserPos.getY()); + } + + addOwnerStyle(owner, worldName, marker, claim); + String desc = getWindowInfo(claim, marker); + marker.setDescription(desc); + markerMap.put(markerid, marker); + } + } + + private void updateClaims() { + Map newmap = new HashMap(); + Sponge.getServer().getWorlds().stream().map(w -> GriefDefender.getCore().getClaimManager(w.getUniqueId())) + .map(ClaimManager::getWorldClaims).forEach(claims -> { + claims.forEach(claim -> updateClaimMarker(claim, newmap)); + }); + + for (AreaMarker oldm : this.areaMarkers.values()) { + oldm.deleteMarker(); + } + + this.areaMarkers = newmap; + } + + private void activate() { + this.markerapi = this.dynmap.getMarkerAPI(); + if (this.markerapi == null) { + return; + } + if (this.reload) { + GriefDefenderPlugin.getInstance().loadConfig(); + if (this.set != null) { + this.set.deleteMarkerSet(); + this.set = null; + } + this.areaMarkers.clear(); + } else { + this.reload = true; + } + + this.set = this.markerapi.getMarkerSet("griefdefender.markerset"); + if (this.set == null) { + this.set = this.markerapi.createMarkerSet("griefdefender.markerset", GriefDefenderPlugin.MOD_ID, null, false); + } else { + this.set.setMarkerSetLabel(GriefDefenderPlugin.MOD_ID); + } + if (this.set == null) { + this.logger.error("Error creating marker set"); + return; + } + + int minzoom = this.cfg.minzoom; + if (minzoom > 0) { + this.set.setMinZoom(minzoom); + } + + this.set.setLayerPriority(this.cfg.layerPriority); + this.set.setHideByDefault(this.cfg.layerHideByDefault); + + new GriefDefenderUpdate(40L); + this.logger.info("Dynmap provider is activated"); + } + + public void onDisable() { + if (this.set != null) { + this.set.deleteMarkerSet(); + this.set = null; + } + this.areaMarkers.clear(); + this.disabled = true; + } + + private class GriefDefenderUpdate implements Consumer { + + public GriefDefenderUpdate(long delay) { + Sponge.getScheduler().createTaskBuilder().execute(this).delayTicks(delay).submit(GDBootstrap.getInstance()); + } + + @Override + public void accept(Task t) { + if (!disabled) { + updateClaims(); + } else { + t.cancel(); + } + } + } + + @Listener(order = Order.POST) + public void onClaimCreate(CreateClaimEvent event) { + new GriefDefenderUpdate(20L); + } + + @Listener(order = Order.POST) + public void onClaimDelete(RemoveClaimEvent event) { + new GriefDefenderUpdate(20L); + } + + @Listener(order = Order.POST) + public void onClaimChange(ChangeClaimEvent event) { + new GriefDefenderUpdate(20L); + } +} \ No newline at end of file