- Replace per-pixel RGBA->ABGR loop in Entry.upload() with a single
MemoryUtil.memCopy() into NativeImage's native buffer. The two layouts
are identical when viewed as little-endian bytes, so no swap is needed.
Cuts 1080p upload time from a ~2M-iter Java loop to one native memcpy.
- Move the frame-pump tick from 20Hz client tick (END_CLIENT_TICK) to
per-render-frame (LevelRenderEvents.START_MAIN). At 60+fps display vs
24fps source, this removes the worst stutter window where a decoded
frame waited up to 50ms for the next tick. Distance-gain math stays on
20Hz where it's plenty.
- Bump version 0.4.6 -> 0.4.7 in gradle.properties and README.
- new: config/video_player.json on first server start. Field preload_urls
is a list of HTTP(S) URLs (≤256 chars each) that the server broadcasts
via PreloadPayload to every player when they finish joining, so common
videos are warmed into each client's video_player_cache/ before they
ever play. Reuses the same PreloadPayload + VideoCache path as
/videopreload, so chat feedback ("[videopreload] 완료") still applies.
- config is loaded once at mod init; invalid entries are dropped with a
WARN line. Edit + restart server to apply changes.
- audio: distance attenuation now uses the panel center (width/2, height/2
offset from the anchor along the renderer's right/up axes) instead of the
anchor block corner, so a 4x4 panel sounds like it's coming from the
middle of the screen and not the bottom-left.
- preload: each client now posts a chat line on start / completion / failure
/ cache-hit, so a command-block sequence like /videopreload -> /videoplace
can be timed against the visible "[videopreload] 완료" message.
- safety: VideoPlayback.tick() verifies the anchor BE still exists at each
active position and forcibly stops playback if it doesn't — covers any
edge case where BLOCK_ENTITY_UNLOAD doesn't fire.
- /videopreload feedback now explicitly states "완료 알림 후 재생하세요".
- fix: stop playback when anchor block entity unloads (BLOCK_ENTITY_UNLOAD)
so deleting a video while audio is playing actually silences it.
- fix: force-stop SourceDataLine and grabber from outside the worker thread
so a blocked line.write() / grab() unblocks immediately on close.
- perf: tune FFmpeg streaming options (buffer_size, probesize, analyzeduration,
max_delay, fflags=+genpts, reconnect_delay_max) and pre-size audio line buffer
to ~0.5s to smooth out mid-stream stutter.
- feat: /videopreload <url> broadcasts a S2C PreloadPayload to all clients;
each client downloads the URL to <gameDir>/video_player_cache/<sha256> and
subsequent playback reads from the local file instead of streaming.
Gated by COMMANDS_GAMEMASTER (op level 2), so command blocks can invoke it.
JavaCPP Loader (javacpp.jar) ships pure-Java code that needs its own
JNI bridge DLL (jnijavacpp) to extract & link other native libraries.
That bridge lives in the platform-specific javacpp-<os>-<arch>.jar
which the old install guide silently omitted — users following it
ended up with a black panel and an UnsatisfiedLinkError for jnijavacpp
because FFmpeg natives could never be loaded.
This bumps the required JavaCV jar list from 4 to 5, updates the
-Xbootclasspath/a: examples on all three OSes, and adds a diagnostic
note for the matching log line.
Docs-only change; mod code is unchanged from v0.4.2.
Reviewer caught that the README still pointed users at video_player-0.4.1.jar
even though 0.4.2 fixes the stick missing-model issue. Updated:
- "current version" header → 0.4.2
- STEP 4 download filename → video_player-0.4.2.jar
- build output path → build/libs/video_player-0.4.2.jar
- STEP 6 verification: explicitly call out that 0.4.1 and below have a known
stick-icon defect (unprefixed item/generated parent rejected by the 26.1.2
model loader) so users on those versions need to upgrade, not just dedupe.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The held video_stick item rendered as the default missing-model cube even
with v0.4.1 jar loaded (lang strings resolved, so the mod itself was active).
Root cause confirmed against Fabric 26.1.2 docs: the new model loader no
longer auto-resolves unprefixed parent paths. `item/generated` needs to be
written as `minecraft:item/generated`.
models/item/video_stick.json — parent → minecraft:item/generated.
gradle.properties — 0.4.1 → 0.4.2.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
User followed README using %APPDATA% in -Xbootclasspath/a: and the official
Mojang launcher passed the literal string through to Java without expanding
it, so boot classpath ended up empty and video stayed black despite all 4
JavaCV jars being present.
Replaced the %APPDATA% example with an absolute C:\Users\<name>\AppData\
Roaming\... template, added a callout warning that the launcher does not
expand env vars in JVM args, and pointed at `echo %APPDATA%` from cmd as the
way to discover the right path.
Also corrected the ffmpeg jar filenames: the bundle zip uses the short form
(e.g. ffmpeg-windows-x86_64.jar), not the Maven-style
ffmpeg-8.0.1-1.5.13-windows-x86_64.jar.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
User reported Incompatible-mods crash because they downloaded
fabric-api-0.140.2+1.21.11.jar from Modrinth (Modrinth's version-filter URL
param does not always restrict the listing to the requested game version).
Replaced the generic search-page link with the direct CDN URL of
fabric-api-0.149.0+26.1.2.jar and added a callout telling readers to verify
the filename suffix ends in +26.1.2.jar.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Previous README used "방법 A/B/C" terminology that confused readers and
recommended Prism over the official launcher. Per user feedback, restructured
into a step-by-step guide assuming the official Mojang launcher:
1. boot 26.1.2 vanilla once to create .minecraft
2. run fabric-installer-1.x for client / 26.1.2 / loader 0.19.2
3. open .minecraft/mods (per-OS instructions)
4. drop fabric-api + video_player-0.4.1.jar, remove old versions
5. install JavaCV — two routes:
5-A. Prism Launcher (easiest)
5-B. official launcher via -Xbootclasspath/a: with Windows/macOS/Linux examples
6. verify with /videostick
Moved Maven coords to a developer footnote. Added install verification step
to disambiguate "missing texture" symptom from leftover old-version jars.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three fixes for v0.4.1:
1. Video stick item rendered as missing-texture because 26.1.2 requires the
new client_item descriptor at assets/<mod>/items/<name>.json. Add it; the
existing models/item/video_stick.json is kept as the underlying model.
2. Quad placement now anchors the local (0,0) corner at the bottom-left of
the wall face the player clicked, so the clicked block is the BL and the
video grows up & right. Previously it was centered on the anchor.
3. EAST/WEST face rotations were swapped, which placed the quad on the far
side of the air block (~1 block away from the wall) instead of flush.
Derived the correct rotations from first principles:
EAST = Axis.YP +90° (local +Z → world +X, +X → -Z = north)
WEST = Axis.YP -90° (local +Z → world -X, +X → +Z = south)
NORTH/SOUTH/UP/DOWN math re-verified — those were already correct.
The anchor block becomes invisible and non-collidable; it exists only as a
BlockEntity host in the air block adjacent to the clicked wall. The renderer
now translates and rotates the textured quad so it sits flush against the
surface of the wall the user actually clicked, on any of the six faces.
Stick interaction:
right-click face → place anchor at hit.relative(face), facing=face, open GUI
right-click face with anchor already there → reopen the GUI
sneak + left-click face with stick → delete the anchor on that face
The anchor's selection outline / collision / occlusion are all empty, so the
player can target the wall block behind it without interference.
JavaCV / streaming polish:
- Bump missing-JavaCV log to WARN so users notice when the runtime jar is
not installed (previously buried at INFO).
- Add HTTP resilience options: `timeout`, `reconnect`, `reconnect_streamed`,
`reconnect_at_eof`, and a `user_agent` so picky servers don't 403 us.
setVolume/Mute previously stored gain without affecting audible output: the
backend only called grabImage() and never opened an audio sink. Switch to
grab() (interleaved video+audio frames), force AV_SAMPLE_FMT_S16 on the
grabber so samples are always interleaved signed 16-bit PCM, open a matching
JavaSound SourceDataLine and write scaled samples per-frame. gain is read
on every block so /videoMute, GUI Mute and the per-tick distance attenuation
now take effect immediately. SourceDataLine.write blocking provides natural
A/V pacing, so the legacy 15ms sleep is dropped when an audio line is open;
sleep is retained as a 60fps cap when there is no audio device.
bump version to 0.3.1.
VideoPlayback now allocates a DynamicTexture per active anchor under a unique
Identifier (registered on Minecraft.getTextureManager()) and pumps RGBA frames
into it via NativeImage.setPixelABGR + DynamicTexture.upload() during the
client tick. Until the backend (JavaCV) produces a first frame, the texture
shows a dark gray placeholder with a thin border so the anchor screen is
visibly present.
VideoAnchorRenderer.submit() now uses SubmitNodeCollector.submitCustomGeometry
with RenderTypes.entityCutout(textureId), drawing a two-sided width×height
quad oriented by Direction.toYRot() + Axis.YP.rotationDegrees. Vertex
attributes use the new VertexConsumer fluent API (addVertex(Matrix4f, ...)
.setColor.setUv.setOverlay(NO_OVERLAY).setLight.setNormal).
JavaCvBackend / WatermediaBackend / WatermediaProbe / VideoBackend are
unchanged — JavaCV is referenced entirely via reflection so the mod jar
remains loadable when the bytedeco classifier jars aren't on the runtime
classpath, in which case the anchor renders its placeholder surface.
- Block/BE/Item: BaseEntityBlock + useItemOn(InteractionResult), useOn(UseOnContext),
setChanged(), loadAdditional(ValueInput) / saveAdditional(ValueOutput) with
getStringOr/getIntOr/getBooleanOr/getFloatOr defaults
- Registries: BuiltInRegistries + ResourceKey + Properties.setId(ResourceKey)
- Networking: CustomPacketPayload.Type + StreamCodec.composite + RegistryFriendlyByteBuf
(note: clientboundPlay/serverboundPlay names in fabric-networking-api-v1 6.3.1)
- Commands: Commands.literal/argument, CommandSourceStack.sendSuccess/sendFailure,
PermissionSet.hasPermission(Permissions.COMMANDS_GAMEMASTER) (level-2 equivalent)
- Client GUI: EditBox / Button / Checkbox / AbstractSliderButton + addRenderableWidget
(no render override; widgets render themselves under the new pipeline)
- Renderer: rewritten as stub against new BlockEntityRenderer<T, S extends BlockEntityRenderState>
pattern (createRenderState / extractRenderState / submit). Stub does not draw a quad yet
— frame upload and dynamic texture surface deferred until Watermedia/JavaCV are
re-audited for Java 25
- Playback: stripped to bookkeeping-only stub (tracks active anchors, no frame pump)
- Client entrypoint: ClientTickEvents.END_LEVEL_TICK (was END_WORLD_TICK), Minecraft.level,
LocalPlayer, Vec3, InteractionResult
./gradlew build passes against MC 26.1.2 + Fabric Loader 0.19.2 + fabric-api 0.149.0+26.1.2.
Block placement, anchor BE, payloads, commands, and GUI are functional; the anchor renders
as the plain block until the new render-state pipeline is wired with a texture.
- net.fabricmc.fabric-loom 1.16-SNAPSHOT (no remap; MC 26.1+ ships unobfuscated)
- gradle.properties: minecraft_version=26.1.2, loader=0.19.2, fabric-api=0.149.0+26.1.2
- Java 25 toolchain
- fabric.mod.json: fabricloader>=0.19.0, java>=25
- Drop multi-version build script + matrix CI (single-target now)
- Backup of 1.21.6/7/8 working tree preserved on mc-1.21.x branch
Source migration to Mojmap names is in progress on follow-up commits;
this commit alone will not build until source files are ported.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>