Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add super pickaxe #215

Merged
merged 5 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/config.ts
SIsilicon marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ export default {
*/
maxBrushRadius: 6,
/**
* Whether blocks broken by the super pickaxe in "single" mode drop.
*/
superPickaxeDrop: true,
/**
* Whether blocks broken by the super pickaxe in "area" and "recursive" mode drop.
*/
superPickaxeManyDrop: false,
/**
* The default amount of blocks that can be "potentially" affected within a single operation.
*/
defaultChangeLimit: -1,
Expand Down
2 changes: 2 additions & 0 deletions src/library/@types/Events.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Dimension,
Entity,
PlayerBreakBlockBeforeEvent,
EntityHitBlockAfterEvent,
EffectAddAfterEvent,
ExplosionAfterEvent,
PistonActivateAfterEvent,
Expand All @@ -28,6 +29,7 @@ export interface EventList {
itemUseBefore: [ItemUseBeforeEvent],
itemUseOnBefore: [ItemUseOnBeforeEvent],
blockBreak: [PlayerBreakBlockBeforeEvent],
blockHit: [EntityHitBlockAfterEvent],
tick: [TickEvent],
entityEffected: [EffectAddAfterEvent],
entityCreate: [EntityCreateEvent],
Expand Down
4 changes: 4 additions & 0 deletions src/library/Minecraft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ class ServerBuild extends ServerBuilder {
* Emit to 'blockBreak' event listener
*/
beforeEvents.playerBreakBlock.subscribe(data => this.emit("blockBreak", data));
/**
* Emit to 'blockHit' event listener
*/
afterEvents.entityHitBlock.subscribe(data => this.emit("blockHit", data));
/**
* Emit to 'worldInitialize' event listener
*/
Expand Down
1 change: 1 addition & 0 deletions src/server/commands/command_list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ import "./navigation/descend.js";
import "./navigation/ceil.js";

import "./tool/tool.js";
import "./tool/superpickaxe.js";

import "./brush/brush.js";
import "./brush/mask.js";
Expand Down
53 changes: 53 additions & 0 deletions src/server/commands/tool/superpickaxe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { registerCommand } from "../register_commands.js";

const registerInformation = {
name: "superpickaxe",
permission: "worldedit.superpickaxe",
description: "commands.wedit:superpickaxe.description",
aliases : ["sp"],
usage: [
{
subName: "single",
},
{
subName: "area",
args: [
{
name: "range",
type: "int",
range: [0, 5] as [number, number]
}
]
},
{
subName: "recursive",
args: [
{
name: "range",
type: "int",
range: [0, 5] as [number, number]
}
]
},
{
subName: "_default"
}
]
};

registerCommand(registerInformation, function (session, builder, args) {
if (args.has("single")) {
session.superPickaxe.mode = "single";
} else if (args.has("area")) {
session.superPickaxe.mode = "area";
session.superPickaxe.range = args.get("range");
} else if (args.has("recursive")) {
session.superPickaxe.mode = "recursive";
session.superPickaxe.range = args.get("range");
} else {
const enabled = (session.superPickaxe.enabled = !session.superPickaxe.enabled);
return "commands.wedit:superpickaxe." + (enabled ? "enabled" : "disabled");
}
session.superPickaxe.enabled = true;
return "commands.wedit:superpickaxe." + session.superPickaxe.mode;
});
12 changes: 12 additions & 0 deletions src/server/sessions.ts
SIsilicon marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ interface regionTransform {
flip: Vector
}

interface superPickaxe {
enabled: boolean,
mode: "single" | "area" | "recursive",
range: number
}

/**
* Represents a WorldEdit user's current session with the addon.
* It manages their selections, operation history, and other things related to WorldEdit per player.
Expand Down Expand Up @@ -82,6 +88,12 @@ export class PlayerSession {
flip: Vector.ONE
};

public superPickaxe: superPickaxe = {
enabled: false,
mode: "single",
range: 0
};

public selection: Selection;

private player: Player;
Expand Down
55 changes: 28 additions & 27 deletions src/server/tools/base_tool.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { BlockPermutation, Player, system } from "@minecraft/server";
import { Player, system } from "@minecraft/server";
import { PlayerSession } from "../sessions.js";
import { Server, Thread } from "@notbeer-api";
import { print, printerr } from "../util.js";
import { RawText, Vector } from "@notbeer-api";

export enum ToolAction {
USE = "use",
USE_ON = "useOn",
BREAK = "break",
HIT = "hit"
}

/**
* The base tool class for handling tools that WorldEdit builders may use.
*/
Expand All @@ -15,15 +22,19 @@ export abstract class Tool {
/**
* The function that's called when the tool is being used on a block.
*/
readonly useOn: (self: Tool, player: Player, session: PlayerSession, loc: Vector) => void | Generator<unknown>;
readonly useOn: (self: Tool, player: Player, session: PlayerSession, loc: Vector) => void | Generator<unknown, void>;
/**
* The function that's called every tick the tool is held.
* The function that's called when the tool has broken a block.
*/
readonly tick: (self: Tool, player: Player, session: PlayerSession, tick: number) => Generator<unknown>;
readonly break: (self: Tool, player: Player, session: PlayerSession, loc: Vector) => void | Generator<unknown, void>;
/**
* The function that's called when the tool has broken a block.
* The function that's called when the tool has hit a block.
*/
readonly breakOn: (self: Tool, player: Player, session: PlayerSession, loc: Vector, brokenBlock: BlockPermutation) => void;
readonly hit: (self: Tool, player: Player, session: PlayerSession, loc: Vector) => void | Generator<unknown, void>;
/**
* The function that's called every tick the tool is held.
*/
readonly tick: (self: Tool, player: Player, session: PlayerSession, tick: number) => Generator<unknown>;
/**
* The permission required for the tool to be used.
*/
Expand All @@ -47,12 +58,11 @@ export abstract class Tool {
private useOnTick = 0;
private lastUse = system.currentTick;

process(session: PlayerSession, tick: number, loc?: Vector, brokenBlock?: BlockPermutation): boolean {
process(session: PlayerSession, tick: number, action: ToolAction, loc?: Vector): boolean {
const player = session.getPlayer();

if (!loc && !this.use || loc && !brokenBlock && !this.useOn || brokenBlock && !this.breakOn) {
return false;
}
if (!this[action]) return false;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const onFail = (e: any) => {
printerr(e.message ? RawText.text(`${e.name}: `).append("translate", e.message) : e, player, true);
Expand All @@ -61,7 +71,7 @@ export abstract class Tool {
}
};

new Thread().start(function* (self: Tool, player: Player, session: PlayerSession, loc: Vector, brokenBlock: BlockPermutation) {
new Thread().start(function* (self: Tool, player: Player, session: PlayerSession, action: ToolAction, loc: Vector) {
self.currentPlayer = player;
session.usingItem = true;
try {
Expand All @@ -72,23 +82,14 @@ export abstract class Tool {
if (system.currentTick - self.lastUse > 4 || self.noDelay) {
self.lastUse = system.currentTick;

if (!loc) {
if (self.useOnTick != tick) {
if (self.use.constructor.name == "GeneratorFunction") {
yield* self.use(self, player, session) as Generator<void, void>;
} else {
self.use(self, player, session) as void;
}
}
} else if (!brokenBlock) {
self.useOnTick = tick;
if (self.useOn.constructor.name == "GeneratorFunction") {
yield* self.useOn(self, player, session, loc) as Generator<void, void>;
if (!(action == ToolAction.USE && self.useOnTick == tick)) {
if (action == ToolAction.USE_ON) self.useOnTick = tick;
const func = self[action];
if (func.constructor.name == "GeneratorFunction") {
yield* func(self, player, session, loc) as Generator<unknown, void>;
} else {
self.useOn(self, player, session, loc) as void;
func(self, player, session, loc) as void;
}
} else {
self.breakOn(self, player, session, loc, brokenBlock);
}
}
} catch(e) {
Expand All @@ -97,7 +98,7 @@ export abstract class Tool {
session.usingItem = false;
}
self.currentPlayer = null;
}, this, player, session, loc, brokenBlock);
}, this, player, session, action, loc);
return true;
}

Expand Down
4 changes: 2 additions & 2 deletions src/server/tools/cycler_tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ class BlockCyclerTool extends Tool {
permission = "worldedit.cycler";
useOn = function (self: BlockCyclerTool, player: Player, session: PlayerSession, loc: Vector3) {
if (player.isSneaking) {
self.breakOn(self, player, session, loc);
self.break(self, player, session, loc);
} else {
self.update(player, loc, true);
}
};

breakOn = function (self: BlockCyclerTool, player: Player, session: PlayerSession, loc: Vector3) {
break = function (self: BlockCyclerTool, player: Player, session: PlayerSession, loc: Vector3) {
self.stateIndex++;
self.update(player, loc, false);
};
Expand Down
1 change: 1 addition & 0 deletions src/server/tools/register_tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ import "./replacer_tool.js";
import "./cycler_tool.js";
import "./button_tools.js";
import "./brush_tools.js";
import "./superpickaxe_tool.js";
4 changes: 2 additions & 2 deletions src/server/tools/replacer_tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ class BlockReplacerTool extends Tool {
permission = "worldedit.repl";
useOn = function (self: BlockReplacerTool, player: Player, session: PlayerSession, loc: Vector3) {
if (player.isSneaking) {
self.breakOn(self, player, session, loc);
self.break(self, player, session, loc);
} else {
self.pattern.setBlock(player.dimension.getBlock(loc));
}
};

breakOn = function (self: BlockReplacerTool, player: Player, session: PlayerSession, loc: Vector3) {
break = function (self: BlockReplacerTool, player: Player, session: PlayerSession, loc: Vector3) {
const pattern = new Pattern();
pattern.addBlock(player.dimension.getBlock(loc).permutation);
session.setToolProperty(null, "pattern", pattern);
Expand Down
4 changes: 2 additions & 2 deletions src/server/tools/selection_tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class SelectionTool extends Tool {
useOn = function (self: Tool, player: Player, session: PlayerSession, loc: Vector3) {
Server.command.callCommand(player, "pos2", [`${loc.x}`, `${loc.y}`, `${loc.z}`]);
};
breakOn = function (self: Tool, player: Player, session: PlayerSession, loc: Vector3) {
break = function (self: Tool, player: Player, session: PlayerSession, loc: Vector3) {
Server.command.callCommand(player, "pos1", [`${loc.x}`, `${loc.y}`, `${loc.z}`]);
};
}
Expand All @@ -20,7 +20,7 @@ class FarSelectionTool extends Tool {
use = function (self: Tool, player: Player) {
Server.command.callCommand(player, player.isSneaking ? "hpos1" : "hpos2");
};
breakOn = function (self: Tool, player: Player) {
break = function (self: Tool, player: Player) {
Server.command.callCommand(player, "hpos1");
};
}
Expand Down
76 changes: 76 additions & 0 deletions src/server/tools/superpickaxe_tool.ts
SIsilicon marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Dimension, Player, Vector3 } from "@minecraft/server";
import { PlayerSession } from "../sessions.js";
import { Tool } from "./base_tool.js";
import { Tools } from "./tool_manager.js";
import { Server, Vector, regionIterateBlocks } from "@notbeer-api";
import { getWorldHeightLimits, locToString } from "server/util.js";
import config from "config.js";

class SuperPickaxeTool extends Tool {
noDelay = true;
permission = "worldedit.superpickaxe";

break = function* (self: SuperPickaxeTool, player: Player, session: PlayerSession, loc: Vector): Generator<void> {
const dimension = player.dimension;
const typeId = dimension.getBlock(loc).typeId;
if (typeId == "minecraft:air") return;
const { mode, range } = session.superPickaxe;
if (mode == "single") {
destroyBlock(dimension, loc, config.superPickaxeDrop);
return;
}
const limits = getWorldHeightLimits(dimension);
if (mode == "area") {
const min = loc.sub(range);
const max = loc.add(range);
min.y = Math.max(min.y, limits[0]);
max.y = Math.min(max.y, limits[1]);
for (const block of regionIterateBlocks(min, max)) {
if (dimension.getBlock(block).typeId == typeId) {
destroyBlock(dimension, block, config.superPickaxeManyDrop);
}
yield;
}
return;
}
const rangeSqr = range * range;
const queue: Vector[] = [loc];
const visited = new Set<string>();
while (queue.length) {
const block = queue.shift();
const str = locToString(block);
if (!visited.has(str) && loc.sub(block).lengthSqr <= rangeSqr && block.y >= limits[0] && block.y <= limits[1] && dimension.getBlock(block).typeId == typeId) {
visited.add(str);
destroyBlock(dimension, block, config.superPickaxeManyDrop);
for (const offset of [[0, 1, 0], [0, -1, 0], [1, 0, 0], [-1, 0, 0], [0, 0, 1], [0, 0, -1]] as [number, number, number][]) {
queue.push(block.add(offset));
}
}
yield;
}
};

hit = this.break;

constructor() {
super();
}
}
Tools.register(SuperPickaxeTool, "superpickaxe", [
"minecraft:diamond_pickaxe",
"minecraft:golden_pickaxe",
"minecraft:iron_pickaxe",
"minecraft:netherite_pickaxe",
"minecraft:stone_pickaxe",
"minecraft:wooden_pickaxe"
], function (player: Player, session: PlayerSession) {
return session.superPickaxe.enabled;
});

function destroyBlock(dimension: Dimension, loc: Vector3, drop: boolean) {
if (drop) {
Server.runCommand(`setblock ${loc.x} ${loc.y} ${loc.z} air destroy`, dimension);
} else {
dimension.getBlock(loc).setType("minecraft:air");
}
}
Loading
Loading