promotional bannermobile promotional banner

CNPCExtended

Cnpc and Mcef integration that lets you display HTML browsers to players, this mod also adds client side queries like getting the players sound,graphics,video settings,. this mod also adds global scripts and npc datas that preloads into new worlds
Back to Files

CNPCExtended-3.0.1-forge-1.20.1.jar

File nameCNPCExtended-3.0.1-forge-1.20.1.jar
Uploader
AsianDoesMCAsianDoesMC
Uploaded
Oct 16, 2026
Downloads
45
Size
462.4 KB
Mod Loaders
Forge
File ID
9204993
Type
R
Release
Supported game versions
  • 1.20.1

Curse Maven Snippet

Forge

implementation fg.deobf("curse.maven:cnpcextended-1480941:9204993")

Learn more about Curse Maven

What's new

# CNPC Extended 3.0.1 (Forge 1.20.1 and Fabric 1.21.1)

Security fixes. Script-facing behaviour is unchanged. Update both server and clients.

- A server could run any code on a player's PC: `runClientFunction` / `sendToClient` could call the client script
  engine's built-ins (`eval`, `load`, `quit`). The server can now only call functions the player's own client scripts
  define.
- A server could make `cnpc://` read any folder on a player's PC (the client trusted the folder path the server sent).
  The client now ignores that path and only reads from disk for the world it is hosting itself.
- A page's events no longer reach its script once the page has been closed for 2 seconds (it was 30 s, so a modified
  client could replay events into a closed page).
- Key codes from clients are limited to real key codes (any number used to grow a server-side map).
- **New: Fabric 1.21.1 build** with the same features as Forge (separate file on this project).

# CNPC Extended 3.0.0 (Forge 1.20.1)

## Upgrading

- Replace the old CNPCExtended jar with `CNPCExtended-3.0.0-forge-1.20.1.jar` on the server **and** every client. Old and new builds refuse to connect to each other (network protocol 3), instead of connecting and misreading packets as 1.x did.
- **MCEF is no longer a separate download.** It is bundled inside the jar, unmodified (LGPL notice in `META-INF/MCEF-NOTICE.txt`). Remove the standalone MCEF jar or leave it; Forge keeps whichever MCEF is newer. On first launch Chromium itself still downloads once into `mods/mcef-libraries`, as before.
- Existing scripts, pages, `config/cnpcextended-*.toml` files and `/cnpcext` commands work unchanged. No 1.x method was removed or changed; everything below is additive or a fix.

## Fixed

