Skip to content

Commit ff72add

Browse files
committed
Refactor rent command to do econ checks first
1 parent 05c564d commit ff72add

2 files changed

Lines changed: 89 additions & 57 deletions

File tree

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,21 @@ record AlreadyOccupied() implements RentResult {}
650650
record UpdateFailed() implements RentResult {}
651651
}
652652

653+
public @NotNull RentResult previewRent(@NotNull String worldGuardRegionId,
654+
@NotNull UUID worldId) {
655+
try (SqlSessionWrapper wrapper = database.openSession()) {
656+
LeaseholdContractMapper leaseholdMapper = wrapper.leaseholdContractMapper();
657+
LeaseholdContractEntity lease = leaseholdMapper.selectByRegion(worldGuardRegionId, worldId);
658+
if (lease == null) {
659+
return new RentResult.NoLeaseholdContract();
660+
}
661+
if (lease.tenantId() != null) {
662+
return new RentResult.AlreadyOccupied();
663+
}
664+
return new RentResult.Success(lease.price(), lease.durationSeconds(), lease.landlordId());
665+
}
666+
}
667+
653668
public @NotNull RentResult rentRegion(@NotNull String worldGuardRegionId,
654669
@NotNull UUID worldId,
655670
@NotNull UUID tenantId) {

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

Lines changed: 74 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -65,73 +65,90 @@ private void execute(@NotNull CommandContext<CommandSourceStack> ctx) {
6565
return;
6666
}
6767
String regionId = region.region().getId();
68+
// Step 1: preview rent eligibility (DB, no mutation)
6869
CompletableFuture.supplyAsync(() -> {
6970
try {
70-
RealtyLogicImpl.RentResult result = logic.rentRegion(
71-
regionId, region.world().getUID(), sender.getUniqueId());
72-
return switch (result) {
73-
case RealtyLogicImpl.RentResult.Success success -> {
74-
Map<String, String> placeholders = logic.getRegionPlaceholders(regionId, region.world().getUID());
75-
yield Map.entry(success, placeholders);
76-
}
77-
case RealtyLogicImpl.RentResult.NoLeaseholdContract ignored -> {
78-
sender.sendMessage(messages.messageFor(MessageKeys.RENT_NO_LEASEHOLD_CONTRACT,
79-
Placeholder.unparsed("region", regionId)));
80-
yield null;
81-
}
82-
case RealtyLogicImpl.RentResult.AlreadyOccupied ignored -> {
83-
sender.sendMessage(messages.messageFor(MessageKeys.RENT_ALREADY_OCCUPIED,
84-
Placeholder.unparsed("region", regionId)));
85-
yield null;
86-
}
87-
case RealtyLogicImpl.RentResult.UpdateFailed ignored -> {
88-
sender.sendMessage(messages.messageFor(MessageKeys.RENT_UPDATE_FAILED,
89-
Placeholder.unparsed("region", regionId)));
90-
yield null;
91-
}
92-
};
71+
return logic.previewRent(regionId, region.world().getUID());
9372
} catch (Exception ex) {
9473
sender.sendMessage(messages.messageFor(MessageKeys.RENT_ERROR,
9574
Placeholder.unparsed("error", ex.getMessage())));
9675
return null;
9776
}
98-
}, executorState.dbExec()).thenAcceptAsync(entry -> {
99-
if (entry == null) {
77+
}, executorState.dbExec()).thenAcceptAsync(preview -> {
78+
if (preview == null) {
10079
return;
10180
}
102-
RealtyLogicImpl.RentResult.Success success = entry.getKey();
103-
double price = success.price();
104-
double balance = economy.getBalance(sender);
105-
if (balance < price) {
106-
sender.sendMessage(messages.messageFor(MessageKeys.RENT_INSUFFICIENT_FUNDS,
107-
Placeholder.unparsed("balance", CurrencyFormatter.format(balance)),
108-
Placeholder.unparsed("price", CurrencyFormatter.format(price))));
109-
return;
110-
}
111-
EconomyResponse response = economy.withdrawPlayer(sender, price);
112-
if (!response.transactionSuccess()) {
113-
sender.sendMessage(messages.messageFor(MessageKeys.RENT_PAYMENT_FAILED,
114-
Placeholder.unparsed("error", response.errorMessage)));
115-
return;
81+
switch (preview) {
82+
case RealtyLogicImpl.RentResult.NoLeaseholdContract ignored ->
83+
sender.sendMessage(messages.messageFor(MessageKeys.RENT_NO_LEASEHOLD_CONTRACT,
84+
Placeholder.unparsed("region", regionId)));
85+
case RealtyLogicImpl.RentResult.AlreadyOccupied ignored ->
86+
sender.sendMessage(messages.messageFor(MessageKeys.RENT_ALREADY_OCCUPIED,
87+
Placeholder.unparsed("region", regionId)));
88+
case RealtyLogicImpl.RentResult.UpdateFailed ignored ->
89+
sender.sendMessage(messages.messageFor(MessageKeys.RENT_UPDATE_FAILED,
90+
Placeholder.unparsed("region", regionId)));
91+
case RealtyLogicImpl.RentResult.Success success -> {
92+
// Step 2: balance check + payment (main thread)
93+
double price = success.price();
94+
double balance = economy.getBalance(sender);
95+
if (balance < price) {
96+
sender.sendMessage(messages.messageFor(MessageKeys.RENT_INSUFFICIENT_FUNDS,
97+
Placeholder.unparsed("balance", CurrencyFormatter.format(balance)),
98+
Placeholder.unparsed("price", CurrencyFormatter.format(price))));
99+
return;
100+
}
101+
if (price > 0) {
102+
EconomyResponse response = economy.withdrawPlayer(sender, price);
103+
if (!response.transactionSuccess()) {
104+
sender.sendMessage(messages.messageFor(MessageKeys.RENT_PAYMENT_FAILED,
105+
Placeholder.unparsed("error", response.errorMessage)));
106+
return;
107+
}
108+
OfflinePlayer landlord = Bukkit.getOfflinePlayer(success.landlordId());
109+
economy.depositPlayer(landlord, price);
110+
}
111+
// Step 3: execute DB mutation
112+
CompletableFuture.supplyAsync(() -> {
113+
try {
114+
RealtyLogicImpl.RentResult result = logic.rentRegion(
115+
regionId, region.world().getUID(), sender.getUniqueId());
116+
if (result instanceof RealtyLogicImpl.RentResult.Success) {
117+
return logic.getRegionPlaceholders(regionId, region.world().getUID());
118+
}
119+
return null;
120+
} catch (Exception ex) {
121+
return null;
122+
}
123+
}, executorState.dbExec()).thenAcceptAsync(placeholders -> {
124+
// Step 4: finalize or refund
125+
if (placeholders == null) {
126+
if (price > 0) {
127+
economy.depositPlayer(sender, price);
128+
}
129+
sender.sendMessage(messages.messageFor(MessageKeys.RENT_UPDATE_FAILED,
130+
Placeholder.unparsed("region", regionId)));
131+
return;
132+
}
133+
ProtectedRegion protectedRegion = region.region();
134+
protectedRegion.getOwners().clear();
135+
protectedRegion.getMembers().clear();
136+
protectedRegion.getOwners().addPlayer(sender.getUniqueId());
137+
regionProfileService.applyFlags(region, RegionState.LEASED, placeholders);
138+
signTextApplicator.updateLoadedSigns(region.world(), regionId, RegionState.LEASED, placeholders);
139+
sender.sendMessage(messages.messageFor(MessageKeys.RENT_SUCCESS,
140+
Placeholder.unparsed("region", regionId),
141+
Placeholder.unparsed("price", CurrencyFormatter.format(price)),
142+
Placeholder.unparsed("duration",
143+
DurationFormatter.format(Duration.ofSeconds(success.durationSeconds())))));
144+
notificationService.queueNotification(success.landlordId(),
145+
messages.messageFor(MessageKeys.NOTIFICATION_REGION_RENTED,
146+
Placeholder.unparsed("player", sender.getName()),
147+
Placeholder.unparsed("price", CurrencyFormatter.format(price)),
148+
Placeholder.unparsed("region", regionId)));
149+
}, executorState.mainThreadExec());
150+
}
116151
}
117-
OfflinePlayer landlord = Bukkit.getOfflinePlayer(success.landlordId());
118-
economy.depositPlayer(landlord, price);
119-
ProtectedRegion protectedRegion = region.region();
120-
protectedRegion.getOwners().clear();
121-
protectedRegion.getMembers().clear();
122-
protectedRegion.getOwners().addPlayer(sender.getUniqueId());
123-
regionProfileService.applyFlags(region, RegionState.LEASED, entry.getValue());
124-
signTextApplicator.updateLoadedSigns(region.world(), regionId, RegionState.LEASED, entry.getValue());
125-
sender.sendMessage(messages.messageFor(MessageKeys.RENT_SUCCESS,
126-
Placeholder.unparsed("region", regionId),
127-
Placeholder.unparsed("price", CurrencyFormatter.format(price)),
128-
Placeholder.unparsed("duration",
129-
DurationFormatter.format(Duration.ofSeconds(success.durationSeconds())))));
130-
notificationService.queueNotification(success.landlordId(),
131-
messages.messageFor(MessageKeys.NOTIFICATION_REGION_RENTED,
132-
Placeholder.unparsed("player", sender.getName()),
133-
Placeholder.unparsed("price", CurrencyFormatter.format(price)),
134-
Placeholder.unparsed("region", regionId)));
135152
}, executorState.mainThreadExec());
136153
}
137154

0 commit comments

Comments
 (0)