@minecraft/creator-tools
Version:
Minecraft Creator Tools command line and libraries.
145 lines (128 loc) • 6.04 kB
text/typescript
// Copyright (c) Mojang AB. All rights reserved.
import {
ActionTypes,
CoreMenuType,
IMenu,
IPlayerUISession,
registerEditorExtension,
} from "@minecraft/server-editor";
/**
* Per player storage can be attached to the IPlayerUISession type in a type safe manner for anything
* you'd like to store and access in a player contextual manner. This makes the data safe for multiplayer
* usage. This type can be passed to the registerEditorExtension function as a generic parameter to make
* all access type safe.
*/
type PerPlayerStorage = {
NUM_TIMES_PLAYER_CLICKED: number;
};
// Output a message in the log window displaying the player's name and the number of times
// the button has been clicked
function showPlayerMessage(uiSession: IPlayerUISession<PerPlayerStorage>) {
if (!uiSession.scratchStorage) {
uiSession.log.error("We're missing scratch storage - this shouldn't happen!");
return;
}
const clickCount = uiSession.scratchStorage.NUM_TIMES_PLAYER_CLICKED;
uiSession.log.info(`Hello ${uiSession.extensionContext.player.name} - You clicked ${clickCount} times`);
}
function incrementClickValue(storage: PerPlayerStorage) {
storage.NUM_TIMES_PLAYER_CLICKED++;
}
/**
* Provides a sample extension registration function
* @public
*/
export function registerBasicsExtension() {
registerEditorExtension<PerPlayerStorage>(
"minimal-template-sample",
// Provide a function closure which is executed when each player connects to the server
// The uiSession object holds the context for the extension and the player
(uiSession) => {
uiSession.log.debug(
`Initializing extension [${uiSession.extensionContext.extensionInfo.name}] for player [${uiSession.extensionContext.player.name}]`
);
// Initialize the player specific, custom extension storage structure with whatever
// the extension needs to store, and assign it to the `uiSession.scratchStorage` variable.
// Using this in combination with JavaScript closure captures, you can access this player/extension
// storage area in whatever events you need it
const storage: PerPlayerStorage = {
NUM_TIMES_PLAYER_CLICKED: 0,
};
uiSession.scratchStorage = storage;
// Create a basic property pane with a button. Property panes are the basic panels on to which you
// can attach buttons, sliders and various other UI elements.
// When you create a property pane, you bind it with an object which contains the values which represent
// the contents of the UI elements you bind to the pane. The binding object is a 'property bag' - a simple
// key/value pair collection which the UI elements modify when they are actioned.
// E.g. if you were to create a slider, the slider would be bound to a property 'mySlider' for example, and
// when you adjust the slider, you can inspect the binding object property 'mySlider' for the current value.
const extensionPane = uiSession.createPropertyPane({
title: {
id: "sample.minimal.pane.title",
},
});
// Creating UI elements like buttons and sliders require a couple of simple steps.
// - Create an action (a function declaration of what you want to happen when the element is actioned)
// - Create the UI element and bind it to the action
// (You can define a single action and bind it to many UI elements if you wish)
const buttonAction = uiSession.actionManager.createAction({
actionType: ActionTypes.NoArgsAction,
onExecute: () => {
// Increment the click value in the player's storage
incrementClickValue(storage);
// Send a message to the log window showing the player's name and the number of times
// you've hit the button
showPlayerMessage(uiSession);
},
});
// Now create a button and bind the action you want to execute when it's pressed
extensionPane.addButton(buttonAction, {
title: { id: "sample.minimal.pane.button.clickme" },
visible: true,
});
// Try to get predefined top level core menu
uiSession.menuBar
.getMenu(CoreMenuType.Extensions)
.then((coreMenu: IMenu) => {
// Create a menu entry in the menu bar Core menu
const extensionMenu = coreMenu.addItem({
label: "sample.minimal.menu.title",
});
// Adds a child menu item to show the property pane
// Note - we're creating an action too, which can be executed when the menu
// item is selected
extensionMenu.addItem(
{
label: "sample.minimal.menu.showpane",
},
uiSession.actionManager.createAction({
actionType: ActionTypes.NoArgsAction,
onExecute: () => {
// make sure we show the property pane (in case you might have closed it)
extensionPane.show();
},
})
);
})
.catch((error: Error) => {
uiSession.log.error(error.message);
});
// Normally we return a collection of IDisposable objects that the extension system will clean
// up and dispose of on shutdown. We don't have any in this example, so let's just return
// an empty collection
return [];
},
// Provide a function which is executed when the player disconnects from the server
// This is where the extension would normally clean up any resources it created/loaded during activation
(uiSession: IPlayerUISession<PerPlayerStorage>) => {
// Do any explicit cleanup when a player is leaving and the extension instance is shutting down
uiSession.log.debug(
`Shutting down extension [${uiSession.extensionContext.extensionInfo.name}] for player [${uiSession.extensionContext.player.name}]`
);
},
// Descriptor for the extension
{
description: "Basic Sample Extension",
}
);
}