- **FPS dropping over time (browser leak).** 1.x only closed a page's Chromium browser when the player pressed Escape. Every `closeHtmlGui()` from a script, every GUI replaced by another GUI, and every screen that opened over a GUI left a live browser rendering in the background. Pages are now closed whenever their screen goes away, and a sweep closes anything orphaned.
- **"Loading browser..." and the 0.5–1 s open delay.** See *Faster opening* below. The text is gone; pages appear when fully drawn, with a small spinner only if a page takes longer than 400 ms.
- Events from a page reached the wrong script when a second page or an overlay from another script was opened. When one GUI replaced another, the old page's close notice ended the **new** page's session, so the new page's events were dropped.
- Page events larger than 32 KB kicked the player. They are now split and rejoined (limit `maxEventKB`, default 1 MB).
- Pages larger than about 1.5 MB failed to open (data: URL limit).
- `cnpc://` images, fonts, sounds and CSS only worked in singleplayer. They are now streamed from the server's scripts folder to every player (cached, unchanged files are not resent).
- Init data could close the `<script>` tag early (text containing `</script>` broke or injected into the page).
- A LAN guest could send client-bound packets that the host's game then ran (opening pages or calling client-script functions on the host). Each packet now only travels in its own direction.
- `syncScriptsPath()` sent the server's disk path to every player; it now only applies to the singleplayer/LAN host.
- `setGuiEscapable()` right after opening was lost if Chromium or the page was still loading.
- `hasOverlay()` stayed true after a page closed itself with `cnpc.close()`, and became false after the player respawned although the overlay was still shown. Key hold state was also lost on respawn.
- `isInGui()` / `getOpenScreen()` only updated when a key was pressed; screens opened by clicking (chests, NPC dialogs) went unnoticed.
- The last released key was reported again every tick (`clientKeyReleased` and a network packet every tick, forever).
- A page with `data-mc-slot` items kept drawing them after the elements were removed.
- One page script error in a `cnpc.onEvent` handler stopped the other handlers for that event.
- A client script function that throws every tick flooded the log; errors are now reported for the first 5 and every 1000th.
- Query answers (`queryKeybinds` etc.) were accepted from any player; now only from the player asked.
- Script commands vanished after `/reload`; deleted script commands still ran; loading N commands resent the command tree N times.
- `/cnpcext sync force` flipped the saved `overwriteExisting` setting on disk and back (left `true` after a crash). It now overrides for that run only, and reloads synced commands immediately.
- The global preloader and the command loader ran in random order at startup, so synced `commands.json` changes needed a second restart.
- `/cnpcext status` leaked a file handle; `/cnpcext version` showed 1.2.0.
- Cutscenes: mouse look was never blocked (the 1.x hook targeted a method that does not exist); starting a cutscene during another turned view bobbing off permanently; leaving a world mid-cutscene left the camera stuck and bobbing off; speed 0 or less never ended; a bad start keyframe crashed the client; the editor's preview played the same object being edited; per-keyframe logs spammed INFO.
- `hideHudElement("actionbar")` hid the title text instead of the action bar (it now hides both, so existing scripts see no loss).
- Skip-menu: a failing auto-load retried in an endless loop.
- (3.0.0 pre-release) Pages served from the https origin showed their source code as plain text: CEF passed `text/html; charset=utf-8` to Chromium unparsed. Pages are now served as `text/html` with a UTF-8 byte order mark.
- The MCEF offline-download fix crashed when the libraries path was unset.

## Faster opening (approach change)

| | 1.x | 3.0 |
|---|---|---|
| Page loading | base64 data: URL of the whole file, every open | served from memory at a per-server https origin (~2x faster for a 500 KB page, no size limit) |
| HTML over the network | whole file on every open (the Arvan menu is 500 KB) | once per login; later opens send a hash + init data |
| First open after launch | cold Chromium start (~490 ms) | Chromium pre-warmed at startup (~140 ms) |
| Showing | "Loading browser..." then a half-built first frame | shown once the page has run its scripts and painted |
| Reopening | new browser every time | opt-in keep-alive: `cnpc.onReopen(cb)` pages reopen in about one frame |
| Chromium message pump | extra pump for the GUI and for every overlay, every frame | one extra pump per frame |
| Reading the HTML file | read and copied on the server thread every open | cached until the file changes |

