2727import java .lang .reflect .Method ;
2828import java .lang .reflect .Proxy ;
2929import java .util .Collection ;
30+ import java .util .List ;
3031import java .util .Locale ;
3132import java .util .Objects ;
3233import java .util .Set ;
3334import java .util .concurrent .ConcurrentHashMap ;
3435import java .util .logging .Level ;
36+ import org .bukkit .command .Command ;
37+ import org .bukkit .command .CommandException ;
38+ import org .bukkit .command .CommandMap ;
39+ import org .bukkit .command .CommandSender ;
3540import org .bukkit .command .PluginCommand ;
41+ import org .bukkit .command .PluginIdentifiableCommand ;
42+ import org .bukkit .plugin .Plugin ;
3643import org .bukkit .plugin .java .JavaPlugin ;
3744
3845public final class MahjongPaperPlugin extends JavaPlugin {
@@ -50,6 +57,7 @@ public final class MahjongPaperPlugin extends JavaPlugin {
5057 private GameRoomSelectionService gameRoomSelectionService ;
5158 private GameRoomSelectionPreviewService gameRoomSelectionPreviewService ;
5259 private PluginTask gameRoomTickTask ;
60+ private Command commandMapFallback ;
5361
5462 @ Override
5563 public void onEnable () {
@@ -133,6 +141,7 @@ public void onEnable() {
133141
134142 @ Override
135143 public void onDisable () {
144+ this .unregisterCommandMapFallback ();
136145 if (this .craftEngine != null ) {
137146 this .craftEngine .disableFurnitureInteractionBridge ();
138147 this .craftEngine .clearTrackedCullables ();
@@ -200,15 +209,39 @@ private boolean registerMahjongCommand(MahjongCommand mahjongCommand) {
200209
201210 PluginCommand command = this .getCommand ("mahjong" );
202211 if (command == null ) {
203- this .getLogger ().severe ("MahjongPaper command is missing from plugin.yml; disabling plugin." );
204- this .getServer ().getPluginManager ().disablePlugin (this );
205- return false ;
212+ return this .registerCommandMapFallback (mahjongCommand );
206213 }
207214 command .setExecutor (mahjongCommand );
208215 command .setTabCompleter (mahjongCommand );
209216 return true ;
210217 }
211218
219+ private boolean registerCommandMapFallback (MahjongCommand mahjongCommand ) {
220+ CommandMap commandMap = this .getServer ().getCommandMap ();
221+ Command fallback = new CommandMapMahjongCommand (this , mahjongCommand );
222+ this .commandMapFallback = fallback ;
223+ commandMap .register ("mahjongpaper" , fallback );
224+ if (commandMap .getCommand ("mahjong" ) == fallback || commandMap .getCommand ("mahjongpaper:mahjong" ) == fallback ) {
225+ return true ;
226+ }
227+
228+ this .unregisterCommandMapFallback ();
229+ this .getLogger ().severe ("MahjongPaper command could not be registered through the server command map; disabling plugin." );
230+ this .getServer ().getPluginManager ().disablePlugin (this );
231+ return false ;
232+ }
233+
234+ private void unregisterCommandMapFallback () {
235+ Command fallback = this .commandMapFallback ;
236+ if (fallback == null ) {
237+ return ;
238+ }
239+ CommandMap commandMap = this .getServer ().getCommandMap ();
240+ commandMap .getKnownCommands ().entrySet ().removeIf (entry -> entry .getValue () == fallback );
241+ fallback .unregister (commandMap );
242+ this .commandMapFallback = null ;
243+ }
244+
212245 private PaperCommandRegistrationResult registerPaperCommand (MahjongCommand mahjongCommand ) {
213246 try {
214247 Class <?> lifecycleEventsClass = Class .forName ("io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents" );
@@ -235,9 +268,9 @@ private PaperCommandRegistrationResult registerPaperCommand(MahjongCommand mahjo
235268 );
236269 registerEventHandler .invoke (lifecycleManager , commandsEventType , handler );
237270 return PaperCommandRegistrationResult .REGISTERED ;
238- } catch (ClassNotFoundException | NoSuchMethodException ex ) {
271+ } catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException ex ) {
239272 return PaperCommandRegistrationResult .UNAVAILABLE ;
240- } catch (IllegalAccessException | InvocationTargetException | NoSuchFieldException ex ) {
273+ } catch (IllegalAccessException | InvocationTargetException ex ) {
241274 this .getLogger ().log (Level .SEVERE , "Failed to register MahjongPaper command through Paper lifecycle events." , ex );
242275 return PaperCommandRegistrationResult .FAILED ;
243276 }
@@ -295,6 +328,52 @@ private enum PaperCommandRegistrationResult {
295328 FAILED
296329 }
297330
331+ private static final class CommandMapMahjongCommand extends Command implements PluginIdentifiableCommand {
332+ private final MahjongPaperPlugin plugin ;
333+ private final MahjongCommand delegate ;
334+
335+ private CommandMapMahjongCommand (MahjongPaperPlugin plugin , MahjongCommand delegate ) {
336+ super (
337+ "mahjong" ,
338+ "Manage MahjongPaper tables and rounds. Use /mahjong help for command explanations." ,
339+ "/mahjong <help|create|botmatch|mode|join|leave|list|spectate|unspectate|table|addbot|removebot|rule|start|state|riichi|tsumo|ron|pon|minkan|chii|kan|skip|kyuushu|settlement|rank|leaderboard|render|inspect|clear|forceend|deletetable|reload>" ,
340+ List .of ()
341+ );
342+ this .plugin = plugin ;
343+ this .delegate = delegate ;
344+ this .setPermission (delegate .permission ());
345+ }
346+
347+ @ Override
348+ public boolean execute (CommandSender sender , String commandLabel , String [] args ) {
349+ if (!this .plugin .isEnabled ()) {
350+ throw new CommandException ("Cannot execute /" + commandLabel + " because MahjongPaper is disabled." );
351+ }
352+ if (!this .testPermission (sender )) {
353+ return true ;
354+ }
355+ try {
356+ return this .delegate .onCommand (sender , this , commandLabel , args );
357+ } catch (Throwable throwable ) {
358+ throw new CommandException ("Unhandled exception while executing /" + commandLabel + "." , throwable );
359+ }
360+ }
361+
362+ @ Override
363+ public List <String > tabComplete (CommandSender sender , String alias , String [] args ) {
364+ try {
365+ return Objects .requireNonNullElse (this .delegate .onTabComplete (sender , this , alias , args ), List .of ());
366+ } catch (Throwable throwable ) {
367+ throw new CommandException ("Unhandled exception while tab-completing /" + alias + "." , throwable );
368+ }
369+ }
370+
371+ @ Override
372+ public Plugin getPlugin () {
373+ return this .plugin ;
374+ }
375+ }
376+
298377 public String reloadMahjongConfiguration () {
299378 this .reloadConfig ();
300379
0 commit comments