luv

Workshop wiki

tests.lisp

luvcraft/tests.lisp

system luvcraft/core/test · 142 definitions · on GitHub

in-package#:luvcraft.tests
defclassrecording-command-encoder
commands:initformnil:accessorrecording-command-encoder-commands
defmethodencode
pushcommand
recording-command-encoder-commandsencoder
encoder
defstruct
overlay-owner-request
:constructormake-overlay-owner-request
function
completion
sb-thread:make-semaphore:count0
:read-onlyt
valuescondition
defclassoverlay-owner-canvas
state:initform:open:accessoroverlay-owner-canvas-state
mailbox:initform
sb-concurrency:make-mailbox:name"Luvcraft overlay owner requests"
:readeroverlay-owner-canvas-mailbox
thread:readeroverlay-owner-canvas-thread
request-enqueued:initform
sb-thread:make-semaphore:count0
:readeroverlay-owner-canvas-request-enqueued
reject-requests-p:initformnil:accessoroverlay-owner-canvas-reject-requests-p
stopped:initform
sb-thread:make-semaphore:count0
:readeroverlay-owner-canvas-stopped
defunrun-overlay-owner-canvas
unwind-protect
loopforrequest=
sb-concurrency:receive-message
overlay-owner-canvas-mailboxcanvas
until
eqrequest:stop
do
handler-case
setf
overlay-owner-request-valuesrequest
multiple-value-list
funcall
overlay-owner-request-functionrequest
0.0d0
error
condition
setf
overlay-owner-request-conditionrequest
condition
sb-thread:signal-semaphore
overlay-owner-request-completionrequest
sb-thread:signal-semaphore
overlay-owner-canvas-stoppedcanvas
defmethodinitialize-instance:after
setf
slot-valuecanvas'thread
sb-thread:make-thread:name"Luvcraft overlay native owner"
defmethodcanvas-state
overlay-owner-canvas-statecanvas
defmethodcanvas-thread-p
and
slot-boundpcanvas'thread
eqsb-thread:*current-thread*
overlay-owner-canvas-threadcanvas
defmethodrequest-canvas-frame
when
overlay-owner-canvas-reject-requests-pcanvas
error"The overlay owner rejected the request before its callback."
unless
eq:open
overlay-owner-canvas-statecanvas
error"The overlay owner canvas is not open."
let
request
make-overlay-owner-requestfunction
sb-concurrency:send-message
overlay-owner-canvas-mailboxcanvas
request
sb-thread:signal-semaphore
overlay-owner-canvas-request-enqueuedcanvas
unless
sb-thread:wait-on-semaphore
overlay-owner-request-completionrequest
:timeout2.0
error"The overlay owner did not service a frame request."
when
overlay-owner-request-conditionrequest
error
overlay-owner-request-conditionrequest
values-list
overlay-owner-request-valuesrequest
defunclose-overlay-owner-canvas
unless
eq:closed
overlay-owner-canvas-statecanvas
setf
overlay-owner-canvas-statecanvas
:closed
sb-concurrency:send-message
overlay-owner-canvas-mailboxcanvas
:stop
unless
sb-thread:wait-on-semaphore
overlay-owner-canvas-stoppedcanvas
:timeout2.0
error"The overlay owner did not stop."
sb-thread:join-thread
overlay-owner-canvas-threadcanvas
canvas
defunwait-for-overlay-owner-request
unless
sb-thread:wait-on-semaphore
overlay-owner-canvas-request-enqueuedcanvas
:timeout1.0
error"The expected overlay owner request was not enqueued."
defclassowned-overlay-probe
canvas:initarg:canvas:readerowned-overlay-canvas
release-count:initform0:accessorowned-overlay-release-count
released-on-owner-p:initformnil:accessorowned-overlay-released-on-owner-p
fail-release-p:initarg:fail-release-p:initformnil:readerowned-overlay-fail-release-p
defmethodrelease-luvcraft-overlay
setf
owned-overlay-released-on-owner-poverlay
canvas-thread-p
owned-overlay-canvasoverlay
incf
owned-overlay-release-countoverlay
when
owned-overlay-fail-release-poverlay
error"Deliberate overlay release failure."
defmethodcanvas-frame-resource-key
declare
ignorecontext
list:capture-targettarget
defclasscapture-frame-key-eviction-probe
keys:initformnil:accessorcapture-frame-key-eviction-probe-keys
canvas:initarg:canvas:readercapture-frame-key-eviction-probe-canvas
owner-thread-p:initformnil:accessorcapture-frame-key-eviction-probe-owner-thread-p
defmethodevict-luvcraft-overlay-frame-key
pushframe-key
capture-frame-key-eviction-probe-keysprobe
setf
capture-frame-key-eviction-probe-owner-thread-pprobe
canvas-thread-p
capture-frame-key-eviction-probe-canvasprobe
probe
deftestcapture-cleanup-evicts-overlay-frame-keys-on-the-canvas-thread
let*
session
make-instance'luvcraft-session:canvascanvas:contextcontext
first
terminal
allocate-instance
find-class'terminal-display
target
list:offscreen-texture
capture
make-instance'application-capture:applicationsession:kind:screenshot
unwind-protect
progn

The second direct presentation stands behind a terminal-display owner, like the Telegram and film-browser wall modes do.

setf
terminal-display-mode-overlayterminal
second
luvcraft-session-overlayssession
listfirstterminal
capture-targetcapture
target
cleanup-capturesessioncapture
dolist
probe
listfirstsecond
ok
equal
list
list:capture-targettarget
capture-frame-key-eviction-probe-keysprobe
ok
capture-frame-key-eviction-probe-owner-thread-pprobe
defteststopped-luvcraft-sessions-consume-and-reject-late-overlays
let*
session
make-instance'luvcraft-session:canvascanvas
owned
make-instance'owned-overlay-probe:canvascanvas
late
make-instance'owned-overlay-probe:canvascanvas:fail-release-pt
conditionnil
unwind-protect
progn
ok
=1
length
luvcraft-session-overlayssession
call-with-stop-controller
luvcraft::luvcraft-session-stop-controllersession
lambda
nil

A duplicate remains idempotent even if the closing native canvas would reject a callback before the attachment gate can inspect it.

setf
overlay-owner-canvas-reject-requests-pcanvas
t
ok
eqowned
ok
zerop
owned-overlay-release-countowned
ok
=1
length
luvcraft-session-overlayssession
setf
overlay-owner-canvas-reject-requests-pcanvas
nil

Removal remains legal after the terminal transition so teardown and constructor rollback never lose their release path.

ok
eqowned
handler-bind
release-warning#'muffle-warning
handler-case
application-attachment-closed
failure
setfconditionfailure
ok
eqlate
application-attachment-closed-attachmentcondition
ok
eq:stopped
application-attachment-closed-statecondition
ok
=1
owned-overlay-release-countowned

The rejected release failed deliberately, but ADD invoked it once and preserved the semantic rejection as the primary condition.

ok
=1
owned-overlay-release-countlate
ok
owned-overlay-released-on-owner-plate
ok
null
luvcraft-session-overlayssession
deftestoverlay-add-versus-stop-never-repopulates-the-terminal-registry
dotimes
iteration16
let*
session
make-instance'luvcraft-session:canvascanvas
overlay
make-instance'owned-overlay-probe:canvascanvas
start
sb-thread:make-semaphore:count0
finished
sb-thread:make-semaphore:count0
add-conditionnil
stop-conditionnil
add-threadnil
stop-threadnil
declare
ignoreiteration
unwind-protect
progn
setfadd-thread
sb-thread:make-thread
lambda
unwind-protect
progn
sb-thread:wait-on-semaphorestart
handler-case
add-luvcraft-overlaysessionoverlay
error
failure
setfadd-conditionfailure
sb-thread:signal-semaphorefinished
:name"Luvcraft racing overlay add"
stop-thread
sb-thread:make-thread
lambda
unwind-protect
progn
sb-thread:wait-on-semaphorestart
handler-case
call-with-stop-controller
luvcraft::luvcraft-session-stop-controllersession
lambda
dolist
attached
copy-list
luvcraft-session-overlayssession
error
failure
setfstop-conditionfailure
sb-thread:signal-semaphorefinished
:name"Luvcraft racing overlay stop"
sb-thread:signal-semaphorestart
sb-thread:signal-semaphorestart
unless
and
sb-thread:wait-on-semaphorefinished:timeout2.0
sb-thread:wait-on-semaphorefinished:timeout2.0
error"The Luvcraft attachment race did not settle."
sb-thread:join-threadadd-thread
setfadd-threadnil
sb-thread:join-threadstop-thread
setfstop-threadnil
ok
nullstop-condition
ok
or
nulladd-condition
ok
eq:stopped
stop-controller-state
luvcraft::luvcraft-session-stop-controllersession
ok
null
luvcraft-session-overlayssession
ok
=1
owned-overlay-release-countoverlay
ok
owned-overlay-released-on-owner-poverlay
sb-thread:signal-semaphorestart
sb-thread:signal-semaphorestart
whenadd-thread
sb-thread:join-threadadd-thread
whenstop-thread
sb-thread:join-threadstop-thread
dolist
attached
copy-list
luvcraft-session-overlayssession
ignore-errors
defclassrecording-chunk-window
locations:initformnil:accessorrecording-window-locations
defmethodlocate-chunk-window-site
push
listxyz
recording-window-locationswindow
valueswindow37:available
defclassrecording-modal-focus
score:initarg:score:initformnil:readerrecording-focus-score
transitions:initformnil:accessorrecording-focus-transitions
events:initformnil:accessorrecording-focus-events
defmethodluvcraft-focus-score
recording-focus-scorefocus
defmethodluvcraft-focus-entered
push:entered
recording-focus-transitionsfocus
defmethodluvcraft-focus-left
push:left
recording-focus-transitionsfocus
defmethodhandle-luvcraft-focus-event
declare
ignoresessioncanvas
pushevent
recording-focus-eventsfocus
deftestmodal-focus-suspends-player-input-and-owns-events
let
session
make-instance'luvcraft-session
first
make-instance'recording-modal-focus
event
make-instance'canvas-key-press-event:timestamp0:key-name:w
setf
movement-urging-p
luvcraft-session-movement-intentsession
:forward
t
movement-intent-jump-requested-p
luvcraft-session-movement-intentsession
t
ok
eqfirst
ok
eqfirst
luvcraft-session-modal-focussession
ok
movement-intent-still-p
luvcraft-session-movement-intentsession

Interpreting keys belongs to the layer above; what the core promises is that a focused object is offered the event and that nothing else sees it.

