Skip to content

Commit 12a6277

Browse files
committed
Add command to list loaders
1 parent daa2f22 commit 12a6277

12 files changed

+558
-20
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.hlysine.create_power_loader;
2+
3+
import com.hlysine.create_power_loader.command.ListLoadersCommand;
4+
import com.mojang.brigadier.CommandDispatcher;
5+
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
6+
import com.mojang.brigadier.tree.CommandNode;
7+
import com.mojang.brigadier.tree.LiteralCommandNode;
8+
import net.minecraft.commands.CommandSourceStack;
9+
import net.minecraft.commands.Commands;
10+
11+
import java.util.Collections;
12+
13+
public class CPLCommands {
14+
15+
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
16+
LiteralArgumentBuilder<CommandSourceStack> root = Commands.literal("powerloader")
17+
.requires(cs -> cs.hasPermission(2))
18+
.then(ListLoadersCommand.register());
19+
20+
LiteralCommandNode<CommandSourceStack> cplRoot = dispatcher.register(root);
21+
22+
CommandNode<CommandSourceStack> c = dispatcher.findNode(Collections.singleton("pl"));
23+
if (c != null)
24+
return;
25+
26+
dispatcher.getRoot().addChild(buildRedirect("pl", cplRoot));
27+
}
28+
29+
/**
30+
* *****
31+
* https://github.com/VelocityPowered/Velocity/blob/8abc9c80a69158ebae0121fda78b55c865c0abad/proxy/src/main/java/com/velocitypowered/proxy/util/BrigadierUtils.java#L38
32+
* *****
33+
* <p>
34+
* Returns a literal node that redirects its execution to
35+
* the given destination node.
36+
*
37+
* @param alias the command alias
38+
* @param destination the destination node
39+
* @return the built node
40+
*/
41+
public static LiteralCommandNode<CommandSourceStack> buildRedirect(final String alias, final LiteralCommandNode<CommandSourceStack> destination) {
42+
// Redirects only work for nodes with children, but break the top argument-less command.
43+
// Manually adding the root command after setting the redirect doesn't fix it.
44+
// See https://github.com/Mojang/brigadier/issues/46). Manually clone the node instead.
45+
LiteralArgumentBuilder<CommandSourceStack> builder = LiteralArgumentBuilder
46+
.<CommandSourceStack>literal(alias)
47+
.requires(destination.getRequirement())
48+
.forward(destination.getRedirect(), destination.getRedirectModifier(), destination.isFork())
49+
.executes(destination.getCommand());
50+
for (CommandNode<CommandSourceStack> child : destination.getChildren()) {
51+
builder.then(child);
52+
}
53+
return builder.build();
54+
}
55+
56+
}
57+

src/main/java/com/hlysine/create_power_loader/CreatePowerLoader.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import net.minecraftforge.api.distmarker.Dist;
1414
import net.minecraftforge.common.MinecraftForge;
1515
import net.minecraftforge.common.world.ForgeChunkManager;
16+
import net.minecraftforge.event.RegisterCommandsEvent;
1617
import net.minecraftforge.eventbus.api.EventPriority;
1718
import net.minecraftforge.eventbus.api.IEventBus;
1819
import net.minecraftforge.fml.DistExecutor;
@@ -47,6 +48,7 @@ public CreatePowerLoader() {
4748

4849
// Register the commonSetup method for mod loading
4950
modEventBus.addListener(this::commonSetup);
51+
forgeEventBus.addListener(this::registerCommands);
5052

5153
// Register ourselves for server and other game events we are interested in
5254
MinecraftForge.EVENT_BUS.register(this);
@@ -71,6 +73,10 @@ private void commonSetup(final FMLCommonSetupEvent event) {
7173
});
7274
}
7375

