Skip to content
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
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,6 @@ for custom locales, from English. Existing translations are preserved.
| Existing sticks stop working after an item config change | Reissue them with `/dsp give`; recognition uses the currently configured material. |
| Frozen blocks need to be released | The player can left-click in Freeze mode, or an administrator can run `/dsp reload`. |

Known limitation: players must sneak to change a candle's `lit` value.

### Compatibility testing

Continuous integration builds the plugin and starts every stable Paper and Folia
Expand Down
75 changes: 72 additions & 3 deletions integration/mineflayer/test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,47 @@ function waitFor(predicate, description, timeout = timeoutMs) {
})
}

async function rightClickBlock(target) {
async function rightClickBlock(target, cursorHeight = 0.5, cursorInset = 0, includeOffhand = false) {
const swingArm = bot.swingArm
bot.swingArm = () => {}
try {
const westFace = target.position.offset(-1, 0, 0).minus(target.position)
const westFaceCenter = target.position.offset(0, 0.5, 0.5).minus(target.position)
const westFaceCenter = target.position.offset(cursorInset, cursorHeight, 0.5).minus(target.position)
await bot.activateBlock(target, westFace, westFaceCenter)
if (includeOffhand) {
bot._client.write('block_place', {
location: target.position,
direction: 4,
hand: 1,
cursorX: cursorInset,
cursorY: cursorHeight,
cursorZ: 0.5,
insideBlock: false,
sequence: 0,
worldBorderHit: false
})
}
} finally {
bot.swingArm = swingArm
}
}

async function leftClickBlock(target) {
const packet = {
location: target.position,
face: 1
}
bot._client.write('block_dig', { ...packet, status: 0 })
bot.swingArm('right')
await bot.waitForTicks(1)
bot._client.write('block_dig', { ...packet, status: 1 })
await bot.waitForTicks(2)
}

function blockProperty(position, property) {
return bot.blockAt(position)?.getProperties()?.[property]
}

async function exerciseFreezeLifecycle(target, expectedBlockName) {
const packetOffset = entityPackets.length
await rightClickBlock(target)
Expand Down Expand Up @@ -140,14 +169,53 @@ try {
await bot.equip(debugStick, 'hand')
await bot.waitForTicks(20)

const targetPosition = bot.entity.position.floored().offset(1, 0, 0)
bot.chat(`/setblock ${targetPosition.x} ${targetPosition.y} ${targetPosition.z} minecraft:candle[lit=false]`)
const candle = await waitFor(() => {
const block = bot.blockAt(targetPosition)
return block?.name === 'candle' ? block : null
}, 'integration-test candle block')
await bot.lookAt(candle.position.offset(0.5, 0.2, 0.5), true)

// The first right click initializes CandleData; one left click then advances to LightableData.
await rightClickBlock(candle, 0.2, 0.4375)
await leftClickBlock(bot.blockAt(targetPosition))
bot.chat(`/setblock ${targetPosition.x} ${targetPosition.y} ${targetPosition.z} minecraft:candle[lit=false]`)
await waitFor(
() => blockProperty(targetPosition, 'lit') === false,
'candle selection reset'
)

bot.setControlState('sneak', true)
await bot.waitForTicks(2)
await rightClickBlock(candle, 0.2, 0.4375)
await waitFor(
() => blockProperty(targetPosition, 'lit') === true,
'candle lighting while sneaking'
)

bot.setControlState('sneak', false)
await bot.waitForTicks(2)
bot.chat(`/setblock ${targetPosition.x} ${targetPosition.y} ${targetPosition.z} minecraft:candle[lit=false]`)
await waitFor(
() => blockProperty(targetPosition, 'lit') === false,
'unlit candle reset'
)
await rightClickBlock(bot.blockAt(targetPosition), 0.2, 0.4375, true)
await bot.waitForTicks(10)
assert.equal(
blockProperty(targetPosition, 'lit'),
true,
'Classic mode must light a candle without sneaking'
)

bot.chat('/dsp mode freeze')
await waitFor(
() => messages.some(message => /freeze|凍結/i.test(message)),
'freeze mode confirmation'
)
await bot.waitForTicks(10)

const targetPosition = bot.entity.position.floored().offset(1, 0, 0)
bot.chat(`/setblock ${targetPosition.x} ${targetPosition.y} ${targetPosition.z} minecraft:stone`)
const target = await waitFor(() => {
const block = bot.blockAt(targetPosition)
Expand All @@ -172,6 +240,7 @@ try {
version,
locale: 'zh_TW',
command: true,
candleWithoutSneaking: true,
miniMessageItem: debugStick.customName?.toString() ?? debugStick.displayName,
virtualEntities: ['item_display', 'block_display'],
removal: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import dev.twme.debugstickpro.playerdata.PlayerDataManager;
import dev.twme.debugstickpro.utils.DebugStickItem;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
Expand All @@ -26,10 +27,14 @@ public void onLeftClickEvent(PlayerInteractEvent event) {
return;
}

Block targetBlock = event.getClickedBlock();
if (targetBlock == null) {
targetBlock = player.getTargetBlockExact(5);
}

event.setCancelled(true);

// TODO: 未來改成這個版本
PlayerDataManager.playerLeftClick(player.getUniqueId());
PlayerDataManager.playerLeftClick(player.getUniqueId(), targetBlock);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@
public class RightClickListener implements Listener {
@EventHandler
public void onRightClick(PlayerInteractEvent event) {

if (event.getHand() != EquipmentSlot.HAND) {
return;
}

Player player = event.getPlayer();

if (!player.hasPermission("debugstickpro.use")) {
Expand All @@ -36,20 +31,31 @@ public void onRightClick(PlayerInteractEvent event) {
return;
}

Block targetBlock = event.getClickedBlock();
if (targetBlock == null) {
targetBlock = player.getTargetBlockExact(5);
}

// In classic mode, don't cancel the event if the target block has no available SubBlockData
PlayerData playerData = PlayerDataManager.getOrCreatePlayerData(player.getUniqueId());
if (playerData.getDebugStickMode() == DebugStickMode.CLASSIC) {
Block targetBlock = player.getTargetBlockExact(5);
if (targetBlock == null || BlockDataSeparater.separate(targetBlock, player.getUniqueId()).isEmpty()) {
return;
}
}

event.setCancelled(true);

// The vanilla client may follow a main-hand PASS with an off-hand interaction.
// Consume that event, but only execute the Debug Stick action for the main hand.
if (event.getHand() != EquipmentSlot.HAND) {
return;
}

PlayerDataManager.playerRightClick(
player.getUniqueId(),
event.getAction(),
targetBlock,
event.getClickedBlock(),
event.getBlockFace()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ public class ClassicLeftClick {

// 更改選擇的 SubBlockData 類型
// change selected SubBlockData type
public static void changeSelectedSubBlockType(UUID playerUUID, PlayerData playerData) {
public static void changeSelectedSubBlockType(UUID playerUUID, PlayerData playerData, Block block) {

Player player = Bukkit.getPlayer(playerUUID);

Block block = player.getTargetBlockExact(5);

if (block == null) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@
import java.util.UUID;

public class ClassicRightClick {
public static void changeSelectedSubBlockDataValue(UUID playerUUID, PlayerData playerData) {
public static void changeSelectedSubBlockDataValue(UUID playerUUID, PlayerData playerData, Block block) {

Player player = Bukkit.getPlayer(playerUUID);

Block block = player.getTargetBlockExact(5);

if (block == null) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@
import dev.twme.debugstickpro.utils.AutoCheckCanChangeUtil;

public class CopyLeftClick {
public static void onLeftClick(UUID playerUUID, PlayerData playerData) {
public static void onLeftClick(UUID playerUUID, PlayerData playerData, Block block) {
Player player = Bukkit.getPlayer(playerUUID);
Block block = player.getTargetBlockExact(5);

if (block == null) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@
import java.util.UUID;

public class CopyRightClick {
public static void onRightClick(UUID playerUUID, PlayerData playerData) {
public static void onRightClick(UUID playerUUID, PlayerData playerData, Block block) {
Player player = Bukkit.getPlayer(playerUUID);
Block block = player.getTargetBlockExact(5);

if (block == null) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,17 +208,18 @@ public static boolean modeChangeEventCancelled(UUID playerUUID, DebugStickMode p
* player left click
*
* @param uuid player UUID
* @param targetBlock interaction target, or null when no block was targeted
*/
public static void playerLeftClick(UUID uuid) {
public static void playerLeftClick(UUID uuid, Block targetBlock) {

PlayerData playerData = getOrCreatePlayerData(uuid);

switch (playerData.getDebugStickMode()) {
case CLASSIC:
ClassicLeftClick.changeSelectedSubBlockType(uuid, playerData);
ClassicLeftClick.changeSelectedSubBlockType(uuid, playerData, targetBlock);
break;
case COPY:
CopyLeftClick.onLeftClick(uuid, playerData);
CopyLeftClick.onLeftClick(uuid, playerData, targetBlock);
break;
case FREEZE:
FreezeLeftClick.onLeftClick(uuid);
Expand All @@ -231,19 +232,20 @@ public static void playerLeftClick(UUID uuid) {
*
* @param uuid player UUID
* @param action click action
* @param targetBlock interaction target, or null when no block was targeted
* @param clickedBlock clicked block
* @param clickedFace clicked face
*/
public static void playerRightClick(UUID uuid, Action action, Block clickedBlock, BlockFace clickedFace) {
public static void playerRightClick(UUID uuid, Action action, Block targetBlock, Block clickedBlock, BlockFace clickedFace) {

PlayerData playerData = getOrCreatePlayerData(uuid);

switch (playerData.getDebugStickMode()) {
case CLASSIC:
ClassicRightClick.changeSelectedSubBlockDataValue(uuid, playerData);
ClassicRightClick.changeSelectedSubBlockDataValue(uuid, playerData, targetBlock);
break;
case COPY:
CopyRightClick.onRightClick(uuid, playerData);
CopyRightClick.onRightClick(uuid, playerData, targetBlock);
break;
case FREEZE:
FreezeRightClick.onRightClick(uuid, action, clickedBlock, clickedFace);
Expand Down
Loading