52
52
import be .seeseemelk .mockbukkit .WorldMock ;
53
53
import be .seeseemelk .mockbukkit .enchantments .EnchantmentMock ;
54
54
import be .seeseemelk .mockbukkit .potion .MockPotionEffectType ;
55
+ import dev .jorel .commandapi .Brigadier ;
55
56
import dev .jorel .commandapi .CommandAPIBukkit ;
56
57
import dev .jorel .commandapi .commandsenders .AbstractCommandSender ;
57
58
import dev .jorel .commandapi .commandsenders .BukkitCommandSender ;
59
+ import dev .jorel .commandapi .commandsenders .BukkitPlayer ;
58
60
import io .papermc .paper .advancement .AdvancementDisplay ;
59
61
import net .kyori .adventure .text .Component ;
60
62
import net .minecraft .SharedConstants ;
61
63
import net .minecraft .advancements .Advancement ;
64
+ import net .minecraft .commands .CommandFunction ;
62
65
import net .minecraft .commands .CommandSourceStack ;
63
66
import net .minecraft .commands .arguments .EntityAnchorArgument .Anchor ;
64
67
import net .minecraft .core .BlockPos ;
68
71
import net .minecraft .server .Bootstrap ;
69
72
import net .minecraft .server .MinecraftServer ;
70
73
import net .minecraft .server .ServerAdvancementManager ;
74
+ import net .minecraft .server .ServerFunctionLibrary ;
75
+ import net .minecraft .server .ServerFunctionManager ;
71
76
import net .minecraft .server .ServerScoreboard ;
72
77
import net .minecraft .server .level .ServerLevel ;
73
78
import net .minecraft .server .level .ServerPlayer ;
74
79
import net .minecraft .server .players .GameProfileCache ;
75
80
import net .minecraft .server .players .PlayerList ;
81
+ import net .minecraft .util .profiling .metrics .profiling .InactiveMetricsRecorder ;
76
82
import net .minecraft .world .item .crafting .Recipe ;
77
83
import net .minecraft .world .item .crafting .RecipeManager ;
84
+ import net .minecraft .world .level .GameRules ;
78
85
import net .minecraft .world .level .Level ;
79
86
import net .minecraft .world .level .storage .loot .BuiltInLootTables ;
80
87
import net .minecraft .world .level .storage .loot .LootTables ;
@@ -97,6 +104,8 @@ public class MockNMS extends Enums {
97
104
List <ServerPlayer > players = new ArrayList <>();
98
105
PlayerList playerListMock ;
99
106
final RecipeManager recipeManager ;
107
+ Map <ResourceLocation , CommandFunction > functions = new HashMap <>();
108
+ Map <ResourceLocation , Collection <CommandFunction >> tags = new HashMap <>();
100
109
101
110
public MockNMS (CommandAPIBukkit <?> baseNMS ) {
102
111
super (baseNMS );
@@ -127,6 +136,7 @@ public MockNMS(CommandAPIBukkit<?> baseNMS) {
127
136
registerDefaultEnchantments ();
128
137
129
138
this .recipeManager = new RecipeManager ();
139
+ this .functions = new HashMap <>();
130
140
registerDefaultRecipes ();
131
141
}
132
142
@@ -320,6 +330,11 @@ public CommandSourceStack getBrigadierSourceFromCommandSender(AbstractCommandSen
320
330
// ChatArgument, AdventureChatArgument
321
331
Mockito .when (css .hasPermission (anyInt ())).thenAnswer (invocation -> sender .isOp ());
322
332
Mockito .when (css .hasPermission (anyInt (), anyString ())).thenAnswer (invocation -> sender .isOp ());
333
+
334
+ // FunctionArgument
335
+ // We don't really need to do anything funky here, we'll just return the same CSS
336
+ Mockito .when (css .withSuppressedOutput ()).thenReturn (css );
337
+ Mockito .when (css .withMaximumPermission (anyInt ())).thenReturn (css );
323
338
}
324
339
return css ;
325
340
}
@@ -449,9 +464,70 @@ public <T> T getMinecraftServer() {
449
464
// RecipeArgument
450
465
Mockito .when (minecraftServerMock .getRecipeManager ()).thenAnswer (i -> this .recipeManager );
451
466
467
+ // FunctionArgument
468
+ // We're using 2 as the function compilation level.
469
+ Mockito .when (minecraftServerMock .getFunctionCompilationLevel ()).thenReturn (2 );
470
+ Mockito .when (minecraftServerMock .getFunctions ()).thenAnswer (i -> {
471
+ ServerFunctionLibrary serverFunctionLibrary = Mockito .mock (ServerFunctionLibrary .class );
472
+
473
+ // Functions
474
+ Mockito .when (serverFunctionLibrary .getFunction (any ())).thenAnswer (invocation -> Optional .ofNullable (functions .get (invocation .getArgument (0 ))));
475
+ Mockito .when (serverFunctionLibrary .getFunctions ()).thenAnswer (invocation -> functions );
476
+
477
+ // Tags
478
+ Mockito .when (serverFunctionLibrary .getTag (any ())).thenAnswer (invocation -> tags .getOrDefault (invocation .getArgument (0 ), List .of ()));
479
+ Mockito .when (serverFunctionLibrary .getAvailableTags ()).thenAnswer (invocation -> tags .keySet ());
480
+
481
+ return new ServerFunctionManager (minecraftServerMock , serverFunctionLibrary ) {
482
+
483
+ // Make sure we don't use ServerFunctionManager#getDispatcher!
484
+ // That method accesses MinecraftServer.vanillaCommandDispatcher
485
+ // directly (boo) and that causes all sorts of nonsense.
486
+ @ Override
487
+ public CommandDispatcher <CommandSourceStack > getDispatcher () {
488
+ return Brigadier .getCommandDispatcher ();
489
+ }
490
+ };
491
+ });
492
+
493
+ Mockito .when (minecraftServerMock .getGameRules ()).thenAnswer (i -> new GameRules ());
494
+ Mockito .when (minecraftServerMock .getProfiler ()).thenAnswer (i -> InactiveMetricsRecorder .INSTANCE .getProfiler ());
495
+
452
496
return (T ) minecraftServerMock ;
453
497
}
454
498
499
+ @ SuppressWarnings ("unchecked" )
500
+ @ Override
501
+ public void addFunction (NamespacedKey key , List <String > commands ) {
502
+ if (Bukkit .getOnlinePlayers ().isEmpty ()) {
503
+ throw new IllegalStateException ("You need to have at least one player on the server to add a function" );
504
+ }
505
+
506
+ ResourceLocation resourceLocation = new ResourceLocation (key .toString ());
507
+ CommandSourceStack css = getBrigadierSourceFromCommandSender (new BukkitPlayer (Bukkit .getOnlinePlayers ().iterator ().next ()));
508
+
509
+ // So for very interesting reasons, Brigadier.getCommandDispatcher()
510
+ // gives a different result in this method than using getBrigadierDispatcher()
511
+ this .functions .put (resourceLocation , CommandFunction .fromLines (resourceLocation , Brigadier .getCommandDispatcher (), css , commands ));
512
+ }
513
+
514
+ @ SuppressWarnings ("unchecked" )
515
+ @ Override
516
+ public void addTag (NamespacedKey key , List <List <String >> commands ) {
517
+ if (Bukkit .getOnlinePlayers ().isEmpty ()) {
518
+ throw new IllegalStateException ("You need to have at least one player on the server to add a function" );
519
+ }
520
+
521
+ ResourceLocation resourceLocation = new ResourceLocation (key .toString ());
522
+ CommandSourceStack css = getBrigadierSourceFromCommandSender (new BukkitPlayer (Bukkit .getOnlinePlayers ().iterator ().next ()));
523
+
524
+ List <CommandFunction > tagFunctions = new ArrayList <>();
525
+ for (List <String > functionCommands : commands ) {
526
+ tagFunctions .add (CommandFunction .fromLines (resourceLocation , Brigadier .getCommandDispatcher (), css , functionCommands ));
527
+ }
528
+ this .tags .put (resourceLocation , tagFunctions );
529
+ }
530
+
455
531
@ Override
456
532
public org .bukkit .advancement .Advancement addAdvancement (NamespacedKey key ) {
457
533
advancementDataWorld .advancements .advancements .put (new ResourceLocation (key .toString ()),
0 commit comments