fix: move indicator farther for GpsModule and integrate GPS with chat sendloc

This commit is contained in:
iceBear67 committed 2026-07-21 15:58:13 +08:00
1 parent 576f2b733a
commit 1b2712c425
6 files changed
+79 -17

No files matched your search

+1 -6
View File
@@ -1,6 +1,6 @@
# 贡献指南
欢迎!如果你想提交投影文件,请前往[此处](https://github.com/saltedfishclub/sfcraft-schematics)
欢迎!如果你想提交投影文件,请前往[此处](https://github.com/saltedfishclub/sfcraft-schematics). 此文件为有经验的开发者提供,如果你是新手,请查看 [NEWBIE.md](./NEWBIE.md) 以了解如何开始。
## 贡献流程
首先,你需要新建一个有关修改内容的分支(如果你没有本仓库的修改权限,请自行 Fork),然后在里面提交代码,最后你就可以发起 Pull Request 并且等待我们审查你的代码了。
@@ -42,11 +42,6 @@ private MinecraftServerSupplier serverSupplier;
在 Mixin 代码中你可能需要拿一些在 Guice 内的对象,此时可以通过 `SFCraft.getInjector().getInstance(TYPE)` 得到对应的对象。
但是注意:如果你的 Mixin 代码是高频操作,请适当地做缓存
#### 使用自定义实体 API
参考 `SFEntityType#init`
自定义实体的写法大体与正常 mod 开发无异。
#### 使用 Room API
文档施工中
@@ -30,6 +30,8 @@ public class GameConfig {
public double toleranceDegrees = 12.0;
// 光标悬浮在视线前方的距离;放远一点航向偏差在画面上更明显,也更不容易糊在脸上
public double cursorDistance = 8.0;
// 鞘翅飞行时改用的光标距离:高速飞行下虚拟实体位置插值滞后,光标会"跟不上",拉远以抵消
public double flyingCursorDistance = 20.0;
public double arriveRadius = 4.0;
// 水平(X-Z)距离大于该值时进入远距离巡航:光标锁在眼睛所在水平面,只随左右转向(yaw)偏移、
// 忽略俯仰,这样用鞘翅高速飞行低头看地面时光标不会跟着往下沉;近于此值则恢复全向指示
@@ -8,6 +8,7 @@ import io.ib67.sfcraft.util.SFConsts;
import net.minecraft.network.chat.ChatDecorator;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.HoverEvent;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.effect.MobEffectInstance;
@@ -16,8 +17,6 @@ import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NonNull;
import java.net.URI;
public class ChatSendLocModule extends ServerModule implements ChatDecorator {
@Inject
SimpleMessageDecorator messageDecorator;
@@ -50,9 +49,12 @@ public class ChatSendLocModule extends ServerModule implements ChatDecorator {
.append(dimensionLabel(key))
.append(" ")
.withColor(Helper.fromRgb(63, 254, 254))
.withStyle(style -> style.withClickEvent(new ClickEvent.OpenUrl(URI.create(
"https://mc.sfclub.cc/map/?world=" + key.identifier().toString().replace(':', '_') + "&zoom=5&x=" + x + "&z=" + z
))));
// 点击运行 /gps 直接开始导航(并附带世界,不在该世界时会被 gps 拒绝),而不是打开网页地图
.withStyle(style -> style
.withClickEvent(new ClickEvent.RunCommand(
"/gps " + x + " " + y + " " + z + " " + key.identifier()))
.withHoverEvent(new HoverEvent.ShowText(
Component.translatable("message.sfcraft.chat.loc_navigate"))));
}
private static Component dimensionLabel(ResourceKey<Level> registryKey) {
@@ -5,6 +5,7 @@ import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import eu.pb4.polymer.virtualentity.api.ElementHolder;
import eu.pb4.polymer.virtualentity.api.attachment.ManualAttachment;
import eu.pb4.polymer.virtualentity.api.elements.SimpleEntityElement;
@@ -19,8 +20,10 @@ import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.DimensionArgument;
import net.minecraft.commands.arguments.coordinates.Coordinates;
import net.minecraft.commands.arguments.coordinates.Vec3Argument;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
@@ -33,6 +36,7 @@ import net.minecraft.world.entity.EntityTypes;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@@ -86,7 +90,27 @@ public class GpsModule extends ServerModule {
.then(LiteralArgumentBuilder.<CommandSourceStack>literal("stop")
.executes(this::stop))
.then(RequiredArgumentBuilder.<CommandSourceStack, Coordinates>argument("target", Vec3Argument.vec3())
.executes(this::start)));
.executes(this::start)
// 可选的目标世界:玩家不在该世界时拒绝导航,便于 .xyz 跨维度分享坐标的点击导航
.then(Commands.argument("world", DimensionArgument.dimension())
.executes(this::startInWorld))));
}
// /gps <target> <world>:先校验玩家是否处于指定世界,不匹配则拒绝导航
private int startInWorld(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
ServerLevel world = DimensionArgument.getDimension(context, "world");
ServerPlayer player = context.getSource().getPlayer();
if (player.level().dimension() != world.dimension()) {
var source = context.getSource();
source.sendFailure(Component.translatable("command.sfcraft.gps.wrong_world",
dimensionName(world.dimension())));
// 虽然无法导航,仍给一张目标世界的网页地图链接,方便玩家先看看目标在哪
Vec3 target = Vec3Argument.getVec3(context, "target");
source.sendSuccess(() -> previewLink(world.dimension(),
(int) Math.round(target.x), (int) Math.round(target.z)), false);
return 0;
}
return start(context);
}
private int start(CommandContext<CommandSourceStack> context) {
@@ -95,11 +119,36 @@ public class GpsModule extends ServerModule {
var old = sessions.remove(player.getUUID());
if (old != null) old.destroy();
sessions.put(player.getUUID(), new GpsSession(player, target));
context.getSource().sendSuccess(() -> Component.translatable("command.sfcraft.gps.started",
Math.round(target.x), Math.round(target.y), Math.round(target.z)), false);
var source = context.getSource();
long x = Math.round(target.x), y = Math.round(target.y), z = Math.round(target.z);
source.sendSuccess(() -> Component.translatable("command.sfcraft.gps.started", x, y, z), false);
// 出发前先给一张网页地图链接,允许玩家提前预览目标周边
source.sendSuccess(() -> previewLink(player.level().dimension(), (int) x, (int) z), false);
return 1;
}
// 可点击的网页地图预览链接
private static Component previewLink(ResourceKey<Level> world, int x, int z) {
String url = webMapUrl(world, x, z);
return Component.translatable("command.sfcraft.gps.preview")
.withStyle(style -> style
.withColor(Helper.fromRgb(63, 254, 254))
.withUnderlined(true)
.withClickEvent(new ClickEvent.OpenUrl(URI.create(url))));
}
private static String webMapUrl(ResourceKey<Level> world, int x, int z) {
return "https://mc.sfclub.cc/map/?world=" + world.identifier().toString().replace(':', '_')
+ "&zoom=5&x=" + x + "&z=" + z;
}
private static Component dimensionName(ResourceKey<Level> key) {
if (key == Level.OVERWORLD) return Component.translatable("message.sfcraft.dimension.name.overworld");
if (key == Level.NETHER) return Component.translatable("message.sfcraft.dimension.name.nether");
if (key == Level.END) return Component.translatable("message.sfcraft.dimension.name.end");
return Component.literal(key.identifier().toString());
}
private int stop(CommandContext<CommandSourceStack> context) {
var session = sessions.remove(context.getSource().getPlayer().getUUID());
if (session == null) {
@@ -165,6 +214,8 @@ public class GpsModule extends ServerModule {
}
void tick(ServerPlayer player, GameConfig.Gps gps) {
// 鞘翅飞行时把光标拉远:高速下虚拟实体位置插值滞后,近距离光标会"跟不上",拉远抵消
double cursorDistance = player.isFallFlying() ? gps.flyingCursorDistance : gps.cursorDistance;
Vec3 eye = player.getEyePosition();
double dx = target.x - eye.x;
double dz = target.z - eye.z;
@@ -179,9 +230,9 @@ public class GpsModule extends ServerModule {
double angle = Math.toDegrees(Math.acos(Mth.clamp(lookHoriz.dot(targetHoriz), -1.0, 1.0)));
Vec3 horiz = angle <= gps.toleranceDegrees ? lookHoriz : targetHoriz;
this.cursorPos = new Vec3(
eye.x + horiz.x * gps.cursorDistance,
eye.x + horiz.x * cursorDistance,
eye.y - CURSOR_HALF_HEIGHT,
eye.z + horiz.z * gps.cursorDistance);
eye.z + horiz.z * cursorDistance);
} else {
// 近距离:恢复全向(含俯仰)指示,便于精确对准竖直方向上的目标。
// 容差内粘在准星方向(画面上纹丝不动),超出则跳到真实目标方向提示玩家回正。
@@ -190,7 +241,7 @@ public class GpsModule extends ServerModule {
Vec3 look = player.getLookAngle();
double angle = Math.toDegrees(Math.acos(Mth.clamp(look.dot(targetDir), -1.0, 1.0)));
Vec3 direction = angle <= gps.toleranceDegrees ? look : targetDir;
double distance = Math.min(gps.cursorDistance, toTarget.length());
double distance = Math.min(cursorDistance, toTarget.length());
this.cursorPos = eye.add(direction.scale(distance)).subtract(0, CURSOR_HALF_HEIGHT, 0);
}
holder.tick();
@@ -40,9 +40,13 @@
"message.sfcraft.elytra.infinity": "ELYTRA: INFINITY",
"message.sfcraft.chat.calling_you": "%s is calling you.",
"message.sfcraft.chat.invalid_position": " (invalid position) ",
"message.sfcraft.chat.loc_navigate": "Click to start navigating here",
"message.sfcraft.dimension.end": " (The End)",
"message.sfcraft.dimension.nether": " (The Nether)",
"message.sfcraft.dimension.unknown": " (Unknown)",
"message.sfcraft.dimension.name.overworld": "Overworld",
"message.sfcraft.dimension.name.nether": "The Nether",
"message.sfcraft.dimension.name.end": "The End",
"message.sfcraft.playground.must_be_in": "You and target must be in playground!",
"message.sfcraft.playground.gamemode_changed": "Your gamemode has been changed to %s",
"message.sfcraft.playground.only_here": "You can only use this in playground!",
@@ -81,5 +85,7 @@
"command.sfcraft.gps.not_active": "No navigation in progress",
"command.sfcraft.gps.arrived": "You have arrived, navigation ended",
"command.sfcraft.gps.dimension_changed": "You left the target's dimension, navigation ended",
"command.sfcraft.gps.wrong_world": "You are not in the target world (%s); navigation refused",
"command.sfcraft.gps.preview": "Click to preview this location on the web map",
"item.sfcraft.standing_firm_totem": "Totem of Restitution"
}
@@ -40,9 +40,13 @@
"message.sfcraft.elytra.infinity": "鞘翅: 无限",
"message.sfcraft.chat.calling_you": "%s 正在叫你。",
"message.sfcraft.chat.invalid_position": " (无效坐标) ",
"message.sfcraft.chat.loc_navigate": "点击开始导航到这里",
"message.sfcraft.dimension.end": " (末地)",
"message.sfcraft.dimension.nether": " (地狱)",
"message.sfcraft.dimension.unknown": " (未知)",
"message.sfcraft.dimension.name.overworld": "主世界",
"message.sfcraft.dimension.name.nether": "地狱",
"message.sfcraft.dimension.name.end": "末地",
"message.sfcraft.playground.must_be_in": "你和目标必须都在游乐园中!",
"message.sfcraft.playground.gamemode_changed": "你的游戏模式已更改为 %s",
"message.sfcraft.playground.only_here": "只能在游乐园中使用此功能!",
@@ -81,5 +85,7 @@
"command.sfcraft.gps.not_active": "当前没有正在进行的导航",
"command.sfcraft.gps.arrived": "已到达目的地,导航结束",
"command.sfcraft.gps.dimension_changed": "已离开目标所在维度,导航结束",
"command.sfcraft.gps.wrong_world": "你不在目标世界(%s),已拒绝导航",
"command.sfcraft.gps.preview": "点击在网页地图上预览该位置",
"item.sfcraft.standing_firm_totem": "代偿图腾"
}