76+
private void registerCommands(RegisterCommandsEvent event) {
77+
CPLCommands.register(event.getDispatcher());
78+
}
79+
7480
public static CreateRegistrate getRegistrate() {
7581
return REGISTRATE;
7682
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package com.hlysine.create_power_loader.command;
2+
3+
import com.hlysine.create_power_loader.content.ChunkLoadManager;
4+
import com.hlysine.create_power_loader.content.ChunkLoader;
5+
import com.hlysine.create_power_loader.content.LoaderMode;
6+
import com.hlysine.create_power_loader.content.WeakCollection;
7+
import com.hlysine.create_power_loader.content.trains.CarriageChunkLoader;
8+
import com.hlysine.create_power_loader.content.trains.StationChunkLoader;
9+
import com.hlysine.create_power_loader.content.trains.TrainChunkLoader;
10+
import com.mojang.brigadier.Command;
11+
import com.mojang.brigadier.arguments.IntegerArgumentType;
12+
import com.mojang.brigadier.builder.ArgumentBuilder;
13+
import com.simibubi.create.foundation.utility.Components;
14+
import com.simibubi.create.foundation.utility.Pair;
15+
import net.minecraft.ChatFormatting;
16+
import net.minecraft.commands.CommandSourceStack;
17+
import net.minecraft.commands.Commands;
18+
import net.minecraft.core.BlockPos;
19+
import net.minecraft.core.registries.Registries;
20+
import net.minecraft.network.chat.ClickEvent;
21+
import net.minecraft.network.chat.Component;
22+
import net.minecraft.network.chat.HoverEvent;
23+
import net.minecraft.resources.ResourceKey;
24+
import net.minecraft.resources.ResourceLocation;
25+
import net.minecraft.server.MinecraftServer;
26+
import net.minecraft.server.level.ServerLevel;
27+
import net.minecraft.world.level.dimension.DimensionType;
28+
import net.minecraft.world.phys.Vec3;
29+
import net.minecraftforge.server.command.EnumArgument;
30+
import org.jetbrains.annotations.Nullable;
31+
32+
import java.util.*;
33+
import java.util.function.BiConsumer;
34+
import java.util.function.Consumer;
35+
import java.util.function.Function;
36+
37+
public class ListLoadersCommand {
38+
39+
public static ArgumentBuilder<CommandSourceStack, ?> register() {
40+
return Commands.literal("list")
41+
.requires(cs -> cs.hasPermission(2))
42+
.then(
43+
Commands.argument("type", EnumArgument.enumArgument(LoaderMode.class))
44+
.then(
45+
Commands.literal("limit")
46+
.then(
47+
Commands.argument("limit", IntegerArgumentType.integer(1))
48+
.executes(handler(true, true))
49+
)
50+
)
51+
.executes(handler(true, false))
52+
)
53+
.then(
54+
Commands.literal("limit")
55+
.then(
56+
Commands.argument("limit", IntegerArgumentType.integer(1))
57+
.executes(handler(false, true))
58+
)
59+
)
60+
.executes(handler(false, false));
61+
}
62+
63+
private static Command<CommandSourceStack> handler(boolean hasMode, boolean hasLimit) {
64+
return ctx -> {
65+
CommandSourceStack source = ctx.getSource();
66+
fillReport(source.getLevel(), source.getPosition(),
67+
hasMode ? ctx.getArgument("type", LoaderMode.class) : null,
68+
hasLimit ? ctx.getArgument("limit", Integer.class) : Integer.MAX_VALUE,
69+
(s, f) -> source.sendSuccess(() -> Components.literal(s).withStyle(st -> st.withColor(f)), false),
70+
(c) -> source.sendSuccess(() -> c, false));
71+
return Command.SINGLE_SUCCESS;
72+
};
73+
}
74+
75+
private static void fillReport(ServerLevel level, Vec3 location, @Nullable LoaderMode mode, int limit, BiConsumer<String, Integer> chat,
76+
Consumer<Component> chatRaw) {
77+
int white = ChatFormatting.WHITE.getColor();
78+
int blue = 0xD3DEDC;
79+
int bright = 0xFFEFEF;
80+
int orange = 0xFFAD60;
81+
82+
List<ChunkLoader> loaders = new LinkedList<>();
83+
if (mode == null) {
84+
for (WeakCollection<ChunkLoader> list : ChunkLoadManager.allLoaders.values()) {
85+
loaders.addAll(list);
86+
}
87+
} else {
88+
loaders.addAll(ChunkLoadManager.allLoaders.get(mode));
89+
}
90+
loaders.removeIf(loader -> loader.getForcedChunks().size() == 0);
91+
92+
Map<ResourceLocation, DimensionType> typeCache = new HashMap<>();
93+
MinecraftServer server = level.getServer();
94+
Function<ResourceLocation, DimensionType> computeType = key -> server.getLevel(ResourceKey.create(Registries.DIMENSION, key)).dimensionType();
95+
List<Pair<ChunkLoader, Pair<ResourceLocation, Vec3>>> pairs = loaders.stream()
96+
.map(loader -> Pair.of(loader, loader.getLocation()))
97+
.map(pair -> Pair.of(pair.getFirst(), Pair.of(pair.getSecond().getFirst(), Vec3.atCenterOf(pair.getSecond().getSecond()))))
98+
.sorted(Comparator
99+
.<Pair<ChunkLoader, Pair<ResourceLocation, Vec3>>>comparingInt(p -> p.getSecond().getFirst().equals(level.dimension().location()) ? 0 : 1)
100+
.thenComparingDouble(p -> p.getSecond().getSecond()
101+
.scale(DimensionType.getTeleportationScale(typeCache.computeIfAbsent(p.getSecond().getFirst(), computeType), level.dimensionType()))
102+
.distanceToSqr(location))
103+
)
104+
.limit(limit)
105+
.toList();
106+
107+
chat.accept("", white);
108+
chat.accept("-+------<< Chunk Loader List >>------+-", white);
109+
chat.accept(pairs.size() + " out of " + loaders.size() + " nearest" + (mode != null ? " " + mode.getSerializedName() : "") + " loaders", blue);
110+
for (Pair<ChunkLoader, Pair<ResourceLocation, Vec3>> pair : pairs) {
111+
ChunkLoader loader = pair.getFirst();
112+
ResourceLocation dimension = pair.getSecond().getFirst();
113+
BlockPos pos = BlockPos.containing(pair.getSecond().getSecond());
114+
115+
chatRaw.accept(createTpButton(dimension, pos,
116+
(mode == null ? " " + loader.getLoaderMode().getSerializedName() + " " : "")
117+
+ "[" + pos.toShortString() + "]"
118+
+ (!dimension.equals(level.dimension().location()) ? " in " + dimension : "")
119+
, white));
120+
121+
chat.accept(
122+
" "
123+
+ loader.getLoaderType().getSerializedName() + " - "
124+
+ loader.getForcedChunks().size() + " chunks"
125+
, orange);
126+
if (loader instanceof TrainChunkLoader trainLoader) {
127+
for (int i = 0; i < trainLoader.carriageLoaders.size(); i++) {
128+
CarriageChunkLoader carriageLoader = trainLoader.carriageLoaders.get(i);
129+
if (carriageLoader.getForcedChunks().isEmpty()) continue;
130+
Pair<ResourceLocation, BlockPos> carriageLocation = carriageLoader.getLocation();
131+
chatRaw.accept(createTpButton(carriageLocation.getFirst(), carriageLocation.getSecond(),
132+
" Carriage " + (i + 1) + " - "
133+
+ "[" + carriageLocation.getSecond().toShortString() + "]"
134+
+ (!carriageLocation.getFirst().equals(level.dimension().location()) ? " in " + carriageLocation.getFirst().toString() : "")
135+
, blue));
136+
chat.accept(
137+
" "
138+
+ carriageLoader.getLoaderType().getSerializedName() + " - "
139+
+ carriageLoader.getForcedChunks().size() + " chunks"
140+
, orange);
141+
}
142+
} else if (loader instanceof StationChunkLoader stationLoader) {
143+
for (StationChunkLoader.AttachedLoader attachment : stationLoader.attachments) {
144+
chatRaw.accept(createTpButton(stationLoader.getLocation().getFirst(), attachment.pos(),
145+
" "
146+
+ attachment.type().getSerializedName()
147+
+ " - "
148+
+ "[" + attachment.pos().toShortString() + "]"
149+
, blue));
150+
}
151+
}
152+
}
153+
chat.accept("-+--------------------------------+-", white);
154+
}
155+
156+
private static Component createTpButton(ResourceLocation dimension, BlockPos blockPos, String text, int color) {
157+
String teleport = "/execute in " + dimension.toString() + " run tp @s " + blockPos.getX() + " " + blockPos.getY() + " " + blockPos.getZ();
158+
return Components.literal(text).withStyle((p_180514_) -> {
159+
return p_180514_.withColor(color)
160+
.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, teleport))
161+
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
162+
Components.literal("Click to teleport")))
163+
.withInsertion(teleport);
164+
});
165+
}
166+
167+
}

