@@ -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