ok
equal
listevent
recording-focus-eventsfirst
ok
movement-intent-still-p
luvcraft-session-movement-intentsession
ok
equal'
:left:entered
recording-focus-transitionsfirst
ok
equal'
:entered
recording-focus-transitionssecond
remove-luvcraft-overlaysessionsecond:release-pnil
ok
null
luvcraft-session-modal-focussession
ok
equal'
:left:entered
recording-focus-transitionssecond
deftestoff-thread-overlay-removal-waits-for-the-borrowing-frame
let*
session
make-instance'luvcraft-session:canvascanvas
overlay
make-instance'owned-overlay-probe:canvascanvas
borrowed
sb-thread:make-semaphore:count0
continue
sb-thread:make-semaphore:count0
frame-finished
sb-thread:make-semaphore:count0
remove-finished
sb-thread:make-semaphore:count0
snapshot-kept-pnil
remove-resultnil
frame-conditionnil
remove-conditionnil
frame-threadnil
remove-threadnil
unwind-protect
progn
add-luvcraft-overlaysessionoverlay
setfframe-thread
sb-thread:make-thread
lambda
handler-case
request-canvas-framecanvas
lambda
timestamp
declare
ignoretimestamp
let
snapshot
copy-list
luvcraft-session-overlayssession
sb-thread:signal-semaphoreborrowed
unless
sb-thread:wait-on-semaphorecontinue:timeout1.0
error"The borrowing frame was not released."
setfsnapshot-kept-p
eqoverlay
firstsnapshot
error
condition
setfframe-conditioncondition
sb-thread:signal-semaphoreframe-finished
:name"Luvcraft borrowing frame"
unless
sb-thread:wait-on-semaphoreborrowed:timeout1.0
error"The overlay frame did not borrow its snapshot."
setfremove-thread
sb-thread:make-thread
lambda
handler-case
setfremove-result
error
condition
setfremove-conditioncondition
sb-thread:signal-semaphoreremove-finished
:name"Luvcraft off-thread overlay removal"
ok
not
sb-thread:wait-on-semaphoreremove-finished:timeout0.02
"remove remains synchronous while the frame owns its snapshot"
ok
zerop
owned-overlay-release-countoverlay
"the borrowed overlay has not been released"
sb-thread:signal-semaphorecontinue
unless
sb-thread:wait-on-semaphoreframe-finished:timeout1.0
error"The borrowing frame did not finish."
unless
sb-thread:wait-on-semaphoreremove-finished:timeout1.0
error"The queued overlay removal did not finish."
sb-thread:join-threadframe-thread
setfframe-threadnil
sb-thread:join-threadremove-thread
setfremove-threadnil
ok
nullframe-condition
ok
nullremove-condition
oksnapshot-kept-p
ok
eqoverlayremove-result
ok
=1
owned-overlay-release-countoverlay
ok
owned-overlay-released-on-owner-poverlay
ok
null
luvcraft-session-overlayssession
sb-thread:signal-semaphorecontinue
whenframe-thread
sb-thread:join-threadframe-thread
whenremove-thread
sb-thread:join-threadremove-thread
deftestmid-frame-overlay-fuse-detaches-without-releasing
let*
session
make-instance'luvcraft-session:canvascanvas
overlay
make-instance'owned-overlay-probe:canvascanvas
unwind-protect
progn
add-luvcraft-overlaysessionoverlay
request-canvas-framecanvas
lambda
timestamp
declare
ignoretimestamp
remove-luvcraft-overlaysessionoverlay:release-pnil
ok
zerop
owned-overlay-release-countoverlay
ok
null
luvcraft-session-overlayssession
deftestoverlay-release-errors-return-through-the-frame-boundary
let*
session
make-instance'luvcraft-session:canvascanvas
overlay
make-instance'owned-overlay-probe:canvascanvas:fail-release-pt
conditionnil
unwind-protect
progn
add-luvcraft-overlaysessionoverlay
handler-case
error
failure
setfconditionfailure
ok
typepcondition'error
ok
=1
owned-overlay-release-countoverlay
ok
owned-overlay-released-on-owner-poverlay
ok
null
luvcraft-session-overlayssession
deftestoverlay-teardown-detaches-all-and-aggregates-release-failures
let*
session
make-instance'luvcraft-session:canvascanvas
first
make-instance'owned-overlay-probe:canvascanvas:fail-release-pt
second
make-instance'owned-overlay-probe:canvascanvas
third
make-instance'owned-overlay-probe:canvascanvas:fail-release-pt
conditionnil
unwind-protect
progn
handler-case
with-release-report

This is the application teardown policy: keep the release report on its owner thread while each individual release crosses, synchronously, to the native canvas owner.

dolist
overlay
copy-list
luvcraft-session-overlayssession
releasing:overlay
release-error
failure
setfconditionfailure
ok
typepcondition'release-error
ok
=2
length
release-error-failurescondition
ok
every
lambda
failure
eq:overlay
release-failure-namefailure
release-error-failurescondition
ok
every
lambda
overlay
=1
owned-overlay-release-countoverlay
listfirstsecondthird
ok
every#'owned-overlay-released-on-owner-p
listfirstsecondthird
ok
null
luvcraft-session-overlayssession
defclassrecording-pointer-canvas
relative-p:initformnil:accessorrecording-canvas-relative-p
defmethodset-canvas-relative-pointer-mode
setf
recording-canvas-relative-pcanvas
not
nullenabled
deftestdefault-residency-radius-is-six-chunks
ok
=6
luvcraft-session-residency-radius
make-instance'luvcraft-session
deftestfocus-borrows-mouse-look-and-hands-it-back
let*
camera
make-instance'fly-camera:yaw0.25:pitch-0.1
session
make-instance'luvcraft-session:canvascanvas:cameracamera
first
make-instance'recording-modal-focus
setf
recording-canvas-relative-pcanvas
t
luvcraft::luvcraft-session-pointer-captured-psession
t
ok
not
recording-canvas-relative-pcanvas
"a focused interaction is given an ordinary cursor"

Moving straight from one interaction to another still owes the capture.

ok
not
recording-canvas-relative-pcanvas
setf1.00.4
ok
recording-canvas-relative-pcanvas
"leaving focus puts the player back into mouse look"
ok
luvcraft::luvcraft-session-pointer-captured-psession
ok
null
luvcraft::luvcraft-session-focus-camera-originsession
"mouse look cancels the competing cinematic return"
ok
<
abs
-0.25
1e-6
setf0.7
ok
<1e-6
"a subsequent mouse delta is not dragged back"

A player who was not in mouse look is not put into it by focusing.

setf
luvcraft::luvcraft-session-pointer-captured-psession
nil
recording-canvas-relative-pcanvas
nil
ok
not
recording-canvas-relative-pcanvas
ok
not
luvcraft::luvcraft-session-pointer-captured-psession
deftestorderly-quit-callback-owns-native-close-once
let*
calls0
session
make-instance'luvcraft-session:quit-function
lambda
stopping-session
declare
ignorestopping-session
incfcalls
event
make-instance'luv:canvas-window-close-request-event:timestamp0
ok
eq:defer-canvas-close
handle-canvas-eventsessionnilevent
ok
eq:defer-canvas-close
handle-canvas-eventsessionnilevent
luv:wait-for-controlled-stop
luvcraft::luvcraft-session-stop-controllersession
ok
=1calls
ok
not
luvcraft-session-running-psession
defmethodluvcraft::hand-item-taken-out
bodysession
declare
ignoreitembodysession
error"fixture failed to open"
deftestfailed-hand-item-initialization-is-not-published-in-hand
let
body
make-instance'player-body
item
make-instance'failing-hand-item
ok
null
player-body-hand-itembody
ok
memberitem
luvcraft::player-body-pocketbody
deftestfailed-phone-mode-is-neither-cached-nor-left-overlaid
let*
unwind-protect
progn
setf
symbol-function
firstsymbols
lambda
&restarguments
declare
ignorearguments
values4020
symbol-function
secondsymbols
lambda
weight
declare
ignoreweight
#P"fixture.ttf"
symbol-function
thirdsymbols
lambda
&restarguments
declare
ignorearguments
display
symbol-function
fourthsymbols
lambda
made-display
declare
ignoremade-display
t
symbol-function
fifthsymbols
lambda
&restarguments
declare
ignorearguments
error"fixture Telegram startup failed"
symbol-function
sixthsymbols
lambda
session-displayremoved-display
declare
ignoresession-display
setfremovedremoved-display
let
ok
null
phone-displayphone
ok
eqdisplayremoved
loopforsymbolinsymbolsforfunctioninoriginalsdo
setf
symbol-functionsymbol
function
deftestsoftware-cursor-follows-screen-pointer-coordinates
let

The Vulkan viewport performs the Y inversion: moving from the centre to the top-left subtracts one in both vertex-coordinate axes here. The quad's own corner offset survives the subtraction as rounding, so the shift is read to single-float tolerance rather than exactly.

flet
shift
lane
-
areftop-leftlane
arefcenterlane
ok
<
abs
+1.0
shift0
1e-5
ok
<
abs
+1.0
shift1
1e-5

The design-grid lanes describe the arrow, not where it is drawn.

ok
equalp
subseqcenter35
subseqtop-left35
deftestcursor-corners-round-without-moving-the-outline

The shader rounds by insetting the outline and growing the distance back, which only reads as the drawn shape if every inset corner sits exactly one radius inside both of the edges that meet there.

let*
ok
=count
lengthinset

The tip is the hotspot, so the design grid starts there.

ok
equal'
0.00.0
firstoutline
ok
equal
list
reduce#'maxoutline:key#'first
reduce#'maxoutline:key#'second
dotimes
indexcount
destructuring-bind
corner-xcorner-y
nthindexinset
dolist
edge
list
-index1
index
let*
from
nth
modedgecount
outline
to
nth
mod
+edge1
count
outline
depth
+
*
firstnormal
-corner-x
firstfrom
*
secondnormal
-corner-y
secondfrom
ok
<
abs
+radiusdepth
1e-4
deftestpointer-reports-coalesce-into-latest-frame-state
let
session
make-instance'luvcraft-session
setf
luvcraft::luvcraft-session-pointer-dirty-psession
nil
dolist
point'
12.018.0
40.055.0
91.073.0
ok
=91.0
luvcraft::luvcraft-session-pointer-xsession
ok
=73.0
luvcraft::luvcraft-session-pointer-ysession
ok
luvcraft::luvcraft-session-pointer-dirty-psession
deftestfocusing-a-terminal-frames-the-whole-wall-above-the-hotbar
let*
world
make-block-world:chunk-width16:chunk-height16:chunk-depth16
camera
make-instance'fly-camera:position
make-vec32.53.50.5
:yaw0.0:pitch0.0
session
make-instance'luvcraft-session:worldworld:cameracamera
let*
surface
display
make-instance'terminal-display:surfacesurface
ordinary-position
luvcraft::copy-camera-position
camera-positioncamera
ordinary
ordinary-focal
aref17

