150 lines
3.7 KiB
Odin
150 lines
3.7 KiB
Odin
#+build !js
|
|
|
|
package assets
|
|
|
|
import "base:runtime"
|
|
import "common:name"
|
|
import "core:log"
|
|
import "core:sync/chan"
|
|
import "core:thread"
|
|
import "libs:physfs"
|
|
import "libs:tracy"
|
|
|
|
ASSET_WATCHER_OPS_BUFFER :: 256
|
|
|
|
// Add asset to watch list
|
|
Asset_Watcher_Op_Add :: struct {
|
|
type: Asset_Type,
|
|
path: name.Name,
|
|
modtime: physfs.sint64,
|
|
}
|
|
// Remove asset from watch list
|
|
Asset_Watcher_Op_Remove :: struct {
|
|
type: Asset_Type,
|
|
path: name.Name,
|
|
}
|
|
|
|
Asset_Watcher_Op :: union #no_nil {
|
|
Asset_Watcher_Op_Add,
|
|
Asset_Watcher_Op_Remove,
|
|
}
|
|
|
|
Asset_Modtime_Watcher :: struct {
|
|
ops: chan.Chan(Asset_Watcher_Op),
|
|
modified_assets: chan.Chan(Watcher_Asset),
|
|
loaded_assets: [dynamic]Watcher_Asset,
|
|
thread: ^thread.Thread,
|
|
}
|
|
|
|
modtime_watcher_init :: proc(watcher: ^Asset_Modtime_Watcher, allocator := context.allocator) {
|
|
err: runtime.Allocator_Error
|
|
watcher.ops, err = chan.create_buffered(
|
|
chan.Chan(Asset_Watcher_Op),
|
|
ASSET_WATCHER_OPS_BUFFER,
|
|
allocator,
|
|
)
|
|
assert(err == nil)
|
|
watcher.modified_assets, err = chan.create_buffered(
|
|
chan.Chan(Watcher_Asset),
|
|
ASSET_WATCHER_OPS_BUFFER,
|
|
allocator,
|
|
)
|
|
watcher.loaded_assets = make_dynamic_array([dynamic]Watcher_Asset, allocator)
|
|
|
|
watcher.thread = thread.create(modtime_watcher_thread_proc)
|
|
watcher.thread.data = watcher
|
|
watcher_context := runtime.default_context()
|
|
watcher_context.logger = context.logger
|
|
watcher_context.allocator = context.allocator
|
|
watcher.thread.init_context = watcher_context
|
|
thread.start(watcher.thread)
|
|
}
|
|
|
|
modtime_watcher_deinit :: proc(watcher: ^Asset_Modtime_Watcher) {
|
|
if !chan.is_closed(&watcher.ops) {
|
|
chan.close(&watcher.ops)
|
|
thread.join(watcher.thread)
|
|
thread.destroy(watcher.thread)
|
|
watcher.thread = nil
|
|
}
|
|
|
|
chan.destroy(&watcher.ops)
|
|
chan.close(&watcher.modified_assets)
|
|
chan.destroy(&watcher.modified_assets)
|
|
delete(watcher.loaded_assets)
|
|
}
|
|
|
|
modtime_watcher_next :: proc(watcher: ^Asset_Modtime_Watcher) -> (asset: Watcher_Asset, ok: bool) {
|
|
return chan.try_recv(watcher.modified_assets)
|
|
}
|
|
|
|
@(private = "file")
|
|
modtime_watcher_thread_proc :: proc(t: ^thread.Thread) {
|
|
tracy.SetThreadName("Asset Watcher")
|
|
|
|
watcher := cast(^Asset_Modtime_Watcher)t.data
|
|
|
|
log.debugf("watcher thread")
|
|
|
|
for !chan.is_closed(&watcher.ops) {
|
|
for recv_op in chan.try_recv(watcher.ops) {
|
|
switch op in recv_op {
|
|
case Asset_Watcher_Op_Add:
|
|
log.debugf("add [{}] {}", op.type, name.to_string(op.path))
|
|
append(
|
|
&watcher.loaded_assets,
|
|
Watcher_Asset{type = op.type, path = op.path, modtime = op.modtime},
|
|
)
|
|
case Asset_Watcher_Op_Remove:
|
|
log.debugf("remove [{}] {}", op.type, name.to_string(op.path))
|
|
i := 0
|
|
for i < len(watcher.loaded_assets) {
|
|
if op.path == watcher.loaded_assets[i].path &&
|
|
op.type == watcher.loaded_assets[i].type {
|
|
unordered_remove(&watcher.loaded_assets, i)
|
|
} else {
|
|
i += 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for &asset in watcher.loaded_assets {
|
|
modtime := physfs.getLastModTime(name.to_cstring(asset.path))
|
|
|
|
if asset.modtime != modtime {
|
|
log.debugf("change [{}] {}", asset.type, name.to_string(asset.path))
|
|
ok := chan.send(
|
|
watcher.modified_assets,
|
|
Watcher_Asset{type = asset.type, path = asset.path, modtime = modtime},
|
|
)
|
|
assert(ok)
|
|
}
|
|
asset.modtime = modtime
|
|
}
|
|
|
|
// To avoid busy loop just in case
|
|
thread.yield()
|
|
}
|
|
}
|
|
|
|
modtime_watcher_add_asset :: proc(
|
|
watcher: ^Asset_Modtime_Watcher,
|
|
type: Asset_Type,
|
|
path: name.Name,
|
|
modtime: physfs.sint64,
|
|
) -> bool {
|
|
return chan.send(
|
|
watcher.ops,
|
|
Asset_Watcher_Op_Add{type = type, path = path, modtime = modtime},
|
|
)
|
|
}
|
|
|
|
modtime_watcher_remove_asset :: proc(
|
|
watcher: ^Asset_Modtime_Watcher,
|
|
type: Asset_Type,
|
|
path: name.Name,
|
|
) -> bool {
|
|
return chan.send(watcher.ops, Asset_Watcher_Op_Remove{type = type, path = path})
|
|
}
|