Overview and architecture
| Plugin folder | /plugins/HyperScript/ |
| Scripts | /plugins/HyperScript/scripts/*.hs |
| Settings | /plugins/HyperScript/config.yml |
| Saved variables | /plugins/HyperScript/data.json, plus MySQL or SQLite if configured |
| Backups | /plugins/HyperScript/Backups/ |
| Admin command | /hyperscript <reload|list|status|version|check|disable|enable|remake|variables|timings|errors|speed|debug|test|search|install|update|docs|new> (alias /hs, permission hyperscript.admin, default op) |
| Requirements | Paper or Spigot 1.16.5+, Java 8+ |
What is in the box
| Feature | Summary |
|---|---|
| Commands and events | Declare commands with permissions, aliases and checked arguments; over 80 events; react to joins, chat, damage, jumps, block breaks, mob spawns, inventory clicks and more, with each event's details available as plain words such as attacker and message |
| Cooldowns and timers | cooldown: 1 hour on any command or event; every 5 seconds: for repeating work; every day at 18:00: for the clock |
| On-screen text | Action bars, titles with fade timings, boss bars, per-player sidebar scoreboards |
| Variables | {name} is saved and shared by every script, {_name} lasts for one run; a name that starts with a player, such as {%player%.coins}, is that player's own. They hold numbers, text, lists, items, durations and locations |
| Functions and options | function reward({_who}, {_amount}): for any script to call, local function for one file; options: for settings named once at the top of a file |
| Players | heal, feed, health, levels, game mode, flight, potion effects, freeze, vanish, pushing, and thirty more things to read or set |
| Entities | Keep a summoned mob in a variable: name it, heal it, send it after someone, remove it |
| Dates | now, now + 1 day, time since, dates written out any way you like |
| Error handling | try: and on error: for lines that might fail |
| Top lists | top 10 players by {coins} across everyone who has ever played |
| Teams and moderation | Coloured teams with tags; kick, ban ... for 7 days, and on player login: to turn players away |
| Inventories | if player has 5 diamond:, remove 5 diamond from player |
| Custom items | item magic wand: names an item once for every script, recipe for magic wand: makes it craftable; give player 1 diamond sword named "&6Excalibur" with lore "..." enchanted with sharpness 5; set what a player wears and holds; hidden tags so a script recognises its own items |
| Chat | Clickable and hoverable messages: send "&a[Home]" with click command "/home" |
| Sounds and resource packs | play sound ... with pitch 1.8 and volume 1.0; send resource pack with response events |
| Worlds and regions | Set time and weather; define box regions and test region at <location> |
| Web requests | send get request to ... and send post request to ... from async: blocks |
| Text and files | split, join, replace, slicing, parse "52" as number; create, read and write text files in the plugin folder |
| Lists and loops | [1, 2, 3], sorted, filter ... where, for each, loop N times, loop from 1 to 10, loop all players, loop-value, break, continue, switch |
| GUIs | Chest menus with clickable slots, written inline; pages and refreshing built in |
| Async blocks | async: moves heavy work off the server thread, sync: comes back |
| Server list and chat | MOTD, player counts and icon in the server list; tab list header and footer; join, quit and chat formats |
| Waiting and loops | wait 5 seconds inside any trigger; loop, for each and while |
| World effects | Floating text and blocks (display entities), particles with hex colours, particle circles, spheres and lines |
| Discord | Webhooks, and through a bot: slash commands, messages sent to a channel, and reacting to what is written there |
| Other plugins | Money through Vault (balance of player); PlaceholderAPI placeholders in scripts, and script variables as placeholders for other plugins |
| Hot reloading | Scripts recompile when the file is saved |
| Update check | Operators are told on joining when a newer version is out |
| Characters and trading | create npc "Guide" at ... with on npc click, and the villager trading window with offers of your own |
| Parties and queues | Players who belong together, and on queue "arena" of 4: when enough are waiting |
| Enchantments and advancements | enchantment lifesteal: and advancement "First blood": blocks of your own |
| The database | run sql and sql rows of on the database the variables are in; shared. variables every server of a network sees |
| A web address of its own | on web request "vote": for websites and other programs to call |
| Finding scripts | /hyperscript search, install and update fetch scripts and new versions from the website |
| Testing | test "...": blocks run with /hyperscript test, and /hyperscript debug shows a script line by line |
| Addons | Other plugins can add statements, values and events to the language |
| Storage | data.json, optionally with a CSV file, SQLite, H2 or MySQL beside it and automatic fallback, plus scheduled backups |
How a script runs
main.hs ──► Lexer ──► ScriptParser ──► execution tree ──► registered once ──► runs on every event
(text) (one pass, (Java objects, (commands and
at load) names resolved) event listeners)
HyperScript does all of its text work once, when a script is loaded. What runs afterwards is a tree of small Java objects, one per statement or operator, that call each other directly.
To be precise about the word "compile": HyperScript compiles to an in-memory execution tree, not to JVM bytecode. The speed comes from moving work out of the hot path, not from code generation:
| Work | A naive line-by-line interpreter | HyperScript |
|---|---|---|
| Parsing a line | Every time the line runs | Once, at load |
Local variables ({_reward}) | Map lookup by name | Array slot assigned at load |
Placeholders (%player.name%) | String search and replace per message | Bound to a resolver at load; the string is pre-split into parts |
Constant math (20 * 60 * 5) | Recomputed each run | Folded to a single constant at load |
| Syntax errors | Discovered when the line is reached | Reported at load with a line number; the file is rejected |
| Unused events | Often listened to anyway | Not registered at all |
| Persistent variables | Disk or database per access | In-memory map; storage I/O on a background thread |
A few consequences worth knowing:
- Errors are found up front. A typo on line 80 is reported when the file loads, not the first time a player happens to trigger that branch. A file with an error is skipped entirely, so a half-valid script never runs. Other files still load.
- Events you do not use cost nothing. If no script contains
on block break:, HyperScript never registers a block-break listener. - Variable reads never touch the disk or the database. A player's data is read while they are logging in (off the server thread) and written back in the background.
Why it survives server updates
The language core (dev.hyperscript.lang, dev.hyperscript.runtime) contains no Bukkit imports. Everything the server provides goes through one interface, ScriptHost. The Bukkit side is compiled against the 1.16.5 API, the oldest supported version, with no version-specific (net.minecraft) code. The places where versions genuinely differ are handled at run time:
| Feature | Difference between versions | How HyperScript handles it |
|---|---|---|
%server.tps% | getTPS() exists on Paper only | Measures tick rate itself with a scheduler task |
%player.ping% | Player#getPing() exists from 1.17 | Uses it when present, otherwise reads the value reflectively; -1 if neither works |
| Registering commands at run time | No public Bukkit API | Reaches the server's command map reflectively |
| Display entities | Added in 1.19.4 | Used reflectively when present; floating text falls back to an invisible armour stand |
| Coloured dust particle | Renamed from REDSTONE to DUST in 1.20.5 | Looks up whichever name the server has |
| GUI click handling | InventoryView changed from a class to an interface in 1.21 | Never touches InventoryView |