Skip to content

Commit

Permalink
feat: add gamerules support: basic data parsing, now possible to cont…
Browse files Browse the repository at this point in the history
…rol/change them via /gamerule

only these gamerules effect implemented: doDaylightCycle
fix: better messaging from output commands (now green)
  • Loading branch information
zardoy committed Nov 20, 2024
1 parent 1d160d5 commit b9ffbd8
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 12 deletions.
14 changes: 13 additions & 1 deletion src/levelDat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '',
Expand All @@ -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: {
Expand Down
4 changes: 2 additions & 2 deletions src/lib/command.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import UserError from './user_error'

export type Ctx<P extends boolean> = P extends true ? {
export type Ctx<P extends boolean> = (P extends true ? {
player: Player
} : {
player?: Player
}
})

type NonFalsey<T> = T extends false ? never : NonNullable<T>

Expand Down
2 changes: 1 addition & 1 deletion src/lib/modules/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion src/lib/modules/daycycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
41 changes: 41 additions & 0 deletions src/lib/modules/gamerules.ts
Original file line number Diff line number Diff line change
@@ -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 <gamerule> <true|false>',
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<string, boolean>
}
}
11 changes: 4 additions & 7 deletions src/lib/modules/world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
})

Expand Down Expand Up @@ -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,
})
}
}
Expand Down Expand Up @@ -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<string, CustomWorld>
/** Contains the overworld world. This is where the default spawn point is */
"overworld": CustomWorld
Expand Down

0 comments on commit b9ffbd8

Please sign in to comment.