Skip to content

Commit efde08e

Browse files
authored
Merge pull request #8 from MCCitiesNetwork/feat/set-price-lease
Ability to set leasehold prices and fix sign editing conflicts
2 parents 76b6bbe + 9479d05 commit efde08e

18 files changed

Lines changed: 180 additions & 56 deletions

File tree

realty-api/src/main/java/io/github/md5sha256/realty/api/RealtyApi.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ record AlreadyHighestBidder() implements BidResult {}
110110

111111
sealed interface SetPriceResult {
112112
record Success() implements SetPriceResult {}
113-
record NoFreeholdContract() implements SetPriceResult {}
113+
record NoContract() implements SetPriceResult {}
114114
record AuctionExists() implements SetPriceResult {}
115115
record OfferPaymentInProgress() implements SetPriceResult {}
116116
record BidPaymentInProgress() implements SetPriceResult {}

realty-common/src/main/java/io/github/md5sha256/realty/database/RealtyApiImpl.java

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -293,25 +293,40 @@ public int removeSanctionedAuctioneer(@NotNull String worldGuardRegionId,
293293
try (SqlSessionWrapper wrapper = database.openSession()) {
294294
FreeholdContractMapper freeholdMapper = wrapper.freeholdContractMapper();
295295
FreeholdContractEntity freehold = freeholdMapper.selectByRegion(worldGuardRegionId, worldId);
296-
if (freehold == null) {
297-
return new SetPriceResult.NoFreeholdContract();
298-
}
299-
if (wrapper.freeholdContractAuctionMapper().existsByRegion(worldGuardRegionId, worldId)) {
300-
return new SetPriceResult.AuctionExists();
301-
}
302-
if (wrapper.freeholdContractOfferPaymentMapper().existsByRegion(worldGuardRegionId, worldId)) {
303-
return new SetPriceResult.OfferPaymentInProgress();
296+
if (freehold != null) {
297+
if (wrapper.freeholdContractAuctionMapper().existsByRegion(worldGuardRegionId, worldId)) {
298+
return new SetPriceResult.AuctionExists();
299+
}
300+
if (wrapper.freeholdContractOfferPaymentMapper().existsByRegion(worldGuardRegionId, worldId)) {
301+
return new SetPriceResult.OfferPaymentInProgress();
302+
}
303+
if (wrapper.freeholdContractBidPaymentMapper().existsByRegion(worldGuardRegionId, worldId)) {
304+
return new SetPriceResult.BidPaymentInProgress();
305+
}
306+
int updated = freeholdMapper.updatePriceByRegion(worldGuardRegionId, worldId, price);
307+
if (updated == 0) {
308+
return new SetPriceResult.UpdateFailed();
309+
}
310+
wrapper.freeholdHistoryMapper().insert(worldGuardRegionId, worldId,
311+
HistoryEventType.SET_PRICE.name(),
312+
freehold.authorityId(), freehold.authorityId(), price);
313+
wrapper.session().commit();
314+
return new SetPriceResult.Success();
304315
}
305-
if (wrapper.freeholdContractBidPaymentMapper().existsByRegion(worldGuardRegionId, worldId)) {
306-
return new SetPriceResult.BidPaymentInProgress();
316+
LeaseholdContractMapper leaseholdMapper = wrapper.leaseholdContractMapper();
317+
LeaseholdContractEntity lease = leaseholdMapper.selectByRegion(worldGuardRegionId, worldId);
318+
if (lease == null) {
319+
return new SetPriceResult.NoContract();
307320
}
308-
int updated = freeholdMapper.updatePriceByRegion(worldGuardRegionId, worldId, price);
321+
int updated = leaseholdMapper.updatePriceByRegion(worldGuardRegionId, worldId, price);
309322
if (updated == 0) {
310323
return new SetPriceResult.UpdateFailed();
311324
}
312-
wrapper.freeholdHistoryMapper().insert(worldGuardRegionId, worldId,
325+
UUID tenantForHistory = lease.tenantId() != null ? lease.tenantId() : lease.landlordId();
326+
wrapper.leaseholdHistoryMapper().insert(worldGuardRegionId, worldId,
313327
HistoryEventType.SET_PRICE.name(),
314-
freehold.authorityId(), freehold.authorityId(), price);
328+
tenantForHistory, lease.landlordId(),
329+
price, lease.durationSeconds(), null);
315330
wrapper.session().commit();
316331
return new SetPriceResult.Success();
317332
}

realty-common/src/main/java/io/github/md5sha256/realty/database/mapper/LeaseholdContractMapper.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ int updateDurationByRegion(@NotNull String worldGuardRegionId,
3939
@NotNull UUID worldId,
4040
long durationSeconds);
4141

42+
int updatePriceByRegion(@NotNull String worldGuardRegionId,
43+
@NotNull UUID worldId,
44+
double price);
45+
4246
int updateLandlordByRegion(@NotNull String worldGuardRegionId,
4347
@NotNull UUID worldId,
4448
@NotNull UUID landlordId);

realty-common/src/main/java/io/github/md5sha256/realty/database/maria/MariaSchemaMigrator.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ INSERT INTO schema_version (version, description) VALUES (?, ?)
4848
new MigrationStep(7, "lease end date", "V7__lease_end_date.sql"),
4949
new MigrationStep(8, "leasehold rename", "V8__leasehold_rename.sql"),
5050
new MigrationStep(9, "clear dates without tenant", "V9__clear_dates_no_tenant.sql"),
51-
new MigrationStep(10, "set unset history events", "V10__set_unset_history_events.sql")
51+
new MigrationStep(10, "set unset history events", "V10__set_unset_history_events.sql"),
52+
new MigrationStep(11, "leasehold set price history", "V11__leasehold_set_price_history.sql")
5253
);
5354

5455
private MariaSchemaMigrator() {

realty-common/src/main/java/io/github/md5sha256/realty/database/maria/mapper/MariaLeaseholdContractMapper.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,19 @@ int updateDurationByRegion(@Param("worldGuardRegionId") @NotNull String worldGua
157157
@Param("worldId") @NotNull UUID worldId,
158158
@Param("durationSeconds") long durationSeconds);
159159

160+
@Override
161+
@Update("""
162+
UPDATE LeaseholdContract lc
163+
INNER JOIN Contract c ON c.contractId = lc.leaseholdContractId AND c.contractType = 'leasehold'
164+
INNER JOIN RealtyRegion rr ON rr.realtyRegionId = c.realtyRegionId
165+
SET lc.price = #{price}
166+
WHERE rr.worldGuardRegionId = #{worldGuardRegionId}
167+
AND rr.worldId = #{worldId}
168+
""")
169+
int updatePriceByRegion(@Param("worldGuardRegionId") @NotNull String worldGuardRegionId,
170+
@Param("worldId") @NotNull UUID worldId,
171+
@Param("price") double price);
172+
160173
@Override
161174
@Update("""
162175
UPDATE LeaseholdContract lc
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
ALTER TABLE LeaseholdHistory
2+
MODIFY COLUMN eventType ENUM (
3+
'RENT', 'UNRENT', 'RENEW', 'LEASEHOLD_EXPIRY',
4+
'SET_DURATION', 'SET_LANDLORD', 'SET_TENANT', 'UNSET_TENANT', 'SET_MAX_EXTENSIONS',
5+
'SET_PRICE'
6+
) NOT NULL;

realty-common/src/test/java/io/github/md5sha256/realty/database/MapperTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,25 @@ void updateDuration() {
563563
}
564564
}
565565

566+
@Test
567+
@DisplayName("updatePriceByRegion updates price")
568+
void updatePrice() {
569+
String regionId = uniqueRegionId();
570+
createLeaseholdRegion(regionId, AUTHORITY);
571+
572+
try (SqlSessionWrapper wrapper = database.openSession();
573+
SqlSession session = wrapper.session()) {
574+
int updated = wrapper.leaseholdContractMapper()
575+
.updatePriceByRegion(regionId, WORLD_ID, 350.0);
576+
session.commit();
577+
Assertions.assertEquals(1, updated);
578+
579+
LeaseholdContractEntity entity = wrapper.leaseholdContractMapper()
580+
.selectByRegion(regionId, WORLD_ID);
581+
Assertions.assertEquals(350.0, entity.price());
582+
}
583+
}
584+
566585
@Test
567586
@DisplayName("updateDurationByRegion returns 0 for nonexistent")
568587
void updateDurationNonexistent() {

realty-paper/src/main/java/io/github/md5sha256/realty/command/AuctionCommandGroup.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import io.github.md5sha256.realty.api.RegionState;
1212
import io.github.md5sha256.realty.api.SignTextApplicator;
1313
import io.github.md5sha256.realty.command.util.DurationParser;
14+
import io.github.md5sha256.realty.command.util.ParseBounds;
1415
import io.github.md5sha256.realty.command.util.SubregionLandlordUpdater;
1516
import io.github.md5sha256.realty.command.util.WorldGuardRegion;
1617
import io.github.md5sha256.realty.command.util.WorldGuardRegionResolver;
@@ -80,8 +81,10 @@ public record AuctionCommandGroup(
8081
base.permission("realty.command.auction")
8182
.required("bidDuration", DurationParser.duration())
8283
.required("paymentDuration", DurationParser.duration())
83-
.required("minBid", DoubleParser.doubleParser(0))
84-
.required("minBidStep", DoubleParser.doubleParser(0))
84+
.required("minBid", DoubleParser.doubleParser(ParseBounds.MIN_STRICTLY_POSITIVE,
85+
Double.MAX_VALUE))
86+
.required("minBidStep", DoubleParser.doubleParser(ParseBounds.MIN_STRICTLY_POSITIVE,
87+
Double.MAX_VALUE))
8588
.optional("region", WorldGuardRegionResolver.worldGuardRegionResolver())
8689
.handler(this::executeCreate)
8790
.build(),
@@ -92,7 +95,8 @@ public record AuctionCommandGroup(
9295
.build(),
9396
base.literal("bid")
9497
.permission("realty.command.auction.bid")
95-
.required("bid", DoubleParser.doubleParser(0))
98+
.required("bid", DoubleParser.doubleParser(ParseBounds.MIN_STRICTLY_POSITIVE,
99+
Double.MAX_VALUE))
96100
.optional("region", WorldGuardRegionResolver.worldGuardRegionResolver())
97101
.handler(this::executeBid)
98102
.build(),

realty-paper/src/main/java/io/github/md5sha256/realty/command/CreateCommand.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import io.github.md5sha256.realty.api.RegionState;
1919
import io.github.md5sha256.realty.command.util.AuthorityParser;
2020
import io.github.md5sha256.realty.command.util.DurationParser;
21+
import io.github.md5sha256.realty.command.util.ParseBounds;
2122
import io.github.md5sha256.realty.command.util.WorldGuardRegion;
2223
import io.github.md5sha256.realty.api.RealtyApi;
2324
import io.github.md5sha256.realty.localisation.MessageContainer;
@@ -76,7 +77,8 @@ public record CreateCommand(@NotNull ExecutorState executorState,
7677

7778
private static final CommandFlag<Double> PRICE_FLAG =
7879
CommandFlag.<Source>builder("price")
79-
.withComponent(DoubleParser.doubleParser(0))
80+
.withComponent(DoubleParser.doubleParser(ParseBounds.MIN_STRICTLY_POSITIVE,
81+
Double.MAX_VALUE))
8082
.build();
8183

8284
private static final CommandFlag<UUID> LANDLORD_FLAG =
@@ -92,7 +94,8 @@ public record CreateCommand(@NotNull ExecutorState executorState,
9294
base.literal("leasehold")
9395
.permission("realty.command.create.leasehold")
9496
.required(NAME, StringParser.stringParser())
95-
.required(PRICE, DoubleParser.doubleParser(0))
97+
.required(PRICE, DoubleParser.doubleParser(ParseBounds.MIN_STRICTLY_POSITIVE,
98+
Double.MAX_VALUE))
9699
.required(PERIOD, DurationParser.duration())
97100
.required(MAX_EXTENSIONS, IntegerParser.integerParser(-1))
98101
.flag(LANDLORD_FLAG)

realty-paper/src/main/java/io/github/md5sha256/realty/command/HistoryCommand.java

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public record HistoryCommand(@NotNull ExecutorState executorState,
5858
Map.entry("UNRENT", MessageKeys.HISTORY_EVENT_UNRENT),
5959
Map.entry("RENEW", MessageKeys.HISTORY_EVENT_RENEW),
6060
Map.entry("LEASEHOLD_EXPIRY", MessageKeys.HISTORY_EVENT_LEASEHOLD_EXPIRY),
61-
Map.entry("SET_PRICE", MessageKeys.HISTORY_EVENT_SET_PRICE),
61+
Map.entry("SET_PRICE", MessageKeys.HISTORY_EVENT_SET_PRICE_FREEHOLD),
6262
Map.entry("UNSET_PRICE", MessageKeys.HISTORY_EVENT_UNSET_PRICE),
6363
Map.entry("SET_TITLEHOLDER", MessageKeys.HISTORY_EVENT_SET_TITLEHOLDER),
6464
Map.entry("UNSET_TITLEHOLDER", MessageKeys.HISTORY_EVENT_UNSET_TITLEHOLDER),
@@ -69,6 +69,11 @@ public record HistoryCommand(@NotNull ExecutorState executorState,
6969
Map.entry("SET_MAX_EXTENSIONS", MessageKeys.HISTORY_EVENT_SET_MAX_EXTENSIONS)
7070
);
7171

72+
/** Event types where leasehold history uses a different message key than freehold for the same name. */
73+
private static final Map<String, String> LEASEHOLD_EVENT_MESSAGE_KEYS = Map.of(
74+
"SET_PRICE", MessageKeys.HISTORY_EVENT_SET_PRICE_LEASEHOLD
75+
);
76+
7277
private static final CommandFlag<HistoryEventType> EVENT_FLAG =
7378
CommandFlag.<Source>builder("event")
7479
.withComponent(EnumParser.enumParser(HistoryEventType.class))
@@ -150,26 +155,34 @@ private void execute(@NotNull CommandContext<Source> ctx) {
150155

151156
for (HistoryEntry entry : result.entries()) {
152157
builder.appendNewline();
153-
String messageKey = resolveEventMessageKey(entry.eventType());
154158
switch (entry) {
155-
case HistoryEntry.Freehold freehold -> builder.append(
156-
messages.messageFor(messageKey,
157-
Placeholder.unparsed("time", DateFormatter.format(settings.get(), freehold.eventTime())),
158-
Placeholder.unparsed("buyer", resolveName(freehold.buyerId())),
159-
Placeholder.unparsed("authority", resolveName(freehold.authorityId())),
160-
Placeholder.unparsed("price", CurrencyFormatter.format(freehold.price()))));
161-
case HistoryEntry.Agent agent -> builder.append(
162-
messages.messageFor(messageKey,
163-
Placeholder.unparsed("time", DateFormatter.format(settings.get(), agent.eventTime())),
164-
Placeholder.unparsed("agent", resolveName(agent.agentId())),
165-
Placeholder.unparsed("actor", resolveName(agent.actorId()))));
166-
case HistoryEntry.Leasehold lease -> builder.append(
167-
messages.messageFor(messageKey,
168-
Placeholder.unparsed("time", DateFormatter.format(settings.get(), lease.eventTime())),
169-
Placeholder.unparsed("tenant", resolveName(lease.tenantId())),
170-
Placeholder.unparsed("landlord", resolveName(lease.landlordId())),
171-
Placeholder.unparsed("price",
172-
lease.price() != null ? CurrencyFormatter.format(lease.price()) : "N/A")));
159+
case HistoryEntry.Freehold freehold -> {
160+
String messageKey = resolveEventMessageKey(freehold.eventType());
161+
builder.append(
162+
messages.messageFor(messageKey,
163+
Placeholder.unparsed("time", DateFormatter.format(settings.get(), freehold.eventTime())),
164+
Placeholder.unparsed("buyer", resolveName(freehold.buyerId())),
165+
Placeholder.unparsed("authority", resolveName(freehold.authorityId())),
166+
Placeholder.unparsed("price", CurrencyFormatter.format(freehold.price()))));
167+
}
168+
case HistoryEntry.Agent agent -> {
169+
String messageKey = resolveEventMessageKey(agent.eventType());
170+
builder.append(
171+
messages.messageFor(messageKey,
172+
Placeholder.unparsed("time", DateFormatter.format(settings.get(), agent.eventTime())),
173+
Placeholder.unparsed("agent", resolveName(agent.agentId())),
174+
Placeholder.unparsed("actor", resolveName(agent.actorId()))));
175+
}
176+
case HistoryEntry.Leasehold lease -> {
177+
String messageKey = resolveLeaseholdEventMessageKey(lease.eventType());
178+
builder.append(
179+
messages.messageFor(messageKey,
180+
Placeholder.unparsed("time", DateFormatter.format(settings.get(), lease.eventTime())),
181+
Placeholder.unparsed("tenant", resolveName(lease.tenantId())),
182+
Placeholder.unparsed("landlord", resolveName(lease.landlordId())),
183+
Placeholder.unparsed("price",
184+
lease.price() != null ? CurrencyFormatter.format(lease.price()) : "N/A")));
185+
}
173186
}
174187
}
175188

@@ -226,6 +239,10 @@ private void appendFooter(@NotNull TextComponent.Builder builder, @NotNull Strin
226239
return key != null ? key : eventType;
227240
}
228241

242+
private static @NotNull String resolveLeaseholdEventMessageKey(@NotNull String eventType) {
243+
return LEASEHOLD_EVENT_MESSAGE_KEYS.getOrDefault(eventType, resolveEventMessageKey(eventType));
244+
}
245+
229246
private static @NotNull String resolveName(@NotNull UUID uuid) {
230247
String name = Bukkit.getOfflinePlayer(uuid).getName();
231248
return name != null ? name : uuid.toString();

0 commit comments

Comments
 (0)