Skip to content
Zizeeria
Menu

News

The cube has never moved

Fix

Pre-production was declared closed on a criterion with the word move in it. Two clients did see each other. Neither of them could walk.

Four days ago this log published a post titled "A cube can move". It is wrong, and which half is wrong is the interesting part. Two Windows clients did connect to the same zone server, the server did count one and then two with a pawn for each, and both windows did render the other player's character. All of that happened and all of it stands. What did not happen is the verb the criterion turns on. Nothing moved. No character in Zizeeria has ever taken a step, because no key on either keyboard was connected to anything.

The movement itself is written, and it is right. The character has a fully configured movement component — walk speed, braking, air control, rotation that follows the direction of travel, network smoothing tuned for the latency the design targets — and it replicates. The movement function takes a two-axis input, flattens it against the camera's yaw so that a player pushing forward while looking down walks along the ground rather than into it, and hands the result to the engine call that proposes a move to the server. That is not a sketch or a stub. It is the finished thing, and it has never once been called.

What is missing is the binding. Three properties say which key means forward: a mapping context and two input actions. All three were marked as set outside of code, under a comment giving the reason — input bindings are content rather than source, and content belongs in an asset a designer can edit. Two things are wrong with that. The asset does not exist: there is no pawn Blueprint, and no Blueprint of any kind anywhere in the project, the entire content tree being three maps and five files belonging to one wooden shield. And the rule at the top of that directory had already ruled the asset out — Blueprints are for the interface and for throwaway prototypes and nowhere else, because a Blueprint on the gameplay critical path is one nobody can diff, review or merge, and moving a pawn is as critical as that path gets. So the comment was reaching for a file the project had decided not to have. The game mode hands out the bare C++ class as the default pawn, nothing ever supplied the three, and they were null from the moment the client started with the movement code sitting behind them.

It fails without saying a word, which is how it survived four days of being described as working. The call that installs the mapping context is inside a test for whether the mapping context exists; each of the two calls that bind a key is inside a test for its own action. A null pointer does not take a branch nobody wrote — it skips the block, and the frame goes on. There is an error log a few lines away, but it guards a different question: whether the input component is an enhanced one, which it always is. So every client log this project has ever produced is silent on the subject, and a silent log reads exactly like a working one.

The script that was meant to certify the milestone states three checks in its own header, and states them well: each client must log a successful join; the server must log two of them, because one client joining twice is not two clients; and each client's log must name a pawn it did not spawn itself, which is the other player arriving over the wire. The third is the one that carries the claim, and it is not implemented at all. The second is asked of the clients instead of the server, which throws away the reason the header gives for asking it — the clients are exactly the witnesses that cannot tell two of them apart from one of them twice — and it prints whatever it finds without judging it. The first does reach a verdict and then does nothing with it. The only exit code the script can return is for a missing client executable, so once it has found something to launch, a run that satisfies none of the three ends exactly as successfully as a run that satisfies all three. That is the run that was recorded as a pass.

So the plain consequence: Phase 0's exit criterion is not met. The plan's sentence is that a cube can move in a networked zone and that two clients can see each other move, and this project's own rule is never to enter the next phase before the current one's criterion is met. Applied to itself, that reopens pre-production until somebody has pressed a key and watched a cube move on another person's screen.

The small part is now done, and it was small. Three objects and the keys that go in them — a mapping context, two input actions, the familiar four letters and the mouse — built in C++ where the rest of this path lives, so they arrive in a diff somebody can read rather than inside a binary file nobody can. A cube walks. Measured rather than watched: pressing each key for a second and a half moves the character along its own axis and no other, with nothing leaking sideways, which is the specific failure this kind of binding produces when the input's shape is declared wrong. Then the binding was deliberately broken again, to confirm that the two error lines added alongside it actually appear. Four days of silence is what let this hide.

That does not close the criterion, and it is worth being exact about why. The sentence is that a cube can move in a networked zone and that two clients can see each other move. What has been demonstrated is one character walking, in a headless run, with the keys injected below the layer a real keyboard goes through. Nobody has yet pressed W on one machine and watched a shape move on another. The gate script has been rewritten to ask that — it parses the server for two joins rather than asking the clients, requires each client to name a pawn it did not spawn, and asserts a position actually changed — and it was pointed at the old recorded run first, where it comes out red and names what is missing. It fails against today's build too, on the movement check, because the fix has not been cooked and shipped to the zone hosts. Pre-production stays open until it goes green on its own terms. A check nobody has watched fail is not a check — it is a script with an opinion.

Both earlier posts keep their addresses. "A cube can move" is now titled "A cube can be seen", which is what its own body described all along — two windows side by side, each showing the other player's character standing on the ground — and it now opens with a note pointing here and no longer ends by handing the project to Phase 1. Its slug is untouched, and that is deliberate: the slug is the identifier a feed reader and the announcer in Discord deduplicate on, so renaming it would send a four-day-old post to everybody a second time, while correcting its words sends nothing. That is the right way round. The text is allowed to be corrected; the identity is not. The post before it, "Two now connected", is left exactly as it stands. It reported what the server logged and nothing more, and it said in its own words that nobody had yet looked at a screen — which is the half of that evening that turned out to be worth the most.