|
8 | 8 | import io.github.md5sha256.realty.api.SignTextApplicator; |
9 | 9 | import io.github.md5sha256.realty.command.util.WorldGuardRegion; |
10 | 10 | import io.github.md5sha256.realty.command.util.WorldGuardRegionResolver; |
| 11 | +import io.github.md5sha256.realty.database.entity.LeaseholdContractEntity; |
11 | 12 | import io.github.md5sha256.realty.database.RealtyLogicImpl; |
12 | 13 | import io.github.md5sha256.realty.localisation.MessageContainer; |
13 | 14 | import io.github.md5sha256.realty.localisation.MessageKeys; |
@@ -71,86 +72,100 @@ private void execute(@NotNull CommandContext<CommandSourceStack> ctx) { |
71 | 72 | Placeholder.unparsed("region", regionId))); |
72 | 73 | return; |
73 | 74 | } |
74 | | - // Step 1: preview (DB read, no mutation) |
| 75 | + // Step 1: query lease to compute refund (DB read, no mutation) |
75 | 76 | CompletableFuture.supplyAsync(() -> { |
76 | 77 | try { |
77 | | - return logic.previewUnrent(regionId, region.world().getUID()); |
| 78 | + return logic.getLeaseholdContract(regionId, region.world().getUID()); |
78 | 79 | } catch (Exception ex) { |
79 | 80 | sender.sendMessage(messages.messageFor(MessageKeys.UNRENT_ERROR, |
80 | 81 | Placeholder.unparsed("error", ex.getMessage()))); |
81 | 82 | return null; |
82 | 83 | } |
83 | | - }, executorState.dbExec()).thenAcceptAsync(preview -> { |
84 | | - if (preview == null) { |
| 84 | + }, executorState.dbExec()).thenAcceptAsync(lease -> { |
| 85 | + if (lease == null) { |
| 86 | + sender.sendMessage(messages.messageFor(MessageKeys.UNRENT_NO_LEASEHOLD_CONTRACT, |
| 87 | + Placeholder.unparsed("region", regionId))); |
85 | 88 | return; |
86 | 89 | } |
87 | | - switch (preview) { |
88 | | - case RealtyLogicImpl.UnrentResult.NoLeaseholdContract ignored -> |
89 | | - sender.sendMessage(messages.messageFor(MessageKeys.UNRENT_NO_LEASEHOLD_CONTRACT, |
90 | | - Placeholder.unparsed("region", regionId))); |
91 | | - case RealtyLogicImpl.UnrentResult.UpdateFailed ignored -> |
92 | | - sender.sendMessage(messages.messageFor(MessageKeys.UNRENT_UPDATE_FAILED, |
93 | | - Placeholder.unparsed("region", regionId))); |
94 | | - case RealtyLogicImpl.UnrentResult.Success success -> { |
95 | | - double refund = success.refund(); |
96 | | - // Step 2: economy — withdraw from landlord, deposit to tenant (main thread) |
97 | | - if (refund > 0) { |
98 | | - OfflinePlayer landlord = Bukkit.getOfflinePlayer(success.landlordId()); |
99 | | - EconomyResponse withdrawResponse = economy.withdrawPlayer(landlord, refund); |
100 | | - if (!withdrawResponse.transactionSuccess()) { |
101 | | - sender.sendMessage(messages.messageFor(MessageKeys.UNRENT_REFUND_FAILED, |
102 | | - Placeholder.unparsed("error", withdrawResponse.errorMessage))); |
103 | | - return; |
104 | | - } |
105 | | - EconomyResponse depositResponse = economy.depositPlayer(sender, refund); |
106 | | - if (!depositResponse.transactionSuccess()) { |
107 | | - economy.depositPlayer(landlord, refund); |
108 | | - sender.sendMessage(messages.messageFor(MessageKeys.UNRENT_REFUND_FAILED, |
109 | | - Placeholder.unparsed("error", depositResponse.errorMessage))); |
110 | | - return; |
111 | | - } |
112 | | - } |
113 | | - // Step 3: DB mutation |
114 | | - CompletableFuture.supplyAsync(() -> { |
115 | | - try { |
116 | | - RealtyLogicImpl.UnrentResult result = logic.unrentRegion( |
117 | | - regionId, region.world().getUID(), sender.getUniqueId()); |
118 | | - if (result instanceof RealtyLogicImpl.UnrentResult.Success) { |
119 | | - return logic.getRegionPlaceholders(regionId, region.world().getUID()); |
120 | | - } |
121 | | - return null; |
122 | | - } catch (Exception ex) { |
123 | | - return null; |
124 | | - } |
125 | | - }, executorState.dbExec()).thenAcceptAsync(placeholders -> { |
126 | | - // Step 4: finalize or revert |
127 | | - if (placeholders == null) { |
128 | | - if (refund > 0) { |
129 | | - economy.withdrawPlayer(sender, refund); |
130 | | - OfflinePlayer landlord = Bukkit.getOfflinePlayer(success.landlordId()); |
131 | | - economy.depositPlayer(landlord, refund); |
132 | | - } |
133 | | - sender.sendMessage(messages.messageFor(MessageKeys.UNRENT_UPDATE_FAILED, |
134 | | - Placeholder.unparsed("region", regionId))); |
135 | | - return; |
136 | | - } |
| 90 | + long totalSeconds = lease.durationSeconds(); |
| 91 | + long remainingSeconds = lease.endDate() == null ? 0 |
| 92 | + : Math.max(0, java.time.Duration.between(java.time.LocalDateTime.now(), lease.endDate()).getSeconds()); |
| 93 | + double refund = totalSeconds > 0 ? lease.price() * remainingSeconds / totalSeconds : 0; |
| 94 | + // Step 2: economy — withdraw from landlord, deposit to tenant (main thread) |
| 95 | + if (refund > 0) { |
| 96 | + OfflinePlayer landlord = Bukkit.getOfflinePlayer(lease.landlordId()); |
| 97 | + EconomyResponse withdrawResponse = economy.withdrawPlayer(landlord, refund); |
| 98 | + if (!withdrawResponse.transactionSuccess()) { |
| 99 | + sender.sendMessage(messages.messageFor(MessageKeys.UNRENT_REFUND_FAILED, |
| 100 | + Placeholder.unparsed("error", withdrawResponse.errorMessage))); |
| 101 | + return; |
| 102 | + } |
| 103 | + EconomyResponse depositResponse = economy.depositPlayer(sender, refund); |
| 104 | + if (!depositResponse.transactionSuccess()) { |
| 105 | + economy.depositPlayer(landlord, refund); |
| 106 | + sender.sendMessage(messages.messageFor(MessageKeys.UNRENT_REFUND_FAILED, |
| 107 | + Placeholder.unparsed("error", depositResponse.errorMessage))); |
| 108 | + return; |
| 109 | + } |
| 110 | + } |
| 111 | + // Step 3: DB mutation |
| 112 | + CompletableFuture.supplyAsync(() -> { |
| 113 | + RealtyLogicImpl.UnrentResult result = logic.unrentRegion( |
| 114 | + regionId, region.world().getUID(), sender.getUniqueId()); |
| 115 | + if (result instanceof RealtyLogicImpl.UnrentResult.Success) { |
| 116 | + Map<String, String> placeholders = logic.getRegionPlaceholders(regionId, region.world().getUID()); |
| 117 | + return Map.entry(result, placeholders); |
| 118 | + } |
| 119 | + return Map.<RealtyLogicImpl.UnrentResult, Map<String, String>>entry(result, Map.of()); |
| 120 | + }, executorState.dbExec()).thenAcceptAsync(entry -> { |
| 121 | + // Step 4: finalize or revert economy |
| 122 | + switch (entry.getKey()) { |
| 123 | + case RealtyLogicImpl.UnrentResult.Success ignored -> { |
137 | 124 | ProtectedRegion protectedRegion = region.region(); |
138 | 125 | protectedRegion.getOwners().clear(); |
139 | 126 | protectedRegion.getMembers().clear(); |
140 | | - regionProfileService.applyFlags(region, RegionState.FOR_LEASE, placeholders); |
141 | | - signTextApplicator.updateLoadedSigns(region.world(), regionId, RegionState.FOR_LEASE, placeholders); |
| 127 | + regionProfileService.applyFlags(region, RegionState.FOR_LEASE, entry.getValue()); |
| 128 | + signTextApplicator.updateLoadedSigns(region.world(), regionId, RegionState.FOR_LEASE, entry.getValue()); |
142 | 129 | sender.sendMessage(messages.messageFor(MessageKeys.UNRENT_SUCCESS, |
143 | 130 | Placeholder.unparsed("region", regionId), |
144 | 131 | Placeholder.unparsed("refund", CurrencyFormatter.format(refund)))); |
145 | | - notificationService.queueNotification(success.landlordId(), |
| 132 | + notificationService.queueNotification(lease.landlordId(), |
146 | 133 | messages.messageFor(MessageKeys.NOTIFICATION_REGION_UNRENTED, |
147 | 134 | Placeholder.unparsed("player", sender.getName()), |
148 | 135 | Placeholder.unparsed("region", regionId), |
149 | 136 | Placeholder.unparsed("refund", CurrencyFormatter.format(refund)))); |
150 | | - }, executorState.mainThreadExec()); |
| 137 | + } |
| 138 | + case RealtyLogicImpl.UnrentResult.NoLeaseholdContract ignored -> { |
| 139 | + revertEconomy(sender, lease, refund); |
| 140 | + sender.sendMessage(messages.messageFor(MessageKeys.UNRENT_NO_LEASEHOLD_CONTRACT, |
| 141 | + Placeholder.unparsed("region", regionId))); |
| 142 | + } |
| 143 | + case RealtyLogicImpl.UnrentResult.UpdateFailed ignored -> { |
| 144 | + revertEconomy(sender, lease, refund); |
| 145 | + sender.sendMessage(messages.messageFor(MessageKeys.UNRENT_UPDATE_FAILED, |
| 146 | + Placeholder.unparsed("region", regionId))); |
| 147 | + } |
151 | 148 | } |
152 | | - } |
| 149 | + }, executorState.mainThreadExec()).exceptionally(ex -> { |
| 150 | + executorState.mainThreadExec().execute(() -> { |
| 151 | + revertEconomy(sender, lease, refund); |
| 152 | + Throwable cause = ex.getCause() != null ? ex.getCause() : ex; |
| 153 | + sender.sendMessage(messages.messageFor(MessageKeys.UNRENT_ERROR, |
| 154 | + Placeholder.unparsed("error", cause.getMessage()))); |
| 155 | + }); |
| 156 | + return null; |
| 157 | + }); |
153 | 158 | }, executorState.mainThreadExec()); |
154 | 159 | } |
155 | 160 |
|
| 161 | + private void revertEconomy(@NotNull Player tenant, |
| 162 | + @NotNull LeaseholdContractEntity lease, |
| 163 | + double refund) { |
| 164 | + if (refund > 0) { |
| 165 | + economy.withdrawPlayer(tenant, refund); |
| 166 | + OfflinePlayer landlord = Bukkit.getOfflinePlayer(lease.landlordId()); |
| 167 | + economy.depositPlayer(landlord, refund); |
| 168 | + } |
| 169 | + } |
| 170 | + |
156 | 171 | } |
0 commit comments