Skip to content

Commit fb3a3bd

Browse files
cev-apixpple
andauthored
Minimap (xpple#112)
* Minimap * Clean up + simplify * Add dimension change detection * Fix waypoint label orientation * Fix rotation bugs * Make minimap move smoothly --------- Co-authored-by: Frederik van der Els <[email protected]>
1 parent 704c174 commit fb3a3bd

File tree

9 files changed

+600
-223
lines changed

9 files changed

+600
-223
lines changed

src/main/java/dev/xpple/seedmapper/SeedMapper.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import dev.xpple.seedmapper.command.commands.DiscordCommand;
1313
import dev.xpple.seedmapper.command.commands.HighlightCommand;
1414
import dev.xpple.seedmapper.command.commands.LocateCommand;
15+
import dev.xpple.seedmapper.command.commands.MinimapCommand;
1516
import dev.xpple.seedmapper.command.commands.SampleCommand;
1617
import dev.xpple.seedmapper.command.commands.SeedMapCommand;
1718
import dev.xpple.seedmapper.command.commands.SourceCommand;
@@ -22,6 +23,7 @@
2223
import dev.xpple.seedmapper.config.SeedResolutionAdapter;
2324
import dev.xpple.seedmapper.render.RenderManager;
2425
import dev.xpple.seedmapper.seedmap.MapFeature;
26+
import dev.xpple.seedmapper.seedmap.MinimapManager;
2527
import dev.xpple.seedmapper.util.SeedDatabaseHelper;
2628
import dev.xpple.seedmapper.util.SeedIdentifier;
2729
import dev.xpple.simplewaypoints.api.SimpleWaypointsAPI;
@@ -80,15 +82,20 @@ public void onInitializeClient() {
8082

8183
SeedDatabaseHelper.fetchSeeds();
8284

83-
KeyMapping keyMapping = KeyBindingHelper.registerKeyBinding(new KeyMapping("key.seedMap", InputConstants.KEY_M, KeyMapping.Category.GAMEPLAY));
85+
KeyMapping seedMapKeyMapping = KeyBindingHelper.registerKeyBinding(new KeyMapping("key.seedMap", InputConstants.KEY_M, KeyMapping.Category.GAMEPLAY));
86+
KeyMapping minimapKeyMapping = KeyBindingHelper.registerKeyBinding(new KeyMapping("key.minimap", InputConstants.KEY_COMMA, KeyMapping.Category.GAMEPLAY));
8487
ClientTickEvents.END_CLIENT_TICK.register(minecraft -> {
85-
while (keyMapping.consumeClick()) {
88+
while (seedMapKeyMapping.consumeClick()) {
8689
minecraft.player.connection.sendCommand("sm:seedmap");
8790
}
91+
while (minimapKeyMapping.consumeClick()) {
92+
minecraft.player.connection.sendCommand("sm:minimap");
93+
}
8894
});
8995

9096
ClientCommandRegistrationCallback.EVENT.register(SeedMapper::registerCommands);
9197
RenderManager.registerEvents();
98+
MinimapManager.registerHudElement();
9299
}
93100

94101
private static void registerCommands(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandBuildContext context) {
@@ -102,5 +109,6 @@ private static void registerCommands(CommandDispatcher<FabricClientCommandSource
102109
SeedMapCommand.register(dispatcher);
103110
DiscordCommand.register(dispatcher);
104111
SampleCommand.register(dispatcher);
112+
MinimapCommand.register(dispatcher);
105113
}
106114
}

src/main/java/dev/xpple/seedmapper/command/CustomClientCommandSource.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import net.minecraft.network.chat.Component;
2222
import net.minecraft.server.permissions.PermissionSet;
2323
import net.minecraft.world.entity.Entity;
24+
import net.minecraft.world.level.dimension.DimensionType;
2425
import net.minecraft.world.phys.Vec2;
2526
import net.minecraft.world.phys.Vec3;
2627
import org.jspecify.annotations.Nullable;
@@ -155,11 +156,7 @@ public int getDimension() throws CommandSyntaxException {
155156
return DimensionArgument.dimension().parse(new StringReader(this.getWorld().dimension().identifier().getPath()));
156157
} catch (CommandSyntaxException _) {
157158
}
158-
return switch (this.getWorld().dimensionType().skybox()) {
159-
case NONE -> Cubiomes.DIM_NETHER();
160-
case OVERWORLD -> Cubiomes.DIM_OVERWORLD();
161-
case END -> Cubiomes.DIM_END();
162-
};
159+
return inferDimension(this.getWorld().dimensionType());
163160
}
164161

165162
public int getVersion() throws CommandSyntaxException {
@@ -180,4 +177,12 @@ public int getGeneratorFlags() throws CommandSyntaxException {
180177
}
181178
return this.getSeed().getSecond().generatorFlags();
182179
}
180+
181+
public static int inferDimension(DimensionType dimensionType) {
182+
return switch (dimensionType.skybox()) {
183+
case NONE -> Cubiomes.DIM_NETHER();
184+
case OVERWORLD -> Cubiomes.DIM_OVERWORLD();
185+
case END -> Cubiomes.DIM_END();
186+
};
187+
}
183188
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package dev.xpple.seedmapper.command.commands;
2+
3+
import com.mojang.brigadier.CommandDispatcher;
4+
import com.mojang.brigadier.exceptions.CommandSyntaxException;
5+
import dev.xpple.seedmapper.command.CustomClientCommandSource;
6+
import dev.xpple.seedmapper.seedmap.MinimapManager;
7+
import dev.xpple.seedmapper.util.SeedIdentifier;
8+
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
9+
10+
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*;
11+
12+
public class MinimapCommand {
13+
14+
public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
15+
dispatcher.register(literal("sm:minimap")
16+
.executes(ctx -> toggle(CustomClientCommandSource.of(ctx.getSource())))
17+
.then(literal("show")
18+
.executes(ctx -> toggle(CustomClientCommandSource.of(ctx.getSource()), true)))
19+
.then(literal("hide")
20+
.executes(ctx -> toggle(CustomClientCommandSource.of(ctx.getSource()), false))));
21+
}
22+
23+
private static int toggle(CustomClientCommandSource source) throws CommandSyntaxException {
24+
return toggle(source, !MinimapManager.isVisible());
25+
}
26+
27+
private static int toggle(CustomClientCommandSource source, boolean show) throws CommandSyntaxException {
28+
if (show) {
29+
SeedIdentifier seed = source.getSeed().getSecond();
30+
int dimension = source.getDimension();
31+
int version = source.getVersion();
32+
int generatorFlags = source.getGeneratorFlags();
33+
MinimapManager.show(seed.seed(), dimension, version, generatorFlags);
34+
return 1;
35+
} else {
36+
MinimapManager.hide();
37+
return 0;
38+
}
39+
}
40+
}

