mirror of
https://github.com/IntellectualSites/PlotSquared.git
synced 2024-11-22 13:16:45 +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:
parent
16e26b910c
commit
86c54bf04c
@ -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" }
|
||||||
|
Loading…
Reference in New Issue
Block a user