The live hotbar occupies 114 pixels at 1280 high, hence 57 in this proportional 960 by 640 framing fixture.

target
luvcraft::terminal-focus-camera-posesurface9606400.00.00.057.0
add-luvcraft-overlaysessiondisplay

Which key reaches this is the command layer's business; what the wall promises is the framing it asks the camera for once it is focused.

ok
eqdisplay
luvcraft-session-modal-focussession

The final pose is head-on and every surface corner lies inside the six-percent picture margin plus the hotbar's excluded lower region.

multiple-value-bind
rightupforward
let*
surface-right
luvcraft::voxel-direction-vec3
luvcraft::terminal-face-frame-right
luvcraft::terminal-face-frame
terminal-surface-facesurface
surface-up
luvcraft::voxel-direction-vec3
luvcraft::terminal-face-frame-up
luvcraft::terminal-face-frame
terminal-surface-facesurface
focal
aspect
/960.0640.0
flet
clip
point
let*
relative
make-vec3
-
vec3-xpoint
camera-xcamera
-
vec3-ypoint
camera-ycamera
-
vec3-zpoint
camera-zcamera
depth
vec3-dotrelativeforward
list
/
*
vec3-dotrelativeright
focal
*depthaspect
-
/
*
vec3-dotrelativeup
focal
depth
loopforcolumnin'
0.03.0
do
loopforrowin'
0.02.0
forpoint=
luvcraft::terminal-offset-pointlower-leftsurface-rightcolumnsurface-uprow
forprojected=
clippoint
do
ok
<=-0.92001
firstprojected
0.92001
ok
<=-0.88001
secondprojected
0.701885
luvcraft::set-camera-posecamera
luvcraft::make-camera-poseordinary-position0.00.0ordinary
let
ok
<focusedordinary
ok
>
aref17
ordinary-focal
ok
null
luvcraft-session-modal-focussession
dotimes
iteration20
declare
ignoreiteration
ok
<1e-5
ok
<
vec3-length
make-vec3
-
camera-xcamera
vec3-xordinary-position
-
camera-ycamera
vec3-yordinary-position
-
camera-zcamera
vec3-zordinary-position
1e-5
deftestfocused-terminal-display-sends-keys-to-its-pty
luv.ghostty:with-terminal
ghostty-terminal:columns32:rows4
let*
device
luv.terminal:open-pty-deviceghostty-terminal:program"/bin/sh":arguments
list"-c""IFS= read -r line; printf 'focused:%s\r\n' \"$line\""
display
make-instance'terminal-display:terminalghostty-terminal:devicedevice
session
make-instance'luvcraft-session
unwind-protect
progn
dolist
event
list
make-instance'canvas-key-press-event:timestamp0:key-name:o:character#\o:unshifted-character#\o
make-instance'canvas-key-press-event:timestamp0:key-name:k:character#\k:unshifted-character#\k
make-instance'canvas-key-press-event:timestamp0:key-name:return:character#\Return:unshifted-character#\Return
ok
eq:exited
deftestterminal-film-mode-fits-the-authored-wall-and-returns-to-shell
let*
world
make-block-world:chunk-width16:chunk-height16:chunk-depth16
session
make-instance'luvcraft-session
aspect
/16.09.0
let*
surface
display
make-instance'terminal-display:surfacesurface
change-terminal-display-modedisplaysession:film
ok
eq:film
terminal-display-modedisplay
change-terminal-display-modedisplaysession:shell
ok
eq:shell
terminal-display-modedisplay
deftestterminal-display-pty-output-marks-a-frame-publication-dirty
luv.ghostty:with-terminal
terminal:columns32:rows4
let
display
make-instance'terminal-display:terminalterminal
attach-terminal-display-ptydisplay:program"/bin/sh":arguments
list"-c""printf 'fresh shell output\\r\\n'"
let
device
terminal-display-devicedisplay
unwind-protect
progn
ok
eq:exited
ok
luvcraft::terminal-display-dirty-pdisplay
deftestblock-smash-particles-rise-fall-and-expire
let*
system
make-instance'block-particle-system
smash-block-particlessystemluvcraft::*grass-block*coordinate
let*
particle
aref
block-particle-system-particlessystem
0
initial-y
luvcraft::block-particle-yparticle
initial-velocity-y
luvcraft::block-particle-velocity-yparticle
ok
>
luvcraft::block-particle-yparticle
initial-y
ok
<
luvcraft::block-particle-velocity-yparticle
initial-velocity-y
defunmake-critter-test-world
&key
surfaceluvcraft::*grass-block*

A one-chunk world with a flat surface top at y=1, for walking animals over.

let
loopforxbelow16do
loopforzbelow16do
setfluvcraft::*dirt-block*surface
world
deftestturtles-stand-on-the-ground-they-walk-over
let*
ok
dotimes
step1200
declare
ignorablestep

It rests on the surface it started on, has not fallen through the world or climbed it, and has actually gone somewhere.

ok
luvcraft::critter-grounded-pturtle

And it stays inside the chunk it was spawned in: a turtle walking into the world boundary turns away from it rather than leaving.

deftestturtles-wander-the-same-way-from-the-same-seed
let
dotimes
attempt2
declare
ignorableattempt
let
turtle
spawn-critter-at:turtleworld828777
dotimes
step600
declare
ignorablestep
ok
equal
firstpositions
secondpositions
deftesta-turtle-says-which-ground-it-lives-on
let
bare
make-critter-test-world:surfaceluvcraft::*stone-block*
ok
spawn-critter-at:turtlemeadow4241
ok
spawn-critter-at:turtle
make-critter-test-world:surfaceluvcraft::*sand-block*
4241

Stone is not turtle country, an occupied cell is nobody's, and a species nothing has claimed is an error rather than a silent absence.

ok
null
spawn-critter-at:turtlebare4241
setfluvcraft::*stone-block*
ok
null
spawn-critter-at:turtlemeadow4241
ok
signals
spawn-critter-at:axolotlmeadow4241
'error
deftesta-critter-population-fills-up-and-forgets-what-wanders-off
let
population
make-instance'critter-population:target-count3

Only the resident chunk offers sites, so the neighbourhood has to be visited a few times before it is as full as it wants to be.

dotimes
declare
ignorableframe
maintain-critter-populationpopulationworld8d08d0
ok
=3
critter-countpopulation
ok
every
critter-population-critterspopulation
maintain-critter-populationpopulationworld900d0900d0
ok
zerop
critter-countpopulation
deftesta-critter-model-is-a-bounded-stream-of-turned-boxes
let*
population
make-instance'critter-population
turtle
spawn-critter-at:turtleworld8285
let
vertices
critter-verticespopulationworld
ok
typepvertices'
arraysingle-float

Every vertex of every turned box stays within the animal: the yaw rotation moves the model around its own position rather than away from it, whatever heading it walks on.

ok
loopforoffsetfrom0below
lengthvertices
byluvcraft::+block-mesh-floats-per-vertex+always
let
dx
-
arefverticesoffset
y
arefvertices
+offset1
dz
-
arefvertices
+offset2
defunmake-critter-riding-session

A headless session with one turtle in front of a player looking at it.

let*
population
make-instance'critter-population
turtle
spawn-critter-at:turtleworld8283
camera
make-instance'fly-camera:position
make-vec38.5d02.3d05.5d0
:yaw0d0:pitch0d0
player
make-instance'block-world-player:position
make-vec38.5d02d05.5d0
session
make-instance'luvcraft-session:worldworld:cameracamera:playerplayer:critterspopulation
valuessessionturtleplayerworld
deftestlooking-at-an-animal-is-looking-past-the-terrain
multiple-value-bind
sessionturtleplayerworld
declare
ignoreplayer

A wall between the two is decided by which the ray reaches first, not by whether the animal is within reach.

setfluvcraft::*stone-block*
setfnil

And an animal beyond the player's reach is out of it.

setf
vec3-z
camera-position
luvcraft-session-camerasession
-8d0
deftestmounting-a-turtle-carries-the-player-and-reins-the-animal
multiple-value-bind
sessionturtleplayerworld
let
ok
typepride'critter-ride
ok
eqturtle
critter-ride-critterride
ok
eqride
luvcraft-session-modal-focussession

A mount carries the player, so the ordinary controller stands down.

An unridden turtle rests first; a reined one walks. The rider steers with the session's own movement intent, the same one the player's legs would have read.

setf
movement-urging-p
luvcraft-session-movement-intentsession
:forward
t
let
dotimes
declare
ignorableframe
advance-critters
luvcraft-session-critterssession
world
/1d060
advance-luvcraft-focusridesession
/1d060
ok
not
turtle-resting-pturtle
ok

Wherever it went, the player went too.

And the camera it asks for is a seat on the animal: over its own footprint, above its back, facing the way it faces give or take the sway of its gait.

let*
position
luvcraft::camera-pose-positionpose
ok
typeppose'luvcraft::camera-pose

Shift-TAB is the same toggle: it puts the rider down somewhere their own box fits and gives the animal its mind back.

ok
null
luvcraft-session-modal-focussession
ok
ok
turtle-resting-pturtle
deftesta-ride-ends-when-its-animal-does
multiple-value-bind
sessionturtleplayerworld
declare
ignoreplayerworld
let
population
luvcraft-session-critterssession
ok
typepride'critter-ride
setf
fill-pointer
critter-population-critterspopulation
0
advance-luvcraft-focusridesession
/1d060
ok
null
luvcraft-session-modal-focussession
ok
eqturtle
critter-ride-critterride
deftestan-animal-nobody-can-ride-is-only-looked-at
let
critter
make-instance'critter

And it ignores a rider's wishes rather than failing to understand them.

