From 87f0b1fc974a405e36f40a861db11789db990ee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Fri, 14 Aug 2020 17:24:34 +0200 Subject: [PATCH 1/2] Add service system and rewrite Auto to use the service system --- Bukkit/build.gradle | 1 + Core/build.gradle | 1 + .../com/plotsquared/core/PlotPlatform.java | 10 ++ .../com/plotsquared/core/command/Auto.java | 147 +++++++----------- .../inject/modules/PlotSquaredModule.java | 2 + .../com/plotsquared/core/plot/PlotArea.java | 22 ++- .../core/services/plots/AutoService.java | 139 +++++++++++++++++ 7 files changed, 224 insertions(+), 98 deletions(-) create mode 100644 Core/src/main/java/com/plotsquared/core/services/plots/AutoService.java diff --git a/Bukkit/build.gradle b/Bukkit/build.gradle index 694c6f319..ddddb16b5 100644 --- a/Bukkit/build.gradle +++ b/Bukkit/build.gradle @@ -115,6 +115,7 @@ shadowJar { include(dependency('org.slf4j:slf4j-api')) include(dependency('javax.inject:javax.inject:1')) include(dependency('aopalliance:aopalliance:1.0')) + include(dependency('com.intellectualsites:Pipeline:1.4.0-SNAPSHOT')) } relocate('net.kyori.text', 'com.plotsquared.formatting.text') diff --git a/Core/build.gradle b/Core/build.gradle index 3189bd211..5b62806a6 100644 --- a/Core/build.gradle +++ b/Core/build.gradle @@ -23,6 +23,7 @@ dependencies { compile group: 'aopalliance', name: 'aopalliance', version: '1.0' // logging implementation("org.apache.logging.log4j:log4j-slf4j-impl:2.8.1") + implementation('com.intellectualsites:Pipeline:1.4.0-SNAPSHOT') } sourceCompatibility = 1.8 diff --git a/Core/src/main/java/com/plotsquared/core/PlotPlatform.java b/Core/src/main/java/com/plotsquared/core/PlotPlatform.java index fbe2d34ff..6daa02a07 100644 --- a/Core/src/main/java/com/plotsquared/core/PlotPlatform.java +++ b/Core/src/main/java/com/plotsquared/core/PlotPlatform.java @@ -28,6 +28,7 @@ package com.plotsquared.core; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; +import com.intellectualsites.services.ServicePipeline; import com.plotsquared.core.backup.BackupManager; import com.plotsquared.core.configuration.caption.LocaleHolder; import com.plotsquared.core.generator.GeneratorWrapper; @@ -262,4 +263,13 @@ public interface PlotPlatform

extends LocaleHolder { return getInjector().getInstance(PermissionHandler.class); } + /** + * Get the {@link ServicePipeline} implementation + * + * @return Service pipeline + */ + @Nonnull default ServicePipeline getServicePipeline() { + return getInjector().getInstance(ServicePipeline.class); + } + } diff --git a/Core/src/main/java/com/plotsquared/core/command/Auto.java b/Core/src/main/java/com/plotsquared/core/command/Auto.java index fd51014c7..d8682eba1 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Auto.java +++ b/Core/src/main/java/com/plotsquared/core/command/Auto.java @@ -25,8 +25,9 @@ */ package com.plotsquared.core.command; -import com.google.common.collect.Lists; +import com.google.common.reflect.TypeToken; import com.google.inject.Inject; +import com.intellectualsites.services.ServicePipeline; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.caption.TranslatableCaption; @@ -34,7 +35,6 @@ import com.plotsquared.core.database.DBFunc; import com.plotsquared.core.events.PlayerAutoPlotEvent; import com.plotsquared.core.events.PlotAutoMergeEvent; import com.plotsquared.core.events.Result; -import com.plotsquared.core.events.TeleportCause; import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.permissions.PermissionHandler; import com.plotsquared.core.player.MetaDataAccess; @@ -42,9 +42,8 @@ import com.plotsquared.core.player.PlayerMetaDataKeys; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotArea; -import com.plotsquared.core.plot.PlotAreaType; -import com.plotsquared.core.plot.PlotId; import com.plotsquared.core.plot.world.PlotAreaManager; +import com.plotsquared.core.services.plots.AutoService; import com.plotsquared.core.util.EconHandler; import com.plotsquared.core.util.EventDispatcher; import com.plotsquared.core.util.Expression; @@ -56,8 +55,10 @@ import net.kyori.adventure.text.minimessage.Template; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.Collections; +import java.util.Iterator; import java.util.List; -import java.util.Set; +import java.util.stream.Collectors; @CommandDeclaration(command = "auto", permission = "plots.auto", @@ -71,13 +72,23 @@ public class Auto extends SubCommand { private final PlotAreaManager plotAreaManager; private final EventDispatcher eventDispatcher; private final EconHandler econHandler; + private final ServicePipeline servicePipeline; @Inject public Auto(@Nonnull final PlotAreaManager plotAreaManager, @Nonnull final EventDispatcher eventDispatcher, - @Nullable final EconHandler econHandler) { + @Nullable final EconHandler econHandler, + @Nonnull final ServicePipeline servicePipeline) { this.plotAreaManager = plotAreaManager; this.eventDispatcher = eventDispatcher; this.econHandler = econHandler; + this.servicePipeline = servicePipeline; + this.servicePipeline.registerServiceType(TypeToken.of(AutoService.class), new AutoService.DefaultAutoService()); + final AutoService.MultiPlotService multiPlotService = new AutoService.MultiPlotService(); + this.servicePipeline.registerServiceImplementation(AutoService.class, multiPlotService, + Collections.singletonList(multiPlotService)); + final AutoService.SinglePlotService singlePlotService = new AutoService.SinglePlotService(); + this.servicePipeline.registerServiceImplementation(AutoService.class, singlePlotService, + Collections.singletonList(singlePlotService)); } public static boolean checkAllowedPlots(PlotPlayer player, PlotArea plotarea, @@ -123,62 +134,32 @@ public class Auto extends SubCommand { return true; } - /** - * Teleport the player home, or claim a new plot - * - * @param player - * @param area - * @param start - * @param schematic - */ - public static void homeOrAuto(final PlotPlayer player, final PlotArea area, PlotId start, - final String schematic) { - Set plots = player.getPlots(); - if (!plots.isEmpty()) { - plots.iterator().next().teleportPlayer(player, TeleportCause.COMMAND, result -> { - }); - } else { - autoClaimSafe(player, area, start, schematic); - } - } - - /** - * Claim a new plot for a player - * - * @param player - * @param area - * @param start - * @param schematic - */ - public static void autoClaimSafe(final PlotPlayer player, final PlotArea area, PlotId start, - final String schematic) { + private void claimSingle(@Nonnull final PlotPlayer player, @Nonnull final Plot plot, + @Nonnull final PlotArea plotArea, @Nullable final String schematic) { try (final MetaDataAccess metaDataAccess = player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_AUTO)) { metaDataAccess.set(true); } - autoClaimFromDatabase(player, area, start, new RunnableVal() { + plot.setOwnerAbs(player.getUUID()); + + final RunnableVal runnableVal = new RunnableVal() { + { + this.value = plot; + } + @Override public void run(final Plot plot) { try { - TaskManager.getPlatformImplementation().sync(new AutoClaimFinishTask(player, plot, area, schematic, - PlotSquared.get().getEventDispatcher())); + TaskManager.getPlatformImplementation().sync( + new AutoClaimFinishTask(player, plot, plotArea, schematic, + PlotSquared.get().getEventDispatcher())); } catch (final Exception e) { e.printStackTrace(); } } - }); - } + }; + + DBFunc.createPlotSafe(plot, runnableVal, () -> claimSingle(player, plot, plotArea, schematic)); - public static void autoClaimFromDatabase(final PlotPlayer player, final PlotArea area, - PlotId start, final RunnableVal whenDone) { - final Plot plot = area.getNextFreePlot(player, start); - if (plot == null) { - whenDone.run(null); - return; - } - whenDone.value = plot; - plot.setOwnerAbs(player.getUUID()); - DBFunc.createPlotSafe(plot, whenDone, - () -> autoClaimFromDatabase(player, area, plot.getId(), whenDone)); } @Override public boolean onCommand(final PlotPlayer player, String[] args) { @@ -313,49 +294,33 @@ public class Auto extends SubCommand { ); } } - // TODO handle type 2 (partial) the same as normal worlds! - if (size_x == 1 && size_z == 1) { - autoClaimSafe(player, plotarea, null, schematic); - return true; + + final List plots = this.servicePipeline + .pump(new AutoService.AutoQuery(player, null, size_x, size_z, plotarea)) + .through(AutoService.class) + .getResult(); + + if (plots.isEmpty()) { + player.sendMessage(TranslatableCaption.of("errors.no_free_plots")); + return false; + } else if (plots.size() == 1) { + this.claimSingle(player, plots.get(0), plotarea, schematic); } else { - if (plotarea.getType() == PlotAreaType.PARTIAL) { - player.sendMessage(TranslatableCaption.of("errors.no_free_plots")); + final Iterator plotIterator = plots.iterator(); + while (plotIterator.hasNext()) { + plotIterator.next().claim(player, !plotIterator.hasNext(), null); + } + final PlotAutoMergeEvent mergeEvent = this.eventDispatcher.callAutoMerge(plots.get(0), + plots.stream().map(Plot::getId).collect(Collectors.toList())); + if (!force && mergeEvent.getEventResult() == Result.DENY) { + player.sendMessage( + TranslatableCaption.of("events.event_denied"), + Template.of("value", "Auto merge") + ); return false; } - while (true) { - PlotId start = plotarea.getMeta("lastPlot", PlotId.of(0, 0)).getNextId(); - PlotId end = PlotId.of(start.getX() + size_x - 1, start.getY() + size_z - 1); - if (plotarea.canClaim(player, start, end)) { - plotarea.setMeta("lastPlot", start); - - for (final PlotId plotId : PlotId.PlotRangeIterator.range(start, end)) { - final Plot plot = plotarea.getPlot(plotId); - if (plot == null) { - return false; - } - plot.claim(player, plotId.equals(end), null); - } - - final List plotIds = Lists.newArrayList((Iterable) - PlotId.PlotRangeIterator.range(start, end)); - final PlotId pos1 = plotIds.get(0); - final PlotAutoMergeEvent mergeEvent = this.eventDispatcher - .callAutoMerge(plotarea.getPlotAbs(pos1), plotIds); - if (!force && mergeEvent.getEventResult() == Result.DENY) { - player.sendMessage( - TranslatableCaption.of("events.event_denied"), - Template.of("value", "Auto merge") - ); - return false; - } - if (!plotarea.mergePlots(mergeEvent.getPlots(), true)) { - return false; - } - break; - } - plotarea.setMeta("lastPlot", start); - } - return true; + return plotarea.mergePlots(mergeEvent.getPlots(), true); } + return true; } } diff --git a/Core/src/main/java/com/plotsquared/core/inject/modules/PlotSquaredModule.java b/Core/src/main/java/com/plotsquared/core/inject/modules/PlotSquaredModule.java index 2103602ff..d68f988e8 100644 --- a/Core/src/main/java/com/plotsquared/core/inject/modules/PlotSquaredModule.java +++ b/Core/src/main/java/com/plotsquared/core/inject/modules/PlotSquaredModule.java @@ -26,6 +26,7 @@ package com.plotsquared.core.inject.modules; import com.google.inject.AbstractModule; +import com.intellectualsites.services.ServicePipeline; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.file.YamlConfiguration; import com.plotsquared.core.inject.annotations.BackgroundPipeline; @@ -44,6 +45,7 @@ public class PlotSquaredModule extends AbstractModule { @Override protected void configure() { final PlotSquared plotSquared = PlotSquared.get(); + bind(ServicePipeline.class).toInstance(ServicePipeline.builder().build()); bind(YamlConfiguration.class).annotatedWith(WorldConfig.class).toInstance(plotSquared.getWorldConfiguration()); bind(File.class).annotatedWith(WorldFile.class).toInstance(plotSquared.getWorldsFile()); bind(File.class).annotatedWith(ConfigFile.class).toInstance(plotSquared.getConfigFile()); diff --git a/Core/src/main/java/com/plotsquared/core/plot/PlotArea.java b/Core/src/main/java/com/plotsquared/core/plot/PlotArea.java index dc2069368..e5a8a3d83 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/PlotArea.java +++ b/Core/src/main/java/com/plotsquared/core/plot/PlotArea.java @@ -83,6 +83,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -908,31 +909,38 @@ public abstract class PlotArea { } } - public boolean canClaim(@Nullable final PlotPlayer player, @Nonnull final PlotId pos1, + @Nullable public List canClaim(@Nullable final PlotPlayer player, @Nonnull final PlotId pos1, @Nonnull final PlotId pos2) { if (pos1.getX() == pos2.getX() && pos1.getY() == pos2.getY()) { if (getOwnedPlot(pos1) != null) { - return false; + return null; } final Plot plot = getPlotAbs(pos1); if (plot == null) { - return false; + return null; + } + if (plot.canClaim(player)) { + return Collections.singletonList(plot); + } else { + return null; } - return plot.canClaim(player); } + final List plots = new LinkedList<>(); for (int x = pos1.getX(); x <= pos2.getX(); x++) { for (int y = pos1.getY(); y <= pos2.getY(); y++) { final PlotId id = PlotId.of(x, y); final Plot plot = getPlotAbs(id); if (plot == null) { - return false; + return null; } if (!plot.canClaim(player)) { - return false; + return null; + } else { + plots.add(plot); } } } - return true; + return plots; } public boolean removePlot(@Nonnull final PlotId id) { diff --git a/Core/src/main/java/com/plotsquared/core/services/plots/AutoService.java b/Core/src/main/java/com/plotsquared/core/services/plots/AutoService.java new file mode 100644 index 000000000..8f149a428 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/services/plots/AutoService.java @@ -0,0 +1,139 @@ +package com.plotsquared.core.services.plots; + +import com.intellectualsites.services.types.Service; +import com.plotsquared.core.player.PlotPlayer; +import com.plotsquared.core.plot.Plot; +import com.plotsquared.core.plot.PlotArea; +import com.plotsquared.core.plot.PlotAreaType; +import com.plotsquared.core.plot.PlotId; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; + +public interface AutoService extends Service> { + + final class AutoQuery { + + private final PlotPlayer player; + private final PlotId startId; + private final int sizeX; + private final int sizeZ; + private final PlotArea plotArea; + + /** + * Crate a new auto query + * + * @param player Player to claim for + * @param startId Plot ID to start searching from + * @param sizeX Number of plots along the X axis + * @param sizeZ Number of plots along the Z axis + * @param plotArea Plot area to search in + */ + public AutoQuery(@Nonnull final PlotPlayer player, @Nullable final PlotId startId, + final int sizeX, final int sizeZ, @Nonnull final PlotArea plotArea) { + this.player = player; + this.startId = startId; + this.sizeX = sizeX; + this.sizeZ = sizeZ; + this.plotArea = plotArea; + } + + /** + * Get the player that the plots are meant for + * + * @return Player + */ + @Nonnull public PlotPlayer getPlayer() { + return this.player; + } + + /** + * Get the plot ID to start searching from + * + * @return Start ID + */ + @Nullable public PlotId getStartId() { + return this.startId; + } + + /** + * Get the number of plots along the X axis + * + * @return Number of plots along the X axis + */ + public int getSizeX() { + return this.sizeX; + } + + /** + * Get the number of plots along the Z axis + * + * @return Number of plots along the Z axis + */ + public int getSizeZ() { + return this.sizeZ; + } + + /** + * Get the plot area to search in + * + * @return Plot area + */ + @Nonnull public PlotArea getPlotArea() { + return this.plotArea; + } + + } + + + final class DefaultAutoService implements AutoService { + + @Override public List handle(@Nonnull final AutoQuery autoQuery) { + return Collections.emptyList(); + } + + } + + + final class SinglePlotService implements AutoService, Predicate { + + @Nullable @Override public List handle(@Nonnull AutoQuery autoQuery) { + final Plot plot = autoQuery.getPlotArea().getNextFreePlot(autoQuery.getPlayer(), autoQuery.getStartId()); + if (plot == null) { + return null; + } + return Collections.singletonList(plot); + } + + @Override public boolean test(@Nonnull final AutoQuery autoQuery) { + return autoQuery.sizeX == 1 && autoQuery.sizeZ == 1; + } + + } + + + final class MultiPlotService implements AutoService, Predicate { + + @Override public List handle(@Nonnull final AutoQuery autoQuery) { + /* TODO: Add timeout? */ + while (true) { + final PlotId start = autoQuery.getPlotArea().getMeta("lastPlot", PlotId.of(0, 0)).getNextId(); + final PlotId end = PlotId.of(start.getX() + autoQuery.getSizeX() - 1, start.getY() + autoQuery.getSizeZ() - 1); + final List plots = autoQuery.getPlotArea().canClaim(autoQuery.getPlayer(), start, end); + if (plots != null && !plots.isEmpty()) { + autoQuery.getPlotArea().setMeta("lastPlot", start); + return plots; + } + } + } + + @Override public boolean test(@Nonnull final AutoQuery autoQuery) { + return autoQuery.getPlotArea().getType() != PlotAreaType.PARTIAL; + } + + } + +} From 351ae1b2c726ac3ce824e9a064110e5ae3b82db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Mon, 17 Aug 2020 01:27:45 +0200 Subject: [PATCH 2/2] Synchronize auto queries --- .../core/services/plots/AutoService.java | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/services/plots/AutoService.java b/Core/src/main/java/com/plotsquared/core/services/plots/AutoService.java index 8f149a428..0ebab9494 100644 --- a/Core/src/main/java/com/plotsquared/core/services/plots/AutoService.java +++ b/Core/src/main/java/com/plotsquared/core/services/plots/AutoService.java @@ -1,5 +1,7 @@ package com.plotsquared.core.services.plots; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.intellectualsites.services.types.Service; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; @@ -11,10 +13,15 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.function.Predicate; public interface AutoService extends Service> { + Cache plotCandidateCache = CacheBuilder.newBuilder() + .expireAfterWrite(20, TimeUnit.SECONDS).build(); + Object plotLock = new Object(); + final class AutoQuery { private final PlotPlayer player; @@ -101,11 +108,17 @@ public interface AutoService extends Service> final class SinglePlotService implements AutoService, Predicate { @Nullable @Override public List handle(@Nonnull AutoQuery autoQuery) { - final Plot plot = autoQuery.getPlotArea().getNextFreePlot(autoQuery.getPlayer(), autoQuery.getStartId()); - if (plot == null) { - return null; - } - return Collections.singletonList(plot); + Plot plot; + do { + synchronized (plotLock) { + plot = autoQuery.getPlotArea().getNextFreePlot(autoQuery.getPlayer(), autoQuery.getStartId()); + if (plot != null && plotCandidateCache.getIfPresent(plot.getId()) == null) { + plotCandidateCache.put(plot.getId(), plot); + return Collections.singletonList(plot); + } + } + } while (plot != null); + return null; } @Override public boolean test(@Nonnull final AutoQuery autoQuery) { @@ -119,13 +132,24 @@ public interface AutoService extends Service> @Override public List handle(@Nonnull final AutoQuery autoQuery) { /* TODO: Add timeout? */ - while (true) { - final PlotId start = autoQuery.getPlotArea().getMeta("lastPlot", PlotId.of(0, 0)).getNextId(); - final PlotId end = PlotId.of(start.getX() + autoQuery.getSizeX() - 1, start.getY() + autoQuery.getSizeZ() - 1); - final List plots = autoQuery.getPlotArea().canClaim(autoQuery.getPlayer(), start, end); - if (plots != null && !plots.isEmpty()) { - autoQuery.getPlotArea().setMeta("lastPlot", start); - return plots; + outer: while (true) { + synchronized (plotLock) { + final PlotId start = + autoQuery.getPlotArea().getMeta("lastPlot", PlotId.of(0, 0)).getNextId(); + final PlotId end = PlotId.of(start.getX() + autoQuery.getSizeX() - 1, + start.getY() + autoQuery.getSizeZ() - 1); + final List plots = + autoQuery.getPlotArea().canClaim(autoQuery.getPlayer(), start, end); + if (plots != null && !plots.isEmpty()) { + autoQuery.getPlotArea().setMeta("lastPlot", start); + for (final Plot plot : plots) { + if (plotCandidateCache.getIfPresent(plot.getId()) != null) { + continue outer; + } + plotCandidateCache.put(plot.getId(), plot); + } + return plots; + } } } }