Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
c62b3f2
feat: add Understudy error classes to Canopy errors directory
ForestOfLight Jun 23, 2026
2783404
feat: add simplayer utils.js with tests
ForestOfLight Jun 24, 2026
57f1292
feat: add simplayer RepeatableAction and Actions classes
ForestOfLight Jun 24, 2026
7fe4333
feat: add simplayer inventory and player info saver classes
ForestOfLight Jun 24, 2026
6a8d220
feat: add Understudy class with lazy-on hook
ForestOfLight Jun 24, 2026
da61bf9
feat: add Understudies with lazy event/interval management and isUnde…
ForestOfLight Jun 24, 2026
8a1b89e
feat: add simplayer rules using Canopy BooleanRule directly
ForestOfLight Jun 24, 2026
e0be712
feat: add all 15 simplayer commands using canopy:player prefix
ForestOfLight Jun 24, 2026
86c4bcd
feat: wire simplayer commands, rules, and startup hooks into Canopy
ForestOfLight Jun 24, 2026
c635598
fix: correct BooleanRule import path and use getNativeValue() in simp…
ForestOfLight Jun 24, 2026
82b11b5
fix: update noSimplayerSaving mock to use getNativeValue in Understud…
ForestOfLight Jun 24, 2026
b64a4af
test: fix erroring tests
ForestOfLight Jun 24, 2026
647c2da
feat: merge understudy in
ForestOfLight Jun 24, 2026
d0e43ce
feat: reintroduce look rotation command
ForestOfLight Jun 24, 2026
b334b14
feat(simplayer): add en_US translation keys for simplayer commands, m…
ForestOfLight Jun 24, 2026
6e12d59
feat(simplayer): localize shared Understudy/Understudies messages
ForestOfLight Jun 24, 2026
84fb142
feat(simplayer): use translation keys for simplayer rule descriptions
ForestOfLight Jun 24, 2026
a3d371a
feat(simplayer): localize not-online errors for tp/stop/sneak/sprint/…
ForestOfLight Jun 24, 2026
ba77d96
fix: fog layers getting overloaded when left enabled
ForestOfLight Jun 24, 2026
da6f7f7
feat: protect against invalid player actions
ForestOfLight Jun 24, 2026
df507d7
fix: add color clearer to simplayer prefix
ForestOfLight Jun 24, 2026
019c722
fix: remove extra %2 from canopy search results
ForestOfLight Jun 24, 2026
f1563d6
refactor: simplify nametag formatting
ForestOfLight Jun 24, 2026
d7d1d18
feat(simplayer): localize online-state errors for claimprojectiles/sw…
ForestOfLight Jun 24, 2026
2261133
feat(simplayer): localize playerlook messages
ForestOfLight Jun 24, 2026
5ffb921
tests: fix tests from rotation arg addition
ForestOfLight Jun 24, 2026
277748f
feat: noSimplayerSaving -> simplayerSaving
ForestOfLight Jun 24, 2026
8deab71
feat(simplayer): localize playermove messages
ForestOfLight Jun 24, 2026
d96f477
feat(simplayer): localize playeraction messages
ForestOfLight Jun 24, 2026
ca7254b
feat(simplayer): localize playerselect messages
ForestOfLight Jun 24, 2026
d9bdc2c
feat(simplayer): localize playerprefix messages
ForestOfLight Jun 24, 2026
a344ded
feat(simplayer): localize playerinventory messages
ForestOfLight Jun 24, 2026
2408066
fix: simplayer inventory saving not working
ForestOfLight Jun 24, 2026
c60b777
fix: remove extra newlines from simplayer cmd error outputs
ForestOfLight Jun 24, 2026
b540dfb
fix: linting errors
ForestOfLight Jun 25, 2026
e748e84
test(simplayer): align error-path assertions with bare-return contract
ForestOfLight Jun 25, 2026
440b5a4
fix: commit untracked simplayerSaving rule
ForestOfLight Jun 25, 2026
1d62279
feat: remove /playerclaimprojectiles
ForestOfLight Jun 25, 2026
643afbd
docs: make donation section slightly more important
ForestOfLight Jun 25, 2026
670b1f9
Merge branch 'dev' into simplayers
ForestOfLight Jun 25, 2026
600359c
tests: fix unused imports
ForestOfLight Jun 25, 2026
dea5ef3
tests: remove unused playerclaimprojectiles test
ForestOfLight Jun 25, 2026
a1aa291
Merge pull request #93 from ForestOfLight/simplayers
ForestOfLight Jun 25, 2026
a011e1c
Merge branch 'ForestOfLight:dev' into dev
IdotIcom Jun 25, 2026
d7a2516
Update Indonesian language file with new commands
IdotIcom Jun 25, 2026
a7d3b22
""
IdotIcom Jun 25, 2026
7411542
feat: wiki for simplayers
ForestOfLight Jun 25, 2026
cd20982
chore(i18n): update Chinese translations
TickPoints Jun 25, 2026
912e742
Update id_ID.lang
IdotIcom Jun 25, 2026
9287d3a
Merge pull request #94 from IdotIcom/dev
ForestOfLight Jun 25, 2026
8e512c4
Merge pull request #95 from TickPoints/dev
ForestOfLight Jun 25, 2026
52745f5
fix: "stop" not in playeraction command autocomplete
ForestOfLight Jun 27, 2026
31255b7
feat: filter out sulfur cubes with blocks inside from creativeOneHitKill
ForestOfLight Jun 28, 2026
02a2055
docs: update README for simplayers
ForestOfLight Jun 28, 2026
d45d5a4
feat: add AABB to /data
ForestOfLight Jun 28, 2026
d41aa26
feat: color ping based on connection health
ForestOfLight Jun 28, 2026
d67ab2f
perf: lazy-init creativeNoTileDrops events
ForestOfLight Jun 28, 2026
362d104
fix: unsubscribe error
ForestOfLight Jun 28, 2026
d9f454c
feat: renderLightLevel rule
ForestOfLight Jun 28, 2026
8870b8a
feat: /cs <player> to auto-tp (op only)
ForestOfLight Jun 29, 2026
b9fa56a
fix: Duplicate rule error on reload with multiple players online
ForestOfLight Jun 29, 2026
34ab37b
feat: support function-valued enums in VanillaCommand
ForestOfLight Jun 29, 2026
b1e8dd0
feat: add Rules.getSettableRuleIDs accessor
ForestOfLight Jun 29, 2026
386f2e2
feat: migrate canopy command to VanillaCommand class
ForestOfLight Jun 29, 2026
d8c0be1
chore: remove obsolete commands.canopy.multiple key
ForestOfLight Jun 29, 2026
e84589e
docs: add numeric quote dependency to /canopy
ForestOfLight Jun 29, 2026
a971651
docs: modify usage for /canopy for rule setting
ForestOfLight Jun 30, 2026
1517bca
refactor: generalize Rules accessor to getRuleIDsByCategory
ForestOfLight Jun 30, 2026
0fe19e3
feat: migrate /info to a VanillaCommand
ForestOfLight Jun 30, 2026
1b668be
docs: update help book usage for /info and /canopy slash commands
ForestOfLight Jun 30, 2026
ac4d447
chore: remove obsolete info command translation keys (en_US)
ForestOfLight Jun 30, 2026
f78ac62
feat: reword canopy cmd description
ForestOfLight Jun 30, 2026
5cb2799
fix: populate /info rule enum from per-class identifier constants
ForestOfLight Jun 30, 2026
da01ffc
refactor: derive /info rule enum from InfoDisplay element classes
ForestOfLight Jun 30, 2026
6d4f32a
docs: remove unnecessary comment
ForestOfLight Jun 30, 2026
a859b6a
refactor: remove unused particle assets
ForestOfLight Jul 7, 2026
e5df303
fix: decorated pot flippinArrows duplication
ForestOfLight Jul 7, 2026
d384e61
feat: playerlook block looks at block face
ForestOfLight Jul 8, 2026
14a89ba
fix: tick command missing non-player names in feedback
ForestOfLight Jul 8, 2026
35b3053
refactor: simplify for getting playername from command origin
ForestOfLight Jul 8, 2026
3e49ff6
fix: ignore thrown error when traveling through portals
ForestOfLight Jul 8, 2026
c8a8b65
fix: ignore thrown error when travelling through portals for all rayc…
ForestOfLight Jul 8, 2026
d24d6dc
chore: vendor jsep ESM build for expression parsing
ForestOfLight Jul 8, 2026
de5ec0f
feat: add region geometry helpers for analyzearea
ForestOfLight Jul 8, 2026
27a064c
feat: add ExpressionEvaluator for analyzearea block expressions
ForestOfLight Jul 8, 2026
5a97844
test: prove && / || short-circuit lazy evaluation in ExpressionEvaluator
ForestOfLight Jul 8, 2026
2fc2035
feat: add RegionLoader ticking-area wrapper for analyzearea
ForestOfLight Jul 8, 2026
1ead256
feat: add AreaAnalyzer block-region scanner for analyzearea
ForestOfLight Jul 8, 2026
1bdb184
feat: add AnalyzeAreaRenderer debug-box renderer for analyzearea
ForestOfLight Jul 8, 2026
9f25482
feat: add Analysis model for analyzearea
ForestOfLight Jul 8, 2026
4f028e7
fix: unload ticking area on all run() paths in Analysis
ForestOfLight Jul 8, 2026
8f692fb
feat: add AreaAnalysisManager world persistence for analyzearea
ForestOfLight Jul 8, 2026
8d5e51a
feat: add AnalyzeAreaUI DDUI forms for analyzearea
ForestOfLight Jul 8, 2026
7658223
fix: pass DropdownItemData objects to CustomForm.dropdown in AnalyzeA…
ForestOfLight Jul 8, 2026
b81dc28
feat: add /analyzearea command with DDUI and world persistence
ForestOfLight Jul 8, 2026
61b4e3b
fix: guard headless feedback for origins without sendMessage in analy…
ForestOfLight Jul 8, 2026
c04a399
fix: scan stored dimension, defer remove teardown, surface match cap …
ForestOfLight Jul 8, 2026
de91e41
feat: add generate_readonly_methods regolith filter
ForestOfLight Jul 8, 2026
11e8e78
feat: tons of tweaks and improvements for /analyzearea
ForestOfLight Jul 8, 2026
65f2d9d
feat: add wiki description if subcommandwikidescription is defined
ForestOfLight Jul 8, 2026
f1dee60
docs: update wiki entry for /analyzearea
ForestOfLight Jul 8, 2026
cc17c31
docs: add missing enums to cmds
ForestOfLight Jul 8, 2026
16265f5
test: fix lookAtBlock test
ForestOfLight Jul 8, 2026
444597a
refactor: /analyzearea
ForestOfLight Jul 8, 2026
09be261
fix: ignore secondary registrations of the same enum
ForestOfLight Jul 9, 2026
1d8c904
feat: allow benign javascript methods in /analyzearea
ForestOfLight Jul 9, 2026
857e653
fix: guard renderSignalStrength against undefined values
ForestOfLight Jul 12, 2026
6a93fa6
fix: creativeOneHitKill not killing non-sulfur cubes
ForestOfLight Jul 12, 2026
3ee5ca0
fix: lang key inconsistancies
ForestOfLight Jul 22, 2026
ff50b57
feat: blockBreakSpeed InfoDisplay rule
ForestOfLight Jul 22, 2026
bad22ae
fix: make horse info clearer
ForestOfLight Jul 22, 2026
d01724d
perf: allow cleaning up removed debugdisplay text objects
ForestOfLight Jul 22, 2026
8083270
fix: /debugdisplay's horse now displays missing stats
ForestOfLight Jul 22, 2026
c5abce2
feat: add horse stats to infodisplay
ForestOfLight Jul 22, 2026
78f76d7
feat: /playerglide <simplayer> <enable>
ForestOfLight Jul 26, 2026
1876ee6
fix: despawn timer start for spawned items
ForestOfLight Jul 26, 2026
1bfd90c
fix: hss calculated off by one in some cases
ForestOfLight Aug 2, 2026
df33208
feat: bump to 1.6.0 and MC 26.40
ForestOfLight Aug 4, 2026
ad0e50c
feat: bump API version to 2.10.0-beta
ForestOfLight Aug 4, 2026
8531fdb
fix: upgrade API contract for fog settings
ForestOfLight Aug 4, 2026
c596e59
feat: retire custom PlayerTameEntity event
ForestOfLight Aug 4, 2026
8d86f00
feat: add entity pickup item removal cause to lifetime tracking
ForestOfLight Aug 4, 2026
4abb58d
feat: add item dropped spawn reason to lifetime tracking
ForestOfLight Aug 4, 2026
18a4886
feat: fix noFog being inaccessible on servers
ForestOfLight Aug 5, 2026
426bae5
Merge branch 'main' into dev
ForestOfLight Aug 5, 2026
cc40d2e
fix: tests with mismatched translation strings
ForestOfLight Aug 5, 2026
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
18 changes: 11 additions & 7 deletions Canopy[BP]/manifest.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
{
"format_version": 2,
"header": {
"name": "Canopy [BP] v1.5.7",
"name": "Canopy [BP] v1.6.0",
"description": "Technical informatics & features addon by §aForestOfLight§r.",
"uuid": "7f6b23df-a583-476b-b0e4-87457e65f7c0",
"version": [
1,
5,
7
6,
0
],
"min_engine_version": [
1,
26,
30
40
]
},
"modules": [
Expand Down Expand Up @@ -42,7 +42,7 @@
"dependencies": [
{
"module_name": "@minecraft/server",
"version": "2.9.0-beta"
"version": "2.10.0-beta"
},
{
"module_name": "@minecraft/server-ui",
Expand All @@ -52,12 +52,16 @@
"module_name": "@minecraft/debug-utilities",
"version": "1.0.0-beta"
},
{
"module_name": "@minecraft/server-gametest",
"version": "1.0.0-beta"
},
{
"uuid": "bcf34368-ed0c-4cf7-938e-582cccf9950d",
"version": [
1,
5,
7
6,
0
]
}
],
Expand Down
4 changes: 2 additions & 2 deletions Canopy[BP]/scripts/constants.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const PACK_VERSION = '1.5.7';
const MC_VERSION = '1.26.30.5';
const PACK_VERSION = '1.6.0';
const MC_VERSION = '1.26.40.5';

export { PACK_VERSION, MC_VERSION };
2 changes: 1 addition & 1 deletion Canopy[BP]/scripts/include/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ export function getNameFromEntityId(id) {
let entityName = id;
try {
const entity = world.getEntity(id);
entityName = entity?.name || entity?.nameTag || entityName;
entityName = entity?.name || entity?.nameTag || id;
} catch (error) {
if (!error.message.includes("is invalid") && error.name !== 'InvalidArgumentError')
throw error;
Expand Down
36 changes: 22 additions & 14 deletions Canopy[BP]/scripts/lib/SRCItemDatabase/ItemDatabase.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,21 @@ class AsyncQueue {
this.processing = false;
}
enqueue(callback) {
this.queue.push(callback);
if (!this.processing) {
this.dequeue();
}
return new Promise((resolve, reject) => {
this.queue.push(async () => {
try {
const result = await callback();
resolve(result);
return result;
} catch (error) {
reject(error);
throw error;
}
});
if (!this.processing) {
this.dequeue();
}
});
}
async dequeue() {
if (this.processing || this.queue.length === 0) return;
Expand Down Expand Up @@ -78,8 +89,7 @@ class SRCItemDatabase {
async set(key, itemStack) {
if (key.length > 12)
throw new Error(`The provided key "${key}" exceeds the maximum allowed length of 12 characters (actual length: ${key.length}).`);
let success = false;
this.asyncQueue.enqueue(() => {
return this.asyncQueue.enqueue(() => {
const newId = this.table + key, existingStructure = world.structureManager.get(newId), location = SRCItemDatabase.location;
if (existingStructure) {
world.structureManager.delete(newId);
Expand All @@ -98,9 +108,8 @@ class SRCItemDatabase {
const structureIds = Array.from(Databases.structureIds.get(this.table) ?? []);
structureIds.push(newId);
Databases.structureIds.set(this.table, structureIds);
success = true;
return true;
});
return success;
};
setMany(items) { return items.map(item => this.set(item.key, item.item)) };
getAsync(key) {
Expand Down Expand Up @@ -139,7 +148,6 @@ class SRCItemDatabase {
setItems(key, items) {
if (key.length > 12)
throw new Error(`The provided key "${key}" exceeds the maximum allowed length of 12 characters (actual length: ${key.length}).`);
let success = false;
return this.asyncQueue.enqueue(() => {
const newId = this.table + key, existingStructure = world.structureManager.get(newId);
if (existingStructure) {
Expand All @@ -157,21 +165,21 @@ class SRCItemDatabase {
saveMode: this.saveMode
});
itemMemory.set(newId, items);
Databases.structureIds.set(this.table, Array.from(Databases.structureIds.get(this.table) ?? []).push(newId));
success = true;
return success;
const structureIds = Array.from(Databases.structureIds.get(this.table) ?? []).filter(id => id !== newId);
structureIds.push(newId);
Databases.structureIds.set(this.table, structureIds);
return true;
});
}
getItems(key) {
if (key.length > 12)
throw new Error(`The provided key "${key}" exceeds the maximum allowed length of 12 characters (actual length: ${key.length}).`);
const newId = this.table + key, location = SRCItemDatabase.location;
if (!itemMemory.get(newId)) return [];
if (!world.structureManager.get(newId)) return [];
SRCItemDatabase.dimension.getEntities({ type: 'minecraft:item', location, maxDistance: 3 }).forEach(item => item.remove())
world.structureManager.place(newId, SRCItemDatabase.dimension, location, { includeBlocks: false, includeEntities: true });
const items = SRCItemDatabase.dimension.getEntities({ type: 'minecraft:item', location: location, maxDistance: 3 });
if (items.length === 0) return undefined;
if (items.length === 0) return [];
const itemStacksArray = [];
for (const item of items) {
itemStacksArray.push(item.getComponent(EntityItemComponent.componentId).itemStack);
Expand Down
4 changes: 4 additions & 0 deletions Canopy[BP]/scripts/lib/canopy/commands/BlockCommandOrigin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { CommandOrigin } from "./CommandOrigin";
import { FeedbackMessageType } from "./FeedbackMessageType";

export class BlockCommandOrigin extends CommandOrigin {
getName() {
return 'Command Block';
}

getSource() {
return this.source.sourceBlock;
}
Expand Down
4 changes: 4 additions & 0 deletions Canopy[BP]/scripts/lib/canopy/commands/CommandOrigin.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export class CommandOrigin {
return this.source.sourceType;
}

getName() {
throw new Error("getName() not implemented");
}

getSource() {
throw new Error("getSource() not implemented");
}
Expand Down
5 changes: 5 additions & 0 deletions Canopy[BP]/scripts/lib/canopy/commands/EntityCommandOrigin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import { CommandOrigin } from "./CommandOrigin";
import { FeedbackMessageType } from "./FeedbackMessageType";

export class EntityCommandOrigin extends CommandOrigin {
getName() {
const source = this.getSource();
return source.nameTag || source.typeId;
}

getSource() {
return this.source.sourceEntity;
}
Expand Down
4 changes: 4 additions & 0 deletions Canopy[BP]/scripts/lib/canopy/commands/PlayerCommandOrigin.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export class PlayerCommandOrigin extends CommandOrigin {
return "Player";
}

getName() {
return this.getSource().name;
}

getSource() {
return this.source.sourceEntity;
}
Expand Down
4 changes: 4 additions & 0 deletions Canopy[BP]/scripts/lib/canopy/commands/ServerCommandOrigin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { CommandOrigin } from "./CommandOrigin";
import { FeedbackMessageType } from "./FeedbackMessageType";

export class ServerCommandOrigin extends CommandOrigin {
getName() {
return "Server";
}

getSource() {
return this.source.sourceType;
}
Expand Down
16 changes: 13 additions & 3 deletions Canopy[BP]/scripts/lib/canopy/commands/VanillaCommand.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CustomCommandSource, CustomCommandStatus, Player, system } from "@minecraft/server";
import { CustomCommandError, CustomCommandErrorReason, CustomCommandSource, CustomCommandStatus, Player, system } from "@minecraft/server";
import { Rules } from "../rules/Rules";
import { VanillaCommands } from "./VanillaCommands";
import { BlockCommandOrigin } from "./BlockCommandOrigin";
Expand Down Expand Up @@ -48,8 +48,18 @@ export class VanillaCommand {

registerEnums(customCommandRegistry) {
if (this.customCommand.enums) {
for (const customEnum of this.customCommand.enums)
customCommandRegistry.registerEnum(customEnum.name, customEnum.values);
for (const customEnum of this.customCommand.enums) {
const values = typeof customEnum.values === 'function'
? customEnum.values()
: customEnum.values;
try {
customCommandRegistry.registerEnum(customEnum.name, values);
} catch (error) {
if (error instanceof CustomCommandError && error.reason === CustomCommandErrorReason.AlreadyRegistered)
continue;
throw error;
}
}
}
}

Expand Down
21 changes: 18 additions & 3 deletions Canopy[BP]/scripts/lib/canopy/rules/Rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ class Rules {
static worldLoaded = false;

static async register(rule) {
const ruleID = rule.getID();
if (this.worldLoaded) {
if (this.exists(rule.getID()))
throw new Error(`[Canopy] Rule with identifier '${rule.getID()}' already exists.`);
this.#rules[rule.getID()] = rule;
if (this.exists(ruleID))
throw new Error(`[Canopy] Rule with identifier '${ruleID}' already exists.`);
this.#rules[ruleID] = rule;
if (rule.getCategory() === "Rules") {
await Promise.resolve();
const value = await rule.getValue();
Expand All @@ -19,6 +20,9 @@ class Rules {
rule.onModify(value);
}
} else {
const alreadyQueued = this.rulesToRegister.some(queuedRule => queuedRule.getID() === ruleID);
if (alreadyQueued || this.exists(ruleID))
return;
this.rulesToRegister.push(rule);
}
}
Expand Down Expand Up @@ -79,6 +83,17 @@ class Rules {
return this.getAll().filter(rule => rule.getCategory() === category);
}

static getRuleIDsByCategory(category) {
const registered = this.getByCategory(category);
const queued = this.rulesToRegister.filter(rule => rule.getCategory() === category);
const ids = new Set([...registered, ...queued].map(rule => rule.getID()));
return [...ids];
}

static getSettableRuleIDs() {
return this.getRuleIDsByCategory("Rules");
}

static registerQueuedRules() {
for (const rule of this.rulesToRegister)
this.register(rule);
Expand Down
7 changes: 7 additions & 0 deletions Canopy[BP]/scripts/lib/jsep/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# jsep (vendored)

Vendored ESM build of [jsep](https://github.com/EricSmekens/jsep) v1.4.0, copied from
`node_modules/jsep/dist/jsep.js`. Do not edit. Used by
`src/classes/analyzearea/ExpressionEvaluator.js` to parse block-analysis expressions.

To update: `npm install jsep@<version>` then copy `dist/jsep.js` here.
Loading
Loading