Skip to content

Releases

v1.25.1

Choose a tag to compare

@github-actions github-actions released this 11 Nov 08:58

A fix for games that add or remove entities during a tick and then index World.Entities.

Fixed

  • World.Entities[i] and Count were O(n) per access after an entity was added or removed during a tick (the view scanned past removed entities on every call), so an indexed loop over thousands of entities took tens of milliseconds. They now read a compacted copy built once per change. Which entities are listed, and their order, are unchanged.

v1.25.0

Choose a tag to compare

@github-actions github-actions released this 11 Nov 08:15

Custom render stages can draw above the UI next to the debug panel, and a 3D view rendered offscreen can be drawn as a 2D texture without copying it back to the CPU.

Added

  • Custom render stages can draw text and rectangles above the UI: Render2DStage.AddOverlay/RemoveOverlay take an IFrameOverlay, and OverlayCanvas gains Outline and Image. Overlays share the stage's 2D batch and glyph atlas, and draw under the development panel (Render2DStage.Overlay), which keeps its own slot, so the two no longer replace each other.
  • Renderer2D.WrapTexture and RebindTexture: a Texture2D over a GPU image the caller owns, such as a sampled Offscreen3D target, drawn by sprites, Ui.Image and overlays with no readback. examples/stage shows both.

Fixed

  • Vulkan adapter choice is deterministic: adapters are kept in one total order (kind, vendor id, device id, name, API version, UUID) instead of the driver enumeration order, so a multi-GPU machine picks the same adapter every time and ONEGAME_GPU_ADAPTER / GpuDeviceOptions.Adapter match the same one. The rule is in docs/interfaces/gpu.md.
  • A presented capture (capture_frame with presented, GameInstance.CapturePresented) no longer returns nothing when the window could not be drawn: it throws PresentedCaptureException with a Reason (minimized, window not drawn, no backbuffer in time, surface lost), which the control op reports in its message. Games with no window or device still return null.

v1.24.0

Choose a tag to compare

@github-actions github-actions released this 11 Nov 04:42

Physics is stable and comes to 3D: RecursiveAI.OneGame.Physics is built on OnePhysics 1.0.0 and ships at the engine version, so every onegame package is now stable.

Added

  • 3D physics spaces saved, stepped and hashed with the world: rigid bodies, characters, vehicles, queries, prediction forks, explicit origin rebasing and debug geometry through the 3D debug renderer. examples/physics/3d shows a falling pile, a walking character and a driven car.

Changed

  • RecursiveAI.OneGame.Physics is stable on OnePhysics 1.0.0 and ships at the engine version, without a preview suffix. 2D physics results and hashes are unchanged from 1.23.0.

Fixed

  • Two games in one process (or a game whose screen was rebuilt after another game started) no longer paste from the other game's clipboard: each text field uses the clipboard of its own window.
  • Vulkan: a submission that does not draw into the acquired swapchain image (such as an upload at startup) no longer consumes that image's acquire and render-done semaphores, which could make the first presented frame wait on the wrong work.

v1.23.0

Choose a tag to compare

@github-actions github-actions released this 10 Nov 18:41

Drift checks get cheap for large scopes: a drift-check tick with 5,000 entities and 16 audiences went from about a second to a few milliseconds on the server. Hashes are unchanged. RecursiveAI.OneGame.Physics ships as 1.23.0-preview.1.

Added

  • driftCheckInterval: in the server configuration (ServerConfig.DriftCheckInterval, default 30, 1 to 10,000 ticks), ServerHost.DriftCheckInterval (code wins over the file; set before Start), Node.DriftCheckInterval, and driftCheckInterval in server_info. A longer interval costs the server and every client less per tick and finds drift later. It cannot be 0 on a server; out-of-range values are errors. hashHistory, hash_at and cluster hash comparisons do not depend on it.
  • Entity.StateVersion and SyncList.Version: counters that change whenever an entity's synced state (or a list) changes, so caches can be keyed on them.

