package djup import "base:runtime" import "core:log" #assert(#config(DJUP_CLIENT, false) || #config(DJUP_SERVER, false)) #assert(!(#config(DJUP_CLIENT, false) && #config(DJUP_SERVER, false))) SimulationContext :: struct { snapshots: []Snapshot, ctx: runtime.Context, assert_func: AssertFunc, } Metadata :: Components entity_id :: distinct i32 Snapshot :: struct { active: bool, tick: u32, metadata: []Metadata, balls: []Ball, player_inputs: []PlayerInput, ctx: ^runtime.Context, } @(export = true) sim_create :: proc "c" (snapshots, entities: i32) -> ^SimulationContext { if snapshots <= 0 || entities <= 0 { return nil } context = runtime.default_context() context.logger = log.create_console_logger() ctx := new(SimulationContext) ctx.ctx = context ctx.ctx.user_ptr = ctx snapshots, err := make([]Snapshot, snapshots) if err != .None { free(ctx) return nil } ctx.snapshots = snapshots error := false for &s in ctx.snapshots { s.ctx = &ctx.ctx if !snapshot_init(&s, entities) { error = true } } if error { for &s in ctx.snapshots { snapshot_deinit(&s) } delete(ctx.snapshots) free(ctx) return nil } log.destroy_console_logger(ctx.ctx.logger) ctx.ctx.logger = log.nil_logger() return ctx } @(export = true) sim_free :: proc "c" (ctx: ^SimulationContext) { if ctx == nil { return } context = ctx.ctx for &s in ctx.snapshots { snapshot_deinit(&s) } delete(ctx.snapshots) free(ctx) } snapshot_init :: proc(s: ^Snapshot, entities: i32) -> bool { make_components :: proc($T: typeid, len: i32, error: ^bool) -> []T { res, err := make([]T, len) if err != .None { log.errorf("Failed to allocate component array: %s", err) error^ = true return nil } return res } error := false components: Components = ~{} for c in components { switch c { case .Exists: s.metadata = make_components(Metadata, entities, &error) case .Ball: s.balls = make_components(Ball, entities, &error) case .PlayerInput: s.player_inputs = make_components(PlayerInput, entities, &error) } } return !error } snapshot_deinit :: proc(s: ^Snapshot) -> bool { delete_components :: proc($T: typeid, slice: []T, error: ^bool) { if slice == nil { return } err := delete(slice) if err != .None && error != nil { log.errorf("Failed to delete component array: %s", err) error^ = true } } error := false components: Components = ~{} for c in components { switch c { case .Exists: delete_components(Metadata, s.metadata, &error) case .Ball: delete_components(Ball, s.balls, &error) case .PlayerInput: delete_components(PlayerInput, s.player_inputs, &error) } } return !error } @(export = true) sim_get_snapshot :: proc "c" (ctx: ^SimulationContext) -> ^Snapshot { if ctx == nil { return nil } for &s in ctx.snapshots { if s.active { continue } s.active = true return &s } return nil } @(export = true) sim_return_snapshot :: proc "c" (ctx: ^SimulationContext, s: ^Snapshot) { if ctx == nil || s == nil { return } s.active = false } @(export = true) snapshot_create_entity :: proc "c" (s: ^Snapshot) -> entity_id { if s == nil { return -1 } i := 0 for i < len(s.metadata) { if .Exists not_in s.metadata[i] { s.metadata[i] = {.Exists} return entity_id(i) } } return -1 } ComponentAddError :: enum u32 { None, InvalidArg, InvalidId, EntityNotFound, } @(export = true) snapshot_add_component :: proc "c" ( s: ^Snapshot, id: entity_id, component: Component, init: rawptr, ) -> ComponentAddError { if s == nil { return .InvalidArg } if (id < 0 && id >= entity_id(len(s.metadata))) { return .InvalidId } if .Exists not_in s.metadata[id] { return .EntityNotFound } add :: proc "contextless" (s: ^Snapshot, id: entity_id, c: Components, slice: []$T, init: ^T) { s.metadata[id] += c if init != nil { slice[id] = init^ } } switch component { case .Exists: add(s, id, {.Exists}, s.metadata, nil) case .Ball: add(s, id, {.Ball}, s.balls, cast(^Ball)init) case .PlayerInput: add(s, id, {.PlayerInput}, s.player_inputs, cast(^PlayerInput)init) } return .None } ComponentRemoveError :: enum u32 { None, InvalidArg, InvalidId, EntityNotFound, } @(export = true) snapshot_remove_components :: proc "c" ( s: ^Snapshot, id: entity_id, components: Components, ) -> ComponentRemoveError { if s == nil { return .InvalidArg } if (id < 0 && id >= entity_id(len(s.metadata))) { return .InvalidId } if .Exists not_in s.metadata[id] { return .EntityNotFound } for c in components { switch c { case .Exists: s.metadata[id] = {} case .Ball: s.balls[id] = Ball{} case .PlayerInput: s.player_inputs[id] = PlayerInput{} } } return .None } @(export = true) snapshot_reset_entity :: proc "c" (s: ^Snapshot, id: entity_id) -> ComponentRemoveError { return snapshot_remove_components(s, id, ~{}) }