move GameLinking struct to separate file
This commit is contained in:
parent
483daaf91e
commit
e995b55ccc
@ -15,11 +15,13 @@ const DataSegment = extern struct {
|
||||
|
||||
const Font = extern struct {
|
||||
asset_id: Lib.Asset.Id,
|
||||
info: Lib.Font.Info,
|
||||
data: DataSegment
|
||||
};
|
||||
|
||||
const Sound = extern struct {
|
||||
asset_id: Lib.Asset.Id,
|
||||
info: Lib.Sound.Info,
|
||||
data: DataSegment
|
||||
};
|
||||
|
||||
@ -82,11 +84,13 @@ pub fn appendSound(
|
||||
self: *AssetBundle,
|
||||
gpa: std.mem.Allocator,
|
||||
id: Lib.Asset.Id,
|
||||
data: []const u8
|
||||
data: []const u8,
|
||||
info: Lib.Sound.Info,
|
||||
) !void {
|
||||
try self.sounds.append(gpa, Sound{
|
||||
.asset_id = id,
|
||||
.data = try self.insertData(gpa, data)
|
||||
.data = try self.insertData(gpa, data),
|
||||
.info = info
|
||||
});
|
||||
}
|
||||
|
||||
@ -94,11 +98,13 @@ pub fn appendFont(
|
||||
self: *AssetBundle,
|
||||
gpa: std.mem.Allocator,
|
||||
id: Lib.Asset.Id,
|
||||
data: []const u8
|
||||
data: []const u8,
|
||||
info: Lib.Font.Info,
|
||||
) !void {
|
||||
try self.fonts.append(gpa, Font{
|
||||
.asset_id = id,
|
||||
.data = try self.insertData(gpa, data)
|
||||
.data = try self.insertData(gpa, data),
|
||||
.info = info
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -371,21 +371,12 @@ pub fn removeSound(self: *AudioSystem, id: Lib.Sound.Id) void {
|
||||
}
|
||||
|
||||
pub const DataOptions = struct {
|
||||
const PlaybackStyle = enum {
|
||||
stream,
|
||||
decode_once,
|
||||
// If the decoded size is less than `stream_threshold`, then .decode_once will by default be used.
|
||||
const stream_threshold = 10 * Math.bytes_per_mib;
|
||||
|
||||
// If the decoded size is less than `stream_threshold`, then .decode_once will by default be used.
|
||||
const stream_threshold = 10 * Math.bytes_per_mib;
|
||||
};
|
||||
|
||||
const Format = enum {
|
||||
vorbis
|
||||
};
|
||||
|
||||
format: Format,
|
||||
format: Lib.Sound.Format,
|
||||
data: []const u8,
|
||||
playback_style: ?PlaybackStyle = null,
|
||||
playback_style: ?Lib.Sound.PlaybackStyle = null,
|
||||
};
|
||||
|
||||
pub fn setSoundData(self: *AudioSystem, id: Lib.Sound.Id, opts: DataOptions) !void {
|
||||
@ -394,12 +385,11 @@ pub fn setSoundData(self: *AudioSystem, id: Lib.Sound.Id, opts: DataOptions) !vo
|
||||
assert(opts.format == .vorbis);
|
||||
const vorbis = try Sound.Vorbis.init(self.gpa, opts.data, self.temp_vorbis_alloc_buffer);
|
||||
|
||||
const PlaybackStyle = DataOptions.PlaybackStyle;
|
||||
const PlaybackStyle = Lib.Sound.PlaybackStyle;
|
||||
var playback_style: PlaybackStyle = undefined;
|
||||
if (opts.playback_style == null) {
|
||||
const stream_threshold = PlaybackStyle.stream_threshold;
|
||||
const decoded_size = vorbis.getChannels() * vorbis.getSampleCount() * @sizeOf(f32);
|
||||
const default_syle: PlaybackStyle = if (decoded_size < stream_threshold) .decode_once else .stream;
|
||||
const default_syle: PlaybackStyle = if (decoded_size < DataOptions.stream_threshold) .decode_once else .stream;
|
||||
|
||||
playback_style = default_syle;
|
||||
} else {
|
||||
|
||||
266
engine/src/engine/game_linking.zig
Normal file
266
engine/src/engine/game_linking.zig
Normal file
@ -0,0 +1,266 @@
|
||||
const std = @import("std");
|
||||
const log = std.log.scoped(.engine);
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const Lib = @import("engine_lib");
|
||||
const GameCallbacks = Lib.Callbacks;
|
||||
const build_options = Lib.build_options;
|
||||
|
||||
const GameLinking = @This();
|
||||
|
||||
kind: union(enum) {
|
||||
static,
|
||||
dynamic: KindDynamic,
|
||||
},
|
||||
callbacks: GameCallbacks,
|
||||
|
||||
const KindDynamic = struct {
|
||||
fan_fd: std.os.linux.fd_t,
|
||||
path: []const u8,
|
||||
dir_path: [:0]const u8,
|
||||
lib: std.DynLib,
|
||||
last_event_at: ?std.time.Instant,
|
||||
ignore_next_event: bool,
|
||||
reload_index: u32,
|
||||
|
||||
// TODO: Make this configurable
|
||||
const event_debounce_ns = std.time.ns_per_ms * 150;
|
||||
|
||||
fn ensureLookup(lib: *std.DynLib, comptime T: type, name: [:0]const u8) !T {
|
||||
return lib.lookup(T, name) orelse return error.MissingSymbol;
|
||||
}
|
||||
|
||||
fn getCallbacksFromLibrary(lib: *std.DynLib) !GameCallbacks {
|
||||
return GameCallbacks{
|
||||
.init = try ensureLookup(lib, GameCallbacks.InitFn, "init"),
|
||||
.tick = try ensureLookup(lib, GameCallbacks.TickFn, "tick"),
|
||||
.deinit = try ensureLookup(lib, GameCallbacks.DeinitFn, "deinit"),
|
||||
.loadAssets = if (build_options.load_assets_at_runtime) try ensureLookup(lib, GameCallbacks.LoadAssetsFn, "loadAssets") else {},
|
||||
.debug = if (build_options.has_imgui) try ensureLookup(lib, GameCallbacks.DebugFn, "debug") else {},
|
||||
};
|
||||
}
|
||||
|
||||
fn init(gpa: std.mem.Allocator, path: []const u8) !KindDynamic {
|
||||
const dir_path_z = try gpa.dupeZ(u8, std.fs.path.dirname(path) orelse ".");
|
||||
errdefer gpa.free(dir_path_z);
|
||||
|
||||
const path_dupe = try gpa.dupe(u8, path);
|
||||
errdefer gpa.free(path_dupe);
|
||||
|
||||
const fan_fd: std.c.fd_t = @bitCast(@as(u32, @truncate(
|
||||
std.os.linux.fanotify_init(.{
|
||||
.REPORT_FID = true,
|
||||
.REPORT_DIR_FID = true,
|
||||
.REPORT_NAME = true,
|
||||
.NONBLOCK = true
|
||||
}, @intFromEnum(std.posix.ACCMODE.RDONLY))
|
||||
)));
|
||||
if (fan_fd == -1) {
|
||||
return error.fanotify_init;
|
||||
}
|
||||
|
||||
const mark_err = std.os.linux.fanotify_mark(
|
||||
@intCast(fan_fd),
|
||||
.{ .ADD = true, .ONLYDIR = true },
|
||||
.{
|
||||
.CLOSE_WRITE = true,
|
||||
.CLOSE_NOWRITE = true,
|
||||
.CREATE = true,
|
||||
.MOVED_TO = true,
|
||||
|
||||
.EVENT_ON_CHILD = true,
|
||||
.ONDIR = true,
|
||||
},
|
||||
@intCast(std.c.AT.FDCWD),
|
||||
dir_path_z
|
||||
);
|
||||
assert(mark_err == 0);
|
||||
|
||||
const lib = try std.DynLib.open(path);
|
||||
|
||||
return KindDynamic{
|
||||
.fan_fd = fan_fd,
|
||||
.dir_path = dir_path_z,
|
||||
.path = path_dupe,
|
||||
.lib = lib,
|
||||
.last_event_at = null,
|
||||
.reload_index = 0,
|
||||
.ignore_next_event = false
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: *KindDynamic, gpa: std.mem.Allocator) void {
|
||||
self.lib.close();
|
||||
gpa.free(self.dir_path);
|
||||
gpa.free(self.path);
|
||||
std.posix.close(self.fan_fd);
|
||||
}
|
||||
|
||||
fn getCallbacks(self: *KindDynamic) !GameCallbacks {
|
||||
return getCallbacksFromLibrary(&self.lib);
|
||||
}
|
||||
|
||||
fn checkForChanges(self: *KindDynamic) !?GameCallbacks {
|
||||
const fanotify = std.os.linux.fanotify;
|
||||
const M = fanotify.event_metadata;
|
||||
const fan_fd = self.fan_fd;
|
||||
|
||||
var need_to_reload = false;
|
||||
|
||||
// TODO: clean this up.
|
||||
// Reuse the same watching logic for assets
|
||||
var events_buf: [256 + 4096]u8 = undefined;
|
||||
while (true) {
|
||||
var len = std.posix.read(fan_fd, &events_buf) catch |err| switch (err) {
|
||||
error.WouldBlock => break,
|
||||
else => |e| return e,
|
||||
};
|
||||
var meta: [*]align(1) M = @ptrCast(&events_buf);
|
||||
|
||||
while (len >= @sizeOf(M) and meta[0].event_len >= @sizeOf(M) and meta[0].event_len <= len) : ({
|
||||
len -= meta[0].event_len;
|
||||
meta = @ptrCast(@as([*]u8, @ptrCast(meta)) + meta[0].event_len);
|
||||
}) {
|
||||
assert(meta[0].vers == M.VERSION);
|
||||
if (meta[0].mask.Q_OVERFLOW) {
|
||||
need_to_reload = true;
|
||||
// TODO:
|
||||
// std.log.warn("file system watch queue overflowed; falling back to fstat", .{});
|
||||
break;
|
||||
}
|
||||
|
||||
var is_lib_event = false;
|
||||
const fid: *align(1) fanotify.event_info_fid = @ptrCast(meta + 1);
|
||||
switch (fid.hdr.info_type) {
|
||||
.DFID_NAME => {
|
||||
const file_handle: *align(1) std.os.linux.file_handle = @ptrCast(&fid.handle);
|
||||
const file_name_z: [*:0]u8 = @ptrCast((&file_handle.f_handle).ptr + file_handle.handle_bytes);
|
||||
const file_name = std.mem.span(file_name_z);
|
||||
|
||||
const lib_name = std.fs.path.basename(self.path);
|
||||
is_lib_event = std.mem.eql(u8,file_name, lib_name);
|
||||
},
|
||||
else => |t| log.warn("unexpected fanotify event '{s}'", .{@tagName(t)}),
|
||||
}
|
||||
|
||||
if (is_lib_event) {
|
||||
need_to_reload = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (need_to_reload) {
|
||||
if (self.ignore_next_event) {
|
||||
self.ignore_next_event = false;
|
||||
} else {
|
||||
self.last_event_at = try std.time.Instant.now();
|
||||
}
|
||||
}
|
||||
|
||||
if (self.last_event_at) |last_event_at| {
|
||||
const now = try std.time.Instant.now();
|
||||
const time_passed = now.since(last_event_at);
|
||||
if (time_passed > event_debounce_ns) {
|
||||
self.last_event_at = null;
|
||||
|
||||
var tmp_dir = try std.fs.openDirAbsolute("/tmp", .{});
|
||||
defer tmp_dir.close();
|
||||
|
||||
var new_filename_buffer: [std.fs.max_name_bytes]u8 = undefined;
|
||||
const tmp_filename = try std.fmt.bufPrint(
|
||||
&new_filename_buffer,
|
||||
"{s}_{}.so",
|
||||
.{std.fs.path.stem(self.path), self.reload_index}
|
||||
);
|
||||
|
||||
try std.fs.Dir.copyFile(
|
||||
std.fs.cwd(), self.path,
|
||||
tmp_dir, tmp_filename,
|
||||
.{}
|
||||
);
|
||||
|
||||
var tmp_path_buffer: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const tmp_path = try std.fmt.bufPrint(
|
||||
&tmp_path_buffer,
|
||||
"/tmp/{s}",
|
||||
.{tmp_filename}
|
||||
);
|
||||
|
||||
var new_lib = try std.DynLib.open(tmp_path);
|
||||
const new_callbacks = try getCallbacksFromLibrary(&new_lib);
|
||||
|
||||
try tmp_dir.deleteFile(tmp_filename);
|
||||
|
||||
log.debug("Reload game code", .{});
|
||||
self.lib.close();
|
||||
self.lib = new_lib;
|
||||
self.reload_index += 1;
|
||||
self.ignore_next_event = true;
|
||||
|
||||
return new_callbacks;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const KindStatic = struct {
|
||||
fn getCallbacks() GameCallbacks {
|
||||
const game = @import("game");
|
||||
return GameCallbacks{
|
||||
.init = game.init,
|
||||
.deinit = game.deinit,
|
||||
.tick = game.tick,
|
||||
.loadAssets = if (build_options.load_assets_at_runtime) game.loadAssets else {},
|
||||
.debug = if (build_options.has_imgui) game.debug else {},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn initDynamic(gpa: std.mem.Allocator, path: []const u8) !GameLinking {
|
||||
if (!build_options.hot_reload) {
|
||||
return error.NotSupported;
|
||||
}
|
||||
|
||||
var dynamic = try KindDynamic.init(gpa, path);
|
||||
errdefer dynamic.deinit(gpa);
|
||||
|
||||
const callbacks = try dynamic.getCallbacks();
|
||||
|
||||
return GameLinking{
|
||||
.kind = .{ .dynamic = dynamic },
|
||||
.callbacks = callbacks
|
||||
};
|
||||
}
|
||||
|
||||
pub fn initStatic() !GameLinking {
|
||||
if (!build_options.statically_linked) {
|
||||
return error.NotStaticallyLinked;
|
||||
}
|
||||
|
||||
return GameLinking{
|
||||
.kind = .static,
|
||||
.callbacks = KindStatic.getCallbacks()
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *GameLinking, gpa: std.mem.Allocator) void {
|
||||
if (build_options.hot_reload and self.kind == .dynamic) {
|
||||
self.kind.dynamic.deinit(gpa);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check(self: *GameLinking) !void {
|
||||
if (self.kind == .static) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!build_options.hot_reload) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (try self.kind.dynamic.checkForChanges()) |new_callbacks| {
|
||||
self.callbacks = new_callbacks;
|
||||
}
|
||||
}
|
||||
@ -28,233 +28,7 @@ pub const Vec2 = Math.Vec2;
|
||||
const rgb = Math.rgb;
|
||||
const Vec4 = Math.Vec4;
|
||||
|
||||
const GameLinking = struct {
|
||||
kind: union(enum) {
|
||||
static,
|
||||
dynamic: struct {
|
||||
fan_fd: std.os.linux.fd_t,
|
||||
path: []const u8,
|
||||
dir_path: [:0]const u8,
|
||||
lib: std.DynLib,
|
||||
last_event_at: ?std.time.Instant,
|
||||
ignore_next_event: bool,
|
||||
reload_index: u32
|
||||
},
|
||||
},
|
||||
callbacks: GameCallbacks,
|
||||
|
||||
// TODO: Make this configurable
|
||||
const event_debounce_ns = std.time.ns_per_ms * 150;
|
||||
|
||||
pub fn initDynamic(gpa: std.mem.Allocator, path: []const u8) !GameLinking {
|
||||
if (!build_options.hot_reload) {
|
||||
return error.NotSupported;
|
||||
}
|
||||
|
||||
const dir_path_z = try gpa.dupeZ(u8, std.fs.path.dirname(path) orelse ".");
|
||||
errdefer gpa.free(dir_path_z);
|
||||
|
||||
const path_dupe = try gpa.dupe(u8, path);
|
||||
errdefer gpa.free(path_dupe);
|
||||
|
||||
const fan_fd: std.c.fd_t = @bitCast(@as(u32, @truncate(
|
||||
std.os.linux.fanotify_init(.{
|
||||
.REPORT_FID = true,
|
||||
.REPORT_DIR_FID = true,
|
||||
.REPORT_NAME = true,
|
||||
.NONBLOCK = true
|
||||
}, @intFromEnum(std.posix.ACCMODE.RDONLY))
|
||||
)));
|
||||
if (fan_fd == -1) {
|
||||
return error.fanotify_init;
|
||||
}
|
||||
|
||||
const mark_err = std.os.linux.fanotify_mark(
|
||||
@intCast(fan_fd),
|
||||
.{ .ADD = true, .ONLYDIR = true },
|
||||
.{
|
||||
.CLOSE_WRITE = true,
|
||||
.CLOSE_NOWRITE = true,
|
||||
.CREATE = true,
|
||||
.MOVED_TO = true,
|
||||
|
||||
.EVENT_ON_CHILD = true,
|
||||
.ONDIR = true,
|
||||
},
|
||||
@intCast(std.c.AT.FDCWD),
|
||||
dir_path_z
|
||||
);
|
||||
assert(mark_err == 0);
|
||||
|
||||
var lib = try std.DynLib.open(path);
|
||||
const callbacks = try getCallbacksFromLibrary(&lib);
|
||||
|
||||
return GameLinking{
|
||||
.kind = .{
|
||||
.dynamic = .{
|
||||
.fan_fd = fan_fd,
|
||||
.dir_path = dir_path_z,
|
||||
.path = path_dupe,
|
||||
.lib = lib,
|
||||
.last_event_at = null,
|
||||
.reload_index = 0,
|
||||
.ignore_next_event = false
|
||||
}
|
||||
},
|
||||
.callbacks = callbacks
|
||||
};
|
||||
}
|
||||
|
||||
pub fn initStatic() !GameLinking {
|
||||
if (!build_options.statically_linked) {
|
||||
return error.NotStaticallyLinked;
|
||||
}
|
||||
|
||||
const game = @import("game");
|
||||
return GameLinking{
|
||||
.kind = .static,
|
||||
.callbacks = GameCallbacks{
|
||||
.init = game.init,
|
||||
.deinit = game.deinit,
|
||||
.tick = game.tick,
|
||||
.loadAssets = if (build_options.load_assets_at_runtime) game.loadAssets else {},
|
||||
.debug = if (build_options.has_imgui) game.debug else {},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *GameLinking, gpa: std.mem.Allocator) void {
|
||||
if (build_options.hot_reload and self.kind == .dynamic) {
|
||||
self.kind.dynamic.lib.close();
|
||||
gpa.free(self.kind.dynamic.dir_path);
|
||||
gpa.free(self.kind.dynamic.path);
|
||||
std.posix.close(self.kind.dynamic.fan_fd);
|
||||
}
|
||||
}
|
||||
|
||||
fn ensureLookup(lib: *std.DynLib, comptime T: type, name: [:0]const u8) !T {
|
||||
return lib.lookup(T, name) orelse return error.MissingSymbol;
|
||||
}
|
||||
|
||||
fn getCallbacksFromLibrary(lib: *std.DynLib) !GameCallbacks {
|
||||
return GameCallbacks{
|
||||
.init = try ensureLookup(lib, GameCallbacks.InitFn, "init"),
|
||||
.tick = try ensureLookup(lib, GameCallbacks.TickFn, "tick"),
|
||||
.deinit = try ensureLookup(lib, GameCallbacks.DeinitFn, "deinit"),
|
||||
.loadAssets = if (build_options.load_assets_at_runtime) try ensureLookup(lib, GameCallbacks.LoadAssetsFn, "loadAssets") else {},
|
||||
.debug = if (build_options.has_imgui) try ensureLookup(lib, GameCallbacks.DebugFn, "debug") else {},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn check(self: *GameLinking) !void {
|
||||
if (self.kind == .static) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!build_options.hot_reload) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fanotify = std.os.linux.fanotify;
|
||||
const M = fanotify.event_metadata;
|
||||
const fan_fd = self.kind.dynamic.fan_fd;
|
||||
|
||||
var need_to_reload = false;
|
||||
|
||||
// TODO: clean this up.
|
||||
// Reuse the same watching logic for assets
|
||||
var events_buf: [256 + 4096]u8 = undefined;
|
||||
while (true) {
|
||||
var len = std.posix.read(fan_fd, &events_buf) catch |err| switch (err) {
|
||||
error.WouldBlock => break,
|
||||
else => |e| return e,
|
||||
};
|
||||
var meta: [*]align(1) M = @ptrCast(&events_buf);
|
||||
|
||||
while (len >= @sizeOf(M) and meta[0].event_len >= @sizeOf(M) and meta[0].event_len <= len) : ({
|
||||
len -= meta[0].event_len;
|
||||
meta = @ptrCast(@as([*]u8, @ptrCast(meta)) + meta[0].event_len);
|
||||
}) {
|
||||
assert(meta[0].vers == M.VERSION);
|
||||
if (meta[0].mask.Q_OVERFLOW) {
|
||||
need_to_reload = true;
|
||||
// TODO:
|
||||
// std.log.warn("file system watch queue overflowed; falling back to fstat", .{});
|
||||
break;
|
||||
}
|
||||
|
||||
var is_lib_event = false;
|
||||
const fid: *align(1) fanotify.event_info_fid = @ptrCast(meta + 1);
|
||||
switch (fid.hdr.info_type) {
|
||||
.DFID_NAME => {
|
||||
const file_handle: *align(1) std.os.linux.file_handle = @ptrCast(&fid.handle);
|
||||
const file_name_z: [*:0]u8 = @ptrCast((&file_handle.f_handle).ptr + file_handle.handle_bytes);
|
||||
const file_name = std.mem.span(file_name_z);
|
||||
|
||||
const lib_name = std.fs.path.basename(self.kind.dynamic.path);
|
||||
is_lib_event = std.mem.eql(u8,file_name, lib_name);
|
||||
},
|
||||
else => |t| log.warn("unexpected fanotify event '{s}'", .{@tagName(t)}),
|
||||
}
|
||||
|
||||
if (is_lib_event) {
|
||||
need_to_reload = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (need_to_reload) {
|
||||
if (self.kind.dynamic.ignore_next_event) {
|
||||
self.kind.dynamic.ignore_next_event = false;
|
||||
} else {
|
||||
self.kind.dynamic.last_event_at = try std.time.Instant.now();
|
||||
}
|
||||
}
|
||||
|
||||
if (self.kind.dynamic.last_event_at) |last_event_at| {
|
||||
const now = try std.time.Instant.now();
|
||||
const time_passed = now.since(last_event_at);
|
||||
if (time_passed > event_debounce_ns) {
|
||||
self.kind.dynamic.last_event_at = null;
|
||||
|
||||
var tmp_dir = try std.fs.openDirAbsolute("/tmp", .{});
|
||||
defer tmp_dir.close();
|
||||
|
||||
var new_filename_buffer: [std.fs.max_name_bytes]u8 = undefined;
|
||||
const tmp_filename = try std.fmt.bufPrint(
|
||||
&new_filename_buffer,
|
||||
"{s}_{}.so",
|
||||
.{std.fs.path.stem(self.kind.dynamic.path), self.kind.dynamic.reload_index}
|
||||
);
|
||||
|
||||
try std.fs.Dir.copyFile(
|
||||
std.fs.cwd(), self.kind.dynamic.path,
|
||||
tmp_dir, tmp_filename,
|
||||
.{}
|
||||
);
|
||||
|
||||
var tmp_path_buffer: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const tmp_path = try std.fmt.bufPrint(
|
||||
&tmp_path_buffer,
|
||||
"/tmp/{s}",
|
||||
.{tmp_filename}
|
||||
);
|
||||
|
||||
var new_lib = try std.DynLib.open(tmp_path);
|
||||
const new_callbacks = try getCallbacksFromLibrary(&new_lib);
|
||||
|
||||
try tmp_dir.deleteFile(tmp_filename);
|
||||
|
||||
log.debug("Reload game code", .{});
|
||||
self.kind.dynamic.lib.close();
|
||||
self.kind.dynamic.lib = new_lib;
|
||||
self.callbacks = new_callbacks;
|
||||
self.kind.dynamic.reload_index += 1;
|
||||
self.kind.dynamic.ignore_next_event = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const GameLinking = @import("game_linking.zig");
|
||||
|
||||
const GameState = struct {
|
||||
gpa: std.mem.Allocator,
|
||||
@ -1072,8 +846,17 @@ fn showDebugWindow(self: *Engine) !void {
|
||||
}
|
||||
}
|
||||
|
||||
const linking_label = if (self.game_linking.kind == .static) "static" else "dynamic";
|
||||
imgui.textFmt("Linking: {s}", .{ linking_label });
|
||||
if (self.game_linking.kind == .dynamic) {
|
||||
imgui.textFmt("Linking: dynamic (from {s})", .{ self.game_linking.kind.dynamic.path });
|
||||
} else {
|
||||
imgui.textFmt("Linking: static", .{ });
|
||||
}
|
||||
|
||||
if (build_options.load_assets_at_runtime) {
|
||||
imgui.textFmt("Asset loading: runtime (from {s})", .{build_options.asset_dir});
|
||||
} else {
|
||||
imgui.textFmt("Asset loading: builtin", .{});
|
||||
}
|
||||
|
||||
imgui.textFmt("Seed: 0x{x:08}", .{ self.seed });
|
||||
|
||||
|
||||
@ -115,6 +115,7 @@ pub const Loader = struct {
|
||||
image_info = .{
|
||||
.width = image.width,
|
||||
.height = image.height,
|
||||
.format = .png
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
const std = @import("std");
|
||||
|
||||
id: Id,
|
||||
info: Info,
|
||||
|
||||
pub const Info = extern struct {
|
||||
format: Format,
|
||||
};
|
||||
|
||||
pub const Format = enum(u8) {
|
||||
ttf
|
||||
};
|
||||
|
||||
pub const Id = enum(u16) {
|
||||
_,
|
||||
|
||||
@ -1,6 +1,21 @@
|
||||
const std = @import("std");
|
||||
|
||||
id: Id,
|
||||
info: Info,
|
||||
|
||||
pub const Info = extern struct {
|
||||
format: Format,
|
||||
playback_style: PlaybackStyle
|
||||
};
|
||||
|
||||
pub const PlaybackStyle = enum(u8) {
|
||||
stream,
|
||||
decode_once,
|
||||
};
|
||||
|
||||
pub const Format = enum(u8) {
|
||||
vorbis
|
||||
};
|
||||
|
||||
pub const Id = enum(u16) {
|
||||
_,
|
||||
|
||||
@ -6,13 +6,20 @@ info: Info,
|
||||
pub const Info = extern struct {
|
||||
width: u32,
|
||||
height: u32,
|
||||
format: Format,
|
||||
|
||||
pub const nil = Info{
|
||||
.width = 0,
|
||||
.height = 0
|
||||
.height = 0,
|
||||
.format = .rgba
|
||||
};
|
||||
};
|
||||
|
||||
pub const Format = enum(u8) {
|
||||
png,
|
||||
rgba
|
||||
};
|
||||
|
||||
pub const Id = enum(u16) {
|
||||
_,
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user