Changed

  • Drift-check ticks no longer re-serialize and re-hash every entity for every audience: each entity caches its full and per-visibility-mask hashes, invalidated by any change (Entity.StateVersion, SyncList.Version), and the digest folds the world hash in the same pass. Hashes, recordings and the wire are unchanged. A check tick with 5,000 idle 4.8 KB entities and 16 audiences went from about 1,000 ms to about 3 ms on the authority and from about 33 ms to about 0.5 ms on a replica.

Fixed

  • 1.22.0: Connection.Channel returned an internal send-pacing wrapper instead of the transport for UDP connections, so code that read the client's proven key through connection.Channel is IAuthenticatedChannel or closed the client through connection.Channel is INetConnection silently failed: such a server trusted no one, and clients joined but never received entities. Connection.Channel is the transport again, as before 1.22; pacing is kept internally.

v1.22.0

Choose a tag to compare

@github-actions github-actions released this 10 Nov 14:25

Per-connection spatial interest: a connection receives only the part of a scope it is looking at, so a scope with many players no longer costs every client the whole world. RecursiveAI.OneGame.Physics ships as 1.22.0-preview.1.

Added

  • [InterestKey] on a synced uint/int property (0 = global), AuthorityScope.SetInterest/ClearInterest/InterestOf, Connection.SetInterest, AuthorityScope.RequireInterest, InterestSet and InterestGrid2D (cells with enter/leave radii, sticky entity keys). Entities entering and leaving a connection's interest are sent as additions and removals; joins carry only in-interest entities. Works for clients, peer nodes and relays, needs no wire change (1.21 clients and servers interoperate), and keeps view hashes and drift checks exact. Hot tables and random streams are not filtered (see docs/interfaces/sync.md).
  • SubmitResult.OutsideInterest; a command to an entity outside the connection's view is answered as an unknown entity.
  • AuthorityScope.Traffic (bytes per entity type), EntitiesSent, EntitiesRemoved, FrameBytes, SnapshotBytes, listed per scope by the server's server_info.
  • ServerHost.MaxSendQueueBytes and ConnectOptions.MaxSendQueueBytes; the default (8 MiB) and how to raise it are documented in transport.md.
  • OG0017 also covers [InterestKey] declarations.
  • A listen: entry takes maxSendQueueBytes; ListenConfig.MaxSendQueueBytes is its code form.
  • ServerHost.ClientJoined: raised when a client is admitted, before its first message, so a game can set its interest before the first snapshot.
  • IBoundedSendChannel: transports with a bounded send queue report how full it is.
  • InterestRequiredException and World.HiddenTablesOnly: once a scope requires interest, making its random streams visible or adding a visible table later is refused.

Changed

  • A node paces what it sends over a bounded transport: a snapshot (or any burst) larger than the send queue is fed in as acknowledgements free room instead of overflowing at once. What waits is capped, and overflow still closes the link.
  • A server logs a send-queue overflow with the setting to raise instead of a bare "left".
  • A ClientJoined handler that throws refuses that one client instead of stopping the server.
  • A transfer's Moved notice names only the entities each watcher could see, and commands from such a watcher to an entity in transit are held or forwarded as before.

v1.21.0

Choose a tag to compare

@github-actions github-actions released this 10 Nov 05:11

Creating thousands of meshes at once no longer leaves the GPU driver holding gigabytes of upload memory. RecursiveAI.OneGame.Physics ships as 1.21.0-preview.1.

Fixed

  • Creating thousands of meshes (or any burst of buffer and texture uploads) in one frame no longer leaves gigabytes of driver memory mapped. The Vulkan backend stages uploads through a few fixed 4 MiB chunks, at most GpuDevice.StagingBudgetBytes (32 MiB) in all: when they are all in use, the writing call waits for the GPU to finish the oldest copies instead of making more buffers. Idle chunks and empty device-memory blocks are returned after a while. A burst of 2,500 chunk-sized meshes used to hold about 1.5 GB of staging for the life of the device; it now peaks at 32 MiB and settles at 16 MiB.

