refactor(c4): restructure C4 scripts and move server side code

This commit is contained in:
2026-01-18 13:40:06 +08:00
parent f996cfff58
commit 1e27eb7e8c
2 changed files with 203 additions and 159 deletions

View File

@@ -1,8 +1,11 @@
/**
* C4 Server Scripts
* Handles C4 block break event to cancel explosion
* Handles C4 use started and activated events
*/
// ==================== Block Break Event Handler ====================
BlockEvents.broken((event) => {
const { block, server } = event;
@@ -43,3 +46,189 @@ BlockEvents.broken((event) => {
}
}
});
// ==================== C4 Event Handlers ====================
/**
* @param {{player: Internal.Player}} event
*/
function handleC4UseStarted(event) {
const server = Utils.getServer();
if (server === null) {
console.error("C4 Handler: Server is not available");
return;
}
// Get shared variables from global
/** @type {typeof shouldActivateC4} */
const shouldActivateC4 = /** @type {any} */ (global["shouldActivateC4"]);
/** @type {typeof shouldStartUseC4} */
const shouldStartUseC4 = /** @type {any} */ (global["shouldStartUseC4"]);
/** @type {{ [key: string]: any }} */
const lastPlayerInfoMap = /** @type {any} */ (global["lastPlayerInfoMap"]);
/** @type {number} */
const C4_USE_TIME = /** @type {any} */ (global["C4_USE_TIME"]);
if (
shouldActivateC4 === undefined ||
shouldStartUseC4 === undefined ||
lastPlayerInfoMap === undefined ||
C4_USE_TIME === undefined
) {
console.error("C4 Handler: Required global variables not available");
return;
}
const player = server.getPlayerList().getPlayer(event.player.uuid);
const level = player.level;
const startTime = level.levelData.gameTime;
const originalItemstack = player.mainHandItem;
server.scheduleRepeatingInTicks(2, (event) => {
const itemstack = player.getMainHandItem();
if (
!shouldActivateC4(
itemstack,
player.level,
/** @type {any} */ (player),
)
) {
player.stopUsingItem();
player.addItemCooldown(originalItemstack.item, 20);
originalItemstack.releaseUsing(
level,
/** @type {any} */ (player),
originalItemstack.count,
);
event.clear();
return;
}
// Get remaining ticks for this use
const remainingTicks =
C4_USE_TIME - (level.levelData.gameTime - startTime);
if (remainingTicks <= 0) {
originalItemstack.finishUsingItem(
level,
/** @type {any} */ (player),
);
delete lastPlayerInfoMap[player.uuid.toString()];
event.clear();
return;
}
itemstack.setHoverName(
/** @type {any} */ (
Component.literal(`C4 - ${(remainingTicks / 20.0).toFixed(1)}s`)
),
);
});
}
/**
* Handle C4 activation event
* @param {C4ActivatedEvent} event
*/
function handleC4Activated(event) {
const server = Utils.getServer();
if (server === null) {
console.error("C4 Handler: Server is not available");
return;
}
const { level, player, explosionTime, explosionPower } = event;
// Get shared variables from global
/** @type {{[key:string]: boolean | null}} */
const toExplosionC4Map = /** @type {any} */ (global["toExplosionC4Map"]);
if (toExplosionC4Map === undefined || toExplosionC4Map === null) {
console.error("C4 Handler: toExplosionC4Map is not available");
return;
}
// Place C4 at player's feet
const c4BlockPos = {
x: Math.floor(player.x),
y: Math.floor(player.y),
z: Math.floor(player.z),
};
const newBlock = level.getBlock(c4BlockPos.x, c4BlockPos.y, c4BlockPos.z);
newBlock.set(/** @type {any} */ ("kubejs:c4"));
// Add record
const newBlockPosString = newBlock.pos.toShortString();
toExplosionC4Map[newBlockPosString] = true;
/**
* TODO: It should use reschedule to replace several schedules
* But reschedule not work at current time.
* Relative Issue: https://github.com/KubeJS-Mods/KubeJS/issues/763
*/
let remainingSeconds = explosionTime / 20;
server.scheduleRepeatingInTicks(20, (scheduledEvent) => {
// Assert C4 exsiting
if (toExplosionC4Map[newBlockPosString] === null) {
scheduledEvent.clear();
return;
}
remainingSeconds -= 1;
if (remainingSeconds <= 0) {
scheduledEvent.clear();
return;
}
server.players.forEach((p) => {
p.tell(
/** @type {any} */ (
Component.literal(`C4还剩 ${remainingSeconds} 秒爆炸`)
),
);
});
});
// Create explosion after countdown
server.scheduleInTicks(explosionTime, (_) => {
// Assert C4 exsiting
if (toExplosionC4Map[newBlockPosString] === null) return;
level.explode(
/** @type {any} */ (null),
c4BlockPos.x + 0.5,
c4BlockPos.y + 0.5,
c4BlockPos.z + 0.5,
explosionPower,
"block",
);
});
}
// ==================== Server Initialization ====================
ServerEvents.loaded((event) => {
/**
* WARNING: Must Do!!!
* Because Kubejs scheduler is not stable
* And need to fire once at first time
* Relative Issue: https://github.com/KubeJS-Mods/KubeJS/issues/763
*/
event.server.scheduleInTicks(1, (_) => {
console.log("Init Scheduler");
});
/** @type {EventBus} */
const eventBus = /** @type {any} */ (global["eventBus"]);
if (eventBus === null) {
console.error("C4 Handler: eventBus is not available");
return;
}
eventBus.register("C4Activated", handleC4Activated);
eventBus.register("C4UseStarted", handleC4UseStarted);
console.log("C4 Handler: Registered C4Activated event handler");
});

