Platform, player and web
The player program, the platform layer (desktop and browser), the browser player, data packs and static SDL linking.
Files: player.odin, platform_native.odin (#+build !js), platform_web.odin (#+build js), player_web.odin (#+build js), pack.odin (#+build !js), sdl_static.odin (#+build linux, darwin, windows).
File overviews
Section titled “File overviews”platform_native.odin
Section titled “platform_native.odin”Platform services, desktop version (Linux, macOS, Windows). platform_web.odin has the same
procedures for the browser (Emscripten), where core:os, virtual memory, SDL3_image and SDL3_mixer
aren’t available. Code elsewhere calls these instead of those packages directly.
platform_web.odin
Section titled “platform_web.odin”Platform services, browser version: a WebAssembly build linked by Emscripten (the editor’s Web
export). Same procedures as platform_native.odin, without core:os (unsupported on js/wasm),
virtual memory (there is none) and SDL3_image (no Emscripten build of it yet):
- files are read through SDL, which reads Emscripten’s virtual file system (the exported game’s data is packaged into it)
- arenas grow from the context allocator, which
Web_Contextmakes Emscripten’s malloc - images: PNG and BMP, with SDL’s own loaders
- audio: SDL3_mixer, built with Emscripten, as on the desktop (audio.odin)
player_web.odin
Section titled “player_web.odin”The player in a browser. The editor’s Web export builds the game’s player (the same main
package as a desktop build) for js_wasm32 with -build-mode:obj, and links it with Emscripten:
emcc <engine>/web/main.c game.wasm.o libSDL3_mixer.a -o index.html -sUSE_SDL=3 -sUSE_SDL_TTF=3 -sALLOW_MEMORY_GROWTH --js-library <engine>/web/odin_env.js --pre-js <engine>/web/odin_env_pre.js --preload-file <project>@/dataEmscripten starts at C main (web/main.c), which calls odin_web_main below; the player’s own
main isn’t used. The browser can’t run a blocking loop, so instead of Application_Run,
Emscripten calls Application_Frame once per animation frame.
- Memory: Emscripten owns the wasm heap, so the allocator is its
malloc(Web_Context). - Files: the project is packaged into Emscripten’s virtual file system under /data.
- web/odin_env.js and web/odin_env_pre.js supply what Odin’s js runtime imports from the “odin_env” module (console output, random bytes, some math).
- Audio: SDL3_mixer, built for the web by the editor (
tools/build_web_mixer.sh: Emscripten has no SDL3_mixer port). Browsers start sound only after the player clicks or presses a key. - Limits: images load with SDL’s built-in PNG and BMP loaders (no SDL_image port exists).
pack.odin
Section titled “pack.odin”The data pack: a game’s files (project.xml, its scenes, its assets) appended to the end of its own executable, so an exported game is one file. Executables ignore what follows them (ELF on Linux, PE on Windows; macOS games keep their files in the .app instead: appending would break its code signature).
At start, the player looks at the end of its executable (Pack_Open). With a pack there, the
project is PACK_ROOT and every file the engine reads under it comes from the pack: the platform
layer’s Platform_ReadFile / Platform_FileExists / Platform_LoadTexture, sounds and music
(Platform_OpenIO), fonts. A file is read from the executable the first time it’s asked for and
kept: SDL keeps reading music and fonts as they play, from that memory.
Layout (little-endian), after the program:
the files' bytes, one after anotherthe index: per file, u32 path length, the path ("/"-separated, relative to the project), u64 offset (from the start of the executable), u64 sizethe footer (32 bytes): u64 index offset, u64 index size, u64 pack start, PACK_MAGIC (8 bytes)Pack_Append writes one (the editor’s desktop exports use it).
Example
files := []string{"project.xml", "scenes/Main.scene.xml", "assets/ship.png"}ok := e.Pack_Append("export/linux_x64/MyGame", "my_project", files)sdl_static.odin
Section titled “sdl_static.odin”Static SDL: when tools/build_sdl_static.sh has built SDL3, SDL3_image, SDL3_ttf and SDL3_mixer for this OS and CPU (sdl/lib/<os>_<arch>/), every program that imports the engine links them instead of the system’s shared libraries. An exported game is then one executable that needs only libc (and loads the OS’s window system, graphics and audio at run time, as SDL always does).
How: Odin’s vendor:sdl3 bindings ask the linker for -lSDL3 (and _image, _ttf, _mixer). The
linker takes the first folder in its search path that has a match, so -L to a folder of only
static archives wins over the system’s .so files. The archives are also listed again here, so
symbols SDL3_ttf, _image and _mixer need from SDL3 resolve whatever order the -l flags came in.
Windows: the vendor bindings name Odin’s own SDL3.lib (and SDL3_image.lib…), import
libraries of the DLLs, by path: no search path can replace them. But MSVC’s linker resolves a
symbol from the first library on its command line that has it, and Odin orders foreign imports by
priority_index first: this file’s, -1, come before the bindings’ (0), so the static libraries
define every SDL function and the import libraries go unused (the game doesn’t load SDL3.dll).
The static libraries need Windows’ own: user32, gdi32, winmm, imm32, ole32, oleaut32, version,
uuid, advapi32, setupapi, shell32.
-define:SDL_STATIC=false links the system’s shared libraries (the DLLs on Windows) even when the
static ones are built.
PlayerOptions
Section titled “PlayerOptions”PlayerOptions :: struct { // The project folder ("." when none was given) projectDir: string, // Play from this scene instead of the project's start scene ("" for the start scene) scene: string, // Print the registered scripts and exit describe: bool, // Draw the physics shapes, whatever the project says physicsDebug: bool, // Developer keys (`Game.devKeys`); the editor's Play passes it, exports never run with it dev: bool, // `--live`: answer an editor's live view on stdin / stdout (live.odin); the editor's Play live: bool, // A project folder was named on the command line (otherwise an exported game finds its own) folderGiven: bool, // `--renderer <name>`: draw with this one, whatever the project says ("" for the project's) renderer: string, // `--quit-after <seconds>`: quit once the game has run that long (0: run until it quits); // CI starts exported games with it to check they start and stop cleanly quitAfter: f64, // `--audio-device <name>`: play sound on this device ("" for the system's default) audioDevice: string, // `--list-audio-devices`: print the playback devices' names and exit listAudioDevices: bool, // `--restore <file>`: start where a snapshot left off (its scene, globals, nodes: hot_reload.odin) restore: string,}What the player was asked to do (Player_ParseArgs)
struct. Source: player.odin:189
MemoryArena
Section titled “MemoryArena”MemoryArena :: vmem.ArenaA growing memory arena: nodes, scenes and the game each own one (virtual memory on the desktop)
type, only in builds with #+build !js. Source: platform_native.odin:23
MemoryArena
Section titled “MemoryArena”MemoryArena :: runtime.ArenaA growing memory arena: nodes, scenes and the game each own one (blocks from the context allocator in the browser)
type, only in builds with #+build js. Source: platform_web.odin:25
PackEntry
Section titled “PackEntry”PackEntry :: struct { // Byte offset from the start of the executable, and length in bytes offset, size: u64,}A file in a pack: where its bytes are in the executable
struct, only in builds with #+build !js. Source: pack.odin:44
Pack :: struct { // An open was tried (`Pack_Open` only tries once) opened: bool, // The file the pack is in, nil when there's no pack file: ^os.File, // The index: each file's path (relative to the project) and where it is (heap-allocated) entries: map[string]PackEntry, // The files read so far, by path (heap-allocated, kept until `Pack_CloseFile`) loaded: map[string][]byte,}An open pack: a file’s index, and the files read from it so far (kept: SDL streams music and fonts
from them). The game’s is gamePack (Pack_Open); tests and tools open their own.
struct, only in builds with #+build !js. Source: pack.odin:53
Procedures
Section titled “Procedures”Player_Run
Section titled “Player_Run”Player_Run :: proc(args: []string) -> intThe game player: runs a project saved by the editor.
player an exported game: play the project next to the program (its data/ folder), else the current folderplayer <project folder> play it, from its start sceneplayer <project folder> --scene <name> play it from another scene (the editor's Play Scene)player ... --physics-debug draw every physics shape, whatever the project saysplayer ... --dev developer keys (the editor's Play): F9 shows / hides shapes, F10 the debug overlayplayer ... --renderer <name> draw with this renderer (vulkan, opengl, software, ...), whatever the project saysplayer ... --quit-after <seconds> quit after that long (smoke tests of exported games)player ... --audio-device <name> play sound on that device (the editor's Settings for Play)player --describe-scripts print the registered scripts as JSON, and exitplayer --list-audio-devices print the playback devices' names, one a line, and exitPlayer_Run is the whole program. The engine’s own player/ package calls it with no game code;
a project with scripts gets its own player, generated by the editor, that imports the project’s
scripts package (which registers its scripts from @(init) procedures) and calls it:
package main
import "core:os"import e "engine:."import scripts "project:scripts"_ :: scripts
main :: proc() { os.exit(e.Player_Run(os.args))}Inputs
- args: The command line, program name first
Returns
- The exit code: 0 after a normal quit (or
--describe-scripts), 1 if the command line is wrong or the project couldn’t be loaded or started
procedure. Source: player.odin:47
Player_Setup
Section titled “Player_Setup”Player_Setup :: proc(options: PlayerOptions, app: ^Application) -> boolLoads the project options names and opens its window, with the start scene (or
options.scene) queued in app. Shared by Player_Run and the browser’s Player_StartWeb;
the caller then runs frames and finally calls Application_Cleanup.
Inputs
- options: What to play (
Player_ParseArgs) - app: The application to set up (zero-initialized)
Returns
- false (logged) if the project or the window couldn’t be set up
procedure. Source: player.odin:89
Player_FindExportedProject
Section titled “Player_FindExportedProject”Player_FindExportedProject :: proc() -> stringWhere an exported game’s project is: the data pack at the end of the program (a single-file
export: PACK_ROOT), else the data folder next to the program (how the editor’s
desktop exports lay a game out), or the program’s own folder, or a macOS app’s
Contents/Resources/data (the program is in Contents/MacOS), or else the current folder. Asset
paths are relative to it, so the game runs from wherever it’s started.
Returns
- The folder (temp allocated)
procedure. Source: player.odin:227
Player_ParseArgs
Section titled “Player_ParseArgs”Player_ParseArgs :: proc(args: []string) -> (PlayerOptions, bool)Reads the player’s command line: [<project folder>] [--scene <name>] [--physics-debug] [--dev] [--renderer <name>] [--audio-device <name>] [--quit-after <seconds>], or --describe-scripts, or
--list-audio-devices (see Player_Run). The folder defaults to the current
one; flags may come in any order.
Inputs
- args: The command line, program name first
Returns
- The options (strings point into
args), and false if the command line isn’t valid (a second folder, or--scene/--rendererwithout a value)
Example
options, ok := e.Player_ParseArgs({"player", "mygame", "--scene", "Level2"})if ok do log.infof("playing %s from %s", options.projectDir, options.scene)procedure. Source: player.odin:258
MemoryArena_InitGrowing
Section titled “MemoryArena_InitGrowing”MemoryArena_InitGrowing :: proc(arena: ^MemoryArena) -> boolSets up a growing arena, which reserves more memory as it fills.
Inputs
- arena: The arena to set up
Returns
- false if the memory couldn’t be reserved
procedure, only in builds with #+build !js. Source: platform_native.odin:34
MemoryArena_Allocator
Section titled “MemoryArena_Allocator”MemoryArena_Allocator :: proc(arena: ^MemoryArena) -> runtime.AllocatorAn allocator that allocates from the arena. Its memory is freed all at once by
MemoryArena_Destroy; individual frees do nothing.
Inputs
- arena: An arena set up with
MemoryArena_InitGrowing
Returns
- The arena’s allocator
procedure, only in builds with #+build !js. Source: platform_native.odin:48
MemoryArena_Destroy
Section titled “MemoryArena_Destroy”MemoryArena_Destroy :: proc(arena: ^MemoryArena)Frees all of the arena’s memory.
Inputs
- arena: The arena
procedure, only in builds with #+build !js. Source: platform_native.odin:58
MemoryArena_BootstrapNew
Section titled “MemoryArena_BootstrapNew”MemoryArena_BootstrapNew :: proc($T: typeid, offset: uintptr, blockSize: uint) -> (^T, bool)Allocates a T inside a new arena that it owns, at offset within T (the arena frees the T with
everything else). Used for nodes.
Inputs
- T: The type to allocate
- offset: Byte offset of the
MemoryArenafield inside T (offset_of) - blockSize: Size in bytes of the arena’s memory blocks
Returns
- The new T (zeroed) and true, or nil and false if the memory couldn’t be reserved
procedure, only in builds with #+build !js. Source: platform_native.odin:74
Platform_ReadFile
Section titled “Platform_ReadFile”Platform_ReadFile :: proc(path: string) -> ([]byte, bool)Reads a whole file, from the data pack if it’s there, else from disk.
Inputs
- path: The file’s path
Returns
- The contents (temp allocated, or the data pack’s memory: don’t free them) and true; false if it can’t be read
procedure, only in builds with #+build !js. Source: platform_native.odin:89
Platform_FileExists
Section titled “Platform_FileExists”Platform_FileExists :: proc(path: string) -> boolWhether a file exists, in the data pack or on disk.
Inputs
- path: The file’s path
Returns
- true if it exists
procedure, only in builds with #+build !js. Source: platform_native.odin:104
Platform_ExecutableDir
Section titled “Platform_ExecutableDir”Platform_ExecutableDir :: proc() -> (string, bool)The folder the program is in.
Returns
- The folder’s path (temp allocated) and true; false if it couldn’t be found
procedure, only in builds with #+build !js. Source: platform_native.odin:114
Platform_LoadTexture
Section titled “Platform_LoadTexture”Platform_LoadTexture :: proc(renderer: ^sdl.Renderer, path: string) -> ^sdl.TextureLoads an image file as a texture: any format SDL3_image reads. Reads the data pack first, then the disk.
Most code loads through ResourceManager (loadTexture), which caches the texture.
Inputs
- renderer: The renderer the texture is for
- path: The image’s path
Returns
- The texture (the caller destroys it), or nil if it can’t be read (SDL’s error says why)
procedure, only in builds with #+build !js. Source: platform_native.odin:132
Platform_LoadSurface
Section titled “Platform_LoadSurface”Platform_LoadSurface :: proc(path: string) -> ^sdl.SurfaceReads an image file into a surface (any format SDL_image reads), from the data pack first.
Inputs
- path: The file
Returns
- The surface (the caller’s), or nil
procedure, only in builds with #+build !js. Source: platform_native.odin:148
Platform_DefaultContext
Section titled “Platform_DefaultContext”Platform_DefaultContext :: proc "contextless" () -> runtime.ContextA context for contextless code (@(init) procedures): Odin’s default
procedure, only in builds with #+build !js. Source: platform_native.odin:156
Player_Main
Section titled “Player_Main”Player_Main :: proc()The whole player program: Player_Run with the command line, exiting with its code. A player’s
main is just this call, so the same main builds for the desktop and the browser (where this
does nothing: the page starts the game through odin_web_main).
procedure, only in builds with #+build !js. Source: platform_native.odin:165
Platform_SaveWrite
Section titled “Platform_SaveWrite”Platform_SaveWrite :: proc(name: string, data: []byte) -> boolWrites a save. On the desktop, <SDL's per-user folder for the game>/saves/<slot>.json,
written to a temporary file first and then moved over the old save, so a crash mid-write can’t
leave half a save. Use Save_Write (save.odin) rather than calling this directly.
Inputs
- name: “<game>/<slot>”
- data: The save’s contents
Returns
- false if it couldn’t be written
procedure, only in builds with #+build !js. Source: platform_native.odin:203
Platform_SaveRead
Section titled “Platform_SaveRead”Platform_SaveRead :: proc(name: string) -> ([]byte, bool)Reads a save written by Platform_SaveWrite.
Inputs
- name: “<game>/<slot>”
Returns
- The contents (temp allocated) and true; false if there’s no such save or it can’t be read
procedure, only in builds with #+build !js. Source: platform_native.odin:221
Platform_SaveExists
Section titled “Platform_SaveExists”Platform_SaveExists :: proc(name: string) -> boolWhether a save exists.
Inputs
- name: “<game>/<slot>”
Returns
- true if it exists
procedure, only in builds with #+build !js. Source: platform_native.odin:237
Platform_SaveDelete
Section titled “Platform_SaveDelete”Platform_SaveDelete :: proc(name: string)Deletes a save, if it exists.
Inputs
- name: “<game>/<slot>”
procedure, only in builds with #+build !js. Source: platform_native.odin:248
Platform_SetWindowIcon
Section titled “Platform_SetWindowIcon”Platform_SetWindowIcon :: proc(window: ^sdl.Window, data: []byte) -> boolGives a window an icon from an image file’s bytes (PNG, JPEG…: what SDL_image reads).
Inputs
- window: The window
- data: The image file’s contents; nil for the engine’s default icon (the Crossfire logo)
Returns
- false when the image can’t be read (logged)
procedure, only in builds with #+build !js. Source: platform_native.odin:266
Platform_WriteFile
Section titled “Platform_WriteFile”Platform_WriteFile :: proc(path: string, data: []byte) -> boolWrites a file (a hot reload’s snapshot: hot_reload.odin). Not the data pack: files the game makes.
Returns
- false when it couldn’t be written
procedure, only in builds with #+build !js. Source: platform_native.odin:283
MemoryArena_InitGrowing
Section titled “MemoryArena_InitGrowing”MemoryArena_InitGrowing :: proc(arena: ^MemoryArena) -> boolSets up a growing arena, which adds WEB_ARENA_BLOCK_SIZE blocks as it fills.
Inputs
- arena: The arena to set up
Returns
- false if the memory couldn’t be allocated
procedure, only in builds with #+build js. Source: platform_web.odin:39
MemoryArena_Allocator
Section titled “MemoryArena_Allocator”MemoryArena_Allocator :: proc(arena: ^MemoryArena) -> runtime.AllocatorAn allocator that allocates from the arena. Its memory is freed all at once by
MemoryArena_Destroy; individual frees do nothing.
Inputs
- arena: An arena set up with
MemoryArena_InitGrowing
Returns
- The arena’s allocator
procedure, only in builds with #+build js. Source: platform_web.odin:53
MemoryArena_Destroy
Section titled “MemoryArena_Destroy”MemoryArena_Destroy :: proc(arena: ^MemoryArena)Frees all of the arena’s memory.
Inputs
- arena: The arena
procedure, only in builds with #+build js. Source: platform_web.odin:63
MemoryArena_BootstrapNew
Section titled “MemoryArena_BootstrapNew”MemoryArena_BootstrapNew :: proc($T: typeid, offset: uintptr, blockSize: uint) -> (^T, bool)Allocates a T inside a new arena that it owns, at offset within T (the arena frees the T with
everything else). Used for nodes.
Inputs
- T: The type to allocate
- offset: Byte offset of the
MemoryArenafield inside T (offset_of) - blockSize: Size in bytes of the arena’s memory blocks
Returns
- The new T (zeroed) and true, or nil and false if the memory couldn’t be allocated
procedure, only in builds with #+build js. Source: platform_web.odin:79
Platform_ReadFile
Section titled “Platform_ReadFile”Platform_ReadFile :: proc(path: string) -> ([]byte, bool)Reads a whole file from Emscripten’s virtual file system (through SDL).
Inputs
- path: The file’s path
Returns
- The contents (temp allocated) and true; false if it can’t be read
procedure, only in builds with #+build js. Source: platform_web.odin:101
Pack_Open
Section titled “Pack_Open”Pack_Open :: proc() -> boolNo data pack in the browser: always false
procedure, only in builds with #+build js. Source: platform_web.odin:115
Pack_Bytes
Section titled “Pack_Bytes”Pack_Bytes :: proc(path: string) -> ([]byte, bool)A file in the game’s data pack. The browser has no data pack.
Inputs
- path: The file’s path (ignored)
Returns
- Always nil, false
procedure, only in builds with #+build js. Source: platform_web.odin:125
Pack_Has
Section titled “Pack_Has”Pack_Has :: proc(path: string) -> boolWhether the game’s data pack has a file. The browser has no data pack.
Inputs
- path: The file’s path (ignored)
Returns
- Always false
procedure, only in builds with #+build js. Source: platform_web.odin:136
Platform_FileExists
Section titled “Platform_FileExists”Platform_FileExists :: proc(path: string) -> boolWhether a file exists in Emscripten’s virtual file system.
Inputs
- path: The file’s path
Returns
- true if it exists
procedure, only in builds with #+build js. Source: platform_web.odin:147
Platform_ExecutableDir
Section titled “Platform_ExecutableDir”Platform_ExecutableDir :: proc() -> (string, bool)The packaged game is at the root of the virtual file system
procedure, only in builds with #+build js. Source: platform_web.odin:152
Platform_LoadTexture
Section titled “Platform_LoadTexture”Platform_LoadTexture :: proc(renderer: ^sdl.Renderer, path: string) -> ^sdl.TextureLoads an image file as a texture. The browser build reads PNG and BMP only (SDL’s own loaders).
Most code loads through ResourceManager (loadTexture), which caches the texture.
Inputs
- renderer: The renderer the texture is for
- path: The image’s path
Returns
- The texture (the caller destroys it), or nil (logged) if it can’t be read
procedure, only in builds with #+build js. Source: platform_web.odin:168
Platform_LoadSurface
Section titled “Platform_LoadSurface”Platform_LoadSurface :: proc(path: string) -> ^sdl.SurfaceReads an image file into a surface: PNG or BMP in the browser.
Inputs
- path: The file
Returns
- The surface (the caller’s), or nil
procedure, only in builds with #+build js. Source: platform_web.odin:188
odin_storage_set
Section titled “odin_storage_set”odin_storage_set :: proc(key: [^]u8, keyLength: i32, value: [^]u8, valueLength: i32) -> i32Stores a value under a key; nonzero on success
procedure, a foreign (C) procedure, only in builds with #+build js. Source: platform_web.odin:200
odin_storage_size
Section titled “odin_storage_size”odin_storage_size :: proc(key: [^]u8, keyLength: i32) -> i32The value’s size in bytes, or -1 if there’s none
procedure, a foreign (C) procedure, only in builds with #+build js. Source: platform_web.odin:202
odin_storage_get
Section titled “odin_storage_get”odin_storage_get :: proc(key: [^]u8, keyLength: i32, buffer: [^]u8, capacity: i32) -> i32Copies the value into the buffer; the bytes copied, or -1 if there’s none
procedure, a foreign (C) procedure, only in builds with #+build js. Source: platform_web.odin:204
odin_storage_remove
Section titled “odin_storage_remove”odin_storage_remove :: proc(key: [^]u8, keyLength: i32)Removes the key’s value
procedure, a foreign (C) procedure, only in builds with #+build js. Source: platform_web.odin:206
Platform_SaveWrite
Section titled “Platform_SaveWrite”Platform_SaveWrite :: proc(name: string, data: []byte) -> boolWrites a save: in the browser, to localStorage under “<game>/<slot>”. Use Save_Write
(save.odin) rather than calling this directly.
Inputs
- name: “<game>/<slot>”
- data: The save’s contents
Returns
- false if it couldn’t be stored
procedure, only in builds with #+build js. Source: platform_web.odin:220
Platform_SaveRead
Section titled “Platform_SaveRead”Platform_SaveRead :: proc(name: string) -> ([]byte, bool)Reads a save written by Platform_SaveWrite.
Inputs
- name: “<game>/<slot>”
Returns
- The contents (temp allocated) and true; false if there’s no such save
procedure, only in builds with #+build js. Source: platform_web.odin:233
Platform_SaveExists
Section titled “Platform_SaveExists”Platform_SaveExists :: proc(name: string) -> boolWhether a save exists.
Inputs
- name: “<game>/<slot>”
Returns
- true if it exists
procedure, only in builds with #+build js. Source: platform_web.odin:250
Platform_SaveDelete
Section titled “Platform_SaveDelete”Platform_SaveDelete :: proc(name: string)Deletes a save, if it exists.
Inputs
- name: “<game>/<slot>”
procedure, only in builds with #+build js. Source: platform_web.odin:260
Platform_SetWindowIcon
Section titled “Platform_SetWindowIcon”Platform_SetWindowIcon :: proc(window: ^sdl.Window, data: []byte) -> boolBrowsers show the page’s favicon: games’ window icons do nothing there
procedure, only in builds with #+build js. Source: platform_web.odin:265
Platform_WriteFile
Section titled “Platform_WriteFile”Platform_WriteFile :: proc(path: string, data: []byte) -> boolWrites a file: the browser has none to write (false)
procedure, only in builds with #+build js. Source: platform_web.odin:270
odin_web_main
Section titled “odin_web_main”odin_web_main :: proc "c" ()The web build’s entry point, called from web/main.c: runs the game’s @(init) procedures (script
registration) and starts the project packaged under /data.
procedure, only in builds with #+build js. Source: player_web.odin:54
Player_Main
Section titled “Player_Main”Player_Main :: proc()Does nothing in the browser: a player’s main isn’t called there (Emscripten calls C main,
which calls odin_web_main). On the desktop it runs the player; see platform_native.odin.
procedure, only in builds with #+build js. Source: player_web.odin:64
Platform_DefaultContext
Section titled “Platform_DefaultContext”Platform_DefaultContext :: proc "contextless" () -> runtime.ContextA context for contextless code (@(init) procedures): Web_Context, so it allocates from Emscripten’s heap
procedure, only in builds with #+build js. Source: player_web.odin:74
Web_Context
Section titled “Web_Context”Web_Context :: proc "contextless" () -> runtime.ContextA context for code running in the browser: Emscripten’s malloc as the allocator, a temporary
arena (freed each frame), and a logger that writes to the browser console. Call at the start of
every entry point the page calls into (context = e.Web_Context()).
procedure, only in builds with #+build js. Source: player_web.odin:83
Player_StartWeb
Section titled “Player_StartWeb”Player_StartWeb :: proc(scene := "")Starts the game in the browser: loads the packaged project and hands Emscripten the frame callback. Returns at once; the game keeps running from the browser’s animation frames.
Inputs
- scene: Start in this scene instead of the project’s start scene (“” for the start scene)
procedure, only in builds with #+build js. Source: player_web.odin:102
Pack_Open
Section titled “Pack_Open”Pack_Open :: proc() -> boolOpens the pack at the end of the running executable, if it has one (once; later calls say whether it did).
Returns
- Whether there’s a pack (paths under PACK_ROOT then read from it)
procedure, only in builds with #+build !js. Source: pack.odin:75
Pack_OpenFile
Section titled “Pack_OpenFile”Pack_OpenFile :: proc(p: ^Pack, path: string) -> boolOpens the pack at the end of a file into p (closing what it had). Pack_Open does it with the
running executable; tools and tests open their own. Close it with Pack_CloseFile.
Inputs
- p: The pack to open into
- path: The file (an executable with a pack appended)
Returns
- Whether the file ends in a pack
Example
pack: e.Packdefer e.Pack_CloseFile(&pack)if e.Pack_OpenFile(&pack, "export/linux_x64/MyGame") { data, ok := e.Pack_FileBytes(&pack, "project.xml") if ok do log.infof("project.xml: %d bytes", len(data))}procedure, only in builds with #+build !js. Source: pack.odin:101
Pack_CloseFile
Section titled “Pack_CloseFile”Pack_CloseFile :: proc(p: ^Pack)Closes a pack and frees what was read from it (slices from Pack_FileBytes become invalid).
Inputs
- p: The pack (a closed or zero one is fine)
procedure, only in builds with #+build !js. Source: pack.odin:148
Pack_Bytes
Section titled “Pack_Bytes”Pack_Bytes :: proc(path: string) -> ([]byte, bool)A file in the game’s pack: its bytes (kept for the program’s life: don’t free them), or false when
there’s no pack or no such file. Opens the pack on first use (Pack_Open).
Inputs
- path: Under PACK_ROOT (“<pack>/assets/ship.png”) or relative to it (“assets/ship.png”); absolute paths are never in the pack
Returns
- The bytes and true, or nil and false
procedure, only in builds with #+build !js. Source: pack.odin:172
Pack_Has
Section titled “Pack_Has”Pack_Has :: proc(path: string) -> boolWhether the game’s pack has a file, without reading it. Opens the pack on first use (Pack_Open).
Inputs
- path: The file’s path (see
Pack_Bytes)
Returns
- true when there’s a pack and it has the file
procedure, only in builds with #+build !js. Source: pack.odin:186
Pack_FileBytes
Section titled “Pack_FileBytes”Pack_FileBytes :: proc(p: ^Pack, path: string) -> ([]byte, bool)A file in an open pack (Pack_Bytes for the game’s own pack). The first call reads it from disk;
later ones return the same bytes.
Inputs
- p: The pack (
Pack_OpenFile) - path: The file’s path (see
Pack_Bytes)
Returns
- The bytes (the pack’s: valid until
Pack_CloseFile, don’t free them) and true, or nil and false when the pack isn’t open, has no such file, or it can’t be read
procedure, only in builds with #+build !js. Source: pack.odin:202
Pack_FileHas
Section titled “Pack_FileHas”Pack_FileHas :: proc(p: ^Pack, path: string) -> boolWhether an open pack has a file, without reading it.
Inputs
- p: The pack (
Pack_OpenFile) - path: The file’s path (see
Pack_Bytes)
Returns
- true when the pack is open and has the file
procedure, only in builds with #+build !js. Source: pack.odin:228
Pack_Append
Section titled “Pack_Append”Pack_Append :: proc(executable, root: string, files: []string, extra: map[string]string = nil) -> stringAppends a pack of a project’s files to an executable (a desktop export’s last step).
Inputs
- executable: The built game
- root: The folder the files are relative to
- files: Their paths under it, “/”-separated (they’re found in the pack by these)
- extra: More files given by content (an export’s own project.xml), by path; they win over
files
Returns
- “” or what went wrong
procedure, only in builds with #+build !js. Source: pack.odin:246
Constants and variables
Section titled “Constants and variables”PLATFORM_WEB
Section titled “PLATFORM_WEB”PLATFORM_WEB :: falseWhether this is the browser build (false here; true in platform_web.odin)
constant, only in builds with #+build !js. Source: platform_native.odin:20
PLATFORM_WEB
Section titled “PLATFORM_WEB”PLATFORM_WEB :: trueWhether this is the browser build (true here; false in platform_native.odin)
constant, only in builds with #+build js. Source: platform_web.odin:22
WEB_ARENA_BLOCK_SIZE
Section titled “WEB_ARENA_BLOCK_SIZE”WEB_ARENA_BLOCK_SIZE :: 16 * 1024Block size of web arenas; they grow by adding blocks
constant, only in builds with #+build js. Source: platform_web.odin:28
PACK_ROOT
Section titled “PACK_ROOT”PACK_ROOT :: "<pack>"The data pack is the desktop exports’ (pack.odin): the browser preloads files instead. The project folder of a packed game: paths under it are the pack’s
constant, only in builds with #+build js. Source: platform_web.odin:113
WEB_PROJECT_DIR
Section titled “WEB_PROJECT_DIR”WEB_PROJECT_DIR :: "/data"Where the Web export packages the project
constant, only in builds with #+build js. Source: player_web.odin:33
PACK_ROOT
Section titled “PACK_ROOT”PACK_ROOT :: "<pack>"The project folder of a packed game: paths under it are the pack’s
constant, only in builds with #+build !js. Source: pack.odin:36
PACK_MAGIC
Section titled “PACK_MAGIC”PACK_MAGIC :: "ODINPAK1"The footer’s last 8 bytes
constant, only in builds with #+build !js. Source: pack.odin:38
SDL_STATIC
Section titled “SDL_STATIC”SDL_STATIC :: #config(SDL_STATIC, true)Link the static SDL libraries when they’re built (false: always the system’s shared ones)
constant, only in builds with #+build linux, darwin, windows. Source: sdl_static.odin:28
SDL_STATIC_LIBS
Section titled “SDL_STATIC_LIBS”SDL_STATIC_LIBS :: "sdl/lib/" + ODIN_OS_STRING + "_" + ODIN_ARCH_STRINGWhere tools/build_sdl_static.sh puts this platform’s static SDL: relative to the engine (foreign imports take that) and absolute (for the linker’s -L, quoted: the linker runs through a shell, and the engine may be in a folder with a space, like a macOS app bundle’s)
constant, only in builds with #+build linux, darwin, windows. Source: sdl_static.odin:33
SDL_STATIC_DIR
Section titled “SDL_STATIC_DIR”SDL_STATIC_DIR :: #directory + SDL_STATIC_LIBSThe same folder as an absolute path (for the linker’s -L)
constant, only in builds with #+build linux, darwin, windows. Source: sdl_static.odin:35
SDL_LINKED_STATICALLY
Section titled “SDL_LINKED_STATICALLY”SDL_LINKED_STATICALLY :: SDL_STATIC && (#exists(SDL_STATIC_LIBS + "/libSDL3.a") || #exists(SDL_STATIC_LIBS + "/sdl3_static.lib"))Whether this build links SDL statically
constant, only in builds with #+build linux, darwin, windows. Source: sdl_static.odin:38
