c699dcc77b
Tidying pass on the KubeJS suite before plot system V1 lands. Sets up
the file structure + conventions + diagnostics so the larger plot
system has a clean foundation.
Changes:
1. NEW kubejs/README.md
File layout doc + conventions: logging prefix [bns/<module>],
persistent-data key naming (bns* prefix), one concern per file,
performance discipline (O(1) lookups, no polling).
2. Split economy_policy.js -> recipes_waystones.js + recipes_numismatics.js
Single-concern files. recipes_waystones removes ALL waystones
crafting (welcome.js distributes the only allowed waystone instead).
recipes_numismatics removes the 5 coin-denomination crafts.
3. NEW kubejs/server_scripts/bns_admin.js
/bns admin subcommands for OP diagnostics:
- info calling player's flags+counts
- waystone-count <player> read stored count
- waystone-set <player> <n> override stored count
- reset-welcome <player> clear bnsWelcomeGranted -- replays
the welcome grant on next login
Registered via ServerEvents.commandRegistry with Brigadier tree,
permission level 2 (OP). No performance cost (operator-triggered only).
4. Logging added to waystones_policy.js + welcome.js
Every action prints `[bns/<module>] ...` so server logs can be
filtered with `journalctl ... | grep '\[bns/'`.
Performance discipline applied:
- All event-handler lookups use Set.has (O(1)), not Array.includes
- No periodic tick polling anywhere
- No network calls in event handlers
- console.info is cheap (no string format unless logged)
Bumps to 0.20.0. Plot system V1 follows in v0.21.x as a separate
multi-file submodule under server_scripts/plots/.
98 lines
3.7 KiB
JavaScript
98 lines
3.7 KiB
JavaScript
// Brass & Sigil — Waystones placement policy
|
|
//
|
|
// Rules:
|
|
// - Regular waystones (6 stone variants): 1 per player. Forces players to
|
|
// build actual transport infrastructure (trains/roads/canals) for
|
|
// long-distance routes instead of waystone-spamming.
|
|
// - Sharestones (16 colors): banned. Pair-teleport between solo-placed
|
|
// sharestones defeats the "build transport" intent, and a sharestone
|
|
// placed just outside someone's claim gives an unrestricted teleport-
|
|
// adjacent vector.
|
|
// - Portstones (16 colors): banned pending separate design discussion.
|
|
// - Warp plates: not restricted (single-block teleport pad; revisit if
|
|
// it becomes a workaround).
|
|
// - Warp scrolls / warp stones: untouched (consumable / cooldown-bound).
|
|
//
|
|
// Per-player count lives in player.persistentData.bnsWaystoneCount.
|
|
// Decrement on break works because waystones-common.toml has
|
|
// restrictedWaystones = ["PLAYER"] -- only the owner can break their own
|
|
// waystone, so breaker == owner in normal play.
|
|
|
|
const WAYSTONE_CAP = 1;
|
|
|
|
const REGULAR_WAYSTONES = new Set([
|
|
'waystones:waystone',
|
|
'waystones:mossy_waystone',
|
|
'waystones:sandy_waystone',
|
|
'waystones:blackstone_waystone',
|
|
'waystones:deepslate_waystone',
|
|
'waystones:end_stone_waystone',
|
|
]);
|
|
|
|
// Build the colored-block ban list once instead of typing 32 strings.
|
|
const COLORS = [
|
|
'white', 'orange', 'magenta', 'light_blue', 'yellow', 'lime',
|
|
'pink', 'gray', 'light_gray', 'cyan', 'purple', 'blue',
|
|
'brown', 'green', 'red', 'black',
|
|
];
|
|
const BANNED_BLOCKS = new Set();
|
|
COLORS.forEach(c => {
|
|
BANNED_BLOCKS.add(`waystones:${c}_sharestone`);
|
|
BANNED_BLOCKS.add(`waystones:${c}_portstone`);
|
|
});
|
|
|
|
// Helper: replace the just-placed block with air and refund the item.
|
|
function refund(event, blockId) {
|
|
event.block.set('minecraft:air');
|
|
if (event.player) {
|
|
event.player.give(Item.of(blockId));
|
|
}
|
|
}
|
|
|
|
BlockEvents.placed(event => {
|
|
const blockId = event.block.id;
|
|
const player = event.player;
|
|
|
|
// Skip non-player placements (dispensers, mob events) -- nothing to refund.
|
|
if (!player) return;
|
|
|
|
if (BANNED_BLOCKS.has(blockId)) {
|
|
refund(event, blockId);
|
|
player.tell(Text.red(
|
|
'That block is disabled. Use a regular Waystone (1 per player) ' +
|
|
'and build transport between distant bases.'
|
|
));
|
|
console.info(`[bns/waystones_policy] denied ${blockId} placement by ${player.username} (banned)`);
|
|
return;
|
|
}
|
|
|
|
if (REGULAR_WAYSTONES.has(blockId)) {
|
|
const data = player.persistentData;
|
|
const current = data.getInt('bnsWaystoneCount');
|
|
if (current >= WAYSTONE_CAP) {
|
|
refund(event, blockId);
|
|
player.tell(Text.red(
|
|
`You already have ${WAYSTONE_CAP} waystone. Break your existing one ` +
|
|
`to relocate, or build infrastructure to reach distant bases.`
|
|
));
|
|
console.info(`[bns/waystones_policy] denied ${blockId} placement by ${player.username} (already at cap ${current})`);
|
|
return;
|
|
}
|
|
data.putInt('bnsWaystoneCount', current + 1);
|
|
console.info(`[bns/waystones_policy] +1 waystone for ${player.username} (now ${current + 1})`);
|
|
}
|
|
});
|
|
|
|
BlockEvents.broken(event => {
|
|
const blockId = event.block.id;
|
|
if (!REGULAR_WAYSTONES.has(blockId)) return;
|
|
const player = event.player;
|
|
if (!player) return; // explosion / world clear -- accept count drift, rare
|
|
const data = player.persistentData;
|
|
const current = data.getInt('bnsWaystoneCount');
|
|
if (current > 0) {
|
|
data.putInt('bnsWaystoneCount', current - 1);
|
|
console.info(`[bns/waystones_policy] -1 waystone for ${player.username} (now ${current - 1})`);
|
|
}
|
|
});
|