attempt to modify a readonly table

why would an attempt to modify a readonly table error happen with jecs here? (if i uncomment any of them it errors)
const playerEntity = world.entity();
// world.set(playerEntity, Player, player);
// world.set(playerEntity, HealthAttribute, { max: 100, value: 0 });
// world.set(playerEntity, FistStrengthStat, { multiplier: 1, value: 0 });
const playerEntity = world.entity();
// world.set(playerEntity, Player, player);
// world.set(playerEntity, HealthAttribute, { max: 100, value: 0 });
// world.set(playerEntity, FistStrengthStat, { multiplier: 1, value: 0 });
Solution:
so, using jecs.component() is for preregistration, and world:component() for normal component creation
Jump to solution
15 Replies
Ohirume
OhirumeOP2mo ago
my src/shared/components.ts
import { component, meta, Name } from "@rbxts/jecs";

export const Player = component<Player>();
meta(Player, Name, "Player");

export const HealthAttribute = component<{ value: number; max: number }>();
meta(HealthAttribute, Name, "HealthAttribute");

export const PunchAbility = component<{ damage: number }>();
meta(PunchAbility, Name, "PunchAbility");

export const FistStrengthStat = component<{ value: number; multiplier: number }>();
meta(FistStrengthStat, Name, "FistStrengthStat");

export const EnduranceStat = component<{ value: number; multiplier: number }>();
meta(EnduranceStat, Name, "EnduranceStat");

export const MovementSpeedStat = component<{ value: number; multiplier: number }>();
meta(MovementSpeedStat, Name, "MovementSpeedStat");

export const JumpForceStat = component<{ value: number; multiplier: number }>();
meta(JumpForceStat, Name, "JumpForceStat");

export const PsychicPowerStat = component<{ value: number; multiplier: number }>();
meta(PsychicPowerStat, Name, "PsychicPowerStat");
import { component, meta, Name } from "@rbxts/jecs";

export const Player = component<Player>();
meta(Player, Name, "Player");

export const HealthAttribute = component<{ value: number; max: number }>();
meta(HealthAttribute, Name, "HealthAttribute");

export const PunchAbility = component<{ damage: number }>();
meta(PunchAbility, Name, "PunchAbility");

export const FistStrengthStat = component<{ value: number; multiplier: number }>();
meta(FistStrengthStat, Name, "FistStrengthStat");

export const EnduranceStat = component<{ value: number; multiplier: number }>();
meta(EnduranceStat, Name, "EnduranceStat");

export const MovementSpeedStat = component<{ value: number; multiplier: number }>();
meta(MovementSpeedStat, Name, "MovementSpeedStat");

export const JumpForceStat = component<{ value: number; multiplier: number }>();
meta(JumpForceStat, Name, "JumpForceStat");

export const PsychicPowerStat = component<{ value: number; multiplier: number }>();
meta(PsychicPowerStat, Name, "PsychicPowerStat");
src/server/systems/player-added.ts
import type { World } from "@rbxts/jecs";
import { onEvent, type SystemTable } from "@rbxts/planck";
import { Players } from "@rbxts/services";
import {
EnduranceStat,
FistStrengthStat,
HealthAttribute,
JumpForceStat,
MovementSpeedStat,
Player,
PsychicPowerStat,
PunchAbility,
} from "shared/components";

const [hasPlayerAdded, collectPlayers] = onEvent(Players, "PlayerAdded");

function playerAddedSystem(world: World): void {
for (const [_, player] of collectPlayers()) {
const playerEntity = world.entity();
world.set(playerEntity, Player, player);
world.set(playerEntity, HealthAttribute, { max: 100, value: 0 });
world.set(playerEntity, FistStrengthStat, { multiplier: 1, value: 0 });
world.set(playerEntity, EnduranceStat, { multiplier: 1, value: 0 });
world.set(playerEntity, MovementSpeedStat, { multiplier: 1, value: 0 });
world.set(playerEntity, JumpForceStat, { multiplier: 1, value: 0 });
world.set(playerEntity, PsychicPowerStat, { multiplier: 1, value: 0 });

world.set(playerEntity, PunchAbility, { damage: 1 });
}
}

export const playerAddedSystemTable: SystemTable<[World]> = {
runConditions: [hasPlayerAdded],
system: playerAddedSystem,
};
import type { World } from "@rbxts/jecs";
import { onEvent, type SystemTable } from "@rbxts/planck";
import { Players } from "@rbxts/services";
import {
EnduranceStat,
FistStrengthStat,
HealthAttribute,
JumpForceStat,
MovementSpeedStat,
Player,
PsychicPowerStat,
PunchAbility,
} from "shared/components";

const [hasPlayerAdded, collectPlayers] = onEvent(Players, "PlayerAdded");

function playerAddedSystem(world: World): void {
for (const [_, player] of collectPlayers()) {
const playerEntity = world.entity();
world.set(playerEntity, Player, player);
world.set(playerEntity, HealthAttribute, { max: 100, value: 0 });
world.set(playerEntity, FistStrengthStat, { multiplier: 1, value: 0 });
world.set(playerEntity, EnduranceStat, { multiplier: 1, value: 0 });
world.set(playerEntity, MovementSpeedStat, { multiplier: 1, value: 0 });
world.set(playerEntity, JumpForceStat, { multiplier: 1, value: 0 });
world.set(playerEntity, PsychicPowerStat, { multiplier: 1, value: 0 });

world.set(playerEntity, PunchAbility, { damage: 1 });
}
}

