A resource's page is drawn over the game with a transparent background. Any HTML, CSS and JavaScript works, React and Vue included.
-- resource.lua
client_scripts { "client/main.lua" }
files { "client/ui" }
-- client/main.lua
web.open("client/ui/index.html")
events.onServer("money", function(amount)
web.emit("money", amount) -- to the page
end)
web.on("buy", function(item) -- from the page
events.emitServer("shop:buy", item)
end)
<!-- client/ui/index.html -->
<div id="money"></div>
<button onclick="m2o.emit('buy', 'pistol')">Buy</button>
<script>
m2o.on("money", (amount) => {
document.getElementById("money").textContent = "$" + amount;
});
</script>
Mouse and keyboard
web.focus(true) gives the page the mouse and keyboard: a cursor appears and
the character stops moving. web.focus(false) hands control back to the game.
Addresses
A page reaches the resource's files as https://nui.m2o/<resource>/<path>;
relative links (style.css, img/logo.png) work as usual.
Svelte, React, Vue
Any framework works: build the project and ship the build. With Vite:
// vite.config.js
export default {
base: "./", // relative paths: the page lives at https://nui.m2o/<resource>/...
build: { outDir: "../client/ui" },
};
-- resource.lua
client_scripts { "client/main.lua" }
files { "client/ui" } -- the build: index.html, assets/...
-- client/main.lua
web.open("client/ui/index.html")
base: "./" matters: with Vite's default / the page asks for /assets/..., which isn't
inside the resource, and stays blank. Inside the page, talk to Lua with m2o.emit / m2o.on as above.
To work on it: npm run build, then restart <resource> in the server console; players get the
new page right away. Players keep the files they downloaded and only fetch what changed.