diff options
| author | Joel Stålnacke <joel@saker.fi> | 2025-07-26 21:09:26 +0300 |
|---|---|---|
| committer | Joel Stålnacke <joel@saker.fi> | 2025-07-26 21:09:26 +0300 |
| commit | 63506e59366acddf4a9e017ad8aebeadcf58c164 (patch) | |
| tree | 2fa106661b85497fc1d63b7743a78e523ab48fba | |
| parent | 53f68bb7b0dce309723675c4b97f726a469031c0 (diff) | |
| -rw-r--r-- | Djup.Native/Constants.cs | 7 | ||||
| -rw-r--r-- | Djup.Native/LibDjup.cs | 27 | ||||
| -rw-r--r-- | Djup.Native/Types.cs | 52 | ||||
| -rw-r--r-- | client/Game.cs | 37 | ||||
| -rw-r--r-- | client/Player.cs | 24 | ||||
| -rw-r--r-- | client/Root.cs | 87 | ||||
| -rw-r--r-- | client/World.cs | 75 | ||||
| -rw-r--r-- | client/game.tscn | 13 | ||||
| -rw-r--r-- | client/player.tscn | 6 | ||||
| -rw-r--r-- | client/project.godot | 25 | ||||
| -rw-r--r-- | client/root.tscn | 6 | ||||
| -rw-r--r-- | client/world.tscn | 6 | ||||
| -rwxr-xr-x | lib/build.sh | 4 | ||||
| -rw-r--r-- | lib/common.h | 1 | ||||
| -rw-r--r-- | lib/constants.h | 12 | ||||
| -rw-r--r-- | lib/physics.c | 124 | ||||
| -rw-r--r-- | lib/physics.h | 17 | ||||
| -rw-r--r-- | lib/player.c | 36 | ||||
| -rw-r--r-- | lib/player.h | 5 | ||||
| -rw-r--r-- | lib/snapshot.c | 99 | ||||
| -rw-r--r-- | lib/snapshot.h | 39 | ||||
| -rw-r--r-- | lib/types.h | 9 | ||||
| -rw-r--r-- | lib/vec2.c | 7 | ||||
| -rw-r--r-- | lib/world.c | 105 | ||||
| -rw-r--r-- | lib/world.h | 31 |
25 files changed, 597 insertions, 257 deletions
diff --git a/Djup.Native/Constants.cs b/Djup.Native/Constants.cs new file mode 100644 index 0000000..90692be --- /dev/null +++ b/Djup.Native/Constants.cs @@ -0,0 +1,7 @@ +namespace Djup.Native; + +public static class Constants +{ + public const int WorldMaxPlayers = 5; + public const int WorldMaxBullets = 5000; +} diff --git a/Djup.Native/LibDjup.cs b/Djup.Native/LibDjup.cs index 0b4a3db..15dcdad 100644 --- a/Djup.Native/LibDjup.cs +++ b/Djup.Native/LibDjup.cs @@ -31,21 +31,24 @@ public static partial class LibDjup [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(log_set_level))] public static partial Vec2 log_set_level(LogLevel minLevel); - [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(world_create))] - public static partial IntPtr world_create(); + [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(physics_context_create))] + public static partial IntPtr physics_context_create(uint snapshots); - [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(world_free))] - public static partial void world_free(IntPtr world); + [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(physics_context_free))] + public static partial void physics_context_free(IntPtr context); - [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(world_create_entity))] - public static partial int world_create_entity(IntPtr world, EntityKind kind); + [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(physics_context_get_snapshot))] + public static partial IntPtr physics_context_get_snapshot(IntPtr context); - [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(world_find_entity))] - public static unsafe partial Entity* world_find_entity(IntPtr world, int entityId); + [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(physics_context_return_snapshot))] + public static partial void physics_context_return_snapshot(IntPtr context, IntPtr snapshot); - [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(world_remove_entity))] - public static partial void world_remove_entity(IntPtr world, int entityId); + [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(physics_tick))] + public static partial int physics_tick(IntPtr currentSnapshot, double delta, IntPtr nextSnapshot); - [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(world_tick))] - public static partial int world_tick(IntPtr world, double delta); + [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(snapshot_put_player))] + public static unsafe partial int snapshot_put_player(IntPtr snapshot, uint playerId, Player *player); + + [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(snapshot_set_player_input))] + public static partial int snapshot_set_player_input(IntPtr snapshot, uint playerId, byte input); } diff --git a/Djup.Native/Types.cs b/Djup.Native/Types.cs index 478208f..7d9c41e 100644 --- a/Djup.Native/Types.cs +++ b/Djup.Native/Types.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Djup.Native; @@ -21,29 +22,60 @@ public struct Vec2 public override string ToString() => $"({X}, {Y})"; } -public enum EntityKind +public enum EntityState : byte { - None = 0, - Ball + Inactive = 0, + Active, + Removed, + Dead } -[StructLayout(LayoutKind.Explicit)] -public struct Entity +public enum PlayerInput : byte { - [FieldOffset(0)] - public EntityKind kind; + Up = 1 << 0, + Down = 1 << 1, + Left = 1 << 2, + Right = 1 << 3 +} - [FieldOffset(4)] - public Ball ball; +[StructLayout(LayoutKind.Sequential)] +public struct Player +{ + public EntityState State { get; } + private byte input; + public Vec2 Position { get; set; } + public Vec2 Velocity { get; set; } } [StructLayout(LayoutKind.Sequential)] -public struct Ball +public struct Bullet { + public EntityState State { get; set; } public Vec2 Position { get; set; } public Vec2 Velocity { get; set; } } +[StructLayout(LayoutKind.Sequential)] +public struct Snapshot +{ + [InlineArray(Constants.WorldMaxPlayers)] + public struct PlayerArray + { + private Player _element0; + } + + [InlineArray(Constants.WorldMaxBullets)] + public struct BallArray + { + private Bullet _element0; + } + + private byte active; + public uint Tick { get; } + public PlayerArray Players { get; } + public BallArray Bullets { get; } +} + public enum LogLevel { Trace, diff --git a/client/Game.cs b/client/Game.cs new file mode 100644 index 0000000..6bf6192 --- /dev/null +++ b/client/Game.cs @@ -0,0 +1,37 @@ +using Godot; +using System; +using Djup.Native; + +public partial class Game : Node2D +{ + private World world; + + private int[] balls = new int[100]; + private Random rng = new(); + + // Called when the node enters the scene tree for the first time. + public unsafe override void _Ready() + { + RenderingServer.SetDefaultClearColor(Colors.LightGray); + LibDjup.log_set_output((level, source, line, msg) => { + string level_name = level switch + { + LogLevel.Trace => "trace", + LogLevel.Debug => "debug", + LogLevel.Info => " info", + LogLevel.Warn => " warn", + LogLevel.Error => "error", + LogLevel.Fatal => "fatal", + _ => "UNKNOWN" + }; + GD.Print($"libdjup {source.PadLeft(10)}:{line.ToString().PadRight(3)} {level_name}: {msg}"); + }); + + world = GetNode<World>("World"); + + world.PutPlayer(0, new Djup.Native.Player() + { + Position = new Vec2(100f, 100f), + }); + } +} diff --git a/client/Player.cs b/client/Player.cs new file mode 100644 index 0000000..da95275 --- /dev/null +++ b/client/Player.cs @@ -0,0 +1,24 @@ +using Godot; +using System; +using Djup.Native; + +public partial class Player : Node2D +{ + [Export] + public World World { get; set; } + public uint Id { get; set; } = 0; + + public unsafe override void _Process(double delta) + { + byte input = 0; + if (Input.IsActionPressed("up")) + input |= (byte)PlayerInput.Up; + if (Input.IsActionPressed("down")) + input |= (byte)PlayerInput.Down; + if (Input.IsActionPressed("left")) + input |= (byte)PlayerInput.Left; + if (Input.IsActionPressed("right")) + input |= (byte)PlayerInput.Right; + World.SetPlayerInput(Id, input); + } +} diff --git a/client/Root.cs b/client/Root.cs deleted file mode 100644 index dfbf0da..0000000 --- a/client/Root.cs +++ /dev/null @@ -1,87 +0,0 @@ -using Godot; -using System; -using Djup.Native; - -public partial class Root : Node2D -{ - private IntPtr native_world; - private int[] balls = new int[100]; - private Random rng = new(); - private const double Tps = 1d/60d; - - // Called when the node enters the scene tree for the first time. - public unsafe override void _Ready() - { - RenderingServer.SetDefaultClearColor(Colors.LightGray); - LibDjup.log_set_output((level, source, line, msg) => { - string level_name = level switch - { - LogLevel.Trace => "trace", - LogLevel.Debug => "debug", - LogLevel.Info => "info", - LogLevel.Warn => "warn", - LogLevel.Error => "error", - LogLevel.Fatal => "fatal" - }; - GD.Print($"libdjup {source}:{line} {level_name.PadLeft(5, ' ')}: {msg}"); - }); - - native_world = LibDjup.world_create(); - for (int i = 0; i < balls.Length; i++) - { - int id = LibDjup.world_create_entity(native_world, EntityKind.Ball); - if (id < 0) - { - throw new Exception("Failed to create entity"); - } - balls[i] = id; - Entity *ent; - var pos = new Vec2(5f * rng.Next(10), 5f * rng.Next(20)); - var vel = LibDjup.vec2_mul(LibDjup.vec2_normal(new Vec2(50f + 5f * rng.Next(10), 50f)), 25.0f); - GD.Print(vel, " --> ", LibDjup.vec2_length(vel)); - ent = LibDjup.world_find_entity(native_world, balls[i]); - ent->ball.Position = pos; - ent->ball.Velocity = vel; - } - } - - public override void _ExitTree() - { - LibDjup.world_free(native_world); - } - - private void DrawBall(Ball ball) - { - //Color color = rng.Next(5) switch - //{ - //0 => Colors.Black, - //1 => Colors.Blue, - //2 => Colors.Red, - //3 => Colors.Green, - //4 => Colors.Pink - //}; - Color color = Colors.Blue; - this.DrawCircle(new Vector2(ball.Position.X, ball.Position.Y), 5f, color, antialiased: true); - } - - public unsafe override void _Draw() - { - for (int i = 0; i < balls.Length; i++) - { - DrawBall(LibDjup.world_find_entity(native_world, balls[i])->ball); - } - } - - public override void _Process(double delta) - { - this.QueueRedraw(); - } - - public override void _PhysicsProcess(double delta) - { - if (LibDjup.world_tick(native_world, Tps) != 0) - { - GD.Print("world_tick failed"); - } - } -} diff --git a/client/World.cs b/client/World.cs new file mode 100644 index 0000000..8f5dfb6 --- /dev/null +++ b/client/World.cs @@ -0,0 +1,75 @@ +using Godot; +using System; +using Djup.Native; + +public partial class World : Node2D +{ + private IntPtr context; + private IntPtr currentSnapshot; + public const double Tps = 1d/60d; + + public unsafe override void _Ready() + { + context = LibDjup.physics_context_create(2); + currentSnapshot = LibDjup.physics_context_get_snapshot(context); + } + + public override void _ExitTree() + { + LibDjup.physics_context_free(context); + } + + public unsafe void PutPlayer(uint id, Djup.Native.Player player) + { + if (LibDjup.snapshot_put_player(currentSnapshot, id, &player) != 0) + { + throw new Exception($"Failed to put player {id}"); + } + } + + public void SetPlayerInput(uint playerId, byte input) + { + int ret = LibDjup.snapshot_set_player_input(currentSnapshot, playerId, input); + if (ret != 0) + { + throw new Exception($"Failed to set input of player {playerId}: {ret}"); + } + } + + private unsafe void DrawPlayer(Djup.Native.Player player) + { + float radius = 50f; + this.DrawCircle(new Vector2(player.Position.X, player.Position.Y), radius, Colors.DarkBlue, antialiased: true); + this.DrawCircle(new Vector2(player.Position.X, player.Position.Y), radius * 0.90f, Colors.Blue, antialiased: true); + } + + public unsafe override void _Draw() + { + Snapshot *s = (Snapshot *)currentSnapshot; + + for (int i = 0; i < Constants.WorldMaxPlayers; i++) + { + var p = s->Players[i]; + if (p.State == EntityState.Active) + { + DrawPlayer(p); + } + } + } + + public override void _Process(double delta) + { + this.QueueRedraw(); + } + + public override void _PhysicsProcess(double delta) + { + IntPtr next = LibDjup.physics_context_get_snapshot(context); + if (LibDjup.physics_tick(currentSnapshot, Tps, next) != 0) + { + throw new Exception("World tick failed"); + } + LibDjup.physics_context_return_snapshot(context, currentSnapshot); + currentSnapshot = next; + } +} diff --git a/client/game.tscn b/client/game.tscn new file mode 100644 index 0000000..4ec7809 --- /dev/null +++ b/client/game.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=4 format=3 uid="uid://w0k2j13hm04i"] + +[ext_resource type="Script" path="res://Game.cs" id="1_t8lam"] +[ext_resource type="PackedScene" uid="uid://drdk1by1fun2q" path="res://world.tscn" id="2_tm4sj"] +[ext_resource type="PackedScene" uid="uid://b0f23dtef1o4h" path="res://player.tscn" id="3_caccr"] + +[node name="Game" type="Node2D"] +script = ExtResource("1_t8lam") + +[node name="World" parent="." instance=ExtResource("2_tm4sj")] + +[node name="Player" parent="." node_paths=PackedStringArray("World") instance=ExtResource("3_caccr")] +World = NodePath("../World") diff --git a/client/player.tscn b/client/player.tscn new file mode 100644 index 0000000..b1f9f5f --- /dev/null +++ b/client/player.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://b0f23dtef1o4h"] + +[ext_resource type="Script" path="res://Player.cs" id="1_bjul0"] + +[node name="Player" type="Node2D"] +script = ExtResource("1_bjul0") diff --git a/client/project.godot b/client/project.godot index 6107d87..403aac4 100644 --- a/client/project.godot +++ b/client/project.godot @@ -11,7 +11,7 @@ config_version=5 [application] config/name="Djup" -run/main_scene="res://root.tscn" +run/main_scene="res://game.tscn" config/features=PackedStringArray("4.3", "C#", "Forward Plus") config/icon="res://icon.svg" @@ -22,3 +22,26 @@ window/stretch/mode="canvas_items" [dotnet] project/assembly_name="Djup" + +[input] + +left={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +] +} +right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +] +} +up={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +] +} +down={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +] +} diff --git a/client/root.tscn b/client/root.tscn deleted file mode 100644 index ded95a0..0000000 --- a/client/root.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://drdk1by1fun2q"] - -[ext_resource type="Script" path="res://Root.cs" id="1_p5oo0"] - -[node name="Root" type="Node2D"] -script = ExtResource("1_p5oo0") diff --git a/client/world.tscn b/client/world.tscn new file mode 100644 index 0000000..0055d3b --- /dev/null +++ b/client/world.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://drdk1by1fun2q"] + +[ext_resource type="Script" path="res://World.cs" id="1_7wii1"] + +[node name="World" type="Node2D"] +script = ExtResource("1_7wii1") diff --git a/lib/build.sh b/lib/build.sh index f305939..3f8b5a6 100755 --- a/lib/build.sh +++ b/lib/build.sh @@ -6,9 +6,9 @@ fail() { } CC=cc -CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -D_POSIX_C_SOURCE=200809L" +CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Og -g -D_POSIX_C_SOURCE=200809L" LDFLAGS="-fPIC -lm" -SOURCES="test.c vec2.c world.c log.c" +SOURCES="test.c vec2.c snapshot.c log.c player.c physics.c" output="build" for arg ; do diff --git a/lib/common.h b/lib/common.h new file mode 100644 index 0000000..d6d8816 --- /dev/null +++ b/lib/common.h @@ -0,0 +1 @@ +#define LENGTH(x) (sizeof(x) / sizeof(x[0])) diff --git a/lib/constants.h b/lib/constants.h new file mode 100644 index 0000000..817fa43 --- /dev/null +++ b/lib/constants.h @@ -0,0 +1,12 @@ +#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/physics.c b/lib/physics.c new file mode 100644 index 0000000..adcf433 --- /dev/null +++ b/lib/physics.c @@ -0,0 +1,124 @@ +#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(size_t snapshots) +{ + 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); + + return ctx; +} + +void +dp_physics_context_free(struct context *ctx) +{ + if (!ctx) + return; + if (ctx->snapshots) + free(ctx->snapshots); + free(ctx); +} + +struct snapshot *dp_physics_context_get_snapshot(struct context *ctx) +{ + size_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) +{ + size_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) +{ + size_t i; + + assert(cur->active); + assert(next->active); + + for (i = 0; i < LENGTH(cur->players); i++) + { + dp_physics_tick_player(&cur->players[i], delta, &next->players[i]); + } + + for (i = 0; i < LENGTH(cur->balls); 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 new file mode 100644 index 0000000..7aa2b31 --- /dev/null +++ b/lib/physics.h @@ -0,0 +1,17 @@ +#include "types.h" + +struct snapshot; + +/* A game world */ +struct context { + size_t snapshots_len; + struct snapshot *snapshots; +}; + +struct context *dp_physics_context_create(size_t snapshots); +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 new file mode 100644 index 0000000..35c3b80 --- /dev/null +++ b/lib/player.c @@ -0,0 +1,36 @@ +#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 new file mode 100644 index 0000000..2f365bf --- /dev/null +++ b/lib/player.h @@ -0,0 +1,5 @@ +#include "types.h" + +struct player; + +vec2 dp_player_calculate_acceleration(const struct player *); diff --git a/lib/snapshot.c b/lib/snapshot.c new file mode 100644 index 0000000..88c17a1 --- /dev/null +++ b/lib/snapshot.c @@ -0,0 +1,99 @@ +#include <string.h> + +#include "common.h" + +#include "snapshot.h" + +int dp_snapshot_put_player(struct snapshot *s, uint32_t id, const struct player *p) +{ + if (!s || !p) + return -1; + if (id >= LENGTH(s->players)) + return -1; + + s->players[id] = *p; + s->players[id].state = STATE_ACTIVE; + return 0; +} + +struct player *dp_snapshot_get_player(struct snapshot *s, uint32_t id) +{ + struct player *p; + + if (!s) + return NULL; + if (id >= LENGTH(s->players)) + return NULL; + + p = &s->players[id]; + if (p->state == STATE_INACTIVE) + return NULL; + return p; +} + +int dp_snapshot_remove_player(struct snapshot *s, uint32_t id) +{ + struct player *p; + + if (!s) + return -1; + if (id >= LENGTH(s->players)) + 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, uint32_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, uint32_t id, const struct ball *b) +{ + if (!s || !b) + return -1; + if (id >= LENGTH(s->balls)) + return -1; + + s->balls[id] = *b; + s->balls[id].state = STATE_ACTIVE; + return 0; +} + +struct ball *dp_snapshot_get_ball(struct snapshot *s, uint32_t id) +{ + struct ball *b; + + if (!s) + return NULL; + if (id >= LENGTH(s->balls)) + return NULL; + + b = &s->balls[id]; + if (b->state == STATE_INACTIVE) + return NULL; + return b; +} + +int dp_snapshot_remove_ball(struct snapshot *s, uint32_t id) +{ + struct ball *b; + + if (!s) + return -1; + if (id >= LENGTH(s->balls)) + 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 new file mode 100644 index 0000000..022d989 --- /dev/null +++ b/lib/snapshot.h @@ -0,0 +1,39 @@ +#include "types.h" +#include "constants.h" + +enum { + STATE_INACTIVE = 0, + STATE_ACTIVE, + STATE_REMOVED, + STATE_DEAD +}; + +struct player { + uint8_t state; + uint8_t input; + char pad[2]; + vec2 pos; + vec2 vel; +}; + +struct ball { + uint8_t state; + vec2 pos; + vec2 vel; +}; + +struct snapshot { + char active; + uint32_t tick; + struct player players[WORLD_MAX_PLAYERS]; + struct ball balls[WORLD_MAX_BULLETS]; +}; + +int dp_snapshot_put_player(struct snapshot *, uint32_t id, const struct player *); +struct player *dp_snapshot_get_player(struct snapshot *, uint32_t id); +int dp_snapshot_remove_player(struct snapshot *, uint32_t id); +int dp_snapshot_set_player_input(struct snapshot *, uint32_t id, uint8_t input); + +int dp_snapshot_put_ball(struct snapshot *, uint32_t id, const struct ball *); +struct ball *dp_snapshot_get_ball(struct snapshot *, uint32_t id); +int dp_snapshot_remove_ball(struct snapshot *, uint32_t id); diff --git a/lib/types.h b/lib/types.h index 8618df9..bf42576 100644 --- a/lib/types.h +++ b/lib/types.h @@ -1,3 +1,10 @@ +#ifndef _TYPES_H +#define _TYPES_H + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + typedef struct { float x; float y; @@ -10,3 +17,5 @@ vec2 dp_vec2_mul(vec2, float scalar); vec2 dp_vec2_dot(vec2, vec2); float dp_vec2_length(vec2); vec2 dp_vec2_normal(vec2); + +#endif @@ -61,7 +61,8 @@ dp_vec2_length(vec2 v) vec2 dp_vec2_normal(vec2 v) { - float a; - a = 1.f / dp_vec2_length(v); - return dp_vec2_mul(v, a); + 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); } diff --git a/lib/world.c b/lib/world.c deleted file mode 100644 index 34d7489..0000000 --- a/lib/world.c +++ /dev/null @@ -1,105 +0,0 @@ -#include <stdlib.h> -#include <string.h> - -#include "types.h" -#include "world.h" -#include "log.h" - -struct world { - struct entity entities[WORLD_MAX_ENTITIES]; -}; - -struct world * -dp_world_create() -{ - struct world *w; - - if (!(w = malloc(sizeof(*w)))) - return NULL; - memset(w, 0, sizeof(*w)); - - return w; -} - -void -dp_free_world(struct world *w) -{ - if (w) - free(w); -} - -int -dp_world_create_entity(struct world *w, int kind) -{ - int i; - struct entity *e; - - if (!w) - return -1; - if (kind == ENTITY_NONE) - return -1; - - for (i = 0; i < WORLD_MAX_ENTITIES; i++) { - e = &w->entities[i]; - if (e->kind != ENTITY_NONE) - continue; - e->kind = kind; - return i; - } - - return -1; -} - -struct entity * -dp_world_find_entity(struct world *w, int entity_id) -{ - struct entity *e; - - if (!w || entity_id < 0) - return NULL; - if (entity_id >= WORLD_MAX_ENTITIES) - return NULL; - - e = &w->entities[entity_id]; - - if (e->kind == ENTITY_NONE) - return NULL; - - return e; -} - -int -dp_world_remove_entity(struct world *w, int entity_id) -{ - struct entity *e; - - if (!(e = dp_world_find_entity(w, entity_id))) - return -1; - - memset(e, 0, sizeof(*e)); - e->kind = ENTITY_NONE; - return 0; -} - -int -dp_world_tick(struct world *w, double delta) -{ - int i; - struct entity *e; - struct entity_ball *ball; - - for (i = 0; i < WORLD_MAX_ENTITIES; i++) - { - e = &w->entities[i]; - switch (e->kind) { - case ENTITY_BALL: - ball = &e->e.ball; - /*ball->pos = dp_vec2_new(500.0, 500.0);*/ - ball->pos = dp_vec2_add(ball->pos, dp_vec2_mul(ball->vel, delta)); - break; - default: - break; - } - } - return 0; -} diff --git a/lib/world.h b/lib/world.h deleted file mode 100644 index 40c3fef..0000000 --- a/lib/world.h +++ /dev/null @@ -1,31 +0,0 @@ -#define WORLD_MAX_ENTITIES 5000 - -enum { - ENTITY_NONE, - ENTITY_BALL -}; - -struct entity_ball { - vec2 pos; - vec2 vel; -}; - -union entity_union { - struct entity_ball ball; -}; - -struct entity { - int kind; - union entity_union e; -}; - -struct world; - -struct world *dp_world_create(); -void dp_world_free(struct world *); - -int dp_world_create_entity(struct world *, int kind); -struct entity *dp_world_find_entity(struct world *, int entity_id); -int dp_world_remove_entity(struct world *, int entity_id); - -int dp_world_tick(struct world *, double delta); |