src/main/java/com/hlysine/create_power_loader/content/AbstractChunkLoaderBlockEntity.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,20 @@
66
import com.simibubi.create.content.kinetics.base.IRotate;
77
import com.simibubi.create.content.kinetics.base.KineticBlockEntity;
88
import com.simibubi.create.content.trains.station.StationBlockEntity;
9+
import com.simibubi.create.foundation.utility.Pair;
910
import com.simibubi.create.foundation.utility.VecHelper;
1011
import net.minecraft.core.BlockPos;
1112
import net.minecraft.core.particles.ParticleTypes;
1213
import net.minecraft.nbt.CompoundTag;
14+
import net.minecraft.resources.ResourceLocation;
1315
import net.minecraft.server.level.ServerLevel;
1416
import net.minecraft.util.RandomSource;
1517
import net.minecraft.world.level.block.entity.BlockEntity;
1618
import net.minecraft.world.level.block.entity.BlockEntityType;
1719
import net.minecraft.world.level.block.state.BlockState;
1820
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
1921
import net.minecraft.world.phys.Vec3;
22+
import org.jetbrains.annotations.NotNull;
2023
import org.jetbrains.annotations.Nullable;
2124

2225
import java.util.HashSet;
@@ -27,7 +30,7 @@
2730
import static com.hlysine.create_power_loader.content.ChunkLoadManager.unforceAllChunks;
2831
import static com.simibubi.create.content.kinetics.base.DirectionalKineticBlock.FACING;
2932

