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/packType'

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/packType'

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
`)