src/main/java/dev/xpple/seedmapper/config/Configs.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,37 @@ private static void setPixelsPerBiome(int pixelsPerBiome) {
8282
PixelsPerBiome = Math.clamp(pixelsPerBiome, SeedMapScreen.MIN_PIXELS_PER_BIOME, SeedMapScreen.MAX_PIXELS_PER_BIOME);
8383
}
8484

85+
@Config(setter = @Config.Setter("setMinimapOffsetX"))
86+
public static int MinimapOffsetX = 4;
87+
88+
private static void setMinimapOffsetX(int offsetX) {
89+
MinimapOffsetX = Math.max(0, offsetX);
90+
}
91+
92+
@Config(setter = @Config.Setter("setMinimapOffsetY"))
93+
public static int MinimapOffsetY = 4;
94+
95+
private static void setMinimapOffsetY(int offsetY) {
96+
MinimapOffsetY = Math.max(0, offsetY);
97+
}
98+
99+
@Config(setter = @Config.Setter("setMinimapWidth"))
100+
public static int MinimapWidth = 205;
101+
102+
private static void setMinimapWidth(int width) {
103+
MinimapWidth = Math.clamp(width, 64, 512);
104+
}
105+
106+
@Config(setter = @Config.Setter("setMinimapHeight"))
107+
public static int MinimapHeight = 205;
108+
109+
private static void setMinimapHeight(int height) {
110+
MinimapHeight = Math.clamp(height, 64, 512);
111+
}
112+
113+
@Config
114+
public static boolean RotateMinimap = true;
115+
85116
@Config(chatRepresentation = "listToggledFeatures")
86117
public static EnumSet<MapFeature> ToggledFeatures = Util.make(() -> {
87118
EnumSet<MapFeature> toggledFeatures = EnumSet.allOf(MapFeature.class);

src/main/java/dev/xpple/seedmapper/mixin/ClientPacketListenerMixin.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package dev.xpple.seedmapper.mixin;
22

3+
import dev.xpple.seedmapper.command.CustomClientCommandSource;
34
import dev.xpple.seedmapper.render.RenderManager;
5+
import dev.xpple.seedmapper.seedmap.MinimapManager;
46
import net.minecraft.client.multiplayer.ClientPacketListener;
57
import net.minecraft.network.protocol.game.ClientboundLoginPacket;
68
import net.minecraft.network.protocol.game.ClientboundRespawnPacket;
@@ -11,13 +13,16 @@
1113

1214
@Mixin(ClientPacketListener.class)
1315
public class ClientPacketListenerMixin {
14-
@Inject(method = "handleLogin", at = @At("HEAD"))
16+
@Inject(method = "handleLogin", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/network/PacketProcessor;)V", shift = At.Shift.AFTER))
1517
private void onHandleLogin(ClientboundLoginPacket packet, CallbackInfo ci) {
1618
RenderManager.clear();
1719
}
1820

19-
@Inject(method = "handleRespawn", at = @At("HEAD"))
21+
@Inject(method = "handleRespawn", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/network/PacketProcessor;)V", shift = At.Shift.AFTER))
2022
private void onHandleRespawn(ClientboundRespawnPacket packet, CallbackInfo ci) {
2123
RenderManager.clear();
24+
25+
int dimension = CustomClientCommandSource.inferDimension(packet.commonPlayerSpawnInfo().dimensionType().value());
26+
MinimapManager.updateDimension(dimension);
2227
}
2328
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package dev.xpple.seedmapper.seedmap;
2+
3+
import dev.xpple.seedmapper.SeedMapper;
4+
import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElementRegistry;
5+
import net.minecraft.client.DeltaTracker;
6+
import net.minecraft.client.Minecraft;
7+
import net.minecraft.client.gui.GuiGraphics;
8+
import net.minecraft.client.player.LocalPlayer;
9+
import net.minecraft.resources.Identifier;
10+
import org.jetbrains.annotations.Nullable;
11+
12+
public final class MinimapManager {
13+
private MinimapManager() {
14+
}
15+
16+
private static @Nullable MinimapScreen minimapScreen;
17+
18+
public static void registerHudElement() {
19+
HudElementRegistry.addLast(Identifier.fromNamespaceAndPath(SeedMapper.MOD_ID, "minimap"), MinimapManager::render);
20+
}
21+
22+
public static boolean isVisible() {
23+
return minimapScreen != null;
24+
}
25+
26+
public static void show(long seed, int dimension, int version, int generatorFlags) {
27+
hide();
28+
minimapScreen = new MinimapScreen(seed, dimension, version, generatorFlags);
29+
}
30+
31+
public static void hide() {
32+
if (minimapScreen != null) {
33+
if (minimapScreen.isInitialized()) {
34+
minimapScreen.onClose();
35+
}
36+
minimapScreen = null;
37+
}
38+
}
39+
40+
public static void updateDimension(int dimension) {
41+
if (minimapScreen == null) {
42+
return;
43+
}
44+
if (minimapScreen.getDimension() == dimension) {
45+
return;
46+
}
47+
show(minimapScreen.getSeed(), dimension, minimapScreen.getVersion(), minimapScreen.getGeneratorFlags());
48+
}
49+
50+
private static void render(GuiGraphics guiGraphics, DeltaTracker deltaTracker) {
51+
if (minimapScreen == null) {
52+
return;
53+
}
54+
Minecraft minecraft = Minecraft.getInstance();
55+
LocalPlayer player = minecraft.player;
56+
minimapScreen.update(player.position(), player.getRotationVector());
57+
minimapScreen.renderToHud(guiGraphics, deltaTracker.getGameTimeDeltaTicks());
58+
}
59+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package dev.xpple.seedmapper.seedmap;
2+
3+
import dev.xpple.seedmapper.config.Configs;
4+
import dev.xpple.seedmapper.util.QuartPos2f;
5+
import net.minecraft.client.Minecraft;
6+
import net.minecraft.client.gui.GuiGraphics;
7+
import net.minecraft.core.BlockPos;
8+
import net.minecraft.util.Mth;
9+
import net.minecraft.world.phys.Vec2;
10+
import net.minecraft.world.phys.Vec3;
11+
12+
// TODO refactor so that the minimap is not a `Screen`; it has no reason to be one
13+
public class MinimapScreen extends SeedMapScreen {
14+
15+
private boolean initialized = false;
16+
private int lastWidth = -1;
17+
private int lastHeight = -1;
18+
19+
public MinimapScreen(long seed, int dimension, int version, int generatorFlags) {
20+
super(seed, dimension, version, generatorFlags, Minecraft.getInstance().player.blockPosition(), Minecraft.getInstance().player.getRotationVector());
21+
}
22+
23+
public void initForOverlay(int width, int height) {
24+
if (this.initialized && width == this.lastWidth && height == this.lastHeight) {
25+
return;
26+
}
27+
this.init(width, height);
28+
this.initialized = true;
29+
this.lastWidth = width;
30+
this.lastHeight = height;
31+
}
32+
33+
public void renderToHud(GuiGraphics guiGraphics, float partialTick) {
34+
boolean rotateMinimap = Configs.RotateMinimap;
35+
int contentWidth = Configs.MinimapWidth;
36+
int contentHeight = Configs.MinimapHeight;
37+
int renderContentWidth = contentWidth;
38+
int renderContentHeight = contentHeight;
39+
if (rotateMinimap) {
40+
int diagonal = Mth.ceil(Math.sqrt(contentWidth * contentWidth + contentHeight * contentHeight));
41+
renderContentWidth = diagonal;
42+
renderContentHeight = diagonal;
43+
}
44+
// ensures super.seedMapWidth == renderContentWidth
45+
int renderWidth = renderContentWidth + 2 * this.horizontalPadding();
46+
// ensures super.seedMapHeight == renderContentHeight
47+
int renderHeight = renderContentHeight + 2 * this.verticalPadding();
48+
49+
this.initForOverlay(renderWidth, renderHeight);
50+
51+
guiGraphics.enableScissor(this.horizontalPadding(), this.verticalPadding(), this.horizontalPadding() + contentWidth, this.verticalPadding() + contentHeight);
52+
53+
var pose = guiGraphics.pose();
54+
pose.pushMatrix();
55+
if (rotateMinimap) {
56+
pose.translate(-this.centerX + (float) (this.horizontalPadding() + contentWidth / 2), -this.centerY + (float) (this.verticalPadding() + contentHeight / 2));
57+
pose.translate(this.centerX, this.centerY);
58+
pose.rotate((float) (-Math.toRadians(this.getPlayerRotation().y) + Math.PI));
59+
pose.translate(-this.centerX, -this.centerY);
60+
}
61+
this.renderBiomes(guiGraphics, Integer.MIN_VALUE, Integer.MIN_VALUE, partialTick);
62+
63+
this.renderFeatures(guiGraphics, Integer.MIN_VALUE, Integer.MIN_VALUE, partialTick);
64+
pose.popMatrix();
65+
66+
if (Configs.RotateMinimap) {
67+
this.drawCenterCross(guiGraphics, this.horizontalPadding() + contentWidth / 2, this.verticalPadding() + contentHeight / 2);
68+
}
69+
70+
guiGraphics.disableScissor();
71+
}
72+
73+
private void drawCenterCross(GuiGraphics guiGraphics, int centerX, int centerY) {
74+
int crossHalf = 3;
75+
int color = 0xFF_FFFFFF;
76+
guiGraphics.fill(centerX - crossHalf, centerY, centerX + crossHalf + 1, centerY + 1, color);
77+
guiGraphics.fill(centerX, centerY - crossHalf, centerX + 1, centerY + crossHalf + 1, color);
78+
}
79+
80+
public void update(Vec3 pos, Vec2 playerRotation) {
81+
this.updatePlayerPosition(BlockPos.containing(pos));
82+
this.updatePlayerRotation(playerRotation);
83+
this.moveCenter(QuartPos2f.fromVec3(pos));
84+
}
85+
86+
public boolean isInitialized() {
87+
return this.initialized;
88+
}
89+
90+
@Override
91+
protected void drawPlayerIndicator(GuiGraphics guiGraphics) {
92+
if (!Configs.RotateMinimap) {
93+
this.drawDirectionArrow(guiGraphics, this.centerX - 10, this.centerY - 10);
94+
}
95+
}
96+
97+
@Override
98+
protected boolean isMinimap() {
99+
return true;
100+
}
101+
102+
@Override
103+
protected int horizontalPadding() {
104+
return Configs.MinimapOffsetX;
105+
}
106+
107+
@Override
108+
protected int verticalPadding() {
109+
return Configs.MinimapOffsetY;
110+
}
111+
}

0 commit comments

Comments
 (0)