11package io .github .mattidragon .demobox ;
22
33import com .mojang .brigadier .Command ;
4+ import com .mojang .brigadier .builder .RequiredArgumentBuilder ;
45import com .mojang .brigadier .context .CommandContext ;
6+ import com .mojang .brigadier .context .ParsedCommandNode ;
57import com .mojang .brigadier .exceptions .CommandSyntaxException ;
68import com .mojang .brigadier .suggestion .SuggestionProvider ;
9+ import com .mojang .brigadier .tree .LiteralCommandNode ;
710import me .lucko .fabric .api .permissions .v0 .Permissions ;
811import net .fabricmc .fabric .api .command .v2 .CommandRegistrationCallback ;
912import net .fabricmc .fabric .api .event .Event ;
13+ import net .minecraft .block .HangingSignBlock ;
14+ import net .minecraft .block .WallHangingSignBlock ;
15+ import net .minecraft .block .entity .SignBlockEntity ;
16+ import net .minecraft .block .entity .SignText ;
1017import net .minecraft .command .CommandSource ;
18+ import net .minecraft .command .argument .BlockPosArgumentType ;
1119import net .minecraft .command .argument .CommandFunctionArgumentType ;
1220import net .minecraft .command .argument .IdentifierArgumentType ;
1321import net .minecraft .command .argument .Vec3ArgumentType ;
22+ import net .minecraft .screen .ScreenTexts ;
1423import net .minecraft .server .command .FunctionCommand ;
1524import net .minecraft .server .command .ServerCommandSource ;
1625import net .minecraft .server .function .CommandFunction ;
1726import net .minecraft .structure .StructureTemplateManager ;
27+ import net .minecraft .text .ClickEvent ;
1828import net .minecraft .text .Text ;
29+ import net .minecraft .util .DyeColor ;
30+ import net .minecraft .util .Formatting ;
1931import net .minecraft .util .Identifier ;
2032import net .minecraft .util .math .Vec3d ;
2133import xyz .nucleoid .plasmid .api .game .GameSpaceManager ;
2638
2739import java .util .Collection ;
2840import java .util .List ;
41+ import java .util .function .UnaryOperator ;
2942
3043import static net .minecraft .server .command .CommandManager .argument ;
3144import static net .minecraft .server .command .CommandManager .literal ;
@@ -42,25 +55,34 @@ public static void register() {
4255 CommandRegistrationCallback .EVENT .addPhaseOrdering (Event .DEFAULT_PHASE , DemoBox .id ("after" ));
4356 CommandRegistrationCallback .EVENT .register (DemoBox .id ("after" ), (dispatcher , registryAccess , environment ) -> {
4457 dispatcher .register (literal ("demobox" )
45- .requires (source -> Permissions .check ( source , "demobox" , true ))
58+ .requires (Permissions .require ( "demobox" , true ))
4659 .then (literal ("leave" )
47- .requires (source -> Permissions .check ( source , "demobox.leave" , true ))
48- .executes (DemoBoxCommand ::leaveGame ))
60+ .requires (Permissions .require ( "demobox.leave" , true ))
61+ .executes (DemoBoxCommand ::executeLeave ))
4962 .then (literal ("open" )
50- .requires (source -> Permissions .check (source , "demobox.open" , 2 ))
51- .then (argument ("template" , IdentifierArgumentType .identifier ())
52- .suggests (STRUCTURE_SUGGESTION_PROVIDER )
53- .executes (context -> execute (context .getSource (), IdentifierArgumentType .getIdentifier (context , "template" ), new Vec3d (0.5 , 2 , 0.5 ), List .of ()))
54- .then (argument ("pos" , Vec3ArgumentType .vec3 ())
55- .executes (context -> execute (context .getSource (), IdentifierArgumentType .getIdentifier (context , "template" ), Vec3ArgumentType .getVec3 (context , "pos" ), List .of ()))
56- .then (argument ("setupFunction" , CommandFunctionArgumentType .commandFunction ())
57- .suggests (FunctionCommand .SUGGESTION_PROVIDER )
58- .executes (context -> execute (context .getSource (), IdentifierArgumentType .getIdentifier (context , "template" ), Vec3ArgumentType .getVec3 (context , "pos" ), CommandFunctionArgumentType .getFunctions (context , "setupFunction" ))))))));
63+ .requires (Permissions .require ("demobox.open" , 2 ))
64+ .then (buildArgTree (DemoBoxCommand ::executeOpen )))
65+ .then (literal ("sign" )
66+ .requires (Permissions .require ("demobox.sign" , 2 ))
67+ .then (argument ("signPos" , BlockPosArgumentType .blockPos ())
68+ .then (buildArgTree (DemoBoxCommand ::executeSign ))))
69+ );
5970 });
6071 }
6172
73+ private static RequiredArgumentBuilder <ServerCommandSource , Identifier > buildArgTree (CommandHandler handler ) {
74+ return argument ("template" , IdentifierArgumentType .identifier ())
75+ .suggests (STRUCTURE_SUGGESTION_PROVIDER )
76+ .executes (context -> handler .execute (context , IdentifierArgumentType .getIdentifier (context , "template" ), new Vec3d (0.5 , 2 , 0.5 ), List .of ()))
77+ .then (argument ("pos" , Vec3ArgumentType .vec3 ())
78+ .executes (context -> handler .execute (context , IdentifierArgumentType .getIdentifier (context , "template" ), Vec3ArgumentType .getVec3 (context , "pos" ), List .of ()))
79+ .then (argument ("setupFunction" , CommandFunctionArgumentType .commandFunction ())
80+ .suggests (FunctionCommand .SUGGESTION_PROVIDER )
81+ .executes (context -> handler .execute (context , IdentifierArgumentType .getIdentifier (context , "template" ), Vec3ArgumentType .getVec3 (context , "pos" ), CommandFunctionArgumentType .getFunctions (context , "setupFunction" )))));
82+ }
83+
6284 // Joinked from GameCommand because brigadier can't deal with childless redirects
63- private static int leaveGame (CommandContext <ServerCommandSource > context ) throws CommandSyntaxException {
85+ private static int executeLeave (CommandContext <ServerCommandSource > context ) throws CommandSyntaxException {
6486 var source = context .getSource ();
6587 var player = source .getPlayerOrThrow ();
6688
@@ -76,7 +98,73 @@ private static int leaveGame(CommandContext<ServerCommandSource> context) throws
7698 return Command .SINGLE_SUCCESS ;
7799 }
78100
79- private static int execute (ServerCommandSource source , Identifier structure , Vec3d pos , Collection <CommandFunction <ServerCommandSource >> functions ) throws CommandSyntaxException {
101+ private static int executeSign (CommandContext <ServerCommandSource > context , Identifier structure , Vec3d pos , Collection <CommandFunction <ServerCommandSource >> functions ) throws CommandSyntaxException {
102+ var signPos = BlockPosArgumentType .getLoadedBlockPos (context , "signPos" );
103+ var source = context .getSource ();
104+ var block = source .getWorld ().getBlockState (signPos ).getBlock ();
105+ if (!(source .getWorld ().getBlockEntity (signPos ) instanceof SignBlockEntity sign )) {
106+ source .sendError (Text .translatable ("command.demobox.sign.missing" ));
107+ return 0 ;
108+ }
109+
110+ var command = "demobox open " + getCommandEnd (context );
111+
112+ UnaryOperator <SignText > textChanger = text -> {
113+ var clickEvent = new ClickEvent .RunCommand (command );
114+ Text [] texts ;
115+ if (block instanceof HangingSignBlock || block instanceof WallHangingSignBlock ) {
116+ texts = new Text []{
117+ Text .translatable ("demobox.hanging_sign.line1" ).formatted (Formatting .GREEN )
118+ .styled (style -> style .withClickEvent (clickEvent )),
119+ Text .translatable ("demobox.hanging_sign.line2" ).formatted (Formatting .GREEN ),
120+ Text .translatable ("demobox.hanging_sign.line3" ).formatted (Formatting .GREEN ),
121+ ScreenTexts .EMPTY
122+ };
123+ } else {
124+ texts = new Text []{
125+ ScreenTexts .EMPTY ,
126+ Text .translatable ("demobox.sign.line1" ).formatted (Formatting .GREEN )
127+ .styled (style -> style .withClickEvent (clickEvent )),
128+ Text .translatable ("demobox.sign.line2" ).formatted (Formatting .GREEN ),
129+ ScreenTexts .EMPTY
130+ };
131+ }
132+ return new SignText (
133+ texts ,
134+ texts ,
135+ DyeColor .LIME ,
136+ true
137+ );
138+ };
139+ sign .changeText (textChanger , true );
140+ sign .changeText (textChanger , false );
141+ sign .setWaxed (true );
142+
143+ source .sendFeedback (() -> Text .translatable ("command.demobox.sign.success" ), true );
144+ return 1 ;
145+ }
146+
147+ /**
148+ * Extracts the argument text from the sign command for use within the sign text.
149+ */
150+ private static String getCommandEnd (CommandContext <ServerCommandSource > context ) {
151+ var nodes = context .getNodes ();
152+ ParsedCommandNode <ServerCommandSource > mainNode = null ;
153+ for (var i = nodes .size () - 1 ; i >= 0 ; i --) {
154+ var node = nodes .get (i );
155+ if (node .getNode () instanceof LiteralCommandNode <ServerCommandSource > literal && literal .getLiteral ().equals ("sign" )) {
156+ mainNode = nodes .get (i + 2 );
157+ break ;
158+ }
159+ }
160+ if (mainNode == null ) {
161+ throw new IllegalStateException ("Cannot find node in parsed command (weird hacks going on???)" );
162+ }
163+ return context .getInput ().substring (mainNode .getRange ().getStart ());
164+ }
165+
166+ private static int executeOpen (CommandContext <ServerCommandSource > context , Identifier structure , Vec3d pos , Collection <CommandFunction <ServerCommandSource >> functions ) throws CommandSyntaxException {
167+ var source = context .getSource ();
80168 var player = source .getPlayerOrThrow ();
81169
82170 DemoBoxGame .open (new DemoBoxGame .Settings (structure , pos , functions .stream ().map (CommandFunction ::id ).toList ()))
@@ -91,4 +179,9 @@ private static int execute(ServerCommandSource source, Identifier structure, Vec
91179 }, player .getServer ());
92180 return Command .SINGLE_SUCCESS ;
93181 }
182+
183+ @ FunctionalInterface
184+ private interface CommandHandler {
185+ int execute (CommandContext <ServerCommandSource > context , Identifier structure , Vec3d pos , Collection <CommandFunction <ServerCommandSource >> functions ) throws CommandSyntaxException ;
186+ }
94187}
0 commit comments