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