Перейти до вмісту
EnshwickEnshwickальфа
Завантажити

Controls

Нестабільне

Цей контент ще не доступний вашою мовою.

Unstable: everything in this chapter needs the unstable feature and may change in any release (Stability).

A mod declares the keys its players press; players change them on the Mods screen.

use enshwick::controls::{Control, Key, Phase};
fn init(load: &mut enshwick::Load<'_>) -> Result<(), enshwick::Off> {
let horn = Control::new("horn", "Sound the horn").key(Key::parse("H")?);
load.control(horn, |fired| {
if fired.phase == Phase::Press {
// Play the horn on this client.
}
})?;
Ok(())
}

The player presses H in the world, and the handler runs on their client.

The client reads a control’s key once a frame while its player is in the world, a building tool in hand and screenshot mode included: never while the chat, a menu, the inventory, another of the game’s screens or a window or dialog of Enshwick or a mod is open, and only while the game’s window has the focus. The game keeps the key too: a mod key that is also a game key does both.

A key is a place on the keyboard, not a letter: Key::parse("Q") is the key left of W on every layout, and the controls page shows it with the name the player’s layout gives it. Names are those of a US keyboard, with Shift, Ctrl and Alt joined by +, such as Shift+E, and the mouse buttons Mouse1 to Mouse5. A control fires when its key goes down with at least its modifiers held, so a control on E fires while the player sprints with Shift. Of the controls on one key, only those with the most of the held modifiers fire: with Shift held, Shift+E fires and E does not. Controls with as many held modifiers all fire, so with Shift and Ctrl held both Shift+E and Ctrl+E do. A control stays down until the key that brought it down goes up; pressing or letting go of a modifier meanwhile fires nothing. Escape, the Windows keys, Alt with F4 or Tab, and a modifier alone cannot be bound, and while Alt is held F4 and Tab fire no control. A numpad digit fires while Num Lock is on and Shift is up, as Windows reports it with Shift as the key the numpad has without Num Lock; the numpad’s Enter is not a key of its own, as Windows reports it as Enter.

BuilderWhat it does
Control::new(name, label)a control; name is 1 to 32 of [a-z0-9-], label the page’s text
.key(key), .second(key)the default key and a second one
.fires(&[Phase, ...])when it fires, in place of Phase::Press alone; empty keeps it
.hold_for(duration)how long a key stays down before Phase::Hold; 500 ms by default
.link(GameAction)fires on the server when the player’s game action fires
.runs(action)runs the mod’s action on the server each time the control fires

In Luau, a control is a table of the builder’s fields: name, label, key, second, fires as a list of phase names, hold_for in seconds, link as the game action’s name, and runs as the action’s id. A key is its text, such as "Shift+E"; enshwick.controls.parse(text) and from_bits(bits) return its own spelling, and bits, code, modifiers and is_mouse read it. The handler gets a table of control, phase and, on the server, player.

Phase::Press fires once when the key goes down; Phase::Hold once when it stayed down for the hold time, never from key repeat; Phase::Release once when it comes up. A key pressed and let go between two frames may be missed.

A control fires on the client, and the server decides what changes. The handler sends the server one of the mod’s player actions with actions.run(id, args): the server checks who may run it, runs its handler and shows the reply as a notice. A control is how a player runs a mod’s action in game; the Mods screen shows no actions.

use enshwick::actions::{Action, Outcome};
use enshwick::controls::{Control, Key};
use enshwick::i18n::Texts;
fn init(load: &mut enshwick::Load<'_>) -> Result<(), enshwick::Off> {
let actions = load.actions(Texts::default())?;
actions.add(Action::new("honk", "Honk"), |_| Outcome::done("Honk!"))?;
load.control(Control::new("horn", "Horn").key(Key::parse("H")?), move |_| {
let _sent = actions.run("honk", &[]);
})?;
Ok(())
}

A control that only runs an action without arguments names it with .runs("honk") instead: the client sends the run each time the control fires, and the handler still runs on the client.

