mirror of
				https://github.com/IntellectualSites/PlotSquared.git
				synced 2025-10-31 17:43:44 +01:00 
			
		
		
		
	feat: rewrite database interactions
The goal of this PR is to completely re-write how PlotSquared interacts with databases. This will be achieved using Flyway, JDBI and new architectural principles.
This commit is contained in:
		| @@ -37,6 +37,10 @@ dependencies { | |||||||
|     // Logging |     // Logging | ||||||
|     compileOnlyApi(libs.log4j) |     compileOnlyApi(libs.log4j) | ||||||
|  |  | ||||||
|  |     // Records are cool. Builders are cooler | ||||||
|  |     compileOnly(libs.recordBuilderProcessor) | ||||||
|  |     annotationProcessor(libs.recordBuilderProcessor) | ||||||
|  |  | ||||||
|     // Other libraries |     // Other libraries | ||||||
|     api(libs.prtree) |     api(libs.prtree) | ||||||
|     api(libs.aopalliance) |     api(libs.aopalliance) | ||||||
|   | |||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | package com.plotsquared.core.player; | ||||||
|  |  | ||||||
|  | public enum PlotRole { | ||||||
|  |     OWNER, | ||||||
|  |     HELPER, | ||||||
|  |     TRUSTED, | ||||||
|  |     DENIED, | ||||||
|  | } | ||||||
| @@ -290,14 +290,14 @@ public class Plot { | |||||||
|      * @param area      the plot's PlotArea |      * @param area      the plot's PlotArea | ||||||
|      * @param merged    an array giving merged plots |      * @param merged    an array giving merged plots | ||||||
|      * @param timestamp when the plot was created |      * @param timestamp when the plot was created | ||||||
|      * @param temp      value representing whatever DBManager needs to to. Do not touch tbh. |      * @param temp      value representing whatever DBManager needs it to. Do not touch tbh. | ||||||
|      */ |      */ | ||||||
|     public Plot( |     public Plot( | ||||||
|             @NonNull PlotId id, |             @NonNull PlotId id, | ||||||
|             UUID owner, |             UUID owner, | ||||||
|             HashSet<UUID> trusted, |             Set<UUID> trusted, | ||||||
|             HashSet<UUID> members, |             Set<UUID> members, | ||||||
|             HashSet<UUID> denied, |             Set<UUID> denied, | ||||||
|             String alias, |             String alias, | ||||||
|             BlockLoc position, |             BlockLoc position, | ||||||
|             Collection<PlotFlag<?, ?>> flags, |             Collection<PlotFlag<?, ?>> flags, | ||||||
| @@ -310,9 +310,9 @@ public class Plot { | |||||||
|         this.area = area; |         this.area = area; | ||||||
|         this.owner = owner; |         this.owner = owner; | ||||||
|         this.settings = new PlotSettings(); |         this.settings = new PlotSettings(); | ||||||
|         this.members = members; |         this.members = new HashSet<>(members); | ||||||
|         this.trusted = trusted; |         this.trusted = new HashSet<>(trusted); | ||||||
|         this.denied = denied; |         this.denied = new HashSet<>(denied); | ||||||
|         this.settings.setAlias(alias); |         this.settings.setAlias(alias); | ||||||
|         this.settings.setPosition(position); |         this.settings.setPosition(position); | ||||||
|         this.settings.setMerged(merged); |         this.settings.setMerged(merged); | ||||||
| @@ -321,9 +321,7 @@ public class Plot { | |||||||
|         if (area != null) { |         if (area != null) { | ||||||
|             this.flagContainer.setParentContainer(area.getFlagContainer()); |             this.flagContainer.setParentContainer(area.getFlagContainer()); | ||||||
|             if (flags != null) { |             if (flags != null) { | ||||||
|                 for (PlotFlag<?, ?> flag : flags) { |                 flags.forEach(this.flagContainer::addFlag); | ||||||
|                     this.flagContainer.addFlag(flag); |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         PlotSquared.platform().injector().injectMembers(this); |         PlotSquared.platform().injector().injectMembers(this); | ||||||
|   | |||||||
| @@ -0,0 +1,56 @@ | |||||||
|  | package com.plotsquared.core.plot; | ||||||
|  |  | ||||||
|  | import com.plotsquared.core.repository.PlotRepository; | ||||||
|  | import com.plotsquared.core.repository.PlotRoleRepository; | ||||||
|  | import com.plotsquared.core.repository.PlotSettingsRepository; | ||||||
|  | import com.plotsquared.core.repository.dbo.PlotSettingsDBOBuilder; | ||||||
|  | import org.checkerframework.checker.nullness.qual.NonNull; | ||||||
|  |  | ||||||
|  | import javax.inject.Inject; | ||||||
|  | import java.util.Collection; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
|  | public final class PlotService { | ||||||
|  |  | ||||||
|  |     private static final String PLOT_SETTINGS_DEFAULT_POSITION = "default"; | ||||||
|  |  | ||||||
|  |     private final PlotRepository plotRepository; | ||||||
|  |     private final PlotSettingsRepository plotSettingsRepository; | ||||||
|  |     private final PlotRoleRepository plotRoleRepository; | ||||||
|  |  | ||||||
|  |     @Inject | ||||||
|  |     public PlotService( | ||||||
|  |         final @NonNull PlotRepository plotRepository, | ||||||
|  |         final @NonNull PlotSettingsRepository plotSettingsRepository, | ||||||
|  |         final @NonNull PlotRoleRepository plotRoleRepository | ||||||
|  |     ) { | ||||||
|  |         this.plotRepository = plotRepository; | ||||||
|  |         this.plotSettingsRepository = plotSettingsRepository; | ||||||
|  |         this.plotRoleRepository = plotRoleRepository; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns a list containing all the plots in the given {@code plotArea}. | ||||||
|  |      * | ||||||
|  |      * @param plotArea the area | ||||||
|  |      * @return all plots in the area | ||||||
|  |      */ | ||||||
|  |     public @NonNull Collection<Plot> getPlotsInArea(final @NonNull PlotArea plotArea) { | ||||||
|  |         return this.plotRepository.getPlotsInArea(plotArea.getId()) | ||||||
|  |                 .map(plotDBO -> { | ||||||
|  |                     final var settings = this.plotSettingsRepository.findById(plotDBO) | ||||||
|  |                             .orElseGet(() -> PlotSettingsDBOBuilder.builder() | ||||||
|  |                                     .plot(plotDBO) | ||||||
|  |                                     .position(PLOT_SETTINGS_DEFAULT_POSITION) | ||||||
|  |                                     .build() | ||||||
|  |                             ); | ||||||
|  |                     return plotDBO.toPlot( | ||||||
|  |                             plotArea, | ||||||
|  |                             settings, | ||||||
|  |                             this.plotRoleRepository.findAllFor(plotDBO), | ||||||
|  |                             List.of() | ||||||
|  |                     ); | ||||||
|  |                 }).collect(Collectors.toList()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,21 @@ | |||||||
|  | package com.plotsquared.core.repository; | ||||||
|  |  | ||||||
|  | import com.plotsquared.core.plot.PlotId; | ||||||
|  | import com.plotsquared.core.repository.dbo.PlotDBO; | ||||||
|  | import org.checkerframework.checker.nullness.qual.NonNull; | ||||||
|  |  | ||||||
|  | import java.util.stream.Stream; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * {@link Repository} storing {@link PlotDBO plots} identified by their respective {@link PlotId}. | ||||||
|  |  */ | ||||||
|  | public interface PlotRepository extends Repository<PlotDBO, Integer> { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns all plots in the given {@code area}. | ||||||
|  |      * | ||||||
|  |      * @param area the plot area | ||||||
|  |      * @return a stream with all plots in the given area. | ||||||
|  |      */ | ||||||
|  |     @NonNull Stream<PlotDBO> getPlotsInArea(@NonNull String area); | ||||||
|  | } | ||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | package com.plotsquared.core.repository; | ||||||
|  |  | ||||||
|  | import com.plotsquared.core.repository.dbo.PlotDBO; | ||||||
|  | import com.plotsquared.core.repository.dbo.PlotRoleDBO; | ||||||
|  | import org.checkerframework.checker.nullness.qual.NonNull; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public interface PlotRoleRepository extends Repository<PlotRoleDBO, PlotRoleDBO.Key> { | ||||||
|  |  | ||||||
|  |     List<PlotRoleDBO> findAllFor(@NonNull PlotDBO plotDBO); | ||||||
|  | } | ||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | package com.plotsquared.core.repository; | ||||||
|  |  | ||||||
|  | import com.plotsquared.core.repository.dbo.PlotDBO; | ||||||
|  | import com.plotsquared.core.repository.dbo.PlotSettingsDBO; | ||||||
|  |  | ||||||
|  | public interface PlotSettingsRepository extends Repository<PlotSettingsDBO, PlotDBO> { | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,28 @@ | |||||||
|  | package com.plotsquared.core.repository; | ||||||
|  |  | ||||||
|  | import org.checkerframework.checker.nullness.qual.NonNull; | ||||||
|  |  | ||||||
|  | import java.util.Optional; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Generic object repository. | ||||||
|  |  * | ||||||
|  |  * @param <T> the type of object stored in the repository. | ||||||
|  |  * @param <U> the type used to identify objects stored in the repository. | ||||||
|  |  */ | ||||||
|  | public interface Repository<@NonNull T, @NonNull U> { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Saves the given object. | ||||||
|  |      * | ||||||
|  |      * @param object {@code the object}. | ||||||
|  |      */ | ||||||
|  |     void save(T object); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Finds the object by its {@code id}. | ||||||
|  |      * | ||||||
|  |      * @param id the id | ||||||
|  |      */ | ||||||
|  |     @NonNull Optional<T> findById(U id); | ||||||
|  | } | ||||||
| @@ -0,0 +1,65 @@ | |||||||
|  | package com.plotsquared.core.repository.dbo; | ||||||
|  |  | ||||||
|  | import com.plotsquared.core.location.BlockLoc; | ||||||
|  | import com.plotsquared.core.plot.Plot; | ||||||
|  | import com.plotsquared.core.plot.PlotArea; | ||||||
|  | import com.plotsquared.core.plot.PlotId; | ||||||
|  | import com.plotsquared.core.plot.flag.PlotFlag; | ||||||
|  | import io.soabase.recordbuilder.core.RecordBuilder; | ||||||
|  | import org.checkerframework.checker.nullness.qual.NonNull; | ||||||
|  | import org.checkerframework.checker.nullness.qual.Nullable; | ||||||
|  |  | ||||||
|  | import java.time.Instant; | ||||||
|  | import java.util.Collection; | ||||||
|  | import java.util.Collections; | ||||||
|  | import java.util.HashSet; | ||||||
|  | import java.util.Objects; | ||||||
|  | import java.util.Set; | ||||||
|  | import java.util.UUID; | ||||||
|  |  | ||||||
|  | @RecordBuilder | ||||||
|  | public record PlotDBO( | ||||||
|  |         @Nullable Integer id, | ||||||
|  |         int plotIdX, | ||||||
|  |         int plotIdZ, | ||||||
|  |         @NonNull String world, | ||||||
|  |         @NonNull UUID owner, | ||||||
|  |         @NonNull Instant timestamp | ||||||
|  | ) { | ||||||
|  |  | ||||||
|  |     public @NonNull Plot toPlot( | ||||||
|  |             final @NonNull PlotArea plotArea, | ||||||
|  |             final @NonNull PlotSettingsDBO plotSettingsDBO, | ||||||
|  |             final @NonNull Collection<PlotRoleDBO> plotRoles, | ||||||
|  |             final @NonNull Collection<PlotFlag<?, ?>> flags | ||||||
|  |     ) { | ||||||
|  |         final PlotId plotId = PlotId.of(this.plotIdX(), this.plotIdZ()); | ||||||
|  |         final int id = Objects.requireNonNull(id(), "id may not be null"); | ||||||
|  |  | ||||||
|  |         final Set<UUID> trusted = new HashSet<>(); | ||||||
|  |         final Set<UUID> members = new HashSet<>(); | ||||||
|  |         final Set<UUID> denied = new HashSet<>(); | ||||||
|  |         for (final PlotRoleDBO plotRole : plotRoles) { | ||||||
|  |             switch (plotRole.plotRole()) { | ||||||
|  |                 case TRUSTED -> trusted.add(plotRole.userId()); | ||||||
|  |                 case HELPER -> members.add(plotRole.userId()); | ||||||
|  |                 case DENIED -> denied.add(plotRole.userId()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return new Plot( | ||||||
|  |                 plotId, | ||||||
|  |                 this.owner(), | ||||||
|  |                 Collections.unmodifiableSet(trusted), | ||||||
|  |                 Collections.unmodifiableSet(members), | ||||||
|  |                 Collections.unmodifiableSet(denied), | ||||||
|  |                 plotSettingsDBO.alias(), | ||||||
|  |                 BlockLoc.fromString(plotSettingsDBO.position()), | ||||||
|  |                 flags, | ||||||
|  |                 plotArea, | ||||||
|  |                 plotSettingsDBO.unwrapMerged(), | ||||||
|  |                 this.timestamp().toEpochMilli(), | ||||||
|  |                 id | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,22 @@ | |||||||
|  | package com.plotsquared.core.repository.dbo; | ||||||
|  |  | ||||||
|  | import com.plotsquared.core.player.PlotRole; | ||||||
|  | import io.soabase.recordbuilder.core.RecordBuilder; | ||||||
|  | import org.checkerframework.checker.nullness.qual.NonNull; | ||||||
|  |  | ||||||
|  | import java.util.UUID; | ||||||
|  |  | ||||||
|  | @RecordBuilder | ||||||
|  | public record PlotRoleDBO( | ||||||
|  |         @NonNull PlotDBO plot, | ||||||
|  |         @NonNull UUID userId, | ||||||
|  |         @NonNull PlotRole plotRole | ||||||
|  | ) { | ||||||
|  |  | ||||||
|  |     public @NonNull Key key() { | ||||||
|  |         return new Key(this.plot(), this.userId()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public record Key(@NonNull PlotDBO plot, @NonNull UUID userId) { | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,29 @@ | |||||||
|  | package com.plotsquared.core.repository.dbo; | ||||||
|  |  | ||||||
|  | import io.soabase.recordbuilder.core.RecordBuilder; | ||||||
|  | import org.checkerframework.checker.nullness.qual.NonNull; | ||||||
|  | import org.checkerframework.checker.nullness.qual.Nullable; | ||||||
|  |  | ||||||
|  | @RecordBuilder | ||||||
|  | public record PlotSettingsDBO( | ||||||
|  |         @NonNull PlotDBO plot, | ||||||
|  |         @Nullable String alias, | ||||||
|  |         Integer merged, | ||||||
|  |         @NonNull String position | ||||||
|  | ) { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Unwraps {@link #merged()} into an array indicating whether the plot is merged in | ||||||
|  |      * any given cardinal direction. The indices of the array correspond to the ordinals of | ||||||
|  |      * {@link com.plotsquared.core.location.Direction}. | ||||||
|  |      * | ||||||
|  |      * @return unwrapped merged status | ||||||
|  |      */ | ||||||
|  |     public boolean@NonNull [] unwrapMerged() { | ||||||
|  |         final boolean[] merged = new boolean[4]; | ||||||
|  |         for (int i = 0; i < 4; i++) { | ||||||
|  |             merged[3 - i] = (this.merged() & 1 << i) != 0; | ||||||
|  |         } | ||||||
|  |         return merged; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,68 @@ | |||||||
|  | create table if not exists ${prefix}plot( | ||||||
|  |     id        int(11)     not null auto_increment, | ||||||
|  |     plot_id_x int(11)     not null, | ||||||
|  |     plot_id_z int(11)     not null, | ||||||
|  |     world     varchar(45) not null, | ||||||
|  |     owner     varchar(40) not null, | ||||||
|  |     timestamp timestamp   not null default current_timestamp, | ||||||
|  |     primary key (id) | ||||||
|  | ) engine=InnoDB default charset=utf8 auto_increment=0; | ||||||
|  |  | ||||||
|  | -- TODO: Migrating existing data to this table. | ||||||
|  | create table if not exists ${prefix}plot_role( | ||||||
|  |     plot_id int(11)                             not null, | ||||||
|  |     user_id varchar(45)                         not null, | ||||||
|  |     role    enum('helper', 'trusted', 'denied') not null, | ||||||
|  |     foreign key (plot_id) references ${prefix}plot(id) on delete cascade, | ||||||
|  |     primary key (plot_id, user_id) | ||||||
|  | ) engine=InnoDB default charset=utf8 auto_increment=0; | ||||||
|  |  | ||||||
|  | create table if not exists ${prefix}plot_comments( | ||||||
|  |     world     varchar(45) not null, | ||||||
|  |     comment   varchar(45) not null, | ||||||
|  |     inbox     varchar(45) not null, | ||||||
|  |     timestamp int(11)     not null, | ||||||
|  |     sender    varchar(45) not null | ||||||
|  | ) engine=InnoDB default charset=utf8 auto_increment=0; | ||||||
|  |  | ||||||
|  | -- TODO: Look into what to do with this one... | ||||||
|  | --  Most data is now found in flags. | ||||||
|  | create table if not exists ${prefix}plot_settings( | ||||||
|  |     plot_plot_id  int(11)       not null, | ||||||
|  |     biome         varchar(45)   default 'FOREST',  -- Unused. Moved to flags. | ||||||
|  |     rain          int(1)        default 0,         -- Unused. Moved to flags. | ||||||
|  |     custom_time   tinyint(1)    default 0,         -- Unused. Moved to flags. | ||||||
|  |     time          int(11)       default 8000,      -- Unused. Moved to flags. | ||||||
|  |     deny_entry    tinyint(1)    default 0,         -- Unused. Moved to flags. | ||||||
|  |     alias         varchar(50)   default null, | ||||||
|  |     merged        int(11)       default null, | ||||||
|  |     position      varchar(50)   not null default 'default', | ||||||
|  |     foreign key (plot_plot_id) references ${prefix}plot(id) on delete cascade, | ||||||
|  |     primary key (plot_plot_id) | ||||||
|  | ) engine=InnoDB default charset=utf8; | ||||||
|  |  | ||||||
|  | -- TODO: Look into adding foreign keys to this. | ||||||
|  | create table if not exists ${prefix}plot_rating( | ||||||
|  |     plot_plot_id    int(11)     not null, | ||||||
|  |     rating          int(2)      not null, | ||||||
|  |     player          varchar(45) not null | ||||||
|  | ) engine=InnoDB default charset=utf8; | ||||||
|  |  | ||||||
|  | -- TODO: Drop the key and make the player ID the key instead. | ||||||
|  | create table if not exists ${prefix}player_meta( | ||||||
|  |     meta_id int(11) not null auto_increment, | ||||||
|  |     uuid    varchar(45) not null, | ||||||
|  |     key     varchar(32) not null, | ||||||
|  |     value   blob not null, | ||||||
|  |     primary key (meta_id) | ||||||
|  | ) engine=InnoDB default charset=utf8 auto_increment=0; | ||||||
|  |  | ||||||
|  | -- TODO: Drop the ID and make (plot_id, flag) the new primary key. | ||||||
|  | create table if not exists ${prefix}plot_flags( | ||||||
|  |     id int(11) not null auto_increment primary key, | ||||||
|  |     plot_id int(11) not null, | ||||||
|  |     flag varchar(64), | ||||||
|  |     value varchar(512), | ||||||
|  |     foreign key (plot_id) references ${prefix}plot(id) on delete cascade, | ||||||
|  |     unique (plot_id, flag) | ||||||
|  | ) engine=InnoDB default charset=utf8 auto_increment=0; | ||||||
| @@ -36,6 +36,7 @@ paperlib = "1.0.7" | |||||||
| squirrelid = "0.3.1" | squirrelid = "0.3.1" | ||||||
| serverlib = "2.3.1" | serverlib = "2.3.1" | ||||||
| http4j = "1.3" | http4j = "1.3" | ||||||
|  | record-builder-processor = "33" | ||||||
|  |  | ||||||
| # Gradle plugins | # Gradle plugins | ||||||
| shadow = "7.1.2" | shadow = "7.1.2" | ||||||
| @@ -86,6 +87,7 @@ arkitektonika = { group = "com.intellectualsites.arkitektonika", name = "Arkitek | |||||||
| http4j = { group = "com.intellectualsites.http", name = "HTTP4J", version.ref = "http4j" } | http4j = { group = "com.intellectualsites.http", name = "HTTP4J", version.ref = "http4j" } | ||||||
| paster = { group = "com.intellectualsites.paster", name = "Paster", version.ref = "paster" } | paster = { group = "com.intellectualsites.paster", name = "Paster", version.ref = "paster" } | ||||||
| guava = { group = "com.google.guava", name = "guava", version.ref = "guava" } | guava = { group = "com.google.guava", name = "guava", version.ref = "guava" } | ||||||
|  | recordBuilderProcessor = { group = "io.soabase.record-builder", name = "record-builder-processor", version.ref = "record-builder-processor" } | ||||||
|  |  | ||||||
| [plugins] | [plugins] | ||||||
| shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" } | shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Alexander Söderberg
					Alexander Söderberg