Added

  • GpuMemoryStats.StagingBytes and GpuDevice.StagingBudgetBytes.

v1.20.0

Choose a tag to compare

@github-actions github-actions released this 09 Nov 22:20

3D scenes get distance fog and a crossfade between two environment maps for day and night cycles, and steady frames on fast GPUs allocate nothing. RecursiveAI.OneGame.Physics ships as 1.20.0-preview.1.

Added

  • Distance fog in the 3D renderer. Scene3D.Fog (FogSettings: Enabled, Color, Start, End, MaxDensity) fades every surface (PBR, toon, flat, unlit, outlines, masked and blended materials, instanced draws, dynamic meshes, billboards) towards a colour with its distance from the camera. It is a per-frame constant: changing it uploads nothing and rebuilds nothing, and steady frames still allocate nothing. Off by default; frames with fog off are unchanged.
  • Environment crossfade. Environment3D.NextMap and Blend mix the backdrop, the diffuse light and the specular light of two environment maps, so a day cycle can step through keyframes without a pop: at Blend 1, making the next map the first and the following one the next at Blend 0 draws the same picture. Changing Blend refilters nothing.
  • Live control environment operation: read and set the fog, the blend weight, the environment intensity and its yaw.

Fixed

  • The Vulkan backend no longer allocates managed memory on a random frame of a steady run. Command encoders are created up front, and upload staging pools are sized from what a submission uses and then kept fixed (a miss waits for the GPU instead of growing the pool). On a fast GPU the pools used to grow whenever the GPU first lagged further behind, which showed up as sporadic small allocations in frames that should allocate nothing.

v1.19.0

Choose a tag to compare

@github-actions github-actions released this 09 Nov 21:29

Meshes a game builds can be added, replaced and freed while it runs, windows remember where they were and how they were shown, Windows games open without a console window, and captures of windowed games show all their art. RecursiveAI.OneGame.Physics ships as 1.19.0-preview.1.

Added

  • Meshes built, replaced and freed at run time. SceneView3D.Models.SetMesh(id, material, vertices, indices) adds or replaces a mesh by name (entities draw it with SceneWriter.Model like any model), Remove and Replace free or swap models and meshes, and Contains checks a name. Renderer3D.CreateMaterial makes a shared material without a glTF file, and Renderer3D.CreateMesh returns a DynamicMesh3D (Update, Dispose) for a hand-filled Scene3D. Buffers are sized in classes and reused, updates copy through the device's staging memory, freed buffers are released once the GPU has finished with them, and nothing waits for the GPU; a soak of thousands of swaps holds GPU memory flat and steady frames allocate nothing.
  • SceneWriter.Model(..., ModelOptions) sets a per-draw opacity and label (ModelOptions.Opacity, Label, Pickable).
  • scene_models live-control operation lists what the 3D view draws: model, label, opacity, entity, triangle count, position, and whether the mesh was built at run time.
  • GpuDevice.MemoryStats reports the backend's device-memory tally (Vulkan).
  • Window placement. Window.Placement reads where a window is and how it is shown as a plain WindowPlacement value (normal rectangle in screen pixels plus Normal/Maximized/Fullscreen) that a game can save with Format() and read back with TryParse; WindowOptions.Placement creates a window from it (a maximized window is created maximized, remembering the rectangle to restore to), and Window.ApplyPlacement, Maximize, Restore and MoveTo change a live one. A placement saved on a monitor that is gone is moved onto a connected screen (PlatformApp.GetWorkAreas, WindowPlacement.ClampTo). Win32 and X11 (EWMH) place position and state; Wayland and Cocoa place size and state only (xdg-shell has no position request); phones and browsers do nothing. Window keeps all its existing members.
  • Live control window operation: read and set the placement (x, y, width, height, state, or a whole placement as text).
  • A Windows game is built as a GUI-subsystem program with no console window by default (a win-* runtime identifier, or a Release build on Windows). OneGameConsole=true keeps the console and OneGameConsole=false makes a Debug build GUI too; a game that sets OutputType to WinExe itself is unchanged. ParentConsole.Attach(), called by GameApp.Run, prints to the terminal that started the program; redirected output is untouched.

