Continue work on hot-reload functionality

This commit is contained in:
Riyyi
2026-09-06 18:06:12 +02:00
parent aea791fe2b
commit 6edcce16c6
3 changed files with 151 additions and 29 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ import "core:time"
import "core:fmt"
@(export)
hello :: proc() {
init :: proc() {
fmt.println("hello from .dll!")
}
+138
View File
@@ -0,0 +1,138 @@
package hot_reload
import "core:dynlib"
import "core:fmt"
import "core:os"
when ODIN_OS == .Windows {
LIB_EXT :: ".dll"
} else when ODIN_OS == .Darwin {
LIB_EXT :: ".dylib"
} else {
LIB_EXT :: ".so"
}
GAME_LIB_DIR :: "build/"
GAME_LIB_PATH :: GAME_LIB_DIR + "game" + LIB_EXT
// -----------------------------------------
Hot_Reload :: struct {
loaded_libs: [dynamic]Game_API,
}
Hot_Reload_Error :: enum u8 {
None = 0,
Load_Game_Lib_Failed = 1,
Unimplemented = 127,
Okay = None,
}
Error :: union #shared_nil {
Hot_Reload_Error,
}
Game_API :: struct {
lib: dynlib.Library,
init: proc(),
update: proc(),
render: proc(),
destroy: proc(),
should_close: proc() -> bool,
api_version: int, // iteration of the game lib
}
// -----------------------------------------
hot_reload_init :: proc() -> (state: Hot_Reload, error: Error) {
api: Game_API
api.api_version = -1
api_ok := load_game_lib(&api)
if !api_ok {
fmt.println("Failed to load Game API")
return {}, .Load_Game_Lib_Failed
}
state.loaded_libs = make([dynamic]Game_API)
append(&state.loaded_libs, api)
return state, nil
}
hot_reload_destroy :: proc(state: ^Hot_Reload) {
for &api in state.loaded_libs {
unload_game_lib(&api)
}
delete(state.loaded_libs)
}
// -----------------------------------------
active_lib :: proc(hr: ^Hot_Reload) -> ^Game_API {
return &hr.loaded_libs[len(hr.loaded_libs) - 1]
}
should_close :: proc(hr: ^Hot_Reload) -> bool {
api := active_lib(hr)
if api.lib == nil do return true
return api.should_close()
}
copy_lib :: proc(to: string) -> bool {
copy_err := os.copy_file(to, GAME_LIB_PATH)
if copy_err != nil {
fmt.printfln(
"error: Failed to copy " + GAME_LIB_PATH + " to {0}: %v",
to,
copy_err,
)
return false
}
return true
}
// Load game lib symbols
load_game_lib :: proc(api: ^Game_API) -> bool {
// Load next iteration
api.api_version += 1
lib_name := fmt.tprintf(
GAME_LIB_DIR + "game_{0}" + LIB_EXT,
api.api_version,
)
copy_lib(lib_name) or_return
// Match the names of the fields in Game_API to symbols in the game DLL
_, ok := dynlib.initialize_symbols(api, GAME_LIB_PATH, "", "lib")
if !ok {
fmt.printfln("Failed initializing symbols: {0}", dynlib.last_error())
}
return ok
}
// Unload game lib, used during full restarts
unload_game_lib :: proc(api: ^Game_API) {
if api.lib != nil {
if !dynlib.unload_library(api.lib) {
fmt.eprintfln(
"error: Failed unloading lib: {0}",
dynlib.last_error(),
)
}
}
if os.remove(
fmt.tprintf(GAME_LIB_DIR + "game_{0}" + LIB_EXT, api.api_version),
) !=
nil {
fmt.printfln(
"error: Failed to remove {0}game_{1}" + LIB_EXT + " copy",
GAME_LIB_DIR,
api.api_version,
)
}
}
+12 -28
View File
@@ -1,37 +1,35 @@
package sindri
import "core:dynlib"
import "core:fmt"
import "core:time"
import "sindri:hot_reload"
VERSION :: #config(VERSION, "dev")
// -----------------------------------------
instance_ready: bool
Game_API :: struct {
__handle: dynlib.Library,
hello: proc(),
should_close: proc() -> bool,
}
main :: proc() {
fmt.println("hello world!")
api: Game_API
load_game_api(&api)
api.hello()
// Hot reload development functionality
hr, err := hot_reload.hot_reload_init()
defer hot_reload.hot_reload_destroy(&hr)
// Initialize window
api := hot_reload.active_lib(&hr)
api.init()
// Initialize Window
os_init()
defer os_destroy()
// Initialize GPU resources
instance_init()
defer instance_destroy()
gt: f32
for !os_should_close() && !api.should_close() {
for !os_should_close() && !hot_reload.should_close(&hr) {
start := time.tick_now()
os_poll_events()
@@ -39,18 +37,4 @@ main :: proc() {
gt = f32(time.duration_seconds(time.tick_since(start)))
}
// Cleanup GPU resources
instance_destroy()
// Cleanup window
os_destroy()
}
load_game_api :: proc(api: ^Game_API) {
// Match the names of the fields in Game_API to symbols in the game DLL
_, ok := dynlib.initialize_symbols(api, "build/game.dylib")
if !ok {
fmt.printfln("Failed initializing symbols: {0}", dynlib.last_error())
}
}