Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f308bfa00 | ||
|
|
bfbdfd2199 |
No files matched your search
@@ -0,0 +1,30 @@
|
||||
# Automatically build the project and run any configured tests for every push
|
||||
# and submitted pull request. This can help catch issues that only occur on
|
||||
# certain platforms or Java versions, and provides a first line of defence
|
||||
# against bad commits.
|
||||
|
||||
name: build
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
- name: validate gradle wrapper
|
||||
uses: gradle/actions/wrapper-validation@v6
|
||||
- name: setup jdk
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '25'
|
||||
distribution: 'microsoft'
|
||||
- name: make gradle wrapper executable
|
||||
run: chmod +x ./gradlew
|
||||
- name: build
|
||||
run: ./gradlew build
|
||||
- name: capture build artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Artifacts
|
||||
path: build/libs/
|
||||
@@ -5,14 +5,14 @@
|
||||
## 功能特性
|
||||
|
||||
- 🕊️ **自由飞行** — 通过命令开启/关闭创造模式飞行能力
|
||||
- ⚡ **飞行速度调节** — 通过命令在 0.01–0.15 范围内自定义飞行速度
|
||||
- ⚡ **飞行速度调节** — 通过命令在 0.1–2.0 范围内自定义飞行速度
|
||||
- 🛡️ **摔落保护** — 飞行状态下免疫摔落伤害
|
||||
- 💾 **状态持久化** — 飞行状态与速度保存到配置文件,退出重进或服务器重启后自动恢复
|
||||
- 💾 **状态持久化** — 重新加入服务器时自动恢复飞行状态
|
||||
- 👥 **多玩家支持** — 管理员可为其他玩家切换飞行状态
|
||||
|
||||
## 环境要求
|
||||
|
||||
- Minecraft **1.21.11**
|
||||
- Minecraft **1.21.8**
|
||||
- Java **21+**
|
||||
- Fabric Loader **≥ 0.19.5**
|
||||
- Fabric API
|
||||
@@ -37,17 +37,16 @@
|
||||
- 如果当前未飞行,执行后**开启飞行**
|
||||
- 如果当前正在飞行,执行后**关闭飞行**
|
||||
|
||||
### `/fly speed <0.01-0.15>`
|
||||
### `/fly speed <0.1-2.0>`
|
||||
|
||||
设置自己的飞行速度,范围为 0.01 到 0.15(默认 0.05)。所有玩家均可执行。
|
||||
设置自己的飞行速度,范围为 0.1 到 2.0(默认 0.05)。所有玩家均可执行。
|
||||
|
||||
```
|
||||
/fly speed 0.10
|
||||
/fly speed 1.5
|
||||
```
|
||||
|
||||
- 数值越大飞行越快,0.15 为允许的最大速度
|
||||
- 数值越大飞行越快,2.0 为允许的最大速度
|
||||
- 设置后立即生效,无需重新开启飞行
|
||||
- 速度会与飞行状态一起保存,退出重进后仍然保留
|
||||
|
||||
### `/fly player <player>`
|
||||
|
||||
@@ -74,11 +73,11 @@
|
||||
|
||||
### 飞行速度
|
||||
|
||||
使用 `/fly speed <数值>` 调整飞行速度(0.01–0.15):
|
||||
使用 `/fly speed <数值>` 调整飞行速度(0.1–2.0):
|
||||
|
||||
- `/fly speed 0.03` — 较慢的飞行速度
|
||||
- `/fly speed 0.05` — 默认速度
|
||||
- `/fly speed 0.15` — 最快速度
|
||||
- `/fly speed 0.5` — 较慢的飞行速度
|
||||
- `/fly speed 1.0` — 中速
|
||||
- `/fly speed 2.0` — 最快速度
|
||||
|
||||
### 管理员操作
|
||||
|
||||
@@ -98,13 +97,13 @@
|
||||
|
||||
### 重连恢复
|
||||
|
||||
玩家的飞行状态(是否飞行)与飞行速度会持久化保存。退出游戏后重新加入,甚至服务器重启,都会自动恢复上次的飞行状态与速度。
|
||||
如果玩家在飞行状态下断开连接,重新加入服务器时飞行状态会自动恢复。
|
||||
|
||||
## FAQ
|
||||
|
||||
### Q: 飞行状态会一直保留吗?
|
||||
|
||||
A: 会。飞行状态(是否飞行)与飞行速度会保存到服务端的 `config/openfly_flight_data.json` 文件。玩家退出重进、服务器重启后都会自动恢复上次的状态。
|
||||
A: 飞行状态在服务器运行期间持续有效。如果你关闭了飞行、或者服务器重启,飞行状态会被重置。但如果你是断开重连,飞行状态会自动恢复。
|
||||
|
||||
### Q: 飞行时还会受到摔落伤害吗?
|
||||
|
||||
|
||||
+4
-4
@@ -5,14 +5,14 @@ org.gradle.configuration-cache=true
|
||||
|
||||
# Fabric Properties
|
||||
# check these on https://fabricmc.net/develop
|
||||
minecraft_version=1.21.11
|
||||
yarn_mappings=1.21.11+build.6
|
||||
minecraft_version=1.21.8
|
||||
yarn_mappings=1.21.8+build.1
|
||||
loader_version=0.19.5
|
||||
loom_version=1.18-SNAPSHOT
|
||||
|
||||
# Mod Properties
|
||||
version=1.2.0-1.21.11
|
||||
version=1.1.0-1.21.8
|
||||
group=top.gtb520.java.mods.openfly
|
||||
|
||||
# Dependencies
|
||||
fabric_api_version=0.141.6+1.21.11
|
||||
fabric_api_version=0.136.1+1.21.8
|
||||
@@ -1,48 +1,28 @@
|
||||
package top.gtb520.java.mods.openfly.Commands;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.arguments.FloatArgumentType;
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents;
|
||||
import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.command.DefaultPermissions;
|
||||
import net.minecraft.command.argument.EntityArgumentType;
|
||||
import net.minecraft.entity.damage.DamageTypes;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import top.gtb520.java.mods.openfly.OpenFly;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class fly {
|
||||
// 默认飞行速度(与原版一致)
|
||||
public static final float DEFAULT_FLY_SPEED = 0.05F;
|
||||
// 记录开启了飞行的玩家UUID
|
||||
private static final Set<UUID> flyingPlayers = new HashSet<>();
|
||||
|
||||
// 飞行速度范围(/fly speed 参数范围)
|
||||
public static final float MIN_FLY_SPEED = 0.01F;
|
||||
public static final float MAX_FLY_SPEED = 0.15F;
|
||||
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
// 记录每个玩家的飞行状态(是否飞行 + 飞行速度),内存缓存 + JSON 持久化
|
||||
private static final Map<UUID, PlayerFlightState> flightStates = new HashMap<>();
|
||||
private static boolean loaded = false;
|
||||
public static final float MIN_FLY_SPEED = 0.1F;
|
||||
public static final float MAX_FLY_SPEED = 2.0F;
|
||||
|
||||
public static void register() {
|
||||
loadStates();
|
||||
|
||||
// 注册命令
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
|
||||
dispatcher.register(
|
||||
@@ -56,7 +36,7 @@ public class fly {
|
||||
}
|
||||
|
||||
UUID playerId = player.getUuid();
|
||||
if (isFlying(playerId)) {
|
||||
if (flyingPlayers.contains(playerId)) {
|
||||
disableFly(player);
|
||||
context.getSource().sendFeedback(() -> Text.literal("已禁用飞行"), false);
|
||||
} else {
|
||||
@@ -65,7 +45,7 @@ public class fly {
|
||||
}
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})
|
||||
// /fly speed <0.01-0.15> - 设置自己的飞行速度(所有玩家可用)
|
||||
// /fly speed <0.1-2.0> - 设置自己的飞行速度(所有玩家可用)
|
||||
.then(CommandManager.literal("speed")
|
||||
.then(CommandManager.argument("speed", FloatArgumentType.floatArg(MIN_FLY_SPEED, MAX_FLY_SPEED))
|
||||
.executes(context -> {
|
||||
@@ -76,24 +56,21 @@ public class fly {
|
||||
}
|
||||
|
||||
float speed = FloatArgumentType.getFloat(context, "speed");
|
||||
PlayerFlightState state = getOrCreateState(player.getUuid());
|
||||
state.flySpeed = speed;
|
||||
player.getAbilities().setFlySpeed(speed);
|
||||
player.sendAbilitiesUpdate();
|
||||
saveStates();
|
||||
context.getSource().sendFeedback(
|
||||
() -> Text.literal("飞行速度已设置为 " + String.format("%.2f", speed)), false);
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})))
|
||||
// /fly player <player> - 切换指定玩家的飞行状态(需要OP权限)
|
||||
.then(CommandManager.literal("player")
|
||||
.requires(source -> source.getPermissions().hasPermission(DefaultPermissions.GAMEMASTERS))
|
||||
.requires(source -> source.hasPermissionLevel(2))
|
||||
.then(CommandManager.argument("player", EntityArgumentType.player())
|
||||
.executes(context -> {
|
||||
ServerPlayerEntity targetPlayer = EntityArgumentType.getPlayer(context, "player");
|
||||
UUID playerId = targetPlayer.getUuid();
|
||||
|
||||
if (isFlying(playerId)) {
|
||||
if (flyingPlayers.contains(playerId)) {
|
||||
disableFly(targetPlayer);
|
||||
context.getSource().sendFeedback(
|
||||
() -> Text.literal("已禁用玩家 " + targetPlayer.getName().getString() + " 的飞行"), true);
|
||||
@@ -110,7 +87,7 @@ public class fly {
|
||||
// 注册摔落伤害事件,取消飞行玩家的摔落伤害
|
||||
ServerLivingEntityEvents.ALLOW_DAMAGE.register((entity, damageSource, amount) -> {
|
||||
if (entity instanceof ServerPlayerEntity player) {
|
||||
if (isFlying(player.getUuid())) {
|
||||
if (flyingPlayers.contains(player.getUuid())) {
|
||||
// 如果是摔落伤害,取消它
|
||||
if (damageSource.isOf(DamageTypes.FALL)) {
|
||||
return false; // 返回false取消伤害
|
||||
@@ -119,111 +96,28 @@ public class fly {
|
||||
}
|
||||
return true; // 其他情况正常受到伤害
|
||||
});
|
||||
|
||||
// 死亡重生后重新应用飞行状态(原版重生流程会重置能力)
|
||||
ServerPlayerEvents.AFTER_RESPAWN.register((oldPlayer, newPlayer, alive) -> {
|
||||
restoreFlyState(newPlayer);
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isFlying(UUID playerId) {
|
||||
PlayerFlightState state = flightStates.get(playerId);
|
||||
return state != null && state.flying;
|
||||
}
|
||||
|
||||
private static PlayerFlightState getOrCreateState(UUID playerId) {
|
||||
return flightStates.computeIfAbsent(playerId, id -> new PlayerFlightState());
|
||||
}
|
||||
|
||||
private static void enableFly(ServerPlayerEntity player) {
|
||||
PlayerFlightState state = getOrCreateState(player.getUuid());
|
||||
state.flying = true;
|
||||
flyingPlayers.add(player.getUuid());
|
||||
player.getAbilities().allowFlying = true;
|
||||
player.getAbilities().flying = true;
|
||||
player.getAbilities().setFlySpeed(state.flySpeed);
|
||||
player.sendAbilitiesUpdate();
|
||||
saveStates();
|
||||
}
|
||||
|
||||
private static void disableFly(ServerPlayerEntity player) {
|
||||
PlayerFlightState state = getOrCreateState(player.getUuid());
|
||||
state.flying = false;
|
||||
flyingPlayers.remove(player.getUuid());
|
||||
player.getAbilities().allowFlying = false;
|
||||
player.getAbilities().flying = false;
|
||||
player.sendAbilitiesUpdate();
|
||||
saveStates();
|
||||
}
|
||||
|
||||
// 玩家加入/重生时恢复飞行状态(是否飞行 + 飞行速度)
|
||||
// 玩家加入时恢复飞行状态
|
||||
public static void restoreFlyState(ServerPlayerEntity player) {
|
||||
PlayerFlightState state = flightStates.get(player.getUuid());
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
// 飞行速度始终恢复,是否飞行按保存的状态恢复
|
||||
player.getAbilities().setFlySpeed(state.flySpeed);
|
||||
if (state.flying) {
|
||||
if (flyingPlayers.contains(player.getUuid())) {
|
||||
player.getAbilities().allowFlying = true;
|
||||
player.getAbilities().flying = true;
|
||||
player.sendAbilitiesUpdate();
|
||||
}
|
||||
player.sendAbilitiesUpdate();
|
||||
}
|
||||
|
||||
// ---------- 持久化 ----------
|
||||
|
||||
private static Path dataFile() {
|
||||
return FabricLoader.getInstance().getConfigDir().resolve("openfly_flight_data.json");
|
||||
}
|
||||
|
||||
private static void loadStates() {
|
||||
if (loaded) {
|
||||
return;
|
||||
}
|
||||
loaded = true;
|
||||
try {
|
||||
Path path = dataFile();
|
||||
if (!Files.exists(path)) {
|
||||
return;
|
||||
}
|
||||
try (Reader reader = Files.newBufferedReader(path)) {
|
||||
Map<String, PlayerFlightState> data = GSON.fromJson(reader, new TypeToken<Map<String, PlayerFlightState>>() {
|
||||
}.getType());
|
||||
if (data != null) {
|
||||
for (Map.Entry<String, PlayerFlightState> entry : data.entrySet()) {
|
||||
try {
|
||||
flightStates.put(UUID.fromString(entry.getKey()), entry.getValue());
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
// 忽略无效的 UUID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
OpenFly.LOGGER.info("OpenFly 已加载 {} 名玩家的飞行状态", flightStates.size());
|
||||
} catch (IOException e) {
|
||||
OpenFly.LOGGER.error("OpenFly 读取飞行状态文件失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void saveStates() {
|
||||
try {
|
||||
Path path = dataFile();
|
||||
Files.createDirectories(path.getParent());
|
||||
// 以 UUID 字符串作为 key 序列化,避免依赖 Gson 的 UUID 适配器
|
||||
Map<String, PlayerFlightState> data = new HashMap<>();
|
||||
for (Map.Entry<UUID, PlayerFlightState> entry : flightStates.entrySet()) {
|
||||
data.put(entry.getKey().toString(), entry.getValue());
|
||||
}
|
||||
try (Writer writer = Files.newBufferedWriter(path)) {
|
||||
GSON.toJson(data, writer);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
OpenFly.LOGGER.error("OpenFly 保存飞行状态文件失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 单个玩家的飞行状态
|
||||
public static class PlayerFlightState {
|
||||
public boolean flying = false;
|
||||
public float flySpeed = DEFAULT_FLY_SPEED;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
package top.gtb520.java.mods.openfly;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -28,21 +26,9 @@ public class OpenFly implements ModInitializer {
|
||||
// 注册飞行命令和摔落伤害事件
|
||||
fly.register();
|
||||
|
||||
// 玩家加入时恢复飞行状态(是否飞行 + 飞行速度)
|
||||
// 玩家加入时恢复飞行状态
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
|
||||
ServerPlayerEntity player = handler.getPlayer();
|
||||
fly.restoreFlyState(player);
|
||||
// 延迟一 tick 再应用一次,避免登录流程后续覆盖玩家能力
|
||||
server.execute(() -> {
|
||||
if (!player.isDisconnected()) {
|
||||
fly.restoreFlyState(player);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 死亡重生后重新应用飞行状态(原版重生流程会重置能力)
|
||||
ServerPlayerEvents.AFTER_RESPAWN.register((oldPlayer, newPlayer, alive) -> {
|
||||
fly.restoreFlyState(newPlayer);
|
||||
fly.restoreFlyState(handler.getPlayer());
|
||||
});
|
||||
|
||||
LOGGER.info("OpenFly Mod 加载完成!");
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.19.5",
|
||||
"minecraft": "~1.21.11",
|
||||
"minecraft": "~1.21.8",
|
||||
"java": ">=21",
|
||||
"fabric-api": "*"
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user