1313import java .lang .invoke .MethodHandle ;
1414import java .lang .invoke .MethodHandles ;
1515import java .lang .invoke .MethodType ;
16+ import java .lang .reflect .InvocationHandler ;
17+ import java .lang .reflect .Method ;
18+ import java .lang .reflect .Proxy ;
1619import java .util .Collection ;
20+ import java .util .Locale ;
1721import java .util .Map ;
1822import java .util .Set ;
1923import java .util .concurrent .ConcurrentHashMap ;
@@ -34,15 +38,16 @@ public final class RuntimeCommandRegistrar {
3438 public record CommandRegistration (
3539 LiteralCommandNode <CommandSourceStack > node ,
3640 Collection <String > aliases ,
37- @ Nullable String description
41+ @ Nullable String description ,
42+ @ Nullable String namespace
3843 ) { }
3944
4045 private static JavaPlugin plugin ;
4146
4247 private static Commands commandRegistrar ;
4348 private static final Map <CommandRegistration , Set <String >> REGISTERED_COMMANDS = new ConcurrentHashMap <>();
44- private static final Set <CommandRegistration > PENDING_REGISTRATION = ConcurrentHashMap .newKeySet ();
45- private static final Set <String > PENDING_UNREGISTRATION = ConcurrentHashMap .newKeySet ();
49+ private static final Set <CommandRegistration > PENDING_REGISTRATIONS = ConcurrentHashMap .newKeySet ();
50+ private static final Set <String > PENDING_UNREGISTRATIONS = ConcurrentHashMap .newKeySet ();
4651
4752 private static @ Nullable MethodHandle SET_VALID ;
4853 private static @ Nullable MethodHandle INVALIDATE ;
@@ -56,13 +61,10 @@ public static void init(JavaPlugin plugin) {
5661 plugin .getLifecycleManager ().registerEventHandler (LifecycleEvents .COMMANDS , commands -> {
5762 commandRegistrar = commands .registrar ();
5863
59- if (!REGISTERED_COMMANDS .isEmpty () || !PENDING_REGISTRATION .isEmpty ()) {
64+ if (!REGISTERED_COMMANDS .isEmpty () || !PENDING_REGISTRATIONS .isEmpty ()) {
6065 REGISTERED_COMMANDS .replaceAll ((command , ignored ) ->
6166 commandRegistrar .register (command .node , command .description , command .aliases ));
62- for (CommandRegistration command : PENDING_REGISTRATION ) {
63- REGISTERED_COMMANDS .put (command , commandRegistrar .register (command .node , command .description , command .aliases ));
64- }
65- PENDING_REGISTRATION .clear ();
67+ processRegistrationSet ();
6668 return ;
6769 }
6870
@@ -88,7 +90,7 @@ public static void init(JavaPlugin plugin) {
8890 * @see #processRegistrations()
8991 */
9092 public static void register (CommandRegistration command ) {
91- PENDING_REGISTRATION .add (command );
93+ PENDING_REGISTRATIONS .add (command );
9294 }
9395
9496 /**
@@ -101,7 +103,7 @@ public static void processRegistrations() {
101103 return ;
102104 }
103105
104- if (!PENDING_UNREGISTRATION .isEmpty ()) {
106+ if (!PENDING_UNREGISTRATIONS .isEmpty ()) {
105107 processUnregistrations ();
106108 }
107109
@@ -113,25 +115,64 @@ public static void processRegistrations() {
113115
114116 try {
115117 SET_VALID .invoke (commandRegistrar );
116- PluginMeta pluginMeta = plugin .getPluginMeta ();
117- for (CommandRegistration command : PENDING_REGISTRATION ) {
118- REGISTERED_COMMANDS .put (command , commandRegistrar .register (pluginMeta , command .node , command .description , command .aliases ));
119- }
118+ processRegistrationSet ();
120119 INVALIDATE .invoke (commandRegistrar );
121120 SYNC_COMMANDS .invoke (Bukkit .getServer ());
122121 } catch (Throwable e ) {
123122 throw Skript .exception (e );
124123 }
125124 }
126125
126+ private static void processRegistrationSet () {
127+ PluginMeta pluginMeta = plugin .getPluginMeta ();
128+ for (CommandRegistration command : PENDING_REGISTRATIONS ) {
129+ if (command .namespace == null ) {
130+ REGISTERED_COMMANDS .put (command , commandRegistrar .register (pluginMeta , command .node , command .description , command .aliases ));
131+ } else {
132+ TemporaryNamePluginMeta handler = new TemporaryNamePluginMeta (pluginMeta , command .namespace );
133+ PluginMeta meta = (PluginMeta ) Proxy .newProxyInstance (pluginMeta .getClass ().getClassLoader (),
134+ new Class <?>[]{PluginMeta .class }, handler );
135+ REGISTERED_COMMANDS .put (command , commandRegistrar .register (meta , command .node , command .description , command .aliases ));
136+ handler .useAlternativeName = false ;
137+ }
138+ }
139+ PENDING_REGISTRATIONS .clear ();
140+ }
141+
142+ private static class TemporaryNamePluginMeta implements InvocationHandler {
143+
144+ final PluginMeta source ;
145+ final String name ;
146+ boolean useAlternativeName = true ;
147+
148+ public TemporaryNamePluginMeta (PluginMeta source , String name ) {
149+ this .source = source ;
150+ this .name = name ;
151+ }
152+
153+ @ Override
154+ public Object invoke (Object proxy , Method method , Object [] args ) throws Throwable {
155+ if (useAlternativeName ) {
156+ String methodName = method .getName ();
157+ if (methodName .equals ("getName" )) {
158+ return name ;
159+ } else if (methodName .equals ("namespace" )) {
160+ return name .toLowerCase (Locale .ROOT );
161+ }
162+ }
163+ return method .invoke (source , args );
164+ }
165+
166+ }
167+
127168 /**
128169 * Adds the {@code command} to the unregistration queue.
129170 * To finalize unregistration, the queue must be processed using {@link #processUnregistrations()}.
130171 * @param command The command to unregister.
131172 * @see #processUnregistrations()
132173 */
133174 public static void unregister (CommandRegistration command ) {
134- PENDING_UNREGISTRATION .addAll (REGISTERED_COMMANDS .remove (command ));
175+ PENDING_UNREGISTRATIONS .addAll (REGISTERED_COMMANDS .remove (command ));
135176 }
136177
137178 /**
@@ -145,7 +186,7 @@ public static void processUnregistrations() {
145186 }
146187
147188 if (useSafeReload ) {
148- PENDING_UNREGISTRATION .clear ();
189+ PENDING_UNREGISTRATIONS .clear ();
149190 Bukkit .reloadData ();
150191 return ;
151192 }
@@ -154,10 +195,10 @@ public static void processUnregistrations() {
154195 try {
155196 SET_VALID .invoke (commandRegistrar );
156197 var root = commandRegistrar .getDispatcher ().getRoot ();
157- for (String command : PENDING_UNREGISTRATION ) {
198+ for (String command : PENDING_UNREGISTRATIONS ) {
158199 REMOVE_COMMAND .invoke (root , command );
159200 }
160- PENDING_UNREGISTRATION .clear ();
201+ PENDING_UNREGISTRATIONS .clear ();
161202 INVALIDATE .invoke (commandRegistrar );
162203 SYNC_COMMANDS .invoke (Bukkit .getServer ());
163204 } catch (Throwable e ) {
0 commit comments