A mod of side server has no code on the players’ clients, so it declares controls that run one of its actions. The server sends those controls to each client with Enshwick that joins: the client’s Controls page lists them under the mod’s name with the mod’s keys, the player may change the keys, and a press sends the run to the server. The server checks who may run the action and shows its reply as a notice, as for any action. The label is a key of the mod’s texts, so each player reads it in their own language. A player without Enshwick gets nothing and plays on.

use enshwick::actions::{Action, Outcome};
use enshwick::controls::{Control, Key};
fn init(load: &mut enshwick::Load<'_>) -> Result<(), enshwick::Off> {
let texts = load.texts_from(
"[en]\n\"day\" = \"Make it day\"\n\n[uk]\n\"day\" = \"Зробити день\"\n",
)?;
let actions = load.actions(texts)?;
actions.add(Action::new("day", "day").admins(), |_| Outcome::done("Morning"))?;
load.control(Control::new("day", "day").key(Key::parse("F8")?).runs("day"), |_| {})?;
Ok(())
}

The server sends up to 24 such controls a mod, each label cut to 160 bytes; a client keeps those of up to 32 mods. While a client hosts the world, it runs the mod itself, so its own copy reads the keys. In another’s world, the server’s controls take the place of the client’s own copy of the mod. The you.welcome example offers its administrators such a key.

A control linked to a game action reads the game’s own decision instead of a key: GameAction::ContextualAction fires whenever the player interacts, on whatever key, button or gamepad they bound to it. It fires on the server, for that player, also for a player without Enshwick; the handler finds the player in fired.player. The handler runs on the game thread after the server’s step, where it may change the world.

use enshwick::controls::{Control, GameAction};
fn init(load: &mut enshwick::Load<'_>) -> Result<(), enshwick::Off> {
let enter = Control::new("enter", "Enter the car").link(GameAction::ContextualAction);
load.control(enter, |fired| {
if let Some(player) = &fired.player {
// Seat `player` if a car is at hand.
let _ = player;
}
})?;
Ok(())
}

GameAction names every member of the game’s player input, such as Jump, Sprint or QuickUse0. A member a game build lacks leaves the control to its keys, with a line in the log.

Reading the game’s controls a player holds

Section titled “Reading the game’s controls a player holds”

The controls a player holds reads which of the game’s controls any player holds this step, on the server, vanilla clients included.

The Mods screen’s Controls page lists every mod’s controls under the mod’s name, the server’s controls of its server-only mods included. A click on a key waits for the next key or combination, which becomes the binding; Escape cancels and Unbind clears it. The left mouse button works the page, so it is bound in the file only. The page warns when two controls share a key; the game’s own keys are not checked.

The page writes mod-controls.toml in the game’s folder: only the keys the player changed, each a scan code with its name beside it. A person may edit the file; a key written as a name, such as "Shift+E", works too, and 0 unbinds. An entry for a control no running mod declares is left alone, with one line in the log.

["you.cars:horn"]
key = 36 # J
second = 0 # none

Keys need Enshwick on the player’s client and a game build whose table finds the game’s current screen; without it the Controls page says the keys are off. Linked controls run in the game server: the dedicated server, and a client while it hosts a game, single player included. There a control with a key and a game action may run its handler on the interface thread and the game thread at once.

With the test feature, the fake loader reads keys as the client does: keys(&down, after) looks at the keys held now, after the last look, and fires each matching control, on a client only; game_actions(&player, &held, after) looks at the game actions the player holds as the server does once a step, and fires the linked controls. FakeHost::bind(name, slot, key) sets one of the player’s keys, FakeHost::side(Side::CLIENT) makes the fake a client, which alone sends player actions, and a mod of side server reads keys there only with FakeHost::hosting(true), as in a game the player hosts; controls() lists the controls with their keys in force, and action_runs() the actions a control sent the server, by its handler or by .runs.