diff --git a/src/levelDat.ts b/src/levelDat.ts
index 00e10e07..88744b07 100644
--- a/src/levelDat.ts
+++ b/src/levelDat.ts
@@ -26,7 +26,7 @@ export const numberToLongArray = (num: bigint | number): [number, number] => {
return [high, low]
}
-export async function writeLevelDat (path: string, value: LevelDatWrite & { time?: number }, oldLevelRaw?) {
+export async function writeLevelDat (path: string, value: LevelDatWrite & { time?: number } & { GameRules }, oldLevelRaw?) {
const nbt = {
type: 'compound',
name: '',
@@ -35,6 +35,18 @@ export async function writeLevelDat (path: string, value: LevelDatWrite & { time
type: 'compound',
value: {
...oldLevelRaw?.value?.Data?.value,
+ GameRules: {
+ type: 'compound',
+ value: {
+ ...oldLevelRaw?.value?.Data?.value.GameRules?.value,
+ ...Object.fromEntries(Object.entries(value.GameRules ?? {}).map(([key, val]) => {
+ return [key, {
+ type: 'string',
+ value: val ? 'true' : 'false'
+ }]
+ })),
+ }
+ },
Version: {
type: 'compound',
value: {
diff --git a/src/lib/command.ts b/src/lib/command.ts
index a04909fa..fab49e26 100644
--- a/src/lib/command.ts
+++ b/src/lib/command.ts
@@ -1,10 +1,10 @@
import UserError from './user_error'
-export type Ctx
= P extends true ? {
+export type Ctx
= (P extends true ? {
player: Player
} : {
player?: Player
-}
+})
type NonFalsey = T extends false ? never : NonNullable
diff --git a/src/lib/modules/commands.ts b/src/lib/modules/commands.ts
index 41d197fc..1c64e992 100644
--- a/src/lib/modules/commands.ts
+++ b/src/lib/modules/commands.ts
@@ -7,7 +7,7 @@ export const player = function (player: Player, serv: Server, { version }: Optio
player.handleCommand = async (str) => {
try {
const res = await serv.commands.use(str, { player }, player.op)
- if (res) player.chat(serv.color.red + res)
+ if (res) player.chat(serv.color.green + res)
} catch (err) {
if (err.userError) player.chat(serv.color.red + 'Error: ' + err.message)
else setTimeout(() => { throw err }, 0)
diff --git a/src/lib/modules/daycycle.ts b/src/lib/modules/daycycle.ts
index 02fb8da8..d54e4cd8 100644
--- a/src/lib/modules/daycycle.ts
+++ b/src/lib/modules/daycycle.ts
@@ -12,7 +12,10 @@ export const server = function (serv: Server) {
serv.time ??= 0
serv.on('tick', (delta, count) => {
- if (!serv.doDaylightCycle) return
+ // TODO
+ // const disabledByGamerule = 'doDayLightCycle doDayLightcycle DayNightCycle'
+ const disabledByGamerule = 'doDayLightCycle'.split(' ').some(x => serv.levelData?.GameRules[x] === 'false') || !serv.gamerules.doDayLightCycle
+ if (!serv.doDaylightCycle || disabledByGamerule) return
if (count % 20 === 0) {
serv.behavior('changeTime', {
old: serv.time,
diff --git a/src/lib/modules/gamerules.ts b/src/lib/modules/gamerules.ts
new file mode 100644
index 00000000..3db457c0
--- /dev/null
+++ b/src/lib/modules/gamerules.ts
@@ -0,0 +1,41 @@
+export const server = function (serv: Server, options: Options) {
+ serv.gamerules ??= {}
+ serv.on('asap', () => {
+ for (const [key, val] of Object.entries(serv.levelData?.GameRules ?? {})) {
+ serv.gamerules[key] = val === 'true'
+ }
+ })
+
+ const gameRules = {
+ 'doDayLightCycle': 'Wether to enable daylight cycle'
+ }
+
+ serv.commands.add({
+ base: 'gamerule',
+ info: '',
+ usage: '/gamerule ',
+ op: true,
+ parse (string, ctx) {
+ return string.split(' ')
+ },
+ action (data, ctx) {
+ const [rule, newVal] = data
+ if (!rule) {
+ return `Available these gamerules: ${Object.entries(gameRules).map(([key, desc]) => `${key}: ${desc}`).join(', ')}`
+ }
+ if (newVal) {
+ serv.gameMode[rule] = newVal === 'false' ? false : true
+ return `set ${rule} to ${serv.gamerules[rule]}`
+ } else {
+ const gamerule = serv.gamerules[rule]
+ return `${rule} is ${gamerule ? 'Enabled' : 'Disabled'} (${gamerule})`
+ }
+ }
+ })
+}
+
+declare global {
+ interface Server {
+ gamerules: Record
+ }
+}
diff --git a/src/lib/modules/world.ts b/src/lib/modules/world.ts
index 66ce8317..49585cf5 100644
--- a/src/lib/modules/world.ts
+++ b/src/lib/modules/world.ts
@@ -183,11 +183,7 @@ export const server: ServerModule = async function (serv, options) {
const world = ctx.player?.world ?? serv.overworld
// todo need concept of command output
const message = `Seed: ${world.seed}`
- if (ctx.player) {
- ctx.player.chat(message)
- } else {
- console.log(message)
- }
+ return message
},
})
@@ -285,7 +281,8 @@ const levelDatWriter = (serv: Server, options: Options) => {
generatorName: serv.levelData?.generatorName ?? serv.overworld.generatorName === 'superflat' ? 'flat' : serv.overworld.generatorName === 'diamond_square' ? 'default' : 'customized',
LevelName: serv.levelData?.LevelName ?? options.levelName!, // todo fix typing
allowCommands: serv.levelData?.allowCommands ?? 1,
- time: serv.time
+ time: serv.time,
+ GameRules: serv.gamerules,
})
}
}
@@ -539,7 +536,7 @@ declare global {
/** Global spawn and respawn point for every player */
spawnPoint?: Vec3
/** Parsed level.dat of the loaded world (only if worldFolder is specificed) */
- levelData?: LevelDatFull
+ levelData?: LevelDatFull & { GameRules?}
worlds: Record
/** Contains the overworld world. This is where the default spawn point is */
"overworld": CustomWorld