13
13
import java .util .HashSet ;
14
14
import java .util .List ;
15
15
import java .util .Map ;
16
+ import java .util .Optional ;
16
17
import java .util .Set ;
17
18
import java .util .stream .Collectors ;
18
19
import java .util .stream .StreamSupport ;
46
47
import be .seeseemelk .mockbukkit .WorldMock ;
47
48
import be .seeseemelk .mockbukkit .enchantments .EnchantmentMock ;
48
49
import be .seeseemelk .mockbukkit .potion .MockPotionEffectType ;
50
+ import dev .jorel .commandapi .Brigadier ;
49
51
import dev .jorel .commandapi .CommandAPIBukkit ;
50
52
import dev .jorel .commandapi .commandsenders .AbstractCommandSender ;
51
53
import dev .jorel .commandapi .commandsenders .BukkitCommandSender ;
54
+ import dev .jorel .commandapi .commandsenders .BukkitPlayer ;
52
55
import net .minecraft .SharedConstants ;
53
56
import net .minecraft .advancements .Advancement ;
57
+ import net .minecraft .commands .CommandFunction ;
54
58
import net .minecraft .commands .CommandSourceStack ;
55
59
import net .minecraft .commands .arguments .EntityAnchorArgument .Anchor ;
56
60
import net .minecraft .core .BlockPos ;
61
65
import net .minecraft .server .Bootstrap ;
62
66
import net .minecraft .server .MinecraftServer ;
63
67
import net .minecraft .server .ServerAdvancementManager ;
68
+ import net .minecraft .server .ServerFunctionLibrary ;
69
+ import net .minecraft .server .ServerFunctionManager ;
64
70
import net .minecraft .server .ServerScoreboard ;
65
71
import net .minecraft .server .level .ServerLevel ;
66
72
import net .minecraft .server .level .ServerPlayer ;
67
73
import net .minecraft .server .players .GameProfileCache ;
68
74
import net .minecraft .server .players .PlayerList ;
75
+ import net .minecraft .tags .Tag ;
76
+ import net .minecraft .tags .TagCollection ;
77
+ import net .minecraft .util .profiling .metrics .profiling .InactiveMetricsRecorder ;
69
78
import net .minecraft .world .effect .MobEffect ;
70
79
import net .minecraft .world .item .crafting .Recipe ;
71
80
import net .minecraft .world .item .crafting .RecipeManager ;
81
+ import net .minecraft .world .level .GameRules ;
72
82
import net .minecraft .world .level .Level ;
73
83
import net .minecraft .world .level .storage .loot .BuiltInLootTables ;
74
84
import net .minecraft .world .level .storage .loot .LootTables ;
@@ -84,6 +94,8 @@ public class MockNMS extends Enums {
84
94
List <ServerPlayer > players = new ArrayList <>();
85
95
PlayerList playerListMock ;
86
96
final RecipeManager recipeManager ;
97
+ Map <ResourceLocation , CommandFunction > functions = new HashMap <>();
98
+ Map <ResourceLocation , Collection <CommandFunction >> tags = new HashMap <>();
87
99
88
100
public MockNMS (CommandAPIBukkit <?> baseNMS ) {
89
101
super (baseNMS );
@@ -113,6 +125,7 @@ public MockNMS(CommandAPIBukkit<?> baseNMS) {
113
125
registerDefaultEnchantments ();
114
126
115
127
this .recipeManager = new RecipeManager ();
128
+ this .functions = new HashMap <>();
116
129
registerDefaultRecipes ();
117
130
}
118
131
@@ -303,6 +316,11 @@ public CommandSourceStack getBrigadierSourceFromCommandSender(AbstractCommandSen
303
316
// ChatArgument, AdventureChatArgument
304
317
Mockito .when (css .hasPermission (anyInt ())).thenAnswer (invocation -> sender .isOp ());
305
318
Mockito .when (css .hasPermission (anyInt (), anyString ())).thenAnswer (invocation -> sender .isOp ());
319
+
320
+ // FunctionArgument
321
+ // We don't really need to do anything funky here, we'll just return the same CSS
322
+ Mockito .when (css .withSuppressedOutput ()).thenReturn (css );
323
+ Mockito .when (css .withMaximumPermission (anyInt ())).thenReturn (css );
306
324
}
307
325
return css ;
308
326
}
@@ -428,9 +446,79 @@ public <T> T getMinecraftServer() {
428
446
// RecipeArgument
429
447
Mockito .when (minecraftServerMock .getRecipeManager ()).thenAnswer (i -> this .recipeManager );
430
448
449
+ // FunctionArgument
450
+ // We're using 2 as the function compilation level.
451
+ Mockito .when (minecraftServerMock .getFunctionCompilationLevel ()).thenReturn (2 );
452
+ Mockito .when (minecraftServerMock .getFunctions ()).thenAnswer (i -> {
453
+ ServerFunctionLibrary serverFunctionLibrary = Mockito .mock (ServerFunctionLibrary .class );
454
+
455
+ // Functions
456
+ Mockito .when (serverFunctionLibrary .getFunction (any ())).thenAnswer (invocation -> Optional .ofNullable (functions .get (invocation .getArgument (0 ))));
457
+ Mockito .when (serverFunctionLibrary .getFunctions ()).thenAnswer (invocation -> functions );
458
+
459
+ // Tags
460
+ Mockito .when (serverFunctionLibrary .getTag (any ())).thenAnswer (invocation -> {
461
+ Collection <CommandFunction > tagsFromResourceLocation = tags .getOrDefault (invocation .getArgument (0 ), List .of ());
462
+ return Tag .fromSet (Set .copyOf (tagsFromResourceLocation ));
463
+ });
464
+ Mockito .when (serverFunctionLibrary .getTags ()).thenAnswer (invocation -> {
465
+ Map <ResourceLocation , Tag <?>> tagMap = new HashMap <>();
466
+ for (Map .Entry <ResourceLocation , Collection <CommandFunction >> entry : tags .entrySet ()) {
467
+ tagMap .put (entry .getKey (), Tag .fromSet (Set .copyOf (entry .getValue ())));
468
+ }
469
+ return TagCollection .of ((Map ) tagMap );
470
+ });
471
+
472
+ return new ServerFunctionManager (minecraftServerMock , serverFunctionLibrary ) {
473
+
474
+ // Make sure we don't use ServerFunctionManager#getDispatcher!
475
+ // That method accesses MinecraftServer.vanillaCommandDispatcher
476
+ // directly (boo) and that causes all sorts of nonsense.
477
+ @ Override
478
+ public CommandDispatcher <CommandSourceStack > getDispatcher () {
479
+ return Brigadier .getCommandDispatcher ();
480
+ }
481
+ };
482
+ });
483
+
484
+ Mockito .when (minecraftServerMock .getGameRules ()).thenAnswer (i -> new GameRules ());
485
+ Mockito .when (minecraftServerMock .getProfiler ()).thenAnswer (i -> InactiveMetricsRecorder .INSTANCE .getProfiler ());
486
+
431
487
return (T ) minecraftServerMock ;
432
488
}
433
489
490
+ @ SuppressWarnings ("unchecked" )
491
+ @ Override
492
+ public void addFunction (NamespacedKey key , List <String > commands ) {
493
+ if (Bukkit .getOnlinePlayers ().isEmpty ()) {
494
+ throw new IllegalStateException ("You need to have at least one player on the server to add a function" );
495
+ }
496
+
497
+ ResourceLocation resourceLocation = new ResourceLocation (key .toString ());
498
+ CommandSourceStack css = getBrigadierSourceFromCommandSender (new BukkitPlayer (Bukkit .getOnlinePlayers ().iterator ().next ()));
499
+
500
+ // So for very interesting reasons, Brigadier.getCommandDispatcher()
501
+ // gives a different result in this method than using getBrigadierDispatcher()
502
+ this .functions .put (resourceLocation , CommandFunction .fromLines (resourceLocation , Brigadier .getCommandDispatcher (), css , commands ));
503
+ }
504
+
505
+ @ SuppressWarnings ("unchecked" )
506
+ @ Override
507
+ public void addTag (NamespacedKey key , List <List <String >> commands ) {
508
+ if (Bukkit .getOnlinePlayers ().isEmpty ()) {
509
+ throw new IllegalStateException ("You need to have at least one player on the server to add a function" );
510
+ }
511
+
512
+ ResourceLocation resourceLocation = new ResourceLocation (key .toString ());
513
+ CommandSourceStack css = getBrigadierSourceFromCommandSender (new BukkitPlayer (Bukkit .getOnlinePlayers ().iterator ().next ()));
514
+
515
+ List <CommandFunction > tagFunctions = new ArrayList <>();
516
+ for (List <String > functionCommands : commands ) {
517
+ tagFunctions .add (CommandFunction .fromLines (resourceLocation , Brigadier .getCommandDispatcher (), css , functionCommands ));
518
+ }
519
+ this .tags .put (resourceLocation , tagFunctions );
520
+ }
521
+
434
522
@ Override
435
523
public org .bukkit .advancement .Advancement addAdvancement (NamespacedKey key ) {
436
524
advancementDataWorld .advancements .advancements .put (new ResourceLocation (key .toString ()),
0 commit comments