Core Engine Flow
This document describes the runtime flow of the Pyrite engine, focusing on how the application initializes, updates, and renders each frame. It is intended as a system-level guide rather than a line-by-line walkthrough of main.position_y.
Engine Initialization
Pyrite boots by constructing a fullscreen OpenGL context and then initializing all major subsystems. The startup sequence is designed so that the render thread can remain responsive while heavier tasks are deferred or executed in background queues.
Multimedia & Context: Initializes Pygame and configures a hardware-accelerated ModernGL context. It requests a core OpenGL profile with depth testing, face culling, and alpha blending enabled.
Configuration Management: Loads user preferences from a local
config.jsonfile and merges them with defaults. Current configurable values includefov,sensitivity,volume,render_distance, andunderwater_tint.Subsystem Instantiation: Bootstraps all major managers and systems:
Textures: Loads and binds image assets to the GPU.Player: Initializes the physics and camera entity.Sounds: Interfaces with the Pygame audio mixer.ShaderProgram: Compiles and manages GLSL shaders.UI: Prepares all interactive menus (Main Menu, Pause, Options).
State Management
Pyrite operates using a robust finite state machine to delegate logic and rendering. The active state is stored in self.game_state and seamlessly transitions between the following modes:
MAIN_MENU: The front-end title screen and world selection interface.LOADING: A blocking transition state that flushes the event queue while heavy procedural generation and async database reads occur.IN_GAME: The active 3D voxel simulation, handing control over to theSceneandPlayerinstances.INVENTORY: Pauses player movement and frees the mouse cursor to allow for drag-and-drop item management.PAUSED: Suspends background logic and draws a darkened UI overlay, allowing players to adjust settings or exit the session.OPTIONS: Renders the configuration sliders, saving changes to disk upon exit.
World Generation and Sessions
When a player selects a world, init_game_session() is invoked. This process ensures a clean environment by:
Seeding: Retrieving an existing seed from the SQLite database or generating a fresh procedural seed.
Instantiating the Scene: Constructing a new
SceneandWorldobject.Pre-loading: Engaging a blocking loop that forces initial chunks to load, build, and mesh.
Safety Flushing: Discarding buffered inputs and relative mouse movements to prevent sudden camera snapping when the game begins.
During this phase, a minimal render_loading_screen() UI overlay is repeatedly drawn to prevent the operating system from flagging the application as unresponsive.
The Main Loop
The core execution cycle is governed by the run() method. Until the application halts, it continuously cycles through three primary stages:
Event Polling (
handle_events): Intercepts raw OS hardware events (mouse clicks, key presses, window closes). It delegates these events dynamically based on the currentgame_state(e.g., passing WASD inputs to the Player in-game, or slider drags to the Options Menu).Logic Updates (
update): Advances internal systems. This increments theworld_session_time, processes physics boundaries, and evaluates UI animations. Delta time is strictly capped to prevent extreme physics desynchronization during performance spikes.Rendering (
render): Clears the hardware frame buffer and dispatches draw calls. If in a menu, it disables depth testing to draw 2D quads. If in-game, it directs theSceneto perform its complex multi-pass rendering pipeline.
Application Control
Safely exiting the application is handled by the quit_game() sequence. It breaks the main execution loop, triggers the world to synchronously serialize all remaining chunks to the SQLite database, and cleanly releases multimedia hardware handles back to the OS.
Detailed Initialization Sequence
Step-by-Step Pyrite Startup:
Pyrite boots by creating a fullscreen OpenGL context, then layering the engine subsystems on top of it. The actual source initializes a fixed fullscreen window using the current desktop resolution, and the configuration file is only used for gameplay options that affect camera, audio, and render streaming.
The key startup stages are:
Initialize Pygame and request an OpenGL 3.3 core context.
Create a fullscreen window at the current desktop resolution.
Create a ModernGL context and enable depth testing, face culling, and blending.
Initialize the frame clock and engine timers.
Load default configuration values, then override them from
config.json.Initialize the texture atlas, shader pipeline, and audio mixer.
Instantiate the player, the menu system, and the main game state container.
The configuration keys currently supported are:
fov: Vertical field of view in degrees.sensitivity: Mouse look sensitivity.volume: Global sound volume.render_distance: Number of chunks loaded around the player.underwater_tint: Whether a blue tint is applied underwater.
Detailed State Machine
State Transitions and Logic:
stateDiagram-v2
direction LR
GameOpens --> MAIN_MENU
MAIN_MENU --> LOADING : Play (world selected)
MAIN_MENU --> OPTIONS : Settings
MAIN_MENU --> GameCloses : Quit
state LOADING {
[*] --> GenerateChunks
GenerateChunks --> CompileNumba
CompileNumba --> Ready
}
LOADING --> IN_GAME : Ready
IN_GAME --> PAUSED : ESC
IN_GAME --> INVENTORY : E
INVENTORY --> IN_GAME : E
PAUSED --> IN_GAME : Resume
PAUSED --> OPTIONS : Settings
PAUSED --> MAIN_MENU : Quit to Main Menu
OPTIONS --> MAIN_MENU : Exit
OPTIONS --> PAUSED : Exit (from pause)
GameCloses
OPTIONS --> PAUSED : Back
State Handler Implementation:
if state == 'MAIN_MENU':
main_menu.handle_events()
main_menu.update()
State Delegation: The engine acts as a pure router, dispatching logic and polling events directly to independent GUI instances or 3D scene objects based on the current finite state.
elif state == 'LOADING':
while not loading_complete:
check_loading_progress()
Blocking Transitions: Specific intensive states, like chunk pre-loading, utilize blocking loops to dedicate entire CPU cycles before yielding control.
The Main Game Loop
High-Level Loop Structure:
delta_time = self.clock.tick(60) / 1000.0
delta_time = min(delta_time, 0.033)
Timing and Clamping: The core execution loop is strictly regulated. To prevent devastating physics synchronization bugs during lag spikes, delta time is hard-capped to a maximum of 33ms.
self.handle_events()
self.update_in_game(delta_time)
self.render()
Synchronous Frame Dispatch: The render thread guarantees the sequence of user input, logical updates, and visual dispatching across every single pass.
Detailed Event Handling
Event Loop:
for event in pygame.event.get():
if event.type == pygame.QUIT: return False
Polling Inputs: Every frame, raw hardware events from the operating system are pulled and flushed through the pygame.event queue.
if self.game_state == 'IN_GAME':
self.player.handle_event(event)
Targeted Dispatch: Keys and mouse inputs are tunneled immediately into the active game_state, preventing un-rendered UI elements from consuming inputs.
Detailed Update Step (In-Game)
Update Sequence (per frame):
self.world.update_loaded_chunks(player_chunk, RENDER_DISTANCE)
Chunk Streaming: The logical step always calculates the player’s world position and commands the background pool to load or drop respective memory boundaries.
self.process_mesh_queue(limit=MAIN_THREAD_MESH_PROCESS_LIMIT_INGAME)
Budgeted Queues: Background workers execute heavily on CPU cores, but VAO/VBO OpenGL initialization is locked strictly to this primary thread and strictly limited per-frame to preserve FPS.
Detailed Render Step
Render Sequence (per frame):
self.ctx.clear(0.53, 0.81, 0.92)
self.ctx.clear_depth(1.0)
Buffer Sweeping: The screen and depth layers are rigorously wiped clean on the graphics card prior to drawing the next visualization.
self.ctx.disable(moderngl.DEPTH_TEST)
self.hud.render(self.ctx, self.shader_program)
Z-Buffering Control: By disabling depth evaluations after geometry rendering, the 2D UI elements are mathematically drawn overtop the entire voxel environment without clipping.
Scene Rendering Pipeline
Scene.render() Detail (in-game world rendering):
visible_chunks = shader_program.frustum_cull(self.camera, self.world.active_chunks)
Vectorized Frustum Culling: The scene strictly bounds rendering to only what physically fits within the player’s 3D cone-of-vision mathematically.
ctx.enable(moderngl.BLEND)
chunk.mesh.render_water()
Multi-Pass Transparency: Water and leaves are rendered entirely on an isolated pass with alphablending to prevent Z-fighting.
World Session Initialization (LOADING state)
init_game_session() Implementation:
self.world = World(world_name)
self.scene = Scene(self.world)
System Sub-Allocation: Core session elements establish a pristine architectural tree.
pygame.event.clear()
pygame.mouse.get_rel()
Accumulated Safety Flushing: Discarding lingering hardware events prior to fully bridging
IN_GAMEprevents severe camera snapping.
Shutdown and Cleanup
quit_game() Implementation:
if self.world:
self.world.unload_all_chunks()
Data Evacuation: Forcefully drops active terrain sectors straight onto persistent SQLite disk memory prior to closing logic.
for vbo in self.vbo_pool:
vbo.release()
VRAM Destruction: Caches pooled inside OpenGL bindings are permanently stripped off the system graphics.
Next Steps
Now that you understand the core engine loop and state machine, proceed to the World Architecture and Multithreading Breakdown breakdown to see how Pyrite utilizes multithreading and queue systems under the hood.