Pages served from the https origin also get `localStorage`, `crypto.subtle` and working relative links (`<img src="img/x.png">` loads from the page's own folder in the scripts directory). Set `httpsOrigin = false` in the client config to load pages as data: URLs like 1.x.

## Added: server script API

On `cnpcext` (every method taking `eventOrPlayer` accepts a CNPC event, an IPlayer or a ServerPlayer):

- `openHtmlGui(e, file)`, `openHtmlGui(e, file, json)`, `openHtmlGui(e, file, json, options)` — options: `{"width":0,"height":0,"escapable":true,"keepAlive":true}`.
- The returned handle: `handle.isOpen()`, `handle.send(event, json)` (only reaches that page, never a newer one), `handle.close()`, `handle.getId()`, plus the existing `setGuiEscapable`.
- `isHtmlGuiOpen(e)`, `closeHtmlGui(e)`, `sendToBrowser(e, event, json)`.
- `sendToOverlay(e, name, event, json)`, `setOverlayPosition(e, name, x, y)`, `setOverlaySize(e, name, w, h)`, `setOverlayInteractive(e, name, on)`, `isOverlayInteractive(e, name)`.
- `getClientBridge(e)` also accepts an IPlayer or event (no `getMCEntity()` needed).

On the client bridge: `isHtmlGuiOpen()`, `openHtmlGui(file, json, options)`, `sendToOverlay`, `setOverlayPosition`, `setOverlaySize`, `isOverlayInteractive`, `getOverlays()`, `sendJsonToClient(channel, json)`, `runClientFunction(name, json)`.

On `htmlGuiEvent(e)`: the 1.x fields are unchanged; new are `e.session`, `e.overlayName`, `e.requestId`, `e.isRequest()`, `e.isOverlay()`, `e.reply(value)` (JS object, array, JSON string or plain value), `e.replyError(message)`, `e.close()` (closes the page that sent the event, if it is still open). `e.player` is always the player's current entity, also after a respawn. `__guiClosed` data now says why: `{"reason":"player"}` or `{"reason":"replaced"}`.

Entity slots: `followCursor` (default true) is now honoured on every CustomNPCs build; 1.x could not pass it to builds such as GBPort 20260227, where entities always followed the cursor. An entity with `followCursor: false` can be spun by dragging it with the left mouse button (`"spin": false` turns that off).

Native slots: data sent to an open page (`sendToBrowser`, `handle.send`, `sendToOverlay`) that contains `overlayItems` / `overlayEntities` now sets those item/entity slots without reopening the page (other slots are kept; an entry with a slot and no item empties it).

HUD: new element names `title`, `bossbar`, `air`, `mounthealth`, `jumpbar`, `subtitles`, and any overlay by full id (`"minecraft:boss_event_progress"`, `"somemod:mana_bar"`). `"all"` hides the same set as 1.x.

## Added: for other mods

`com.cnpcextended.api.HtmlPageEvent.Message` and `.Closed` are posted on `MinecraftForge.EVENT_BUS` (server thread) before the script gets page traffic. Mods that open pages with `ClientBridgeAPI.openHtmlGui` handle them there (`getSource()`, `getName()`, `getData()`, `reply()`); cancelling keeps the event from the script. This replaces mixins into CNPCExtended internals, which 3.0 no longer matches.

## Added: page API (`window.cnpc`)

- `cnpc.request(name, data, timeoutMs)` → Promise, answered by `e.reply(...)` / `e.replyError(...)` in the script.
- `cnpc.onReopen(cb)` — opt into keep-alive; `cb(initData)` runs with the new data on each reopen. `park` / `reopen` events.
- `cnpc.holdReady()` / `cnpc.ready()` — keep the page hidden until async setup is done.
- `cnpc.offEvent(name[, cb])`, `cnpc.setEscapable(bool)`, `cnpc.playSound(id, volume, pitch)`, `cnpc.log(...)` (to the game log).
- `cnpc.version`, `cnpc.kind` (`"gui"`/`"overlay"`), `cnpc.name` (overlay name), `cnpc.session`.
- Events are sent at the end of the current task instead of on the next animation frame.

## New config

Server/common `cnpcextended-common.toml`, section `[htmlGui]`: `streamAssets` (true), `maxAssetKB` (16384), `scriptAssetFolders` (`web`, `gui`, `html`, `assets` — the only top-level folders `.js`/`.json` files are served from, so script sources and data never reach players), `maxEventKB` (1024).

Client `cnpcextended-client.toml`, section `[htmlGui]`: `keepAlivePages` (2), `keepAliveMinutes` (10), `httpsOrigin` (true), `prewarm` (true), `loadingIndicatorDelayMs` (400, -1 = never).

## Compatibility notes

- Everything a 1.x page or script could call still exists with the same signature and result. Checked against the 1.x jar: no public method, field or class that scripts reach was removed.
- Kept on purpose, although odd: `sendToBrowser(event, CompoundTag)` still sends numbers without type suffix and booleans as 0/1; init text that is not JSON is still passed through; the bridge is still placed after a bare `<head>` (moving it would switch some pages between quirks and standards mode); `cnpc.sendEvent(name, falsy)` still sends `{}`; GUI width/height are still ignored (full screen).
- `__guiClosed` still does not fire when a script closes the GUI or another (non-HTML) screen opens over it, as in 1.x; use `handle.isOpen()` / `isHtmlGuiOpen()` to check.