export const playerAddedSystemTable: SystemTable<[World]> = {
runConditions: [hasPlayerAdded],
system: playerAddedSystem,
};
src/server/main.server.ts
import { applets, register } from "@rbxts/jabby";
import { createHook } from "@rbxts/yetanothernet";
import { routes } from "shared/routes";
import { scheduler } from "shared/scheduler";
import { sharedWorld } from "shared/world";
import { playerAddedSystemTable } from "./systems/player-added";
import { trainFistStrengthSystemTable } from "./systems/train-fist-strength";

register({
applet: applets.world,
configuration: {
world: sharedWorld,
},
name: "world",
});

const [beginFrame, endFrame] = createHook(routes);

scheduler.addSystem({
system: () => {
beginFrame();
},
});

scheduler.addSystem(playerAddedSystemTable);
scheduler.addSystem(trainFistStrengthSystemTable);

scheduler.addSystem({
system: () => {
endFrame();
},
});
import { applets, register } from "@rbxts/jabby";
import { createHook } from "@rbxts/yetanothernet";
import { routes } from "shared/routes";
import { scheduler } from "shared/scheduler";
import { sharedWorld } from "shared/world";
import { playerAddedSystemTable } from "./systems/player-added";
import { trainFistStrengthSystemTable } from "./systems/train-fist-strength";

register({
applet: applets.world,
configuration: {
world: sharedWorld,
},
name: "world",
});

const [beginFrame, endFrame] = createHook(routes);

scheduler.addSystem({
system: () => {
beginFrame();
},
});

scheduler.addSystem(playerAddedSystemTable);
scheduler.addSystem(trainFistStrengthSystemTable);

scheduler.addSystem({
system: () => {
endFrame();
},
});
src/shared/world.ts
import { world } from "@rbxts/jecs";

export const sharedWorld = world();
import { world } from "@rbxts/jecs";

export const sharedWorld = world();
src/shared/scheduler.ts
import { Scheduler } from "@rbxts/planck";
import JabbyPlugin from "@rbxts/planck-jabby";
import { Plugin as RunServicePlugin } from "@rbxts/planck-runservice";
import { sharedWorld } from "./world";

export const scheduler = new Scheduler(sharedWorld);
scheduler.addPlugin(new JabbyPlugin());
scheduler.addPlugin(new RunServicePlugin());
import { Scheduler } from "@rbxts/planck";
import JabbyPlugin from "@rbxts/planck-jabby";
import { Plugin as RunServicePlugin } from "@rbxts/planck-runservice";
import { sharedWorld } from "./world";

export const scheduler = new Scheduler(sharedWorld);
scheduler.addPlugin(new JabbyPlugin());
scheduler.addPlugin(new RunServicePlugin());
code criticism is also welcome, tryna learn this new paradigm and i need to learn new best practices
PepeElToro41
PepeElToro412mo ago
yeah I probably wouldnt do this and that's probably why the problem
Solution
PepeElToro41
PepeElToro412mo ago
so, using jecs.component() is for preregistration, and world:component() for normal component creation
PepeElToro41
PepeElToro412mo ago
preregistration comes with a problem and its that you should require your world after you preregistered all your components so having a world singleton in a module and also a components folder will cause a lot of bugs so I would either, not have a world module if you wanna use preregistration or dont use preregistration and create your components with world:component()
Ohirume
OhirumeOP2mo ago
if i had to create components thru world.component, how would i access them from other systems?
PepeElToro41
PepeElToro412mo ago
same way I mean you have a "./world" so just import that instead when creating your components or the other one is not having a world module and passing your world in your systems like you did here but for everything like fusion scopes lmao
Ohirume
OhirumeOP2mo ago
which one would u recommend the first one seems easier to do
PepeElToro41
PepeElToro412mo ago
I personally use world:component() its easier and preregistration has no benefit for me
Ohirume
OhirumeOP2mo ago
trade offs?
PepeElToro41
PepeElToro412mo ago
I dont know honestly maybe for testing, you can create multiple worlds but I personally just import the same world module in tests too
Ohirume
OhirumeOP2mo ago
honestly i think this preregistration code was from a time where i didnt have a world module and needed the components quick
PepeElToro41
PepeElToro412mo ago
yeah for testing preregistration would be useful because you can create a lot of worlds and all of them would have all of your components already because they are preregistered
Ohirume
OhirumeOP2mo ago
yeah and i think i kept getting cyclic dependencies cuz i could not wrap my head around organizing ecs code which is why i had to do that good to know what does this mean tho? https://discord.com/channels/476080952636997633/476080952636997635/1427032911953006715 and why would it cause that
PepeElToro41
PepeElToro412mo ago
the error happens when the component has no row for data that mostly happens when you use tags with world:set but I would imagine it happened to you because all your component ids got mixed up
Ohirume
OhirumeOP2mo ago
alright thanks it works now

Did you find this page helpful?