diff --git a/run/config/SignMeUp/Waypoints.json b/run/config/SignMeUp/Waypoints.json index 06a1e15..58ca93b 100644 --- a/run/config/SignMeUp/Waypoints.json +++ b/run/config/SignMeUp/Waypoints.json @@ -5,28 +5,36 @@ "description": "没什么好描述的", "x": -211, "y": 95, - "z": 66 + "z": 66, + "rx": 0.0, + "ry": 0.0 }, { "name": "主展馆", "description": "", "x": -127, "y": 96, - "z": 40 + "z": 40, + "rx": 0.0, + "ry": 0.0 }, { "name": "111", "description": "222", "x": -137, "y": 97, - "z": 24 + "z": 24, + "rx": 0.0, + "ry": 0.0 }, { "name": "新路径点", "description": "描述文本", "x": -127, "y": 70, - "z": 40 + "z": 40, + "rx": 0.0, + "ry": 0.0 } ] } \ No newline at end of file diff --git a/src/main/java/org/teacon/signmeup/api/MiniMap.java b/src/main/java/org/teacon/signmeup/api/MiniMap.java new file mode 100644 index 0000000..db1ef84 --- /dev/null +++ b/src/main/java/org/teacon/signmeup/api/MiniMap.java @@ -0,0 +1,38 @@ +package org.teacon.signmeup.api; + +import net.neoforged.fml.loading.FMLLoader; +import org.teacon.signmeup.hud.MiniMapAPI; + +public interface MiniMap { + static MiniMap getInstance() { + if (!FMLLoader.getDist().isClient()) { + throw new IllegalStateException("This API is only available on Client."); + } + return MiniMapAPI.INSTANCE; + } + + /** + *

Set the visibility of MiniMap.

+ * + *

Mods should make sure the minimap is hided in a minimize time + * After the expected time range, mods must invoke setMiniMapVisibility(modID, true) + * to show the minimap normally.

+ * + *

For the first time a mod hide the minimap, a message will be shown + * to make players clear that the minimap is hided. And it will also be shown + * on the map screen.

+ * + *

There's no need for different mods to check whether the minimap is visibility. + * Minimap won't be shown if any mod set it invisible.

+ * + *

This method can be invoked from any threads.

+ * + * @param modID the caller's mod id. + * @param visibility whether the MiniMap should be visible. + * @throws IllegalArgumentException if target mod (specific by modID) is not loaded + * @throws IllegalStateException if the same mod wants to hide / show the minimap, but + * it's already hided / shown + * @throws NullPointerException if modID is null + */ + void setMiniMapVisibility(String modID, boolean visibility); +} diff --git a/src/main/java/org/teacon/signmeup/command/OpCommands.java b/src/main/java/org/teacon/signmeup/command/OpCommands.java index 35b4842..091bca4 100644 --- a/src/main/java/org/teacon/signmeup/command/OpCommands.java +++ b/src/main/java/org/teacon/signmeup/command/OpCommands.java @@ -9,8 +9,10 @@ import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.arguments.coordinates.RotationArgument; import net.minecraft.commands.arguments.coordinates.Vec3Argument; import net.minecraft.commands.arguments.coordinates.WorldCoordinates; +import net.minecraft.core.Rotations; import net.minecraft.network.chat.Component; import org.teacon.signmeup.config.Waypoints; import org.teacon.signmeup.network.RemoveWaypointPacket; @@ -29,10 +31,12 @@ public static void waypoints(CommandDispatcher dispatcher) { .requires(commandSourceStack -> commandSourceStack.hasPermission(2)) .then(Commands.literal("set") .then(Commands.argument("pos", Vec3Argument.vec3(false)) - .then(Commands.argument("name", StringArgumentType.string()) - .executes(OpCommands::setWaypoint) - .then(Commands.argument("description", StringArgumentType.string()) + .then(Commands.argument("rotation", RotationArgument.rotation()) + .then(Commands.argument("name", StringArgumentType.string()) .executes(OpCommands::setWaypoint) + .then(Commands.argument("description", StringArgumentType.string()) + .executes(OpCommands::setWaypoint) + ) ) ) ) @@ -51,7 +55,8 @@ private static int setWaypoint(CommandContext context) { var pos = context.getArgument("pos", WorldCoordinates.class).getBlockPos(context.getSource()); var name = context.getArgument("name", String.class); var description = context.getArgument("description", String.class); - var waypoint = new Waypoints.WayPoint(name, description, pos.getX(), pos.getY(), pos.getZ()); + var rotation = context.getArgument("rotation", WorldCoordinates.class).getRotation(context.getSource()); + var waypoint = new Waypoints.WayPoint(name, description, pos.getX(), pos.getY(), pos.getZ(), rotation.x, rotation.y); ConfigHelper.getConfigWrite(Waypoints.class, waypoints -> { waypoints.waypoints.stream().filter(w -> w.name.equals(waypoint.name)).findFirst().ifPresentOrElse( @@ -59,7 +64,7 @@ private static int setWaypoint(CommandContext context) { () -> { context.getSource().sendSuccess(() -> Component.literal("Added " + waypoint), true); waypoints.waypoints.add(waypoint); - NetworkHelper.sendToAllPlayers(new SetWaypointPacket(name, description, pos)); + NetworkHelper.sendToAllPlayers(new SetWaypointPacket(name, description, pos, new Rotations(rotation.x, 0, rotation.y))); } ); }); diff --git a/src/main/java/org/teacon/signmeup/config/Waypoints.java b/src/main/java/org/teacon/signmeup/config/Waypoints.java index 93251a4..1b83551 100644 --- a/src/main/java/org/teacon/signmeup/config/Waypoints.java +++ b/src/main/java/org/teacon/signmeup/config/Waypoints.java @@ -19,29 +19,24 @@ public String getChildDirName() { public static class WayPoint { public String name, description; public int x, y, z; + public float rx, ry; - public WayPoint(String name, String description, int x, int y, int z) { + public WayPoint(String name, String description, int x, int y, int z, float rx, float ry) { this.name = name; this.description = description; this.x = x; this.y = y; this.z = z; - } - - public WayPoint(String name, String description, BlockPos pos) { - this.name = name; - this.description = description; - this.x = pos.getX(); - this.y = pos.getY(); - this.z = pos.getZ(); + this.rx = rx; + this.ry = ry; } public WayPoint() { - this("", "", 0, 0, 0); + this("", "", 0, 0, 0, 0, 0); } public static WayPoint dumbWayPoint(String name) { - return new WayPoint(name, "", 0, 0, 0); + return new WayPoint(name, "", 0, 0, 0, 0, 0); } @Override @@ -59,7 +54,7 @@ public int hashCode() { @Override public String toString() { - return "[WayPoint name=" + name + ", description=" + description + ", <" + x + ", " + y + ", " + z + ">]"; + return "[WayPoint name=" + name + ", description=" + description + ", <" + x + ", " + y + ", " + z + ">, rotation=<" + rx + ", " + ry + ">]"; } } } diff --git a/src/main/java/org/teacon/signmeup/gui/map/MapScreen.java b/src/main/java/org/teacon/signmeup/gui/map/MapScreen.java index f797d3b..0e02262 100644 --- a/src/main/java/org/teacon/signmeup/gui/map/MapScreen.java +++ b/src/main/java/org/teacon/signmeup/gui/map/MapScreen.java @@ -3,6 +3,7 @@ import cn.ussshenzhou.t88.gui.advanced.THoverSensitiveImageButton; import cn.ussshenzhou.t88.gui.screen.TScreen; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; import net.neoforged.neoforge.client.ClientHooks; @@ -10,6 +11,9 @@ import org.teacon.signmeup.gui.map.bp.CommandsButtonPanel; import org.teacon.signmeup.gui.map.bp.WayPointsButtonPanel; import org.teacon.signmeup.gui.settings.SettingsScreen; +import org.teacon.signmeup.hud.MiniMapAPI; + +import java.util.Set; /** * @author USS_Shenzhou @@ -48,6 +52,15 @@ public void render(GuiGraphics graphics, int pMouseX, int pMouseY, float pPartia super.render(graphics, pMouseX, pMouseY, pPartialTick); wayPointsButtonPanel.highlight(mapPanel.getHighlightWaypoints(pMouseX, pMouseY)); + + String hider = MiniMapAPI.INSTANCE.getHiderString(); + if (hider != null) { + Font font = Minecraft.getInstance().font; + graphics.drawCenteredString( + font, Component.translatable("hud.sign_up.minimap.hide", hider), + graphics.guiWidth() / 2, graphics.guiHeight() - font.lineHeight * 2, 0xFFE8DDCD + ); + } } public String getHighlightWaypoints() { diff --git a/src/main/java/org/teacon/signmeup/hud/MiniMapAPI.java b/src/main/java/org/teacon/signmeup/hud/MiniMapAPI.java new file mode 100644 index 0000000..07ce548 --- /dev/null +++ b/src/main/java/org/teacon/signmeup/hud/MiniMapAPI.java @@ -0,0 +1,64 @@ +package org.teacon.signmeup.hud; + +import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.fml.loading.moddiscovery.ModInfo; +import org.teacon.signmeup.api.MiniMap; + +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Collectors; + +public final class MiniMapAPI implements MiniMap { + public static final MiniMapAPI INSTANCE = new MiniMapAPI(); + + private final Map ID_NAME_MAP = FMLLoader.getLoadingModList().getMods().stream().collect(Collectors.toMap( + ModInfo::getModId, ModInfo::getDisplayName + )); + + private final CopyOnWriteArrayList hider = new CopyOnWriteArrayList<>(); + + @Override + public void setMiniMapVisibility(String modID, boolean visibility) { + Objects.requireNonNull(modID, "Argument modID cannot be null"); + if (!ID_NAME_MAP.containsKey(modID)) { + throw new IllegalArgumentException("No such mod: " + modID); + } + + if (!(visibility ? hider.remove(modID) : hider.addIfAbsent(modID))) { + throw new IllegalStateException("The minimap has already been " + (!visibility ? "hided" : "visible") + " by " + modID); + } + } + + public boolean visible() { + return hider.isEmpty(); + } + + public String getHiderString() { + if (hider.isEmpty()) { + return null; + } + + Iterator iterator = hider.iterator(); + if (!iterator.hasNext()) { + return null; + } + String m1 = iterator.next(); + if (!iterator.hasNext()) { + return m1; + } + String m2 = iterator.next(); + if (!iterator.hasNext()) { + return m1 + ", " + m2; + } + int size = hider.size() - 2; + if (size > 1) { + return m1 + ", " + m2 + ", ... (" + size + " more mods)"; + } else if (size == 1) { + return m1 + ", " + m2 + ", ... (1 more mod)"; + }else { + return m1 + ", " + m2; + } + } +} diff --git a/src/main/java/org/teacon/signmeup/hud/MiniMapPanel.java b/src/main/java/org/teacon/signmeup/hud/MiniMapPanel.java index 6e5fa56..977aaf2 100644 --- a/src/main/java/org/teacon/signmeup/hud/MiniMapPanel.java +++ b/src/main/java/org/teacon/signmeup/hud/MiniMapPanel.java @@ -10,6 +10,8 @@ import org.teacon.signmeup.SignMeUp; import org.teacon.signmeup.config.MiniMap; +import java.util.Set; + import static net.minecraft.util.Mth.PI; /** @@ -45,8 +47,8 @@ public MiniMapPanel() { @Override public void tickT() { super.tickT(); - var debugOn = Minecraft.getInstance().getDebugOverlay().showDebugScreen(); - children.forEach(childTComponent -> childTComponent.setVisibleT(!debugOn)); + var visible = !Minecraft.getInstance().getDebugOverlay().showDebugScreen() && MiniMapAPI.INSTANCE.visible(); + children.forEach(childTComponent -> childTComponent.setVisibleT(visible)); } @Override diff --git a/src/main/java/org/teacon/signmeup/network/SetWaypointPacket.java b/src/main/java/org/teacon/signmeup/network/SetWaypointPacket.java index 9871947..0cde74d 100644 --- a/src/main/java/org/teacon/signmeup/network/SetWaypointPacket.java +++ b/src/main/java/org/teacon/signmeup/network/SetWaypointPacket.java @@ -6,6 +6,7 @@ import cn.ussshenzhou.t88.network.annotation.NetPacket; import io.netty.buffer.ByteBuf; import net.minecraft.core.BlockPos; +import net.minecraft.core.Rotations; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.neoforged.neoforge.network.handling.IPayloadContext; @@ -16,7 +17,7 @@ * @author USS_Shenzhou */ @NetPacket(modid = SignMeUp.MODID) -public record SetWaypointPacket(String name, String description, BlockPos pos) { +public record SetWaypointPacket(String name, String description, BlockPos pos, Rotations rotation) { @Codec public static final StreamCodec STREAM_CODEC = StreamCodec.composite( @@ -26,12 +27,14 @@ public record SetWaypointPacket(String name, String description, BlockPos pos) { SetWaypointPacket::description, BlockPos.STREAM_CODEC, SetWaypointPacket::pos, + Rotations.STREAM_CODEC, + SetWaypointPacket::rotation, SetWaypointPacket::new ); @ClientHandler public void clientHandler(IPayloadContext context){ - var waypoint = new Waypoints.WayPoint(name, description, pos.getX(), pos.getY(), pos.getZ()); + var waypoint = new Waypoints.WayPoint(name, description, pos.getX(), pos.getY(), pos.getZ(), rotation.getX(), rotation.getZ()); ConfigHelper.getConfigWrite(Waypoints.class, waypoints -> waypoints.waypoints.add(waypoint)); } } diff --git a/src/main/java/org/teacon/signmeup/network/TeleportToWayPointPacket.java b/src/main/java/org/teacon/signmeup/network/TeleportToWayPointPacket.java index 9548389..67ccc53 100644 --- a/src/main/java/org/teacon/signmeup/network/TeleportToWayPointPacket.java +++ b/src/main/java/org/teacon/signmeup/network/TeleportToWayPointPacket.java @@ -5,13 +5,16 @@ import cn.ussshenzhou.t88.network.annotation.NetPacket; import cn.ussshenzhou.t88.network.annotation.ServerHandler; import io.netty.buffer.ByteBuf; +import net.minecraft.network.chat.Component; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.server.level.ServerLevel; import net.neoforged.neoforge.network.handling.IPayloadContext; import org.teacon.signmeup.SignMeUp; import org.teacon.signmeup.config.Waypoints; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; /** @@ -32,9 +35,19 @@ public record TeleportToWayPointPacket(String name) { @ServerHandler public void serverHandler(IPayloadContext context) { context.enqueueWork(() -> { - var waypoints = WAYPOINTS.get(name); var player = context.player(); - player.teleportTo(waypoints.x, waypoints.y, waypoints.z); + if (!(player.level() instanceof ServerLevel level)) { + return; + } + + for (Waypoints.WayPoint waypoint : ConfigHelper.getConfigRead(Waypoints.class).waypoints) { + if (waypoint.name.equals(name)) { + player.teleportTo(level, waypoint.x, waypoint.y, waypoint.z, Set.of(), waypoint.rx, waypoint.ry); + return; + } + } + + player.sendSystemMessage(Component.literal("Open a new SMU map! The waypoint data is out of date.")); }); } } diff --git a/src/main/resources/assets/sign_up/lang/en_us.json b/src/main/resources/assets/sign_up/lang/en_us.json index 380d5c3..17b67de 100644 --- a/src/main/resources/assets/sign_up/lang/en_us.json +++ b/src/main/resources/assets/sign_up/lang/en_us.json @@ -3,11 +3,13 @@ "key.sign_up.open_map": "Show Map", "key.sign_up.open_new_map": "New Map Instance", - "gui.sign_up.minimap": " Minimap", + "gui.sign_up.minimap": "Minimap", "gui.sign_up.minimap.minimap": "Minimap", "gui.sign_up.minimap.minimap.on": "On", "gui.sign_up.minimap.minimap.off": "Off", "gui.sign_up.minimap.range": "Minimap Range", "gui.sign_up.minimap.rotate": "Follow Player Rotating", - "gui.sign_up.minimap.ssaa": "SSAA Ratio" + "gui.sign_up.minimap.ssaa": "SSAA Ratio", + + "hud.sign_up.minimap.hide": "Minimap is hided by: %s" } \ No newline at end of file diff --git a/src/main/resources/assets/sign_up/lang/zh_cn.json b/src/main/resources/assets/sign_up/lang/zh_cn.json index 0e0dcd2..d6c8df4 100644 --- a/src/main/resources/assets/sign_up/lang/zh_cn.json +++ b/src/main/resources/assets/sign_up/lang/zh_cn.json @@ -1,3 +1,15 @@ { + "key.categories.sign_up": "Sign Me Up", + "key.sign_up.open_map": "打开地图", + "key.sign_up.open_new_map": "打开新地图", + "gui.sign_up.minimap": "小地图", + "gui.sign_up.minimap.minimap": "小地图", + "gui.sign_up.minimap.minimap.on": "开启", + "gui.sign_up.minimap.minimap.off": "关闭", + "gui.sign_up.minimap.range": "小地图渲染范围", + "gui.sign_up.minimap.rotate": "跟随玩家旋转", + "gui.sign_up.minimap.ssaa": "SSAA 角度", + + "hud.sign_up.minimap.hide": "小地图被以下模组隐藏:%s" } \ No newline at end of file