From 8de0240b14b0284f1fb636c301194d8d7cabe2e6 Mon Sep 17 00:00:00 2001 From: CrackedMatter <81803926+CrackedMatter@users.noreply.github.com> Date: Sat, 6 Jan 2024 04:01:30 +0100 Subject: [PATCH] Fix height limit stuff (#225) --- src/server/commands/navigation/ascend.ts | 38 +++++++++--------- src/server/commands/navigation/ceil.ts | 27 ++++++++----- src/server/commands/navigation/descend.ts | 40 +++++++++---------- src/server/commands/navigation/thru.ts | 5 ++- src/server/commands/navigation/unstuck.ts | 21 +++++----- src/server/commands/navigation/up.ts | 20 ++++++---- src/server/commands/utilities/drain.ts | 2 + .../commands/utilities/floodfill_func.ts | 8 ++-- src/server/tools/navigation_tool.ts | 6 ++- src/server/util.ts | 36 +---------------- 10 files changed, 98 insertions(+), 105 deletions(-) diff --git a/src/server/commands/navigation/ascend.ts b/src/server/commands/navigation/ascend.ts index 2cba82732..1ce0ca8ca 100644 --- a/src/server/commands/navigation/ascend.ts +++ b/src/server/commands/navigation/ascend.ts @@ -1,7 +1,8 @@ import { PlayerUtil } from "@modules/player_util.js"; -import { RawText, Vector } from "@notbeer-api"; +import { RawText } from "@notbeer-api"; import { registerCommand } from "../register_commands.js"; import { Player } from "@minecraft/server"; +import { getWorldHeightLimits } from "server/util.js"; const registerInformation = { name: "ascend", @@ -11,40 +12,39 @@ const registerInformation = { { name: "levels", type: "int", + range: [1, null] as [number, null], default: 1 } ] }; function ascend(builder: Player) { - const location = PlayerUtil.getBlockLocation(builder); const dimension = builder.dimension; + const limits = getWorldHeightLimits(dimension); + const blockLoc = PlayerUtil.getBlockLocation(builder); - for (let i = location.y + 3; i <= 319; i++) { - const floor = new Vector(location.x, i - 1, location.z); - const legs = new Vector(location.x, i, location.z); - const head = new Vector(location.x, i + 1, location.z); + for (blockLoc.y = Math.max(limits[0], blockLoc.y);; blockLoc.y++) { + if (blockLoc.y > limits[1]) return false; + if (dimension.getBlock(blockLoc).isAir) continue; + if (blockLoc.y + 1 <= limits[1] && !dimension.getBlock(blockLoc.offset(0, 1, 0)).isAir) continue; + if (blockLoc.y + 2 <= limits[1] && !dimension.getBlock(blockLoc.offset(0, 2, 0)).isAir) continue; - let invalid = false; - - if (dimension.getBlock(floor).isAir) invalid = true; - if (!dimension.getBlock(legs).isAir) invalid = true; - if (!dimension.getBlock(head).isAir) invalid = true; - - if (!invalid) { - builder.teleport(new Vector(location.x + 0.5, legs.y, location.z + 0.5), { dimension }); - return RawText.translate("commands.wedit:thru.explain"); - } + builder.teleport(blockLoc.offset(0.5, 1, 0.5), { dimension }); + return true; } - return RawText.translate("commands.wedit:ascend.obstructed"); } registerCommand(registerInformation, function (session, builder, args) { const levels = args.get("levels") as number; - for (let level = 0; level < levels; level++) { - ascend(builder); + let count = 0; + while (ascend(builder)) { + count++; + if (count == levels) break; } + if (count == 0) { + throw RawText.translate("commands.wedit:ascend.obstructed"); + } return RawText.translate("commands.wedit:thru.explain"); }); diff --git a/src/server/commands/navigation/ceil.ts b/src/server/commands/navigation/ceil.ts index ad351f3d3..48b9228f6 100644 --- a/src/server/commands/navigation/ceil.ts +++ b/src/server/commands/navigation/ceil.ts @@ -1,6 +1,7 @@ import { PlayerUtil } from "@modules/player_util.js"; import { RawText } from "@notbeer-api"; import { registerCommand } from "../register_commands.js"; +import { getWorldHeightLimits } from "server/util.js"; const registerInformation = { name: "ceil", @@ -17,19 +18,27 @@ const registerInformation = { }; registerCommand(registerInformation, function (session, builder, args) { - const clearance = args.get("clearance") as number; - - let blockLoc = PlayerUtil.getBlockLocation(builder); + let clearance = args.get("clearance") as number; const dimension = builder.dimension; - for (let i = 0;; i++, blockLoc = blockLoc.offset(0, 1, 0)) { - if (!dimension.getBlock(blockLoc.offset(0, 2, 0)).isAir) { - blockLoc = blockLoc.offset(0, clearance < i ? -clearance : -i, 0); + const limits = getWorldHeightLimits(dimension); + const blockLoc = PlayerUtil.getBlockLocation(builder).offset(0, 2, 0); + + for (let i = 0;; i++, blockLoc.y++) { + if (blockLoc.y > limits[1]) { + throw RawText.translate("commands.wedit:ascend.obstructed"); + } + if (blockLoc.y >= limits[0] && !dimension.getBlock(blockLoc).isAir) { + if (clearance > i) clearance = i; break; } } - const block = dimension.getBlock(blockLoc.offset(0, -1, 0)); - builder.teleport(blockLoc.offset(0.5, 0, 0.5), { dimension }); - if (block.isAir) block.setType("minecraft:glass"); + blockLoc.y -= clearance + 3; + if (blockLoc.y >= limits[0] && blockLoc.y <= limits[1]) { + const block = dimension.getBlock(blockLoc); + if (block.isAir) block.setType("minecraft:glass"); + } + + builder.teleport(blockLoc.offset(0.5, 1, 0.5), { dimension }); return RawText.translate("commands.wedit:up.explain"); }); diff --git a/src/server/commands/navigation/descend.ts b/src/server/commands/navigation/descend.ts index 285d797a2..08c2d3070 100644 --- a/src/server/commands/navigation/descend.ts +++ b/src/server/commands/navigation/descend.ts @@ -1,7 +1,8 @@ import { PlayerUtil } from "@modules/player_util.js"; -import { RawText, Vector } from "@notbeer-api"; +import { RawText } from "@notbeer-api"; import { registerCommand } from "../register_commands.js"; import { Player } from "@minecraft/server"; +import { getWorldHeightLimits } from "server/util.js"; const registerInformation = { name: "descend", @@ -11,40 +12,39 @@ const registerInformation = { { name: "levels", type: "int", + range: [1, null] as [number, null], default: 1 } ] }; function descend(builder: Player) { - const location = PlayerUtil.getBlockLocation(builder); const dimension = builder.dimension; + const limits = getWorldHeightLimits(dimension); + const blockLoc = PlayerUtil.getBlockLocation(builder); - for (let i = location.y - 3; i >= -64; i--) { - const floor = new Vector(location.x, i - 1, location.z); - const legs = new Vector(location.x, i, location.z); - const head = new Vector(location.x, i + 1, location.z); + for (blockLoc.y = Math.min(limits[1], blockLoc.y - 2);; blockLoc.y--) { + if (blockLoc.y < limits[0]) return false; + if (dimension.getBlock(blockLoc).isAir) continue; + if (blockLoc.y + 1 <= limits[1] && !dimension.getBlock(blockLoc.offset(0, 1, 0)).isAir) continue; + if (blockLoc.y + 2 <= limits[1] && !dimension.getBlock(blockLoc.offset(0, 2, 0)).isAir) continue; - let invalid = false; - - if (dimension.getBlock(floor).isAir) invalid = true; - if (!dimension.getBlock(legs).isAir) invalid = true; - if (!dimension.getBlock(head).isAir) invalid = true; - - if (!invalid) { - builder.teleport(new Vector(location.x + 0.5, legs.y, location.z + 0.5), { dimension }); - return RawText.translate("commands.wedit:thru.explain"); - } + builder.teleport(blockLoc.offset(0.5, 1, 0.5), { dimension }); + return true; } - return RawText.translate("commands.wedit:descend.obstructed"); } registerCommand(registerInformation, function (session, builder, args) { const levels = args.get("levels") as number; - for (let level = 0; level < levels; level++) { - descend(builder); + let count = 0; + while (descend(builder)) { + count++; + if (count == levels) break; } + if (count == 0) { + throw RawText.translate("commands.wedit:descend.obstructed"); + } return RawText.translate("commands.wedit:thru.explain"); -}); \ No newline at end of file +}); diff --git a/src/server/commands/navigation/thru.ts b/src/server/commands/navigation/thru.ts index f468289e7..82dec835f 100644 --- a/src/server/commands/navigation/thru.ts +++ b/src/server/commands/navigation/thru.ts @@ -2,6 +2,7 @@ import { Vector } from "@notbeer-api"; import { registerCommand } from "../register_commands.js"; import { Cardinal } from "@modules/directions.js"; import { PlayerUtil } from "@modules/player_util.js"; +import { getWorldHeightLimits } from "server/util.js"; const registerInformation = { name: "thru", @@ -11,12 +12,14 @@ const registerInformation = { registerCommand(registerInformation, function (session, builder) { const dimension = builder.dimension; + const limits = getWorldHeightLimits(dimension); const blockLoc = PlayerUtil.getBlockLocation(builder); const dir = new Cardinal().getDirection(builder); function isSpaceEmpty(loc: Vector) { - return dimension.getBlock(loc).isAir && dimension.getBlock(loc.offset(0, 1, 0)).isAir; + return (loc.y < limits[0] || loc.y > limits[1] || dimension.getBlock(loc).isAir) && + (loc.y + 1 < limits[0] || loc.y + 1 > limits[1] || dimension.getBlock(loc.offset(0, 1, 0)).isAir); } let testLoc = blockLoc.offset(dir.x, dir.y, dir.z); diff --git a/src/server/commands/navigation/unstuck.ts b/src/server/commands/navigation/unstuck.ts index 28f7c524d..4f49c54ab 100644 --- a/src/server/commands/navigation/unstuck.ts +++ b/src/server/commands/navigation/unstuck.ts @@ -1,6 +1,7 @@ import { PlayerUtil } from "@modules/player_util.js"; import { RawText } from "@notbeer-api"; import { registerCommand } from "../register_commands.js"; +import { getWorldHeightLimits } from "server/util.js"; const registerInformation = { name: "unstuck", @@ -10,17 +11,15 @@ const registerInformation = { }; registerCommand(registerInformation, function (session, builder) { - const blockLoc = PlayerUtil.getBlockLocation(builder); const dimension = builder.dimension; - do { - if (dimension.getBlock(blockLoc).isAir && - dimension.getBlock(blockLoc.offset(0, 1, 0)).isAir) { - break; - } - } - // eslint-disable-next-line no-cond-assign - while (blockLoc.y += 1); + const limits = getWorldHeightLimits(dimension); + const blockLoc = PlayerUtil.getBlockLocation(builder); - builder.teleport(blockLoc.offset(0.5, 0, 0.5), { dimension }); - return RawText.translate("commands.wedit:unstuck.explain"); + for (blockLoc.y = Math.max(limits[0], blockLoc.y);; blockLoc.y++) { + if (blockLoc.y <= limits[1] && !dimension.getBlock(blockLoc).isAir) continue; + if (blockLoc.y + 1 <= limits[1] && !dimension.getBlock(blockLoc.offset(0, 1, 0)).isAir) continue; + + builder.teleport(blockLoc.offset(0.5, 0, 0.5), { dimension }); + return RawText.translate("commands.wedit:unstuck.explain"); + } }); diff --git a/src/server/commands/navigation/up.ts b/src/server/commands/navigation/up.ts index 356fccd65..14ba3d2b8 100644 --- a/src/server/commands/navigation/up.ts +++ b/src/server/commands/navigation/up.ts @@ -1,6 +1,7 @@ import { PlayerUtil } from "@modules/player_util.js"; import { RawText } from "@notbeer-api"; import { registerCommand } from "../register_commands.js"; +import { getWorldHeightLimits } from "server/util.js"; const registerInformation = { name: "up", @@ -17,17 +18,22 @@ const registerInformation = { registerCommand(registerInformation, function (session, builder, args) { const height = args.get("height") as number; - - let blockLoc = PlayerUtil.getBlockLocation(builder); const dimension = builder.dimension; - for (let i = 0; i < height; i++, blockLoc = blockLoc.offset(0, 1, 0)) { - if (!dimension.getBlock(blockLoc.offset(0, 2, 0)).isAir) { + const limits = getWorldHeightLimits(dimension); + const blockLoc = PlayerUtil.getBlockLocation(builder).offset(0, 2, 0); + + for (let i = 0; i < height; i++, blockLoc.y++) { + if (blockLoc.y >= limits[0] && blockLoc.y <= limits[1] && !dimension.getBlock(blockLoc).isAir) { break; } } - const block = dimension.getBlock(blockLoc.offset(0, -1, 0)); - builder.teleport(blockLoc.offset(0.5, 0, 0.5), { dimension }); - if (block.isAir) block.setType("minecraft:glass"); + blockLoc.y -= 3; + if (blockLoc.y >= limits[0] && blockLoc.y <= limits[1]) { + const block = dimension.getBlock(blockLoc); + if (block.isAir) block.setType("minecraft:glass"); + } + + builder.teleport(blockLoc.offset(0.5, 1, 0.5), { dimension }); return RawText.translate("commands.wedit:up.explain"); }); diff --git a/src/server/commands/utilities/drain.ts b/src/server/commands/utilities/drain.ts index 563609fac..f522b48de 100644 --- a/src/server/commands/utilities/drain.ts +++ b/src/server/commands/utilities/drain.ts @@ -4,6 +4,7 @@ import { BlockPermutation } from "@minecraft/server"; import { SphereShape } from "../../shapes/sphere.js"; import { registerCommand } from "../register_commands.js"; import { floodFill } from "./floodfill_func.js"; +import { canPlaceBlock } from "server/util.js"; const registerInformation = { name: "drain", @@ -45,6 +46,7 @@ registerCommand(registerInformation, function* (session, builder, args) { let drainStart: Vector; for (const offset of fluidLookPositions) { const loc = playerBlock.offset(offset.x, offset.y, offset.z); + if (!canPlaceBlock(loc, dimension)) continue; const block = dimension.getBlock(loc); if (block.typeId.match(waterMatch) || (args.has("w") && block.isWaterlogged)) { fluidMatch = waterMatch; diff --git a/src/server/commands/utilities/floodfill_func.ts b/src/server/commands/utilities/floodfill_func.ts index b82325f1b..86957b548 100644 --- a/src/server/commands/utilities/floodfill_func.ts +++ b/src/server/commands/utilities/floodfill_func.ts @@ -51,9 +51,11 @@ export function* floodFill(start: Vector3, size: num result.set(locToString(block), true); for (const offset of offsets) { const newCtx = {...ctx}; - if (spread(newCtx, offset)) { - addNeighbor(block, offset, newCtx); - } + try { + if (spread(newCtx, offset)) { + addNeighbor(block, offset, newCtx); + } + } catch { /* pass */ } } } diff --git a/src/server/tools/navigation_tool.ts b/src/server/tools/navigation_tool.ts index caed0c2ef..2a3ed78e2 100644 --- a/src/server/tools/navigation_tool.ts +++ b/src/server/tools/navigation_tool.ts @@ -3,12 +3,16 @@ import { Tool } from "./base_tool.js"; import { Tools } from "./tool_manager.js"; import { PlayerUtil } from "@modules/player_util.js"; import { Server } from "@notbeer-api"; +import { getWorldHeightLimits } from "server/util.js"; class NavigationTool extends Tool { permission = "worldedit.navigation"; use = function (self: Tool, player: Player) { - if (!player.dimension.getBlock(PlayerUtil.getBlockLocation(player).offset(0, 1, 0)).isAir) { + const dimension = player.dimension; + const limits = getWorldHeightLimits(dimension); + const blockLoc = PlayerUtil.getBlockLocation(player).offset(0, 1, 0); + if (blockLoc.y >= limits[0] && blockLoc.y <= limits[1] && !dimension.getBlock(blockLoc).isAir) { Server.command.callCommand(player, "unstuck", []); } else if (player.isSneaking) { Server.command.callCommand(player, "thru", []); diff --git a/src/server/util.ts b/src/server/util.ts index 1dc520f48..602889212 100644 --- a/src/server/util.ts +++ b/src/server/util.ts @@ -36,45 +36,13 @@ export function getViewVector(entity: Entity | Player): Vector3 { return entity.getViewDirection(); } -const worldY = new Map(); -function findHeightLimits(dim: Dimension) { - const limits: [number, number] = [null, null]; - - for (const p of world.getPlayers()) { - if (p.dimension != dim) { - continue; - } - - for (let i = -512; i <= 512; i += 16) { - const canPlace = canPlaceBlock(new Vector(p.location.x, i, p.location.z), dim); - if (limits[0] == null) { - if (canPlace) { - limits[0] = i; - } - } else if (limits[1] == null) { - if (!canPlace) { - limits[1] = i - 17; - } - } - } - break; - } - - if (limits[0] != null && limits[1] != null) { - worldY.set(dim, limits); - } -} - /** * Gets the minimum and maximum Y levels of a dimension. * @param dim The dimension we're querying. * @return The minimum and maximum Y levels. */ -export function getWorldHeightLimits(dim: Dimension) { - if (!worldY.has(dim)) { - findHeightLimits(dim); - } - return worldY.get(dim) ?? [ -512, 511 ]; +export function getWorldHeightLimits(dim: Dimension): [number, number] { + return [dim.heightRange.min, dim.heightRange.max - 1]; } /**