1313import org .jetbrains .annotations .NotNull ;
1414
1515import java .util .ArrayList ;
16+ import java .util .Arrays ;
1617import java .util .EnumSet ;
18+ import java .util .LinkedHashSet ;
1719import java .util .LinkedHashMap ;
1820import java .util .List ;
1921import java .util .Locale ;
2325public class LockettePro extends JavaPlugin {
2426
2527 private static final String PERM_PDC_ON = "lockettepro.pdc.on" ;
28+ private static final String PERM_PDC_OFF = "lockettepro.pdc.off" ;
2629 private static final String PERM_PDC_INFO = "lockettepro.pdc.info" ;
2730 private static final String PERM_PDC_RENAME = "lockettepro.pdc.rename" ;
2831 private static final String PERM_PDC_PERMISSION = "lockettepro.pdc.permission" ;
@@ -122,13 +125,8 @@ public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Comman
122125 }
123126 return list ;
124127 }
125- if (args != null && args .length == 2 && "permission" .equalsIgnoreCase (args [0 ]) && sender .hasPermission (PERM_PDC_PERMISSION )) {
126- List <String > list = new ArrayList <>();
127- list .add ("xx:" );
128- list .add ("rw:" );
129- list .add ("ro:" );
130- list .add ("--:" );
131- return list ;
128+ if (args != null && args .length >= 2 && "permission" .equalsIgnoreCase (args [0 ]) && sender .hasPermission (PERM_PDC_PERMISSION )) {
129+ return tabCompletePermission (args , sender );
132130 }
133131 if (args != null && args .length == 2 && "group" .equalsIgnoreCase (args [0 ]) && sender .hasPermission (PERM_PDC_GROUP )) {
134132 return List .of ("create" , "delete" , "add" , "remove" , "info" , "list" );
@@ -145,6 +143,71 @@ public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Comman
145143 return null ;
146144 }
147145
146+ private List <String > tabCompletePermission (String [] args , CommandSender sender ) {
147+ List <String > modes = List .of ("xx:" , "rw:" , "ro:" , "--:" );
148+ if (args .length == 2 ) {
149+ String token = args [1 ] == null ? "" : args [1 ];
150+ int split = token .indexOf (':' );
151+ if (split < 0 ) {
152+ return filterByPrefix (modes , token );
153+ }
154+
155+ String modePrefix = token .substring (0 , split + 1 );
156+ String subjectPrefix = token .substring (split + 1 );
157+ if (!modes .contains (modePrefix .toLowerCase (Locale .ROOT ))) {
158+ return filterByPrefix (modes , token );
159+ }
160+ return filterByPrefix (
161+ buildPermissionSubjectCandidates (sender ).stream ()
162+ .map (subject -> modePrefix + subject )
163+ .toList (),
164+ token
165+ );
166+ }
167+
168+ if (args .length == 3 && args [1 ] != null && args [1 ].contains (":" )) {
169+ return filterByPrefix (buildPermissionSubjectCandidates (sender ), args [2 ]);
170+ }
171+
172+ return List .of ();
173+ }
174+
175+ private List <String > buildPermissionSubjectCandidates (CommandSender sender ) {
176+ LinkedHashSet <String > candidates = new LinkedHashSet <>();
177+
178+ if (sender instanceof Player player ) {
179+ PermissionGroupStore .GroupSnapshot own = PermissionGroupStore .getOwnedGroup (player .getUniqueId ());
180+ if (own != null ) {
181+ candidates .add ("[g:" + own .name () + "]" );
182+ }
183+ }
184+
185+ List <String > onlineNames = Bukkit .getOnlinePlayers ()
186+ .stream ()
187+ .map (Player ::getName )
188+ .sorted (String ::compareToIgnoreCase )
189+ .toList ();
190+ candidates .addAll (onlineNames );
191+
192+ candidates .add ("#hopper" );
193+
194+ candidates .addAll (Config .getEveryoneSignStrings ());
195+ candidates .addAll (Config .getContainerBypassSignStrings ());
196+
197+ return new ArrayList <>(candidates );
198+ }
199+
200+ private List <String > filterByPrefix (List <String > candidates , String rawPrefix ) {
201+ String prefix = rawPrefix == null ? "" : rawPrefix .toLowerCase (Locale .ROOT );
202+ List <String > list = new ArrayList <>();
203+ for (String candidate : candidates ) {
204+ if (candidate .toLowerCase (Locale .ROOT ).startsWith (prefix )) {
205+ list .add (candidate );
206+ }
207+ }
208+ return list ;
209+ }
210+
148211 public boolean onCommand (@ NotNull CommandSender sender , Command cmd , @ NotNull String commandLabel , final String [] args ) {
149212 if (cmd .getName ().equals ("lockettepro" )) {
150213 if (args .length == 0 ) {
@@ -334,6 +397,7 @@ public boolean onCommand(@NotNull CommandSender sender, Command cmd, @NotNull St
334397 private void registerPlayerSubCommands () {
335398 playerSubCommands .clear ();
336399 playerSubCommands .put ("on" , new PlayerSubCommand (PERM_PDC_ON , (player , args ) -> handlePdcLockOn (player )));
400+ playerSubCommands .put ("off" , new PlayerSubCommand (PERM_PDC_OFF , (player , args ) -> handlePdcLockOff (player )));
337401 playerSubCommands .put ("info" , new PlayerSubCommand (PERM_PDC_INFO , (player , args ) -> handlePdcInfo (player )));
338402 playerSubCommands .put ("rename" , new PlayerSubCommand (PERM_PDC_RENAME , this ::handlePdcRename ));
339403 playerSubCommands .put ("permission" , new PlayerSubCommand (PERM_PDC_PERMISSION , this ::handlePdcPermission ));
@@ -387,6 +451,35 @@ private void handlePdcLockOn(Player player) {
387451 }
388452 }
389453
454+ private void handlePdcLockOff (Player player ) {
455+ Block block = ContainerPdcLockManager .getTargetedContainer (player );
456+ if (block == null ) {
457+ Utils .sendMessages (player , Config .getLang ("pdc-target-container-needed" ));
458+ return ;
459+ }
460+
461+ ContainerPdcLockManager .LockData data = ContainerPdcLockManager .getLockData (block );
462+ if (!data .hasPdcData () || !data .isLocked ()) {
463+ Utils .sendMessages (player , Config .getLang ("pdc-not-locked" ));
464+ return ;
465+ }
466+
467+ boolean owner = ContainerPdcLockManager .isOwner (block , player );
468+ boolean adminOverride = player .hasPermission ("lockettepro.admin.break" );
469+ if (!owner && !adminOverride ) {
470+ Utils .sendMessages (player , Config .getLang ("pdc-no-owner-permission" ));
471+ Utils .playAccessDenyEffect (player , block );
472+ return ;
473+ }
474+
475+ if (ContainerPdcLockManager .writeLockData (block , false , java .util .Collections .emptyMap ())) {
476+ Utils .sendMessages (player , Config .getLang ("pdc-lock-disabled" ));
477+ Utils .refreshLockedContainerPdcTagLater (block );
478+ } else {
479+ Utils .sendMessages (player , Config .getLang ("pdc-lock-disable-failed" ));
480+ }
481+ }
482+
390483 private void handlePdcInfo (Player player ) {
391484 Block block = ContainerPdcLockManager .getTargetedContainer (player );
392485 if (block == null ) {
@@ -478,17 +571,29 @@ private void handlePdcPermission(Player player, String[] args) {
478571 return ;
479572 }
480573
481- if (args .length != 2 ) {
574+ if (args .length < 2 ) {
482575 Utils .sendMessages (player , Config .getLang ("pdc-permission-usage" ));
483576 return ;
484577 }
485578
486- ContainerPdcLockManager .PermissionMutation mutation = ContainerPdcLockManager .parsePermissionMutation (args [1 ]);
579+ String mutationRaw = String .join (" " , Arrays .copyOfRange (args , 1 , args .length )).trim ();
580+ if (mutationRaw .isEmpty ()) {
581+ Utils .sendMessages (player , Config .getLang ("pdc-permission-usage" ));
582+ return ;
583+ }
584+
585+ ContainerPdcLockManager .PermissionMutation mutation = ContainerPdcLockManager .parsePermissionMutation (mutationRaw );
487586 if (mutation == null ) {
488587 Utils .sendMessages (player , Config .getLang ("pdc-permission-invalid" ));
489588 return ;
490589 }
491590
591+ if (ContainerPdcLockManager .isSelfSubject (player , mutation .subject ())
592+ && mutation .access () != ContainerPdcLockManager .PermissionAccess .OWNER ) {
593+ Utils .sendMessages (player , Config .getLang ("pdc-self-owner-change-blocked" ));
594+ return ;
595+ }
596+
492597 LinkedHashMap <String , ContainerPdcLockManager .PermissionAccess > preview = new LinkedHashMap <>(data .permissions ());
493598 if (mutation .access () == ContainerPdcLockManager .PermissionAccess .NONE ) {
494599 preview .remove (mutation .subject ());
@@ -606,8 +711,7 @@ private void sendGroupInfo(Player player, PermissionGroupStore.GroupSnapshot gro
606711 Utils .sendMessages (player , Config .getLang ("group-info-header" ));
607712 player .sendMessage (ChatColor .GOLD + " - Name: " + ChatColor .RESET + group .name ());
608713 UUID ownerId = group .owner ();
609- Player online = Bukkit .getPlayer (ownerId );
610- String ownerText = online == null ? ownerId .toString () : online .getName () + "#" + ownerId ;
714+ String ownerText = ContainerPdcLockManager .describeSubject (ownerId .toString ());
611715 player .sendMessage (ChatColor .GOLD + " - Owner: " + ChatColor .RESET + ownerText );
612716 if (group .nodes ().isEmpty ()) {
613717 player .sendMessage (ChatColor .GOLD + " - Nodes: " + ChatColor .RESET + ChatColor .GRAY + "(none)" + ChatColor .RESET );
0 commit comments