View File

@@ -2,14 +2,15 @@ const $TickEvent$PlayerTickEvent = Java.loadClass(
"net.minecraftforge.event.TickEvent$PlayerTickEvent",
);
const $ServerStartedEvent = Java.loadClass(
"net.minecraftforge.event.server.ServerStartedEvent",
);
const C4_EXPLOSION_TIME = 10 * 20; // 7 seconds in ticks
const C4_EXPLOSION_POWER = 128; // Explosion power (TNT is 4)
const C4_USE_TIME = 5 * 20; // 5 seconds in ticks
// Export constants for server scripts
global["C4_EXPLOSION_TIME"] = C4_EXPLOSION_TIME;
global["C4_EXPLOSION_POWER"] = C4_EXPLOSION_POWER;
global["C4_USE_TIME"] = C4_USE_TIME;
// Tolerance for floating point comparison
const ANGLE_TOLERANCE = 0.001;
const POS_TOLERANCE = 0.01;
@@ -28,6 +29,9 @@ let operationKeyMapping;
*/
const lastPlayerInfoMap = {};
// Export for server scripts
global["lastPlayerInfoMap"] = lastPlayerInfoMap;
/**
* @type {{[key:string]: boolean}}
*/
@@ -114,6 +118,9 @@ function shouldActivateC4(itemstack, level, player) {
);
}
// Export for server scripts
global["shouldActivateC4"] = shouldActivateC4;
/**
* @param {Internal.Player} player
* @param {Internal.Level} level
@@ -150,6 +157,9 @@ function shouldStartUseC4(player, level) {
return true;
}
// Export for server scripts
global["shouldStartUseC4"] = shouldStartUseC4;
// ==================== Block Registration ====================
StartupEvents.registry("block", (event) => {
@@ -280,158 +290,3 @@ ForgeEvents.onEvent($TickEvent$PlayerTickEvent, (event) => {
}
}
});
// ==================== Server Side Logic ====================
/**
* @param {{player: Internal.Player}} event
*/
function handleC4UseStarted(event) {
const server = Utils.getServer();
if (server === null) {
console.error("C4 Handler: Server is not available");
return;
}
const player = server.getPlayerList().getPlayer(event.player.uuid);
const level = player.level;
const startTime = level.levelData.gameTime;
const originalItemstack = player.mainHandItem;
server.scheduleRepeatingInTicks(2, (event) => {
const itemstack = player.getMainHandItem();
if (
!shouldActivateC4(
itemstack,
player.level,
/** @type {any} */ (player),
)
) {
player.stopUsingItem();
player.addItemCooldown(originalItemstack.item, 20);
originalItemstack.releaseUsing(
level,
/** @type {any} */ (player),
originalItemstack.count,
);
event.clear();
return;
}
// Get remaining ticks for this use
const remainingTicks =
C4_USE_TIME - (level.levelData.gameTime - startTime);
if (remainingTicks <= 0) {
originalItemstack.finishUsingItem(
level,
/** @type {any} */ (player),
);
delete lastPlayerInfoMap[player.uuid.toString()];
event.clear();
return;
}
itemstack.setHoverName(
/** @type {any} */ (
Component.literal(`C4 - ${(remainingTicks / 20.0).toFixed(1)}s`)
),
);
});
}
/**
* Handle C4 activation event
* @param {C4ActivatedEvent} event
*/
function handleC4Activated(event) {
const server = Utils.getServer();
if (server === null) {
console.error("C4 Handler: Server is not available");
return;
}
const { level, player, explosionTime, explosionPower } = event;
// Place C4 at player's feet
const c4BlockPos = {
x: Math.floor(player.x),
y: Math.floor(player.y),
z: Math.floor(player.z),
};
const newBlock = level.getBlock(c4BlockPos.x, c4BlockPos.y, c4BlockPos.z);
newBlock.set(/** @type {any} */ ("kubejs:c4"));
// Add record
const newBlockPosString = newBlock.pos.toShortString();
toExplosionC4Map[newBlockPosString] = true;
/**
* TODO: It should use reschedule to replace several schedules
* But reschedule not work at current time.
* Relative Issue: https://github.com/KubeJS-Mods/KubeJS/issues/763
*/
let remainingSeconds = explosionTime / 20;
server.scheduleRepeatingInTicks(20, (scheduledEvent) => {
// Assert C4 exsiting
if (toExplosionC4Map[newBlockPosString] === null) {
scheduledEvent.clear();
return;
}
remainingSeconds -= 1;
if (remainingSeconds <= 0) {
scheduledEvent.clear();
return;
}
server.players.forEach((p) => {
p.tell(
/** @type {any} */ (
Component.literal(`C4还剩 ${remainingSeconds} 秒爆炸`)
),
);
});
});
// Create explosion after countdown
server.scheduleInTicks(explosionTime, (_) => {
// Assert C4 exsiting
if (toExplosionC4Map[newBlockPosString] === null) return;
level.explode(
/** @type {any} */ (null),
c4BlockPos.x + 0.5,
c4BlockPos.y + 0.5,
c4BlockPos.z + 0.5,
explosionPower,
"block",
);
});
}
ForgeEvents.onEvent($ServerStartedEvent, (event) => {
/**
* WARNING: Must Do!!!
* Because Kubejs scheduler is not stable
* And need to fire once at first time
* Relative Issue: https://github.com/KubeJS-Mods/KubeJS/issues/763
*/
event.server.scheduleInTicks(1, (_) => {
console.log("Init Scheduler");
});
/** @type {EventBus} */
const eventBus = /** @type {any} */ (global["eventBus"]);
if (eventBus === null) {
console.error("C4 Handler: eventBus is not available");
return event;
}
eventBus.register("C4Activated", handleC4Activated);
eventBus.register("C4UseStarted", handleC4UseStarted);
console.log("C4 Handler: Registered C4Activated event handler");
});