ok
null
deftesta-turtle-and-the-player-are-both-bodies
let
turtle
make-instance'turtle
player
make-instance'block-world-player
dolist
body
listturtleplayer
ok
ok
plusp
ok
deftestvec3-is-imported-from-its-arithmetic-representation-package
dolist
package-name'
"LUVCRAFT.WORLD""LUVCRAFT"
multiple-value-bind
symbolstatus
find-symbol"VEC3"package-name
ok
eqstatus:internal
ok
eq
symbol-package'vec3
find-package"LUV.ARITHMETIC.LISP.VEC3"
deftestplayer-storage-publishes-quantities-without-wrapping-values
let*
velocity
make-vec34d05d06d0
player
make-instance'block-world-player:positionposition:velocityvelocity
ok
eqposition
player-positionplayer
ok
eq:world-position
luv.arithmetic:quantity-specification-name
ok
eq:point
luv.arithmetic:quantity-specification-character
ok
=1
luv.arithmetic:quantity-specification-tensor-order
let
ok
equalp
make-vec33d04.5d06d0
predicted
ok
eqposition
player-positionplayer
ok
eqvelocity
player-velocityplayer
deftestsky-frame-structure-publishes-quantities-without-changing-layout
let*
direction
luvcraft::sky-frame-parameters-sun-directionsky
ok
typepdirection'vec3
ok
eq:world-direction
luv.arithmetic:quantity-specification-name
ok
ok
typep
luvcraft::sky-frame-parameters-fog-farsky
'single-float
deftestsemantic-owner-audit-exposes-camera-sky-material-and-timing-fields
dolist
claim'
fly-cameraluvcraft::yaw:camera-yaw
fly-cameraluvcraft::sensitivity:look-sensitivity
sky-clockluvcraft::rate:sky-cycle-rate
sky-clockluvcraft::pinned-day-fraction:day-fraction
luvcraft::sky-keyframeluvcraft::sun-color:linear-rgb
luvcraft::sky-keyframeluvcraft::fog-far:view-distance
block-kindluvcraft::light-opacity:block-light-attenuation-step
block-kindluvcraft::surface-emission:material-emission
luvcraft::luvcraft-frame-sampleluvcraft::simulation-seconds:simulation-duration
luvcraft::luvcraft-frame-benchmarkluvcraft::drain-seconds:benchmark-drain-duration
luvcraft::production-resultluv.production::elapsed-seconds:production-duration
luvcraft::luvcraft-lighting-stateluvcraft::last-latency-seconds:lighting-reconciliation-duration
luvcraft-sessionluvcraft::last-frame-time:monotonic-frame-time
luvcraft-sessionluvcraft::physics-accumulator:physics-accumulated-duration
deftestchunk-window-protocol-selects-representation-at-crossings
let*
space
make-voxel-space:chunk-shape
make-chunk-shape:width2:height2:depth2
window

A local step remains pure domain arithmetic: the window is not asked.

multiple-value-bind
offsetlocalcrossingmaterializationavailability
ok
=offset1
ok
=
local-coordinate-xlocal
1
ok
nullcrossing
ok
nullmaterialization
ok
eqavailability:local
ok
null
recording-window-locationswindow

Crossing selects the aggregate once; a fifth window participates by adding a method, with no type switch in the continuation operation.

multiple-value-bind
offsetlocalcrossingmaterializationavailability
ok
=offset37
ok
=
local-coordinate-xlocal
0
ok
ok
eqmaterializationwindow
ok
eqavailability:available
ok
equal
recording-window-locationswindow
'
200
deftestchunk-window-neighbor-iteration-retains-only-explicit-copies
let*
space
make-voxel-space:chunk-shape
make-chunk-shape:width3:height3:depth3
window
neighborsnil
do-chunk-window-neighbors
offsetdestinationcrossingdirectionmaterializationavailabilitywindowdomain*voxel-face-directions*
ok
eqavailability:local
push
listoffset
copy-local-coordinatedestination
neighbors
ok
=
lengthneighbors
6
ok
null
recording-window-locationswindow
setfneighborsnil
do-chunk-window-neighbors
offsetdestinationcrossingdirectionmaterializationavailabilitywindowdomain*voxel-face-directions*
whencrossing
pushcrossingneighbors
ok
equalneighbors
ok
equal
recording-window-locationswindow
'
-111
deftestcurrent-meshing-windows-share-location-availability
let*
world
make-block-world:chunk-width2:chunk-height2:chunk-depth2
chunk
dolist
window
listworldneighborhoodsnapshot
multiple-value-bind
materializationoffsetavailability
okmaterialization

Offsets belong to each representation: the live/neighborhood chunks use local dense order, while the snapshot includes a halo.

