概要
runCommand() と Script の処理時間を比較します。
この記事は影月氏の hasItemスピードテスト(コマンド VS ScriptAPI) のインスパイアです。
おそらく他処理の検証も追記します。
バージョン:@minecraft/server 1.13.0
検証
検証内容 : スコア検知
プレイヤーまたは仮想スコアに、特定のスコア値があるかどうか返すプログラム(10000回)
プレイヤースコアが1かどうか
スコア無し command : 306 ms / script 108
スコア不一致 command : 264 ms / script 75
スコア一致 command : 271 ms / script 74
仮想スコア(test)が1かどうか
スコア無し command : 321 ms / script 106
スコア不一致 command : 296 ms / script 77
スコア一致 command : 267 ms / script 76
Script が優勢
// 特定のスコア値を持っているかどうかを返す関数
function hasScoreScript(target, objectiveId, score) {
try {
const objective = world.scoreboard.getObjective(objectiveId);
return objective?.getScore(target) === score;
} catch {
return false;
}
}
function hasScoreCommand(target, objectiveId, score) {
let result;
if (typeof target === "string") {
result = world.getDimension("overworld").runCommand(`execute if score ${target} ${objectiveId} matches ${score}`);
} else {
result = target.runCommand(`execute if score @s ${objectiveId} matches ${score}`);
}
return result.successCount === 1;
}
検証内容 : タグの追加・削除
特定の座標とディメンションにおいて、指定したブロックを設置するプログラム(10000回)
1回目 command : 480 ms / script 66
2回目 command : 495 ms / script 86
3回目 command : 469 ms / script 63
Script が優勢
// プレイヤーにタグのつけ外しを行う関数
function addRemoveTagCommand(player, tag) {
player.runCommand(`tag @s add ${tag}`);
player.runCommand(`tag @s remove ${tag}`);
}
function addRemoveTagScript(player, tag) {
player.addTag(tag);
player.removeTag(tag);
}
検証内容 : ブロックの設置
特定の座標とディメンションに、指定したブロックを設置するプログラム(10000回)
<Dimension>.getBlock().setType() | .setPermutation() の場合
ブロックstates無
1回目 command : 327 ms / script(setPerm) 127 / script(setType) 116
2回目 command : 309 ms / script(setPerm) 123 / script(setType) 113
3回目 command : 304 ms / script(setPerm) 122 / script(setType) 113
ブロックstates有
1回目 command : 361 ms / script(setPerm) 201
2回目 command : 359 ms / script(setPerm) 203
3回目 command : 342 ms / script(setPerm) 230
<Dimension>.setType() | .setPermutation() の場合
ブロックstates無
1回目 script(setPerm) 85 / script(setType) 74
2回目 script(setPerm) 85 / script(setType) 74
3回目 script(setPerm) 90 / script(setType) 79
ブロックstates有
1回目 script(setPerm) 187
2回目 script(setPerm) 182
3回目 script(setPerm) 195
Script が優勢
ブロックStatesが無い場合は setType() のほうが優勢
さらに、getBlock()を挟まず、<dimension>.setType() | .setPermutation() のほうが優勢
// 座標に特定のブロックを設置する関数
// コマンド
function setBlockCommand(dimensionId, location, blockId, states = "[]") {
const { x, y, z } = location;
world.getDimension(dimensionId).runCommand(`setblock ${x} ${y} ${z} ${blockId} ${states}`);
}
// Script 1 getBlock() を行う場合
function setPermScript(dimensionId, location, blockId, states = undefined) {
const blockPerm = BlockPermutation.resolve(blockId, states);
world.getDimension(dimensionId).getBlock(location).setPermutation(blockPerm);
}
function setTypeScript(dimensionId, location, blockId) {
world.getDimension(dimensionId).getBlock(location).setType(blockId);
}
// Script 2 getBlock() を行わない場合
function setPermScript2(dimensionId, location, blockId, states = undefined) {
const blockPerm = BlockPermutation.resolve(blockId, states);
world.getDimension(dimensionId).setBlockPermutation(location, blockPerm);
}
function setTypeScript2(dimensionId, location, blockId) {
world.getDimension(dimensionId).setBlockType(location, blockId);
}
検証内容 : ブロックの範囲設置
一定範囲(32×32×32)に指定したブロックを設置するプログラム(1回)
範囲内がすべて空気の場合
1回目 command : 40 ms / script(setPerm) 170 / script(fillBlocks) 66
2回目 command : 41 ms / script(setPerm) 169 / script(fillBlocks) 42
3回目 command : 43 ms / script(setPerm) 171 / script(fillBlocks) 64
範囲内がすべて設置済みの場合
command : 5 ms / script(setPerm) 130 / script(fillBlocks) 5
※ fillBlocks() はベータ版のみ有効
Command が優勢
fillBlocks() も同等レベルに速い
設置済みの場合は、for setPerm() より圧倒的に両者が速い
// 一定範囲に特定のブロックを設置する関数
function fillCommand(dimensionId, pos1, pos2, blockId, states = "[]") {
const { x: x1, y: y1, z: z1 } = pos1;
const { x: x2, y: y2, z: z2 } = pos2;
world.getDimension(dimensionId).runCommand(`fill ${x1} ${y1} ${z1} ${x2} ${y2} ${z2} ${blockId} ${states}`);
}
// for setPermutation()
function fillScript(dimensionId, start, end, blockId, states = undefined) {
const minX = Math.min(start.x, end.x); const maxX = Math.max(start.x, end.x);
const minY = Math.min(start.y, end.y); const maxY = Math.max(start.y, end.y);
const minZ = Math.min(start.z, end.z); const maxZ = Math.max(start.z, end.z);
const dimension = world.getDimension(dimensionId);
let blockPerm;
if (states) {
blockPerm = BlockPermutation.resolve(blockId, blockPerm);
}
for (let x = minX; x <= maxX; x++) {
for (let y = minY; y <= maxY; y++) {
for (let z = minZ; z <= maxZ; z++) {
const location = { x, y, z };
if (blockPerm) {
dimension.setBlockPermutation(location, blockPerm);
} else {
dimension.setBlockType(location, blockId);
}
}
}
}
}
// fillBlocks()
function fillScript2(dimensionId, start, end, blockId, states = undefined) {
const dimension = world.getDimension(dimensionId);
let blockPerm;
if (states) {
blockPerm = BlockPermutation.resolve(blockId, blockPerm);
}
dimension.fillBlocks(new BlockVolume(start, end), states ? blockPerm : blockId, {});
}
検証内容 : ブロック検知
特定の座標とディメンションにおいて、指定したブロックIDおよびブロックstatesが存在するかどうかを返すプログラム(10000回)
ブロックstates無
ブロック不一致 command : 341 ms / script 117
ブロック一致 command : 329 ms / script 109
ブロックstates有
ブロック不一致 command : 356 ms / script 193
ブロック一致 command : 321 ms / script 189
Script が優勢
// 座標に特定のブロックがあるかどうかを返す関数
function matchesBlockScript(dimensionId, location, blockId, states = undefined) {
return world.getDimension(dimensionId).getBlock(location).matches(blockId, states);
}
function matchesBlockCommand(dimensionId, location, blockId, states = "[]") {
const { x, y, z } = location;
const result = world.getDimension(dimensionId).runCommand(`execute if block ${x} ${y} ${z} ${blockId} ${states}`);
return result.successCount === 1;
}
検証用コード
import { system, world } from "@minecraft/server";
system.afterEvents.scriptEventReceive.subscribe(ev => {
const { id } = ev;
if (id === "speedTest:run") {
system.runJob(speedTest(ev.sourceEntity))
}
});
function* speedTest(player) {
let commandProcessingTime = 0;
let scriptProcessingTime = 0;
// コマンドの処理時間
commandProcessingTime += getProcessingTime(10000, hasScoreCommand, "test", "test", 1);
// スクリプトの処理時間
scriptProcessingTime += getProcessingTime(10000, hasScoreScript, "test", "test", 1);
world.sendMessage(`Command: ${commandProcessingTime} ms / Script: ${scriptProcessingTime} ms`);
}
function getProcessingTime(count, func, ...args) {
const startTime = Date.now();
for (let i = 0; i < count; i++) {
func(...args);
}
const endTime = Date.now();
return endTime - startTime;
}
コメント