summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJoel Stålnacke <joel@saker.fi>2026-03-23 19:50:18 +0200
committerJoel Stålnacke <joel@saker.fi>2026-03-23 19:50:18 +0200
commit3a1f9fe6800ef5fe7bbf1c0a5976720383ba84fe (patch)
treef1218eb4085a654f6bb70671b41b4a74e646c052 /lib
parent3870982f640260822bf605e693782ce9f5d79cf6 (diff)
Rewrite native code in Odin and use ECSodin
Diffstat (limited to 'lib')
-rw-r--r--lib/.gitignore2
-rwxr-xr-xlib/build.sh29
-rw-r--r--lib/common.h1
-rw-r--r--lib/common.odin24
-rw-r--r--lib/components.odin18
-rw-r--r--lib/constants.h12
-rw-r--r--lib/log.c55
-rw-r--r--lib/log.h23
-rw-r--r--lib/log.odin116
-rw-r--r--lib/ols.json13
-rw-r--r--lib/physics.c146
-rw-r--r--lib/physics.h17
-rw-r--r--lib/player.c36
-rw-r--r--lib/player.h5
-rw-r--r--lib/simulation.odin261
-rw-r--r--lib/snapshot.c142
-rw-r--r--lib/snapshot.h43
-rw-r--r--lib/systems.odin44
-rw-r--r--lib/test.c24
-rw-r--r--lib/types.h21
-rw-r--r--lib/vec2.c68
21 files changed, 500 insertions, 600 deletions
diff --git a/lib/.gitignore b/lib/.gitignore
index 567609b..ea1472e 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -1 +1 @@
-build/
+output/
diff --git a/lib/build.sh b/lib/build.sh
index 3f8b5a6..1695dfe 100755
--- a/lib/build.sh
+++ b/lib/build.sh
@@ -5,11 +5,9 @@ fail() {
exit 1
}
-CC=cc
-CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Og -g -D_POSIX_C_SOURCE=200809L"
-LDFLAGS="-fPIC -lm"
-SOURCES="test.c vec2.c snapshot.c log.c player.c physics.c"
-output="build"
+ODIN_FLAGS="-vet -warnings-as-errors -reloc-mode=pic"
+
+output="output"
for arg ; do
case "$arg" in
@@ -19,6 +17,15 @@ for arg ; do
--output=*)
output="${arg#*=}"
;;
+ --release)
+ release=1
+ ;;
+ --client)
+ ODIN_FLAGS="$ODIN_FLAGS -define:DJUP_CLIENT=true"
+ ;;
+ --server)
+ ODIN_FLAGS="$ODIN_FLAGS -define:DJUP_SERVER=true"
+ ;;
*)
echo "usage"
;;
@@ -42,10 +49,20 @@ fi
case "$runtime" in
linux-x64)
sofile="libdjup.so"
+ target="linux_amd64"
;;
*)
fail "Unsupported runtime $runtime"
esac
+if [ ! -z "$release" ]; then
+ optimize="-o=speed"
+else
+ optimize="-o=none"
+fi
+
echo Building for runtime "$runtime ..."
-mkdir -p "$output/$runtime" && $CC $CFLAGS $SOURCES -shared $LDFLAGS -o "$output/$runtime/$sofile" && echo Done
+echo ODIN_FLAGS=$ODIN_FLAGS
+mkdir -p "$output/$runtime" && \
+ odin build . -build-mode=shared -out="$output/$runtime/$sofile" $ODIN_FLAGS $optimize && \
+ echo Done
diff --git a/lib/common.h b/lib/common.h
deleted file mode 100644
index d6d8816..0000000
--- a/lib/common.h
+++ /dev/null
@@ -1 +0,0 @@
-#define LENGTH(x) (sizeof(x) / sizeof(x[0]))
diff --git a/lib/common.odin b/lib/common.odin
new file mode 100644
index 0000000..3c6679f
--- /dev/null
+++ b/lib/common.odin
@@ -0,0 +1,24 @@
+package djup
+
+import "core:math"
+
+vec2 :: [2]f32
+
+vec2_dot :: proc(a, b: vec2) -> vec2 {
+ return vec2{a.x * b.x, a.y * b.y}
+}
+
+vec2_length :: proc(v: vec2) -> f32 {
+ if v == (vec2{0, 0}) {
+ return 0
+ }
+ return math.sqrt(math.pow(v.x, 2) + math.pow(v.y, 2))
+}
+
+vec2_normal :: proc(v: vec2) -> vec2 {
+ len := vec2_length(v)
+ if len == 0 {
+ return vec2{0, 0}
+ }
+ return v * (1 / len)
+}
diff --git a/lib/components.odin b/lib/components.odin
new file mode 100644
index 0000000..56407a3
--- /dev/null
+++ b/lib/components.odin
@@ -0,0 +1,18 @@
+package djup
+
+Component :: enum i32 {
+ Exists,
+ Ball,
+ PlayerInput,
+}
+
+Components :: bit_set[Component;i32]
+
+Ball :: struct {
+ position: vec2,
+ velocity: vec2,
+}
+
+PlayerInput :: struct {
+ direction: vec2,
+}
diff --git a/lib/constants.h b/lib/constants.h
deleted file mode 100644
index 817fa43..0000000
--- a/lib/constants.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#define WORLD_MAX_PLAYERS 5
-#define WORLD_MAX_BULLETS 5000
-
-#define PLAYER_ACCELERATION 150.0f
-#define PLAYER_DRAG 2.0f
-
-enum {
- INPUT_UP = 1 << 0,
- INPUT_DOWN = 1 << 1,
- INPUT_LEFT = 1 << 2,
- INPUT_RIGHT = 1 << 3
-};
diff --git a/lib/log.c b/lib/log.c
deleted file mode 100644
index c45f735..0000000
--- a/lib/log.c
+++ /dev/null
@@ -1,55 +0,0 @@
-#include <stdarg.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "log.h"
-
-/* Message buffer */
-#define MBUF_SIZE 512
-
-static void (*log_output)(int level, const char *source, int line, const char *msg) = NULL;
-static int min_level = LOGL_INFO;
-
-void
-dp_log_set_output(void (*output)(int, const char *, int, const char *))
-{
- log_output = output;
-}
-
-void
-dp_log_set_level(int level)
-{
- min_level = level;
-}
-
-void
-dp_log_write(int level, const char *source, int line, const char *fmt, ...)
-{
- char mbuf[MBUF_SIZE];
- char *msg;
- va_list va;
- int len;
-
- if (level < min_level)
- return;
- if (!log_output)
- return;
-
- msg = mbuf;
- va_start(va, fmt);
- len = vsnprintf(msg, MBUF_SIZE, fmt, va);
- va_end(va);
-
- if (len >= MBUF_SIZE)
- {
- msg = (char *)malloc(len + 1);
- va_start(va, fmt);
- len = vsnprintf(msg, len + 1, fmt, va);
- va_end(va);
- }
-
- log_output(level, source, line, msg);
-
- if (msg != mbuf)
- free(msg);
-}
diff --git a/lib/log.h b/lib/log.h
deleted file mode 100644
index e1cdcb8..0000000
--- a/lib/log.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#include <stdio.h>
-
-enum { LOGL_TRACE, LOGL_DEBUG, LOGL_INFO, LOGL_WARN, LOGL_ERROR, LOGL_FATAL };
-
-/*
- * log_info("Hello %s!", "World");
- * log_warn("warning");
- */
-#define log_trace(...) dp_log_write(LOGL_TRACE, __FILE__, __LINE__, __VA_ARGS__)
-#define log_debug(...) dp_log_write(LOGL_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
-#define log_info(...) dp_log_write(LOGL_INFO, __FILE__, __LINE__, __VA_ARGS__)
-#define log_warn(...) dp_log_write(LOGL_WARN, __FILE__, __LINE__, __VA_ARGS__)
-#define log_error(...) dp_log_write(LOGL_ERROR, __FILE__, __LINE__, __VA_ARGS__)
-#define log_fatal(...) dp_log_write(LOGL_FATAL, __FILE__, __LINE__, __VA_ARGS__)
-
-void dp_log_set_output(void (*)(int level, const char *source, int line, const char *msg));
-
-/*
- * Sets the minimum log level. Level is inclusive.
- */
-void dp_log_set_level(int level);
-
-void dp_log_write(int level, const char *source, int line, const char *fmt, ...);
diff --git a/lib/log.odin b/lib/log.odin
new file mode 100644
index 0000000..eb80b5a
--- /dev/null
+++ b/lib/log.odin
@@ -0,0 +1,116 @@
+package djup
+
+import "base:runtime"
+import "core:c/libc"
+import "core:fmt"
+import "core:log"
+
+LogLevel :: enum i32 {
+ Debug = 0,
+ Info = 1,
+ Warning = 2,
+ Error = 3,
+ Fatal = 4,
+}
+
+LogFunc :: #type proc "c" (
+ level: LogLevel,
+ message: string,
+ source_procedure: string,
+ source_file: string,
+ source_line: i32,
+)
+
+AssertFunc :: #type proc "c" (
+ prefix, message: string,
+ source_procedure: string,
+ source_file: string,
+ source_line: i32,
+)
+
+@(export = true)
+sim_log_init :: proc "c" (ctx: ^SimulationContext, logf: LogFunc, assertf: AssertFunc) {
+ if ctx == nil {
+ return
+ }
+
+ log_proc :: proc(
+ data: rawptr,
+ level: runtime.Logger_Level,
+ msg: string,
+ _: runtime.Logger_Options,
+ loc: runtime.Source_Code_Location,
+ ) {
+ f: LogFunc = cast(LogFunc)data
+ log_level: LogLevel
+ switch level {
+ case .Debug:
+ log_level = .Debug
+ case .Info:
+ log_level = .Info
+ case .Warning:
+ log_level = .Warning
+ case .Error:
+ log_level = .Error
+ case .Fatal:
+ log_level = .Fatal
+ }
+ f(log_level, msg, loc.procedure, loc.file_path, loc.line)
+ }
+ ctx.assert_func = assertf
+
+ assert_proc :: proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! {
+ ctx := cast(^SimulationContext)context.user_ptr
+ if ctx.assert_func == nil {
+ fmt.eprintfln(
+ "assertion failure: %s (%s:%d): %s %s",
+ loc.procedure,
+ loc.file_path,
+ loc.line,
+ prefix,
+ message,
+ )
+ libc.abort()
+ }
+ ctx.assert_func(prefix, message, loc.procedure, loc.file_path, loc.line)
+ fmt.println("AssertFunc returned")
+ libc.abort()
+ }
+
+ if logf != nil {
+ ctx.ctx.logger = runtime.Logger {
+ procedure = log_proc,
+ data = cast(rawptr)logf,
+ }
+ } else {
+ context = ctx.ctx
+ ctx.ctx.logger = log.nil_logger()
+ }
+
+ if assertf != nil {
+ ctx.ctx.assertion_failure_proc = assert_proc
+ }
+}
+
+@(export = true)
+sim_set_log_level :: proc "c" (ctx: ^SimulationContext, level: LogLevel) {
+ minimum_level: runtime.Logger_Level
+ switch level {
+ case .Debug:
+ minimum_level = .Debug
+ case .Info:
+ minimum_level = .Info
+ case .Warning:
+ minimum_level = .Warning
+ case .Error:
+ minimum_level = .Error
+ case .Fatal:
+ minimum_level = .Fatal
+ case:
+ context = ctx.ctx
+ log.errorf("Invalid minimum log level %d", level)
+ return
+ }
+
+ ctx.ctx.logger.lowest_level = minimum_level
+}
diff --git a/lib/ols.json b/lib/ols.json
new file mode 100644
index 0000000..89c69d7
--- /dev/null
+++ b/lib/ols.json
@@ -0,0 +1,13 @@
+{
+ "$schema": "https://raw.githubusercontent.com/DanielGavin/ols/master/misc/ols.schema.json",
+ "profile": "default",
+ "profiles": [
+ {
+ "name": "default",
+ "checker_path": [ "." ],
+ "defines": {
+ "ODIN_DEBUG": "true"
+ }
+ }
+ ]
+}
diff --git a/lib/physics.c b/lib/physics.c
deleted file mode 100644
index 0039bd7..0000000
--- a/lib/physics.c
+++ /dev/null
@@ -1,146 +0,0 @@
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "common.h"
-#include "player.h"
-#include "snapshot.h"
-#include "types.h"
-
-#include "physics.h"
-
-struct context *
-dp_physics_context_create(int32_t snapshots, int32_t players, int32_t balls)
-{
- int32_t i, j;
- struct context *ctx = NULL;
-
- if (!(ctx = malloc(sizeof(*ctx))))
- return NULL;
- memset(ctx, 0, sizeof(*ctx));
-
- ctx->snapshots_len = snapshots;
- if (!(ctx->snapshots = malloc(sizeof(*ctx->snapshots) * snapshots)))
- {
- free(ctx);
- return NULL;
- }
- memset(ctx->snapshots, 0, sizeof(*ctx->snapshots) * snapshots);
-
- for (i = 0; i < snapshots; i++)
- {
- if (dp_snapshot_init(&ctx->snapshots[i], players, balls) != 0)
- {
- for (j = 0; j < i; j++)
- {
- dp_snapshot_deinit(&ctx->snapshots[j]);
- }
- free(ctx);
- }
- }
-
- return ctx;
-}
-
-void
-dp_physics_context_free(struct context *ctx)
-{
- int32_t i;
- if (!ctx)
- return;
- if (ctx->snapshots)
- {
- for (i = 0; i < ctx->snapshots_len; i++)
- {
- dp_snapshot_deinit(&ctx->snapshots[i]);
- }
- free(ctx->snapshots);
- }
- free(ctx);
-}
-
-struct snapshot *
-dp_physics_context_get_snapshot(struct context *ctx)
-{
- int32_t i;
- struct snapshot *s;
-
- for (i = 0; i < ctx->snapshots_len; i++)
- {
- s = &ctx->snapshots[i];
- if (!s->active)
- {
- s->active = true;
- return s;
- }
- }
- return NULL;
-}
-
-void
-dp_physics_context_return_snapshot(struct context *ctx, struct snapshot *s)
-{
- int32_t i;
-
- assert(s->active);
-
- for (i = 0; i < ctx->snapshots_len; i++)
- {
- if (s == &ctx->snapshots[i])
- {
- s->active = false;
- }
- }
-
- assert(!s->active);
-}
-
-static void
-dp_physics_tick_player(const struct player *cur, double delta, struct player *next)
-{
- vec2 acc;
-
- next->state = cur->state;
- if (next->state == STATE_REMOVED)
- next->state = STATE_INACTIVE;
- if (next->state == STATE_INACTIVE)
- return;
-
- acc = dp_player_calculate_acceleration(cur);
- next->vel = dp_vec2_add(cur->vel, dp_vec2_mul(acc, delta));
- next->pos = dp_vec2_add(cur->pos, dp_vec2_mul(next->vel, delta));
-}
-
-static void
-dp_physics_tick_ball(const struct ball *cur, double delta, struct ball *next)
-{
- next->state = cur->state;
- if (next->state == STATE_REMOVED)
- next->state = STATE_INACTIVE;
- if (next->state == STATE_INACTIVE)
- return;
-
- next->pos = dp_vec2_add(cur->pos, dp_vec2_mul(cur->vel, delta));
-}
-
-int
-dp_physics_tick(const struct snapshot *cur, double delta, struct snapshot *next)
-{
- int32_t i;
-
- assert(cur->active);
- assert(next->active);
-
- for (i = 0; i < cur->players_len; i++)
- {
- dp_physics_tick_player(&cur->players[i], delta, &next->players[i]);
- }
-
- for (i = 0; i < cur->balls_len; i++)
- {
- dp_physics_tick_ball(&cur->balls[i], delta, &next->balls[i]);
- }
-
- next->tick = cur->tick + 1;
- return 0;
-}
diff --git a/lib/physics.h b/lib/physics.h
deleted file mode 100644
index e1c53f3..0000000
--- a/lib/physics.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#include "types.h"
-
-struct snapshot;
-
-/* A game world */
-struct context {
- int32_t snapshots_len;
- struct snapshot *snapshots;
-};
-
-struct context *dp_physics_context_create(int32_t snapshots, int32_t players, int32_t balls);
-void dp_physics_context_free(struct context *);
-
-struct snapshot *dp_physics_context_get_snapshot(struct context *);
-void dp_physics_context_return_snapshot(struct context *, struct snapshot *);
-
-int dp_physics_tick(const struct snapshot *current, double delta, struct snapshot *next);
diff --git a/lib/player.c b/lib/player.c
deleted file mode 100644
index 35c3b80..0000000
--- a/lib/player.c
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "snapshot.h"
-
-#include "player.h"
-
-vec2
-dp_player_calculate_acceleration(const struct player *p)
-{
- vec2 acc, dec;
- int x = 0, y = 0;
-
- if (p->input & INPUT_UP)
- {
- y -= 1;
- }
- if (p->input & INPUT_DOWN)
- {
- y += 1;
- }
-
- if (p->input & INPUT_LEFT)
- {
- x -= 1;
- }
- if (p->input & INPUT_RIGHT)
- {
- x += 1;
- }
-
- acc = dp_vec2_new((float)x, (float)y);
- acc = dp_vec2_normal(acc);
- acc = dp_vec2_mul(acc, PLAYER_ACCELERATION);
-
- dec = dp_vec2_mul(p->vel, -1.0f * PLAYER_DRAG);
-
- return dp_vec2_add(acc, dec);
-}
diff --git a/lib/player.h b/lib/player.h
deleted file mode 100644
index 2f365bf..0000000
--- a/lib/player.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#include "types.h"
-
-struct player;
-
-vec2 dp_player_calculate_acceleration(const struct player *);
diff --git a/lib/simulation.odin b/lib/simulation.odin
new file mode 100644
index 0000000..0ac8e89
--- /dev/null
+++ b/lib/simulation.odin
@@ -0,0 +1,261 @@
+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, ~{})
+}
diff --git a/lib/snapshot.c b/lib/snapshot.c
deleted file mode 100644
index d7dde50..0000000
--- a/lib/snapshot.c
+++ /dev/null
@@ -1,142 +0,0 @@
-#include <stdlib.h>
-#include <string.h>
-
-#include "common.h"
-
-#include "snapshot.h"
-
-int
-dp_snapshot_init(struct snapshot *s, int32_t players_len, int32_t balls_len)
-{
- if (!s)
- return -1;
-
- if (!(s->players = calloc(players_len, sizeof(*s->players))))
- return -1;
- if (!(s->balls = calloc(balls_len, sizeof(*s->balls))))
- {
- free(s->players);
- return -1;
- }
- s->players_len = players_len;
- s->balls_len = balls_len;
- return 0;
-}
-
-void
-dp_snapshot_deinit(struct snapshot *s)
-{
- if (!s)
- return;
- if (s->players)
- {
- free(s->players);
- s->players = NULL;
- }
- if (s->balls)
- {
- free(s->balls);
- s->balls = NULL;
- }
-}
-
-int
-dp_snapshot_put_player(struct snapshot *s, int32_t id, const struct player *p)
-{
- if (!s || !p)
- return -1;
- if (id >= s->players_len)
- return -1;
-
- s->players[id] = *p;
- s->players[id].state = STATE_ACTIVE;
- return 0;
-}
-
-struct player *
-dp_snapshot_get_player(struct snapshot *s, int32_t id)
-{
- struct player *p;
-
- if (!s)
- return NULL;
- if (id >= s->players_len)
- return NULL;
-
- p = &s->players[id];
- if (p->state == STATE_INACTIVE)
- return NULL;
- return p;
-}
-
-int
-dp_snapshot_remove_player(struct snapshot *s, int32_t id)
-{
- struct player *p;
-
- if (!s)
- return -1;
- if (id >= s->players_len)
- return -1;
-
- p = dp_snapshot_get_player(s, id);
- memset(p, 0, sizeof(*p));
- p->state = STATE_REMOVED;
- return 0;
-}
-
-int
-dp_snapshot_set_player_input(struct snapshot *s, int32_t id, uint8_t input)
-{
- struct player *p;
-
- if (!(p = dp_snapshot_get_player(s, id)))
- return -1;
- p->input = input;
- return 0;
-}
-
-int
-dp_snapshot_put_ball(struct snapshot *s, int32_t id, const struct ball *b)
-{
- if (!s || !b)
- return -1;
- if (id >= s->balls_len)
- return -1;
-
- s->balls[id] = *b;
- s->balls[id].state = STATE_ACTIVE;
- return 0;
-}
-
-struct ball *
-dp_snapshot_get_ball(struct snapshot *s, int32_t id)
-{
- struct ball *b;
-
- if (!s)
- return NULL;
- if (id >= s->balls_len)
- return NULL;
-
- b = &s->balls[id];
- if (b->state == STATE_INACTIVE)
- return NULL;
- return b;
-}
-
-int
-dp_snapshot_remove_ball(struct snapshot *s, int32_t id)
-{
- struct ball *b;
-
- if (!s)
- return -1;
- if (id >= s->balls_len)
- return -1;
-
- b = dp_snapshot_get_ball(s, id);
- memset(b, 0, sizeof(*b));
- b->state = STATE_REMOVED;
- return 0;
-}
diff --git a/lib/snapshot.h b/lib/snapshot.h
deleted file mode 100644
index 028eac2..0000000
--- a/lib/snapshot.h
+++ /dev/null
@@ -1,43 +0,0 @@
-#include "types.h"
-#include "constants.h"
-
-enum {
- STATE_INACTIVE = 0,
- STATE_ACTIVE,
- STATE_REMOVED,
- STATE_DEAD
-};
-
-struct player {
- uint8_t state;
- uint8_t input;
- vec2 pos;
- vec2 vel;
-};
-
-struct ball {
- uint8_t state;
- vec2 pos;
- vec2 vel;
-};
-
-struct snapshot {
- char active;
- uint32_t tick;
- int32_t players_len;
- struct player *players;
- int32_t balls_len;
- struct ball *balls;
-};
-
-int dp_snapshot_init(struct snapshot *, int32_t players_len, int32_t balls_len);
-void dp_snapshot_deinit(struct snapshot *);
-
-int dp_snapshot_put_player(struct snapshot *, int32_t id, const struct player *);
-struct player *dp_snapshot_get_player(struct snapshot *, int32_t id);
-int dp_snapshot_remove_player(struct snapshot *, int32_t id);
-int dp_snapshot_set_player_input(struct snapshot *, int32_t id, uint8_t input);
-
-int dp_snapshot_put_ball(struct snapshot *, int32_t id, const struct ball *);
-struct ball *dp_snapshot_get_ball(struct snapshot *, int32_t id);
-int dp_snapshot_remove_ball(struct snapshot *, int32_t id);
diff --git a/lib/systems.odin b/lib/systems.odin
new file mode 100644
index 0000000..288bd01
--- /dev/null
+++ b/lib/systems.odin
@@ -0,0 +1,44 @@
+package djup
+
+DELTA: f32 : 1.0 / 60.0
+
+@(export = true)
+sim_tick :: proc "c" (cur, next: ^Snapshot) {
+ if cur == nil || next == nil {
+ return
+ }
+ context = next.ctx^
+
+ next.tick = cur.tick + 1
+ copy(next.metadata, cur.metadata)
+
+ player_input_system(cur, next)
+ ball_movement_system(cur, next)
+}
+
+player_input_system :: proc(cur, next: ^Snapshot) {
+ for &e, id in soa_zip(m = cur.metadata, input = cur.player_inputs, ball = cur.balls) {
+ if e.m < {.Exists, .PlayerInput, .Ball} {
+ continue
+ }
+
+ PLAYER_ACCELERATION :: 1500
+ PLAYER_DRAG :: 2
+
+ acc :=
+ vec2_normal(e.input.direction) * PLAYER_ACCELERATION + e.ball.velocity * -PLAYER_DRAG
+ e.ball.velocity += acc * DELTA
+ next.player_inputs[id] = e.input
+ }
+}
+
+ball_movement_system :: proc(cur, next: ^Snapshot) {
+ for &e, id in soa_zip(m = cur.metadata, ball = cur.balls) {
+ if e.m < {.Exists, .Ball} {
+ continue
+ }
+
+ next.balls[id].position = e.ball.velocity * DELTA
+ next.balls[id].velocity = e.ball.velocity
+ }
+}
diff --git a/lib/test.c b/lib/test.c
deleted file mode 100644
index 70b32fa..0000000
--- a/lib/test.c
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <stdio.h>
-
-#include "types.h"
-
-void
-dp_hello_world()
-{
- printf("Hello World\n");
-}
-
-int
-dp_get_num()
-{
- return 1;
-}
-
-vec2
-dp_get_vec2()
-{
- vec2 vec;
- vec.x = 1.0f;
- vec.y = 2.5f;
- return vec;
-}
diff --git a/lib/types.h b/lib/types.h
deleted file mode 100644
index bf42576..0000000
--- a/lib/types.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef _TYPES_H
-#define _TYPES_H
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-
-typedef struct {
- float x;
- float y;
-} vec2;
-
-vec2 dp_vec2_new(float, float);
-vec2 dp_vec2_add(vec2, vec2);
-vec2 dp_vec2_sub(vec2, vec2);
-vec2 dp_vec2_mul(vec2, float scalar);
-vec2 dp_vec2_dot(vec2, vec2);
-float dp_vec2_length(vec2);
-vec2 dp_vec2_normal(vec2);
-
-#endif
diff --git a/lib/vec2.c b/lib/vec2.c
deleted file mode 100644
index 55f5078..0000000
--- a/lib/vec2.c
+++ /dev/null
@@ -1,68 +0,0 @@
-#include <assert.h>
-#include <math.h>
-
-#include "types.h"
-
-vec2
-dp_vec2_new(float x, float y)
-{
- vec2 new;
-
- assert(isfinite(x));
- assert(isfinite(y));
-
- new.x = x;
- new.y = y;
- return new;
-}
-
-vec2
-dp_vec2_add(vec2 a, vec2 b)
-{
- vec2 new;
- new.x = a.x + b.x;
- new.y = a.y + b.y;
- return new;
-}
-
-vec2
-dp_vec2_sub(vec2 a, vec2 b)
-{
- vec2 new;
- new.x = a.x - b.x;
- new.y = a.y - b.y;
- return new;
-}
-
-vec2
-dp_vec2_mul(vec2 vec, float scalar)
-{
- vec2 new;
- new.x = vec.x * scalar;
- new.y = vec.y * scalar;
- return new;
-}
-
-vec2
-dp_vec2_dot(vec2 a, vec2 b)
-{
- vec2 dot;
- dot.x = a.x * b.x;
- dot.y = a.y * b.y;
- return dot;
-}
-
-float
-dp_vec2_length(vec2 v)
-{
- return sqrtf(powf(v.x, 2.f) + powf(v.y, 2.f));
-}
-
-vec2
-dp_vec2_normal(vec2 v)
-{
- float len = dp_vec2_length(v);
- if (len == 0.0f)
- return dp_vec2_new(0.0f, 0.0f);
- return dp_vec2_mul(v, 1.0f / len);
-}