ok
typepoffset'
integer0
ok
eqavailability:available
multiple-value-bind
materializationoffsetavailability
ok
nullmaterialization
ok
nulloffset
ok
eqavailability:unavailable
deftestvoxel-light-fields-retain-distinct-quantity-definitions
let*
world
make-block-world:chunk-width2:chunk-height2:chunk-depth2
chunk
let*
light
block-chunk-light-fieldchunk
entry
nth-value0
captured-representation
block-properties
luvcraft::light-region-entry-block-propertiesentry
ok
eq:sky-propagation-level
luv.arithmetic:quantity-specification-name
ok
eq:block-propagation-level
luv.arithmetic:quantity-specification-name
ok
typepresident-representation'luvcraft::voxel-light-columns
ok
typepcaptured-representation'luvcraft::voxel-light-columns
ok
not
eqresident-representationcaptured-representation
ok
eq
block-chunk-domainchunk
dolist
lane-and-quantity'
luvcraft::propagation-loss:block-light-attenuation-step
luvcraft::emission-level:block-light-emission-step
destructuring-bindlane-and-quantity
ok
eqquantity
luv.arithmetic:quantity-specification-name
luv.arithmetic:declaration-quantity-specification
luv.arithmetic.records:columnar-row-lane-declaration
luvcraft::block-light-properties-row-declarationblock-properties
lane
dolist
claim`
,light:sky-light
,light:block-light
,entry:block-content
,entry:sky-light
,entry:block-light
,snapshot:block-content
,snapshot:sky-light
,snapshot:block-light
destructuring-bind
materializationname
claim
deftestblock-meshes-carry-a-repeated-product-matching-the-shader-contract
let*
world
make-block-world:chunk-width2:chunk-height2:chunk-depth2
chunk
mesh
mesh-block-chunk
make-instance'exposed-face-mesher
worldchunk
declaration
block-mesh-vertex-declarationmesh
element
luv.arithmetic:repeated-quantity-layout-element-layoutlayout
ok
=luvcraft::+block-mesh-floats-per-vertex+
luv.arithmetic:repeated-quantity-layout-stridelayout
ok
=
length
block-mesh-verticesmesh
ok
signals
make-instance'block-mesh:vertices
make-array11:element-type'single-float
:vertex-count1:face-count0
'error
deftestworld-text-model-carries-atlas-band-and-ink-metadata
let*
serialized
luv.slug::make-slug-serialized-outline:horizontal-band-count7:vertical-band-count5
resource
luv.slug::make-slug-device-glyph:serializedserialized
glyph
luv.slug:make-slug-glyph-placement:resourceresource:origin-x0.0:origin-y0.0:outline-min-x0.0:outline-min-y0.0:outline-max-x1.0:outline-max-y1.0
locations
make-hash-table:test#'eq
atlas
luv.slug:make-slug-glyph-atlas:locationslocations
instancesnil
setf
gethashresourcelocations
'
1729
instances
ok
=24
lengthinstances
ok
=10.0
arefinstances2

The quad is the exact outline: the vertex stage dilates it by pixels.

ok
<
abs
-0.5
arefinstances3
1e-6
ok
<
abs
-0.5
arefinstances7
1e-6
ok
=7.0
arefinstances11
ok
=5.0
arefinstances14
ok
=17.0
arefinstances15
ok
=29.0
arefinstances16

Geometry carries only the static em padding (none by default); band selection receives the exact bounds used by pack-slug-outline. Their spare Z lanes carry the default ink components.

ok
=0.0
arefinstances9
ok
=1.0
arefinstances12
let
let
ok
=-0.035
arefpadded9
ok
=1.035
arefpadded12
ok
equalp
0.00.00.32
subseqinstances1821
ok
equalp
1.01.00.48
subseqinstances2124
ok
=0.96
arefinstances17
deftestempty-terminal-presentations-have-no-drawable-glyphs
let*
domain
make-instance'luvcraft::terminal-grid-domain:columns80:rows24
ok
every
lambda
character
char=character#\Space
luvcraft::terminal-grid-presentation-characterspresentation
deftestterminal-block-material-rectangles-become-one-display-surface
let
world
make-block-world:chunk-width16:chunk-height16:chunk-depth16

:BACK reads left-to-right in world X from a viewer on negative Z.

multiple-value-bind
surfacestatus
ok
eqstatus:rectangle
ok
=3
terminal-surface-widthsurface
ok
=2
terminal-surface-heightsurface
ok
=2
world-coordinate-x
terminal-surface-originsurface
ok
=3
world-coordinate-y
terminal-surface-originsurface
let
ok
=2.0
vec3-xlower-left
ok
=3.0
vec3-ylower-left
ok
=4.0
vec3-zlower-left

One missing voxel leaves an L-shaped component. The retained screen becomes invalid and rediscovery refuses to pretend it is rectangular.

edit-block-atnilworld334
multiple-value-bind
splitsplit-status
ok
nullsplit
ok
eqsplit-status:non-rectangular
deftestterminal-discovery-is-a-compiled-discover-once-frontier-program
let
ok
eq:discover-once
luvcraft.frontier:frontier-program-definition-familydefinition
ok
luvcraft.frontier:frontier-program-definition-retain-admissions-pdefinition
ok
functionp
luvcraft.frontier:frontier-realization-drain-functionrealization
ok
functionp
luvcraft.frontier:frontier-realization-admit-functionrealization

A rectangle straddling a chunk seam is discovered as one component whose retained admitted sites are exactly its blocks, without any coordinate objects retained per member.

let
world
make-block-world:chunk-width16:chunk-height16:chunk-depth16
multiple-value-bind
executionstatus
luvcraft::discover-terminal-componentworld1544:backluvcraft:*terminal-block*:air
ok
eqstatus:component
ok
=18
luvcraft.frontier:frontier-execution-visitsexecution
ok
=18
luvcraft.frontier:frontier-site-buffer-length
luvcraft.frontier:frontier-execution-admitted-sitesexecution
ok
plusp
luvcraft.frontier:frontier-execution-crossingsexecution

Every popped site exposes its four coplanar relations.

ok
=
*418
luvcraft.frontier:frontier-execution-relationsexecution
multiple-value-bind
surfacestatus
find-terminal-surfaceworld1544:back
ok
eqstatus:rectangle
ok
=6
terminal-surface-widthsurface
ok
=3
terminal-surface-heightsurface
ok
=13
world-coordinate-x
terminal-surface-originsurface

A covered seed and a wrong material report their own statuses.

setfluvcraft::*stone-block*
ok
eq:covered
nth-value1
find-terminal-surfaceworld1544:back
ok
eq:not-terminal
nth-value1
find-terminal-surfaceworld1543:back
deftestterminal-grid-fits-the-unified-surface-not-individual-blocks
let*
world
make-block-world:chunk-width16:chunk-height16:chunk-depth16
domain
make-instance'luvcraft::terminal-grid-domain:columns80:rows24
let
surface
multiple-value-bind
luvcraft::fit-terminal-grid-in-surfacedomainsurface0.61.00.121.0
ok
<
abs
-scale
/7.7648.0
1e-6
ok
<1e-6
ok
ok
<
abs
-left0.12
1e-6
ok
>bottom0.12

Eight blocks can carry eighty columns because font fit is a surface projection; no terminal-cell count is attached to one voxel.

ok
=10
/
luvcraft::terminal-grid-domain-columnsdomain
terminal-surface-widthsurface
deftestpacked-light-worklists-preserve-order-and-release-entries
let*
entry
gethash
chunk-domain-coordinate
block-chunk-domainchunk
luvcraft::light-region-entriesregion
lifo
level
let*
bucket
aref
luvcraft::light-worklist-bucketslifo
0
entries
luvcraft::light-worklist-bucket-entry-lanebucket
multiple-value-bind
poppedpopped-offsetpopped-levelpresent-p
okpresent-p
ok
eqentrypopped
ok
=2popped-offset
ok
=12popped-level
multiple-value-bind
poppedpopped-offsetpopped-levelpresent-p
okpresent-p
ok
eqentrypopped
ok
=1popped-offset
ok
=3popped-level
ok
loopforindexbelow
array-total-sizeentries
always
null
row-major-arefentriesindex
ok
eqentries
luvcraft::light-worklist-bucket-entry-lane
aref
luvcraft::light-worklist-bucketslifo
0
dolist
item'
13
212
312
45
dolist
expected'
312
212
45
13
multiple-value-bind
poppedpopped-offsetpopped-levelpresent-p
okpresent-p
ok
eqentrypopped
ok
=
firstexpected
popped-offset
ok
=
secondexpected
popped-level
loopforbucketacross
luvcraft::light-worklist-bucketslevel
forentries=
luvcraft::light-worklist-bucket-entry-lanebucket
do
ok
loopforindexbelow
array-total-sizeentries
always
null
row-major-arefentriesindex
multiple-value-bind
poppedoffsetpopped-levelpresent-p
ok
nullpopped
ok
nulloffset
ok
nullpopped-level
ok
nullpresent-p
zdefunzoned-test-function
value
"A small definition used to prove inferred zones preserve function shape."
declare
typefixnumvalue
values
1+value
1-value
zdefmethodzoned-test-method
value
declare
ignoresubject
*value2
zdefun
explicit-zoned-test-function:zone:test/explicit-definition:valuevalue
value
value
defuntree-occurrences
needletree
cond
eqneedletree
1
consptree
+
tree-occurrencesneedle
cartree
tree-occurrencesneedle
cdrtree
t0
deftestinstrumentation-macros-keep-their-body-singular
dolist
form'
with-tracy-zone
:test/expansion
compile-marker
with-cpu-trace-zone
:test/expansion
compile-marker
with-luvcraft-frame-timing
nilluvcraft-frame-sample-frame-seconds:test/expansion
compile-marker
ok
=1
tree-occurrences'compile-marker
macroexpand-1form
deftestconcise-zones-preserve-definitions-and-infer-stable-names
let
trace
make-cpu-trace:label"concise zones"
with-cpu-trace
trace
zone
:test/region:value3
multiple-value-bind
abovebelow
ok
=8above
ok
=6below
ok
string="A small definition used to prove inferred zones preserve function shape."
ok
equal'
:test/region:test/explicit-definition"luvcraft.tests/zoned-test-function""luvcraft.tests/zoned-test-method<luvcraft.tests/zoned-test-subject>"
mapcar#'cpu-trace-zone-name
deftestcpu-trace-zones-are-nested-reusable-and-bounded
let
trace
make-cpu-trace:label"test"
let*
first-zones
outer
firstfirst-zones
inner
secondfirst-zones
ok
=2
lengthfirst-zones
ok
eq:outer
cpu-trace-zone-nameouter
ok
eq:inner
cpu-trace-zone-nameinner
ok
=-1
cpu-trace-zone-parent-indexouter
ok
=0
cpu-trace-zone-parent-indexinner
ok
>=
cpu-trace-zone-bytes-consedouter
cpu-trace-zone-bytes-consedinner
let
second-zones
ok
=1
lengthsecond-zones
ok
eqouter
firstsecond-zones
ok
eq:again
cpu-trace-zone-name
firstsecond-zones
let
text
with-output-to-string
stream
print-cpu-tracetracestream
ok
search"inclusive"text
ok
search"allocated"text
ok
search"garbage collection"text
ok
search"again"text
deftestruntime-observations-measure-allocation-and-garbage-collection
let
observation
make-runtime-observation
retainednil
old-nursery-size
sb-ext:bytes-consed-between-gcs
unwind-protect
progn
setf
sb-ext:bytes-consed-between-gcs
*10241024

Establish the small nursery before observing automatic GC. Explicit SB-EXT:GC calls intentionally do not run after-GC hooks.

sb-ext:gc:fullt
with-runtime-observation
observation
setfretained
looprepeat64collect
make-array
*2561024
:element-type'
unsigned-byte8
:initial-element17
setf
sb-ext:bytes-consed-between-gcs
old-nursery-size
ok
=17
aref
firstretained
0
ok
>=
runtime-observation-bytes-consedobservation
*1610241024
ok
plusp
runtime-observation-garbage-collectionsobservation
ok
>=
runtime-observation-gc-secondsobservation
0d0
ok
plusp
runtime-observation-elapsed-secondsobservation
deftesttracy-source-locations-are-interned-per-zone

Tracy tells zones apart by the address of their source location, and recompiling a file re-runs the LOAD-TIME-VALUE that asks for one. Two requests describing the same zone therefore have to answer with the same pointer, or a recompile in the middle of a capture would split the zone.

let
first
luv:tracy-source-location"test/zone":file"tests.lisp"
again
luv:tracy-source-location"test/zone":file"tests.lisp"
other
luv:tracy-source-location"test/other":file"tests.lisp"
ok
cffi:pointer-eqfirstagain
ok
not
cffi:pointer-eqfirstother
ok
string="canvas/frame"
luv:tracy-zone-name:canvas/frame
ok
string="already a name"
luv:tracy-zone-name"already a name"
defteststreaming-trace-quiescence-requires-a-complete-publication-frontier
let
quiet'
:center
00
:desired81:outstanding0:staged0:products81:lighting-dirty-pnil:errors0
dolist
busy'
:outstanding1
:staged1
:products80
:lighting-dirty-pt
:errors1
let
state
copy-listquiet
setf
getfstate
firstbusy
secondbusy
deftesttracy-zone-contexts-remain-natively-nested

The shim owns TracyCZoneCtx values at their native size. Lisp observes only the stack discipline that with-tracy-zone relies on, so adding fields to Tracy's opaque context cannot silently truncate zone-end events again.

if
okt"This machine has no Tracy client to check the zone ABI against."
let
unwind-protect
progn
luv:start-tracy:application-name"luv tests"
let
location
ok
zerop
luv::%tracy-zone-depth
luv::%tracy-emit-zone-beginlocation1
ok
=1
luv::%tracy-zone-depth
luv::%tracy-emit-zone-beginlocation1
ok
=2
luv::%tracy-zone-depth
luv::%tracy-emit-zone-value2
luv::%tracy-emit-zone-end
ok
=1
luv::%tracy-zone-depth
luv::%tracy-emit-zone-end
ok
zerop
luv::%tracy-zone-depth
deftesttexture-preparation-is-a-backend-neutral-command
let
encoder
prepare-textureencoder:shadow-depth:texture-binding
let
command
first
recording-command-encoder-commandsencoder
ok
typepcommand'gpu-prepare-texture-command
ok
eq:shadow-depth
luv::gpu-prepare-texture-command-texturecommand
ok
eq:texture-binding
luv::gpu-prepare-texture-command-usagecommand
deftestframe-performance-summary-is-comparison-friendly
let
samples
make-array4
dotimes
index4
let
sample
luvcraft::make-luvcraft-frame-sample
setf
luvcraft::luvcraft-frame-sample-frame-secondssample
/
1+index
1000d0
arefsamplesindex
sample
let
benchmark
luvcraft::make-luvcraft-frame-benchmark:samplessamples
multiple-value-bind
medianp95meanmaximum
luvcraft::luvcraft-frame-metric-summarybenchmark#'luvcraft::luvcraft-frame-sample-frame-seconds
ok
=2.5d0median
ok
=4d0p95
ok
=2.5d0mean
ok
=4d0maximum
defteststreaming-frame-summary-covers-only-the-transition
let
samples
make-array4
dotimes
index4
let
sample
luvcraft::make-luvcraft-frame-sample
setf
luvcraft::luvcraft-frame-sample-frame-secondssample
/
1+index
1000d0
arefsamplesindex
sample
let*
benchmark
luvcraft::make-luvcraft-frame-benchmark:scenario:streaming:samplessamples:width960:height640:presentation-width1920:presentation-height1280:completion-seconds0.004d0:entering-chunk-count9:settled-frame1
text
with-output-to-string
stream
ok
=2
lengthtransition
ok
search"scene: 960x640; presentation: 1920x1280"text
ok
search"9 entering chunks, 2 frames"text
ok
search"settled: frame 1"text
defclassgated-production-request
gate:initarg:gate:readergated-production-request-gate
value:initarg:value:readergated-production-request-value
defmethodluvcraft::perform-production-request
sb-thread:wait-on-semaphore
gated-production-request-gaterequest
gated-production-request-valuerequest
defclasstitle-canvas
title:initarg:title:accessorcanvas-title
defunproduction-system-active-request
system
sb-thread:with-mutex
luv.production::production-system-locksystem
defunwait-until
predicate&key
timeout2.0
let
deadline
+
get-internal-real-time
round
*timeoutinternal-time-units-per-second
loopuntil
funcallpredicate
when
>=
get-internal-real-time
deadline
dodo
sleep0.001
finally
deftestlittle-world-is-deterministic-and-chunked
let
ok
typep
block-world-sourcefirst
'little-world-source
ok
=
little-world-source-seed
block-world-sourcefirst
77
ok
loopforxfrom-16below32always
loopforzfrom-16below32always
loopforybelow16always
multiple-value-bind
first-blockfirst-status
multiple-value-bind
second-blocksecond-status
and
eqfirst-status:resident
eqsecond-status:resident
eqfirst-blocksecond-block
multiple-value-bind
blockstatus
ok
nullblock
ok
eqstatus:absent
let*
source
make-instance'little-world-source:seed77
world
make-block-world:sourcesource
let
revision
block-world-revisionworld
ok
=
block-world-revisionworld
revision
deftestlittle-world-edits-survive-rematerialization
let*
world
make-little-block-world:chunk-radius0:seed31
source
block-world-sourceworld
ok
edit-block-atnilworld111
multiple-value-bind
blockpresent-p
block-edit-at
little-world-source-editssource
111
okpresent-p
ok
nullblock
ok
=
block-edit-overlay-count
little-world-source-editssource
1
multiple-value-bind
blockstatus
ok
eqstatus:resident
ok
nullblock

Explicit placement into generated air is an overlay value too.

edit-block-atluvcraft::*stone-block*world2142
ok
eqluvcraft::*stone-block*
ok
=
block-edit-overlay-count
little-world-source-editssource
2
deftestlittle-world-save-descriptions-round-trip-semantic-state
let*
world
make-empty-little-block-world:chunk-width12:chunk-height20:chunk-depth10:seed913
source
block-world-sourceworld
camera
make-instance'fly-camera:yaw1.25:pitch-0.35
player
make-instance'block-world-player:position
make-vec3-20.5d07.25d044.0d0
record-block-edit
little-world-source-editssource
luvcraft::*crystal-block*-19844
record-block-edit
little-world-source-editssource
nil34-5
let
description
make-luvcraft-save-descriptionworld:cameracamera:playerplayer:selected-blockluvcraft::*crystal-block*

Stable coordinate order makes saves readable and diffs meaningful.

ok
equal
mapcar
lambda
edit
getfedit:at
getf
rest
getf
rest
getf
restdescription
:world
:source
:edits
'
-19844
34-5
multiple-value-bind
restoredresume
let*
restored-space
block-world-spacerestored
shape
voxel-space-chunk-shaperestored-space
restored-source
block-world-sourcerestored
ok
=
chunk-shape-widthshape
12
ok
=
chunk-shape-heightshape
20
ok
=
chunk-shape-depthshape
10
ok
=
little-world-source-seedrestored-source
913
ok
=
block-edit-overlay-count
little-world-source-editsrestored-source
2
ok
eq
block-edit-at
little-world-source-editsrestored-source
-19844
luvcraft::*crystal-block*
multiple-value-bind
blockpresent-p
block-edit-at
little-world-source-editsrestored-source
34-5
okpresent-p
ok
nullblock
center-little-world-residencyrestored-sourcerestored-24:radius0
multiple-value-bind
blockstatus
world-block-atrestored-19844
ok
eqstatus:resident
ok
eqblockluvcraft::*crystal-block*
center-little-world-residencyrestored-sourcerestored0-1:radius0
multiple-value-bind
blockstatus
world-block-atrestored34-5
ok
eqstatus:resident
ok
nullblock
multiple-value-bind
restored-camerarestored-playerselected-block
ok
=
camera-yawrestored-camera
1.25
ok
=
camera-pitchrestored-camera
-0.35
ok
=
player-xrestored-player
-20.5d0
ok
=
player-yrestored-player
7.25d0
ok
=
player-zrestored-player
44.0d0
ok
eqselected-blockluvcraft::*crystal-block*
deftestcamera-uniform-coerces-vec3-at-the-gpu-boundary
let*
uniform
camera-uniform-data
make-instance'fly-camera:position
make-vec38d011d0-6d0
:yaw1.25d0:pitch-0.35d0
1280720
ok
typepuniform'
simple-arraysingle-float
20
ok
=20
luv.arithmetic:quantity-layout-extent
ok
equalp
subsequniform04
8.011.0-6.00.0
deftestframe-uniform-product-matches-the-live-shader-contract
let*
ok
=76
luv.arithmetic:quantity-layout-extenthost-layout
ok
luv.arithmetic:quantity-layout=host-layoutshader-layout

Four dense matrix rows are representation for the declared :WORLD-TO-SHADOW map, not sixteen falsely homogeneous quantities.

loopforpositionfrom60below76do
deftestworld-save-validation-rejects-unknown-meaning
ok
signals
restore-luvcraft-save-description'
:luvcraft-world:format-version99:world
:block-world
:resumenil
ok
signals
restore-block-save-description:block'
:name:missing-material
ok
signals
restore-world-source-save-description:little-world'
:source-version99:seed1:edits
deftestretired-gnome-world-edits-migrate-to-explicit-air
let
overlay
luvcraft::restore-block-edit-overlay'
:at
486-82
:value
:block:name:gnome
multiple-value-bind
blockpresent-p
block-edit-atoverlay486-82
okpresent-p
ok
nullblock
ok
equal'
:at
486-82
:value
:air
deftestasynchronous-world-checkpoints-flush-the-latest-description
uiop:with-temporary-file
:pathnamepathname:prefix"luvcraft-checkpoint-":suffix".sexp"
let*
multiple-value-bind
restoredresume
ok
nullresume
ok
=
little-world-source-seed
block-world-sourcerestored
202
deftestlittle-world-residency-follows-a-bounded-window
let*
world
make-little-block-world:chunk-radius1:seed31
source
block-world-sourceworld
edit-block-atnilworld111
multiple-value-bind
enteringleaving
center-little-world-residencysourceworld20:radius1
ok
=
lengthentering
6
ok
=
lengthleaving
6
multiple-value-bind
chunkpresent-p
ok
nullchunk
ok
nullpresent-p
center-little-world-residencysourceworld00:radius1
multiple-value-bind
blockstatus
ok
eqstatus:resident
ok
nullblock
let
revision
block-world-revisionworld
multiple-value-bind
enteringleaving
center-little-world-residencysourceworld00:radius1
ok
nullentering
ok
nullleaving
ok
=
block-world-revisionworld
revision
deftestblock-atlas-and-mesh-vertices-carry-material-readings
let*
ok
equal
array-dimensionsatlas
ok
equal
array-dimensionsnormal-atlas
ok
subtypep
array-element-typeatlas
'
unsigned-byte32
ok
subtypep
array-element-typenormal-atlas
'
unsigned-byte32

Painted tiles fill a prefix of the capacity; the headroom past them stays zero, waiting for a live image to define a new material into it.

ok
zerop
arefatlas8
*16tile-count
ok
/=
arefatlas88
arefatlas8
+8
*316
ok
/=
arefatlas88
arefatlas8
+8
*916

The colour atlas remains ordinary opaque sRGB material colour.

ok
loopfortilebelowtile-countalways
loopforxbelowluvcraft::+block-atlas-tile-size+always
loopforybelowluvcraft::+block-atlas-tile-size+always
=255
ldb
byte824
arefatlasy
+x
*tile16
ok
loopfortilebelowtile-countalways
/=
ldb
byte824
arefnormal-atlas3
+3
*tile16
ldb
byte824
arefnormal-atlas11
+12
*tile16

The normal materialization is derived from exactly that height field: alpha preserves it byte-for-byte, rgb stays unit length within RGBA8 quantization, and at least one tangent lane responds to relief.

ok
loopforybelowluvcraft::+block-atlas-tile-size+always
loopforxbelowforpixel=
arefnormal-atlasyx
fornx=
-
/
ldb
byte80
pixel
127.5
1.0
forny=
-
/
ldb
byte88
pixel
127.5
1.0
fornz=
-
/
ldb
byte816
pixel
127.5
1.0
always
<
abs
-
+
*nxnx
*nyny
*nznz
1.0
0.025
ok
loopforybelowluvcraft::+block-atlas-tile-size+thereis
loopforxbelowforpixel=
arefnormal-atlasyx
thereis
or
/=
ldb
byte80
pixel
128
/=
ldb
byte88
pixel
128
flet
face
name
findnameluvcraft::*block-faces*:key#'block-face-name
ok
eq
block-face-tileluvcraft::*grass-block*
face:top
:grass-top
ok
eq
block-face-tileluvcraft::*grass-block*
face:front
:grass-side
ok
eq
block-face-tileluvcraft::*grass-block*
face:bottom
:dirt
ok
eq
block-face-tileluvcraft::*wood-block*
face:top
:wood-end
ok
eq
block-face-tileluvcraft::*sand-block*
face:top
:sand
ok
eq
block-face-tileluvcraft::*snow-block*
face:top
:snow
ok
eq
block-face-tile*crystal-block*
face:top
:crystal
ok
eq
block-face-tile*terminal-block*
face:front
:terminal
ok
eq
block-face-tileluvcraft::*cactus-block*
face:front
:cactus-side
ok
eq
block-face-tileluvcraft::*cactus-block*
face:top
:cactus-end
ok
=
block-light-emission*crystal-block*
12
ok
=
block-surface-emission*crystal-block*
1.2
ok
=
block-surface-emission*terminal-block*
0.16
ok
equal
mapcar#'block-kind-name
'
:grass:dirt:stone:wood:leaves:sand:snow:crystal:terminal:urbit:gravel:clay:mud:moss:cactus:cobblestone:stone-bricks:bricks:planks:sandstone:slate:tape:fountain:lava-spring:flowers
let*
world
make-block-world:chunk-width2:chunk-height2:chunk-depth2
chunk
setfluvcraft::*stone-block*
let
mesh
mesh-block-chunk
make-instance'exposed-face-mesher
worldchunk
ok
=
length
block-mesh-verticesmesh
deftestlittle-world-has-readable-biome-materials
let
source
make-instance'little-world-source:seed121
materials
make-hash-table:test#'eq
loopforxfrom-96to96by4do
loopforzfrom-96to96by4forsurface=do
setf
gethashmaterials
t
ok
gethashluvcraft::*grass-block*materials
ok
gethashluvcraft::*sand-block*materials
ok
gethashluvcraft::*snow-block*materials
deftestcrosshair-and-numbered-materials-are-playable-state
let*
canvas
make-instance'title-canvas:title"luvcraft test"
session
make-instance'luvcraft-session:canvascanvas:title-base"luvcraft test":selected-blockluvcraft::*stone-block*
ok
eqluvcraft::*grass-block*
ok
eq
luvcraft-session-selected-blocksession
luvcraft::*grass-block*
ok
eqluvcraft::*snow-block*
ok
eq*crystal-block*
ok
eq*terminal-block*
ok
search"1–9,0 select"
ok
search"terminal"

The tenth slot is the urbit material, and its chip is the 0 key.

ok
eqluvcraft::*urbit-block*
ok
search"[0] urbit"
ok
eqluvcraft::*gravel-block*
ok
search"[inventory]"
deftesturbit-wall-boots-a-comet-once-and-resumes-its-pier

The pier lives under the checkout's build directory, named by the wall.

let
ok
search"build/urbit/comet"
namestringpier

A pier vere has not made an .urb in boots as a comet; one it has, resumes. The urbit itself is not run here: booting a comet is a networked, minutes-long affair that belongs on a wall, not in a test.

let*
pier
merge-pathnames
make-pathname:directory'
:relative"luv-urbit-test-pier"
uiop:temporary-directory
unwind-protect
progn
ok
equal
list"-c"
namestringpier
ensure-directories-exist
merge-pathnames#P".urb/"pier
ok
equal
list
namestringpier
uiop:delete-directory-treepier:validatet:if-does-not-exist:ignore
deftestblock-inventory-supports-creative-and-finite-stacks
let*
creative
make-block-inventory:blocks
listluvcraft::*stone-block*
finite
make-block-inventory:blocks
listluvcraft::*dirt-block*
:quantity2
entry
block-inventory-entry-forfiniteluvcraft::*dirt-block*
ok
equal
listluvcraft::*stone-block*
ok
null
block-inventory-entry-quantity
first
block-inventory-entriescreative
ok
remove-block-from-inventorycreativeluvcraft::*stone-block*1000
ok
remove-block-from-inventoryfiniteluvcraft::*dirt-block*
ok
=1
block-inventory-entry-quantityentry
ng
remove-block-from-inventoryfiniteluvcraft::*dirt-block*2
add-block-to-inventoryfiniteluvcraft::*dirt-block*4
ok
=5
block-inventory-entry-quantityentry
add-block-to-inventoryfinite*crystal-block*3
ok
equal
listluvcraft::*dirt-block**crystal-block*
ok
=3
block-inventory-entry-quantity
block-inventory-entry-forfinite*crystal-block*
deftestnumbered-selection-follows-the-session-inventory
let*
canvas
make-instance'title-canvas:title"inventory test"
inventory
make-block-inventory:blocks
listluvcraft::*wood-block**crystal-block*
session
make-instance'luvcraft-session:canvascanvas:inventoryinventory:selected-blockluvcraft::*wood-block*
ok
eq*crystal-block*
ok
eq*crystal-block*
luvcraft-session-selected-blocksession
ok
ok
search"1–2 select"
deftestinventory-and-ten-slot-quickbar-have-independent-extents
let*
extra
make-instance'block-kind:name:test-extra:face-tiles'
:all:stone
:categories'
:building
:display-color'
0.40.50.6
inventory
canvas
make-instance'title-canvas:title"inventory extent test"
session
make-instance'luvcraft-session:canvascanvas:inventoryinventory:selected-blockluvcraft::*grass-block*
ok
=
1+base-count

The full inventory may select a block with no number key; the title makes that distinction visible rather than advertising an eleventh key.

ok
eqextra
select-luvcraft-blocksession
1+base-count
ok
search"[inventory]"
ok
search"1–9,0 select"
deftestgazetteer-names-semantic-gameplay-views
let*
names
mapcar#'luvcraft-gazetteer-view-nameviews
ok
equalnames
remove-duplicatesnames:test#'eq
dolist
name'
:little-world-noon:little-world-dusk:shadow-forest:glow-floor:crystal-seam:shadow-yard
ok
findnamenames
let*
world
funcall
luvcraft::luvcraft-gazetteer-view-world-factoryview
ok
eq*crystal-block*
ok
=
nth-value1
block-light-emission*crystal-block*
ok
=
nth-value1
1-
block-light-emission*crystal-block*
deftestshadow-yard-gazetteer-has-raised-casters-over-receiver
let*
world
funcall
luvcraft::luvcraft-gazetteer-view-world-factoryview
ok
eqluvcraft::*snow-block*
ok
eqluvcraft::*stone-block*
ok
eq
world-block-atworld10810
luvcraft::*stone-block*
ok
null
ok
null
ok
=
nth-value0
15
deftestshadow-projection-ignores-subtexel-camera-translation
let*
clock
make-instance'sky-clock:pinned-day-fraction0.42
first-camera
make-instance'fly-camera:position
make-vec30d00d00d0
nearby-camera
make-instance'fly-camera:position
make-vec30.01d00d00.01d0
farther-camera
make-instance'fly-camera:position
make-vec30.25d00d00.25d0
anchornil
first-rows
multiple-value-bind
rowsnew-anchor
luvcraft::shadow-frame-rowsfirst-cameraskyanchor
setfanchornew-anchor
rows
nearby-rows
multiple-value-bind
rowsnew-anchor
luvcraft::shadow-frame-rowsnearby-cameraskyanchor
setfanchornew-anchor
rows
farther-rows
multiple-value-bind
rowsnew-anchor
luvcraft::shadow-frame-rowsfarther-cameraskyanchor
setfanchornew-anchor
rows

The first two rows locate the orthographic footprint. Translation smaller than one 0.0625-world-unit shadow texel cannot move it, to within the rounding of the anchor's own walk, which is a millionth of a texel against the texel's 1/1024 of the footprint.

flet
same-footprint-p
rowsother
every
lambda
ab
<1e-9
subseqrows08
subseqother08
ok
same-footprint-pfirst-rowsnearby-rows
ok
not
same-footprint-pfirst-rowsfarther-rows
deftestshadow-lattice-turns-about-the-camera-not-the-origin

A frame's texel lattice is the set of world points with integer light-space texel coordinates. When the sun moves, the lattice has to turn, and it must do so about the eye: a point near the camera should see nearly the same texel coordinate before and after, however far the camera stands from the world origin.

let*
camera
make-instance'fly-camera:position
make-vec3700d070d0-300d0
probe
list702.371.1-301.7
flet
probe-texel
multiple-value-bind
rowsanchor
luvcraft::shadow-frame-rowscamera
sky-frame-parameters
make-instance'sky-clock:pinned-day-fractionday-fraction
profile
anchor
values
loopforrowin
list
subseqrows04
subseqrows48
collect
*texels-per-unitluvcraft::+luvcraft-shadow-half-extent+
+
nth3row
loopforindexbelow3sum
*
nthindexrow
nthindexprobe
anchor
multiple-value-bind
beforeanchor
probe-texel0.42nil
let
after
probe-texel0.42003anchor

One frame of a ten-minute day turns the sun by 1.9e-4 radians; a probe three units from the eye may move a thousandth of a texel, not the tenth of a texel the origin pivot gave it.

loopforbinbeforeforainafterdo
ok
<0.05
deftestshadow-projection-is-continuous-through-old-up-axis-threshold
let*
camera
make-instance'fly-camera
before
luvcraft::shadow-frame-rowscamera
sky-frame-parameters
make-instance'sky-clock:pinned-day-fraction0.451
profile
after
luvcraft::shadow-frame-rowscamera
sky-frame-parameters
make-instance'sky-clock:pinned-day-fraction0.453
profile
right-dot
loopforindexbelow3sum
*
nthindexbefore
nthindexafter

Row X has length 1/extent. Undo that scale before comparing the neighboring orientations around the former abs(forward.y)=0.92 switch.

deftesttemporal-frame-derivatives-expose-change-and-flicker
let
first
102030255405060255
second
131736255405060255
third
161442255435363255
multiple-value-bind
differencemeanmaximumchanged
ok
equalpdifference
404040255000255
ok
<
abs
-mean
/2.0255.0
1e-6
ok
<
abs
-maximum
/4.0255.0
1e-6
ok
=changed0.5
multiple-value-bind
differencemeanmaximumchanged
ok
equalpdifference
000255303030255
ok
<
abs
-mean
/1.5255.0
1e-6
ok
<
abs
-maximum
/3.0255.0
1e-6
ok
=changed0.5
deftestscalar-player-walks-collides-and-jumps
let*
world
make-block-world:chunk-width4:chunk-height4:chunk-depth4
camera
make-instance'fly-camera:position
make-vec31.54.621.5
:yaw0d0:pitch0d0
player
make-instance'block-world-player:position
make-vec31.5d03d01.5d0
loopforxbelow4do
loopforzbelow4do
setfluvcraft::*stone-block*

Gravity settles the body exactly on the block tops.

dotimes
step240
declare
ignorablestep
step-block-world-playerplayerworldcameraintent
/1d0120d0
ok
<
abs
-
player-yplayer
1d0
1d-5
ok
player-grounded-pplayer
ok
<
abs
-
camera-ycamera
2.62d0
1d-5

A held right input accelerates into, but not through, a two-block wall.

setfluvcraft::*stone-block*luvcraft::*stone-block*
movement-urging-pintent:right
t
dotimes
step120
declare
ignorablestep
step-block-world-playerplayerworldcameraintent
/1d0120d0
ok
<=
player-xplayer
2.700001d0
ok
<
abs
-
player-yplayer
1d0
1d-5
ok
player-grounded-pplayer
setf
movement-urging-pintent:right
nil

Jump is an edge request, not a second form of flying.

let
ground-y
player-yplayer
step-block-world-playerplayerworldcameraintent
/1d0120d0
:jump-pt
ok
>
player-yplayer
ground-y
ok
not
player-grounded-pplayer
dotimes
step120
declare
ignorablestep
step-block-world-playerplayerworldcameraintent
/1d0120d0
ok
<
abs
-
player-yplayer
1d0
1d-5
ok
player-grounded-pplayer
deftestscalar-player-autojumps-a-clear-one-block-ledge
let*
world
make-block-world:chunk-width8:chunk-height4:chunk-depth4
camera
make-instance'fly-camera:position
make-vec31.52.621.5
:yaw0d0:pitch0d0
player
make-instance'block-world-player:position
make-vec31.5d01d01.5d0
:grounded-pt
highest-y
player-yplayer
loopforxbelow8do
loopforzbelow4do
setfluvcraft::*stone-block*
setfluvcraft::*stone-block*
movement-urging-pintent:right
t
dotimes
step120
declare
ignorablestep
step-block-world-playerplayerworldcameraintent
/1d0120d0
setfhighest-y
maxhighest-y
player-yplayer
ok
>highest-y2d0
ok
>
player-xplayer
3.3d0
deftestdestinational-body-routes-around-terrain-and-arrives-continuously
let*
world
make-block-world:chunk-width8:chunk-height4:chunk-depth8
player
make-instance'block-world-player:position
make-vec31.5d01d01.5d0
:grounded-pt
loopforxbelow8do
loopforzbelow8do
setfluvcraft::*stone-block*

A head-high wall closes the direct line but leaves both sides open.

setfluvcraft::*stone-block*luvcraft::*stone-block*action
start-body-move-toplayerworld511
ok
eq:running
body-move-action-statusaction
ok
>
length
body-move-action-pathaction
4
"the planned intent names a route, not a straight velocity"
let
start
start-x
player-xplayer
start-z
player-zplayer
advance-body-movementplayerworld
/1d0120d0
ok
or
>
abs
-
player-xplayer
start-x
1d-6
>
abs
-
player-zplayer
start-z
1d-6
"the physical body starts moving before its public cell changes"
ok
equalstart
ok
eq:running
body-move-action-statusaction
looprepeat2399while
eq:running
body-move-action-statusaction
do
advance-body-movementplayerworld
/1d0120d0
ok
eq:arrived
body-move-action-statusaction
ok
equal'
511
ok
<
abs
-
player-xplayer
5.5d0
0.12d0
ok
<
abs
-
player-zplayer
1.5d0
0.12d0
deftestdestinational-body-reports-an-unstandable-place-immediately
let*
world
make-block-world:chunk-width4:chunk-height4:chunk-depth4
player
make-instance'block-world-player:position
make-vec31.5d01d01.5d0
:grounded-pt
loopforxbelow4do
loopforzbelow4do
setfluvcraft::*stone-block*
let
ok
eq:failed
body-move-action-statusaction
ok
search"not a clear supported cell"
body-move-action-detailaction

Waiting after an immediate terminal result is also immediate; the completion notification may already be sitting in its mailbox.

deftestmeshing-and-editing-cross-a-chunk-boundary
let
world
make-block-world:chunk-width2:chunk-height2:chunk-depth2
let
left
right
setfluvcraft::*stone-block*luvcraft::*stone-block*
let
mesher
make-instance'exposed-face-mesher
flet
face-count
+
block-mesh-face-count
mesh-block-chunkmesherworldleft
block-mesh-face-count
mesh-block-chunkmesherworldright
ok
=
face-count
10
let
revision
block-world-revisionworld
setfnil
ok
=
block-world-revisionworld
1+revision
ok
=
face-count
6
setfluvcraft::*stone-block*
ok
=
face-count
10
deftestchunk-mesh-is-exactly-sized
let*
world
make-block-world:chunk-width2:chunk-height2:chunk-depth2
chunk
mesher
make-instance'exposed-face-mesher
setfluvcraft::*stone-block*
let*
mesh
mesh-block-chunkmesherworldchunk
vertices
block-mesh-verticesmesh
ok
=
block-mesh-face-countmesh
6
ok
=
lengthvertices
ok
=
array-total-sizevertices
lengthvertices

Vertex lanes are atlas-independent: local corner coordinates, the tile offset under the atlas mapping, and four base-three edge digits.

loopforoffsetfrom0below
lengthvertices
byluvcraft::+block-mesh-floats-per-vertex+do
ok
member
arefvertices
+offset3
'
0.031250.96875
ok
member
arefvertices
+offset4
'
0.031250.96875
ok
=
arefvertices
+offset12
ok
=
arefvertices
+offset13
80.0
deftestimmutable-mesh-snapshot-is-bit-identical-to-owner-side-meshing
let*
world
make-little-block-world:chunk-radius1:seed121
chunk
mesher
make-instance'exposed-face-mesher
snapshot
direct
mesh-block-chunkmesherworldchunk
copied
mesh-block-snapshotmeshersnapshot
ok
equalstamp
block-mesh-snapshot-dependency-stampsnapshot
ok
=
block-mesh-face-countdirect
block-mesh-face-countcopied
ok
=
block-mesh-vertex-countdirect
block-mesh-vertex-countcopied
ok
equalp
block-mesh-verticesdirect
block-mesh-verticescopied
setfnil
ok
equalp
block-mesh-verticescopied
block-mesh-vertices
mesh-block-snapshotmeshersnapshot
deftestproduction-system-coalesces-desired-work-and-stops-cooperatively
let
system
unwind-protect
let*
first
make-instance'luvcraft::little-world-load-request:key'
:load
000
:priority4:seed1:demand-token1:width8:height8:depth8
latest
make-instance'luvcraft::little-world-load-request:key'
:load
000
:priority0:seed2:demand-token2:width8:height8:depth8
multiple-value-bind
resultpresent-p
sb-concurrency:receive-message
luv.production::production-system-result-mailboxsystem
:timeout5.0
okpresent-p
ok
null
luvcraft::production-result-conditionresult
ok
not
sb-thread:thread-alive-p
luv.production::production-system-threadsystem
deftestproduction-system-keeps-one-result-behind-its-owner
let*
system
luvcraft::make-single-worker-production-system:name"luv production backpressure test"
first-gate
sb-thread:make-semaphore:count0
second-gate
sb-thread:make-semaphore:count0
first
make-instance'gated-production-request:key:first:gatefirst-gate:value:first
second
make-instance'gated-production-request:key:second:gatesecond-gate:value:second
unwind-protect
progn

Scheduling while FIRST is active must remain desired work, not a second queued wake which can run behind an unread first result.

sb-thread:signal-semaphorefirst-gate
ok
wait-until
lambda
and
=1
sb-concurrency:mailbox-count
luv.production::production-system-result-mailboxsystem
ok
=1
sb-concurrency:mailbox-count
luv.production::production-system-result-mailboxsystem
ok
nth-value1
gethash:second
luv.production::production-system-desiredsystem
multiple-value-bind
resultpresent-p
okpresent-p
ok
eq
luvcraft::production-result-valueresult
:first
sb-thread:signal-semaphoresecond-gate
multiple-value-bind
resultpresent-p
sb-concurrency:receive-message
luv.production::production-system-result-mailboxsystem
:timeout2.0
okpresent-p
ok
eq
luvcraft::production-result-valueresult
:second
sb-thread:signal-semaphorefirst-gate
sb-thread:signal-semaphoresecond-gate
deftestprebuilt-world-remains-desired-for-asynchronous-meshing
let*
world
make-block-world:chunk-width8:chunk-height8:chunk-depth8
first
system
luvcraft::make-single-worker-production-system:name"luv static residency test"
session
make-instance'luvcraft-session:worldworld:player
make-instance'block-world-player:position
make-vec30d00d00d0
:production-systemsystem
unwind-protect
progn
ok
gethash
luvcraft-session-desired-chunkssession
ok
gethash
luvcraft-session-desired-chunkssession
ok
not
gethash
luvcraft-session-desired-chunkssession
ok
gethash
luvcraft-session-desired-chunkssession
deftestchunk-mesh-products-have-narrow-neighbor-dependencies
let*
world
make-block-world:chunk-width4:chunk-height4:chunk-depth4
left
right
mesher
make-instance'exposed-face-mesher
setfluvcraft::*stone-block*luvcraft::*stone-block*
ok
=
block-mesh-face-count
mesh-block-chunkmesherworldleft
5
ok
=
block-mesh-face-count
mesh-block-chunkmesherworldright
5
let

This changes RIGHT, but not the boundary LEFT's mesh observes.

setfluvcraft::*stone-block*
ok
equalstamp

This touches RIGHT's -X boundary and must invalidate LEFT.

setfluvcraft::*stone-block*
ok
let
let
replacement
ok
not
equalstamp
defuntest-luvcraft-chunk-product
chunkstamp
make-instance'luvcraft::luvcraft-chunk-product:coordinate
chunk-domain-coordinate
block-chunk-domainchunk
:dependency-stampstamp:meshnil:vertex-buffernil
deftestboundary-mesh-replacements-publish-as-one-visible-cohort
let*
world
make-block-world:chunk-width2:chunk-height2:chunk-depth2
left
right
session
make-instance'luvcraft::luvcraft-session:worldworld
left-key'
000
right-key'
100
setf
gethashleft-key
luvcraft::luvcraft-session-desired-chunkssession
t
gethashright-key
luvcraft::luvcraft-session-desired-chunkssession
tluvcraft::*stone-block*luvcraft::*stone-block*
let
setf
gethashleft-key
luvcraft::luvcraft-session-chunk-productssession
old-left
gethashright-key
luvcraft::luvcraft-session-chunk-productssession
old-right

Removing RIGHT's boundary block also exposes a face owned by LEFT. One completed replacement must leave the whole old pair visible.

setfnil
let
setf
gethashright-key
luvcraft::luvcraft-session-staged-chunk-productssession
new-right
ok
eqold-left
gethashleft-key
luvcraft::luvcraft-session-chunk-productssession
ok
eqold-right
gethashright-key
luvcraft::luvcraft-session-chunk-productssession
let
setf
gethashleft-key
luvcraft::luvcraft-session-staged-chunk-productssession
new-left
ok
eqnew-left
gethashleft-key
luvcraft::luvcraft-session-chunk-productssession
ok
eqnew-right
gethashright-key
luvcraft::luvcraft-session-chunk-productssession
deftestcamera-edits-the-resident-lattice
let*
world
make-block-world:chunk-width4:chunk-height4:chunk-depth4
camera
make-instance'fly-camera:position
make-vec30.51.51.5
:yaw
/pi2
:pitch0.0
session
make-instance'luvcraft-session:worldworld:cameracamera:selected-blockluvcraft::*dirt-block*

The second stone means placing after removing the first still has a solid target beyond the empty adjacent site.

setfluvcraft::*stone-block*luvcraft::*stone-block*
multiple-value-bind
coordinatestatus
edit-luvcraft-blocksession:remove
ok
eqstatus:edited
ok
=
world-coordinate-xcoordinate
2
ok
null
let
occupied-session
make-instance'luvcraft-session:worldworld:cameracamera:player
make-instance'block-world-player:position
make-vec32.5d01d01.5d0
:selected-blockluvcraft::*dirt-block*
multiple-value-bind
coordinatestatus
edit-luvcraft-blockoccupied-session:place
ok
nullcoordinate
ok
eqstatus:blocked
ok
null
multiple-value-bind
coordinatestatus
edit-luvcraft-blocksession:place
ok
eqstatus:edited
ok
=
world-coordinate-xcoordinate
2
ok
eqluvcraft::*dirt-block*