Fixed

  • Captures taken in a windowed game (capture_frame, capture_sequence, scenario and onegame capture captures) could miss art the window had not drawn before, and could show stale glyph pages. The capture rendered in a different pixel format than the window's swapchain, which gave it a second renderer, and the art atlas hands each upload (large standalone rasters, changed pages) to one renderer only. Captures now render in the swapchain's format, so there is one renderer and one set of textures, and the read-back swaps the channels.

v1.18.0

Choose a tag to compare

@github-actions github-actions released this 09 Nov 16:30

Cluster scenarios reach every followed scope, servers report per-connection view hashes, damaged save slots read as missing, and 3D views gain portals, runtime emissive and opacity, frame timing in the overlay and camera/pick on hand-filled scenes. RecursiveAI.OneGame.Physics ships as 1.18.0-preview.1.

Added

  • Cluster scenarios can command a followed scope: command: { entity: Arena, name: Hit, scope: 2 } on a client node (and the command_send op's new scope argument) looks the entity up in that scope's replica and forwards the command to its authority, whose trust checks apply as for any player. Unfollowed scopes fail with not_found.
  • net_status.followed lists every other scope the client's node is subscribed to, including ones the game subscribed to itself, and each entry carries fullHash and filtered; for a filtered replica hash is the view hash the authority computes for that connection.
  • Server hash_at names the connection of each view (clients) and takes an optional client, returning that connection's view hash as view; ServerHost.HashesAt(tick, client) is the API form.
  • A 3D view can be shown inside another (portal windows, monitors, mirrors): new Offscreen3D(renderer, w, h, format, sampled: true) renders another Scene3D into a target that Material3D.SetTexture(MaterialSlot3D, texture) binds to any material texture slot, with no model re-upload. ResetTexture restores the file's texture.
  • Runtime material changes without reloading a model: Material3D.Emissive, EmissiveStrength and Opacity, and ModelInstance3D.Opacity (plus Label). Translucent surfaces use the existing blended path (far to near, no depth write, no shadow or outline).
  • The debug overlay shows per-frame timing: CPU frame, simulation and draw times with a history bar chart, and GPU pass times where the backend has timestamps. The overlay and info operations return the same numbers as timing. GameInstance.Timings, GameInstance.ReadGpuTimings and RenderGraph.ReadPassTimes are the API; timing runs only while the overlay is shown.
  • camera, pick and project work for a Render3DStage that has no SceneView3D: Render3DStage.Manual (ManualScene3D, IScene3DProbe) answers over the stage's Scene, and games with their own stage implement IScene3DStage.Manual.

Fixed

  • SaveSlots.Exists is false for a slot whose save bytes are damaged (truncated or garbled): it now checks the recorded payload hash, without restoring a world. List leaves such slots out too.

v1.17.1

Choose a tag to compare

@github-actions github-actions released this 09 Nov 15:05

A fix for Wayland windows presenting with vsync. RecursiveAI.OneGame.Physics ships as 1.17.1-preview.1.

Fixed

  • A Wayland window with vsync presenting while its compositor withholds frame callbacks (covered, minimized or on another desktop) no longer parks the thread that runs the simulation and network pump, so a connected game keeps ticking and stays connected. The host now paces vsync on Wayland itself with the window's own frame callbacks and queue-replacing presents, skips drawing while a callback is outstanding, and never waits more than 100 ms for a swapchain image in a realtime run. Window.PresentWouldBlock and Window.OnPresenting are the platform hooks.