30-
public abstract class AbstractChunkLoaderBlockEntity extends KineticBlockEntity {
33+
public abstract class AbstractChunkLoaderBlockEntity extends KineticBlockEntity implements ChunkLoader {
3134

3235
public final LoaderType type;
3336
protected BlockPos lastBlockPos;
@@ -46,11 +49,33 @@ public AbstractChunkLoaderBlockEntity(BlockEntityType<?> typeIn, BlockPos pos, B
4649
this.type = type;
4750
}
4851

52+
@Override
53+
public @NotNull Set<LoadedChunkPos> getForcedChunks() {
54+
return forcedChunks;
55+
}
56+
57+
@Override
58+
public LoaderMode getLoaderMode() {
59+
return LoaderMode.STATIC;
60+
}
61+
62+
@Override
63+
public LoaderType getLoaderType() {
64+
return type;
65+
}
66+
67+
@Override
68+
public @Nullable Pair<ResourceLocation, BlockPos> getLocation() {
69+
return Pair.of(getLevel().dimension().location(), getBlockPos());
70+
}
71+
4972
public void updateAttachedStation(StationBlockEntity be) {
5073
if (attachedStation != null) {
5174
if (attachedStation.getStation() instanceof CPLGlobalStation station) {
5275
station.getLoader().removeAttachment(getBlockPos());
5376
}
77+
} else {
78+
removeFromManager();
5479
}
5580
attachedStation = be;
5681
if (attachedStation != null) {
@@ -59,6 +84,8 @@ public void updateAttachedStation(StationBlockEntity be) {
5984
} else {
6085
deferredEdgePoint = true; // The GlobalStation is only created in the next tick after the station block is placed
6186
}
87+
} else {
88+
addToManager();
6289
}
6390
}
6491

@@ -73,6 +100,8 @@ public void initialize() {
73100
BlockEntity be = getLevel().getBlockEntity(getBlockPos().relative(getBlockState().getValue(FACING).getOpposite()));
74101
if (!(be instanceof StationBlockEntity sbe)) return;
75102
updateAttachedStation(sbe);
103+
} else {
104+
addToManager();
76105
}
77106
}
78107

@@ -162,6 +191,7 @@ public void destroy() {
162191
if (server)
163192
unforceAllChunks(level.getServer(), getBlockPos(), forcedChunks);
164193
updateAttachedStation(null);
194+
removeFromManager();
165195
}
166196

167197
@Override
@@ -171,6 +201,7 @@ public void remove() {
171201
if (server)
172202
unforceAllChunks(level.getServer(), getBlockPos(), forcedChunks);
173203
updateAttachedStation(null);
204+
removeFromManager();
174205
}
175206

176207
@Override

src/main/java/com/hlysine/create_power_loader/content/ChunkLoadManager.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ public class ChunkLoadManager {
3232
*/
3333
public static Level tickLevel;
3434

35+
public static final Map<LoaderMode, WeakCollection<ChunkLoader>> allLoaders = new HashMap<>();
36+
37+
public static void addLoader(LoaderMode mode, ChunkLoader loader) {
38+
allLoaders.computeIfAbsent(mode, $ -> new WeakCollection<>()).add(loader);
39+
}
40+
41+
public static void removeLoader(LoaderMode mode, ChunkLoader loader) {
42+
allLoaders.computeIfAbsent(mode, $ -> new WeakCollection<>()).remove(loader);
43+
}
44+
3545
public static void onServerWorldTick(TickEvent.LevelTickEvent event) {
3646
if (event.phase == TickEvent.Phase.END)
3747
return;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.hlysine.create_power_loader.content;
2+
3+
import com.hlysine.create_power_loader.content.ChunkLoadManager.LoadedChunkPos;
4+
import com.simibubi.create.foundation.utility.Pair;
5+
import net.minecraft.core.BlockPos;
6+
import net.minecraft.resources.ResourceLocation;
7+
import org.jetbrains.annotations.NotNull;
8+
import org.jetbrains.annotations.Nullable;
9+
10+
import java.util.Set;
11+
12+
public interface ChunkLoader {
13+
@NotNull
14+
Set<LoadedChunkPos> getForcedChunks();
15+
16+
LoaderMode getLoaderMode();
17+
18+
LoaderType getLoaderType();
19+
20+
@Nullable
21+
Pair<ResourceLocation, BlockPos> getLocation();
22+
23+
default void addToManager() {
24+
ChunkLoadManager.addLoader(getLoaderMode(), this);
25+
}
26+
27+
default void removeFromManager() {
28+
ChunkLoadManager.removeLoader(getLoaderMode(), this);
29+
}
30+
}

0 commit comments

Comments
 (0)