Skip to main content

Custom Resources

Sandstone features a system for supporting custom resources. Like for all resources, you need to provide a name, which can include a namespace and folders. You then provide the definition of the resource.

Raw Resource​

Sometimes, you're not building a library and you don't need to add much auxiliary function to your resource, in that case RawResource is perfect.

Syntax​

RawResource('scripts/drop_heads.sc', `
// A Balanced way to get player heads in survival.

// stay loaded
__config() -> (
m(
l('stay_loaded','true')
)
)

__on_player_dies(player) -> (
if(rand(1) <= 0.3,
xv = rand(0.5)-0.25
yv = rand(0.5)
zv = rand(0.5)-0.25

motion = '[' + xv + 'd, ' + yv + 'd, ' + zv + 'd' + ']'
data = '{Motion: ' + motion + ', Item: {id: "minecraft:player_head", Count:1b, tag:{SkullOwner: "' + player + '"}}}, PickupDelay: 3s'
spawn('item', pos(player), data)
)
)`)

The above example places the Carpet Mod scarpet script in (datapack folder)/scripts/drop_heads.sc.

Other packs​

RawResource(resourcePack(), 'assets/minecraft/models/entity/pig.gecko.json', getExistingResource('entity_models/pig.gecko.json'))

The above example places the GeckoLib Mod entity model in (resource pack folder)/assets/minecraft/entity/pig.gecko.json.

Custom Resource​

Sometimes you're building out support for an unsupported type of resource you'll use a lot, in which case you can create a custom resource!

const [core, CustomResource] = makeCustomResource

export type GameEventListenerJSON = {
/** The event to listen to. */
event: GAME_EVENTS

/** Optional. A list of predicates to check before running the function. */
conditions?: PredicateJSON[]

/** The function to run when the event is triggered. */
function: string | MCFunctionClass<undefined, undefined>
}

export class GameEventListenerClass extends CustomResource {
public GameEventListenerJSON: GameEventListenerJSON

constructor(sandstoneCore: SandstoneCore, name: string, listener: GameEventListenerJSON, opts: ResourceClassArguments<'default'>) {
super(sandstoneCore, name, {
...opts,
type: 'game_event',
folder: sandstoneCore.pack.resourceToPath(name, ['game_events']),
extension: 'json',
})

this.GameEventListenerJSON = listener
}

getValue = () => JSON.stringify(this.GameEventListenerJSON)
}

export function GameEventListener(name: string, listener: GameEventListenerJSON, opts?: ResourceClassArguments<'default'>) {
return new GameEventListenerClass(core, name, listener, {
addToSandstoneCore: true,
creator: 'user',
...opts,
})
}

GameEventListener('mining', {
event: 'block_destroy',

conditions: [{
condition: 'minecraft:entity_properties',
entity: 'this',
predicate: {
type: 'player',
},
}],

function: MCFunction('mining', () => {
say('I mined a block!')
}),
})

This would provide full support for the Picoblaze mod's implementation of game event listeners!

Custom Packs​

In some cases you may need to export multiple separate datapacks, or an entirely custom directory of resources, like the config for a mod.

Separate Datapack​

import { PackType } from 'sandstone/pack'

class DataPackFoo extends PackType {
constructor() {
super('datapack-foo', 'saves/$worldName$/datapacks/$packName$-foo', 'world/datapacks/$packName$-foo', 'datapacks/$packName$-foo', 'server', true, 'data', true)
}

handleOutput = async (type: 'output' | 'client' | 'server', readFile: handlerReadFile, writeFile: handlerWriteFile) => {
if (type === 'output') {
await writeFile('pack.mcmeta', JSON.stringify({
pack_format: 21,
description: 'Foo Pack',
}))
}
}
}

const fooPack = sandstonePack.packTypes.set('datapack-foo', new DataPackFoo()).get('datapack-foo')!

MCFunction('bar', () => {
say('bar')
}, {
packType: fooPack,
})

Custom Pack​

import { PackType } from 'sandstone/pack'

class ModConfig extends PackType {
constructor() {
super('mod-config', 'config', 'config', 'config', 'both')
}
}

export const modConfig = new ModConfig()

sandstonePack.packTypes.set('mod-config', )

RawResource(modConfig, 'techy_haven/main.cfg', `
foo=bar
baz=21
`)