diff options
| author | Joel Stålnacke <joel@saker.fi> | 2024-10-11 13:31:44 +0300 |
|---|---|---|
| committer | Joel Stålnacke <joel@saker.fi> | 2024-10-13 13:34:46 +0300 |
| commit | 53a1cdf5bee2955995dfbf441f5354d1dcfc1e0c (patch) | |
| tree | be8c2894226a2b7e1a47f7583f2041df75f795b3 | |
| parent | 4bac6ae2e725a1997674fd3369bf4ea032235d8b (diff) | |
Add Godot client
| -rw-r--r-- | Djup.Native/.gitignore | 2 | ||||
| -rw-r--r-- | Djup.Native/AddRuntimeTargetsToDepsJson.targets | 12 | ||||
| -rw-r--r-- | Djup.Native/Djup.Native.csproj | 39 | ||||
| -rw-r--r-- | Djup.Native/LibDjup.cs | 36 | ||||
| -rw-r--r-- | Djup.Native/Program.cs | 3 | ||||
| -rw-r--r-- | Djup.Native/Types.cs | 41 | ||||
| -rwxr-xr-x | Djup.Native/patch-deps.sh | 4 | ||||
| -rw-r--r-- | client/.gitattributes | 2 | ||||
| -rw-r--r-- | client/.gitignore | 3 | ||||
| -rw-r--r-- | client/Djup.csproj | 15 | ||||
| -rw-r--r-- | client/Djup.sln | 19 | ||||
| -rw-r--r-- | client/Root.cs | 74 | ||||
| -rw-r--r-- | client/icon.svg | 1 | ||||
| -rw-r--r-- | client/icon.svg.import | 37 | ||||
| -rw-r--r-- | client/project.godot | 24 | ||||
| -rw-r--r-- | client/root.tscn | 6 | ||||
| -rwxr-xr-x | lib/build.sh | 2 | ||||
| -rw-r--r-- | lib/test.c | 15 | ||||
| -rw-r--r-- | lib/types.h | 6 | ||||
| -rw-r--r-- | lib/vec2.c | 46 | ||||
| -rw-r--r-- | lib/world.c | 104 | ||||
| -rw-r--r-- | lib/world.h | 31 |
22 files changed, 521 insertions, 1 deletions
diff --git a/Djup.Native/.gitignore b/Djup.Native/.gitignore new file mode 100644 index 0000000..c6e49ef --- /dev/null +++ b/Djup.Native/.gitignore @@ -0,0 +1,2 @@ +obj/ +bin/ diff --git a/Djup.Native/AddRuntimeTargetsToDepsJson.targets b/Djup.Native/AddRuntimeTargetsToDepsJson.targets new file mode 100644 index 0000000..fbb4c43 --- /dev/null +++ b/Djup.Native/AddRuntimeTargetsToDepsJson.targets @@ -0,0 +1,12 @@ +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + The .NET SDK does not support project-to-project dependencies + with native binary dependencies, so we need to add them manually. + Patch Project.deps.json by adding the required depencies to + Djup.Native "runtimeTargets" section. + --> + <!-- https://github.com/dotnet/sdk/issues/19929 --> + <Target Name="AddRuntimeTargetsToDepsJson" AfterTargets="AfterBuild"> + <Exec Command="$(MSBuildThisFileDirectory)patch-deps.sh '$(OutputPath)'" /> + </Target> +</Project> diff --git a/Djup.Native/Djup.Native.csproj b/Djup.Native/Djup.Native.csproj new file mode 100644 index 0000000..107e293 --- /dev/null +++ b/Djup.Native/Djup.Native.csproj @@ -0,0 +1,39 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> + + <PropertyGroup> + <BuildDir>$(MSBuildThisFileDirectory)../lib/build</BuildDir> + </PropertyGroup> + + <ItemGroup Condition=" '$(Configuration)'=='Release' "> + <Content Include="$(BuildDir)/linux-x64/libdjup.so"> + <TargetPath>runtimes/linux-x64/native/libdjup.so</TargetPath> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <PackagePath>runtimes/linux-x64/native/</PackagePath> + <Pack>true</Pack> + </Content> + </ItemGroup> + + <Choose> + <When Condition=" '$(Configuration)'=='Debug'"> + <PropertyGroup> + <Arch>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)</Arch> + <IsLinux Condition="$([MSBuild]::IsOsPlatform('Linux'))">true</IsLinux> + </PropertyGroup> + + <ItemGroup Condition=" $(IsLinux) And '$(Arch)' == X64 "> + <Content Include="$(BuildDir)/linux-x64/libdjup.so"> + <TargetPath>runtimes/linux-x64/native/libdjup.so</TargetPath> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + </ItemGroup> + </When> + </Choose> + +</Project> diff --git a/Djup.Native/LibDjup.cs b/Djup.Native/LibDjup.cs new file mode 100644 index 0000000..fece18a --- /dev/null +++ b/Djup.Native/LibDjup.cs @@ -0,0 +1,36 @@ +using System.Runtime.InteropServices; + +namespace Djup.Native; + +public static partial class LibDjup +{ + const string LibraryName = "djup"; + const string Prefix = "dp_"; + + [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(hello_world))] + public static partial void hello_world(); + + [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(get_num))] + public static partial int get_num(); + + [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(get_vec2))] + public static partial Vec2 get_vec2(); + + [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(world_create))] + public static partial IntPtr world_create(); + + [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(world_free))] + public static partial void world_free(IntPtr world); + + [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(world_create_entity))] + public static partial int world_create_entity(IntPtr world, EntityKind kind); + + [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(world_remove_entity))] + public static partial void world_remove_entity(IntPtr world, int entityId); + + [LibraryImport(LibraryName, EntryPoint = Prefix + nameof(world_tick))] + public static partial int world_tick(IntPtr world, double delta); +} diff --git a/Djup.Native/Program.cs b/Djup.Native/Program.cs new file mode 100644 index 0000000..1b374b8 --- /dev/null +++ b/Djup.Native/Program.cs @@ -0,0 +1,3 @@ +/*using Djup.Native;*/ +/**/ +/*LibDjup.hello_world();*/ diff --git a/Djup.Native/Types.cs b/Djup.Native/Types.cs new file mode 100644 index 0000000..c930bd6 --- /dev/null +++ b/Djup.Native/Types.cs @@ -0,0 +1,41 @@ +using System.Runtime.InteropServices; + +namespace Djup.Native; + +[StructLayout(LayoutKind.Sequential)] +public struct Vec2 +{ + public float X { get; } + public float Y { get; } + + public Vec2(float x, float y) + { + X = x; + Y = y; + } + + public override string ToString() => $"({X}, {Y})"; +} + +public enum EntityKind +{ + None = 0, + Ball +} + +[StructLayout(LayoutKind.Explicit)] +public struct Entity +{ + [FieldOffset(0)] + public EntityKind kind; + + [FieldOffset(4)] + public Ball ball; +} + +[StructLayout(LayoutKind.Sequential)] +public struct Ball +{ + public Vec2 Position { get; set; } + public Vec2 Velocity { get; set; } +} diff --git a/Djup.Native/patch-deps.sh b/Djup.Native/patch-deps.sh new file mode 100755 index 0000000..d1f3bbf --- /dev/null +++ b/Djup.Native/patch-deps.sh @@ -0,0 +1,4 @@ +#!/bin/sh +depsjson="$(find "$1" -name '*.deps.json')" +patched="$(jq '.targets.".NETCoreApp,Version=v8.0"."Djup.Native/1.0.0" += {"runtimeTargets": {"runtimes/linux-x64/native/libdjup.so": {"rid": "linux-x64", "assetType": "native"} }}' "$depsjson")" +echo -n "$patched" > "$depsjson" diff --git a/client/.gitattributes b/client/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/client/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..0af181c --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,3 @@ +# Godot 4+ specific ignores +.godot/ +/android/ diff --git a/client/Djup.csproj b/client/Djup.csproj new file mode 100644 index 0000000..0e34542 --- /dev/null +++ b/client/Djup.csproj @@ -0,0 +1,15 @@ +<Project Sdk="Godot.NET.Sdk/4.3.0"> + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net7.0</TargetFramework> + <TargetFramework Condition=" '$(GodotTargetPlatform)' == 'ios' ">net8.0</TargetFramework> + <EnableDynamicLoading>true</EnableDynamicLoading> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\Djup.Native\Djup.Native.csproj" /> + </ItemGroup> + + <Import Project="../Djup.Native/AddRuntimeTargetsToDepsJson.targets" /> +</Project> diff --git a/client/Djup.sln b/client/Djup.sln new file mode 100644 index 0000000..fc24000 --- /dev/null +++ b/client/Djup.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Djup", "Djup.csproj", "{77DE270B-0994-425F-9CA2-4E664136B608}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + ExportDebug|Any CPU = ExportDebug|Any CPU + ExportRelease|Any CPU = ExportRelease|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {77DE270B-0994-425F-9CA2-4E664136B608}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77DE270B-0994-425F-9CA2-4E664136B608}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77DE270B-0994-425F-9CA2-4E664136B608}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU + {77DE270B-0994-425F-9CA2-4E664136B608}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU + {77DE270B-0994-425F-9CA2-4E664136B608}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU + {77DE270B-0994-425F-9CA2-4E664136B608}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU + EndGlobalSection +EndGlobal diff --git a/client/Root.cs b/client/Root.cs new file mode 100644 index 0000000..e39a485 --- /dev/null +++ b/client/Root.cs @@ -0,0 +1,74 @@ +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); + + 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 = new Vec2(50f + 5f * rng.Next(10), 50f); + 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/icon.svg b/client/icon.svg new file mode 100644 index 0000000..9d8b7fa --- /dev/null +++ b/client/icon.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>
\ No newline at end of file diff --git a/client/icon.svg.import b/client/icon.svg.import new file mode 100644 index 0000000..57c75ba --- /dev/null +++ b/client/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cg5s4tfihtewl" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/client/project.godot b/client/project.godot new file mode 100644 index 0000000..6107d87 --- /dev/null +++ b/client/project.godot @@ -0,0 +1,24 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="Djup" +run/main_scene="res://root.tscn" +config/features=PackedStringArray("4.3", "C#", "Forward Plus") +config/icon="res://icon.svg" + +[display] + +window/stretch/mode="canvas_items" + +[dotnet] + +project/assembly_name="Djup" diff --git a/client/root.tscn b/client/root.tscn new file mode 100644 index 0000000..ded95a0 --- /dev/null +++ b/client/root.tscn @@ -0,0 +1,6 @@ +[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/lib/build.sh b/lib/build.sh index 13aaf8e..8cf9d11 100755 --- a/lib/build.sh +++ b/lib/build.sh @@ -8,7 +8,7 @@ fail() { CC=cc CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -D_POSIX_C_SOURCE=200809L" LDFLAGS="-fPIC" -SOURCES="test.c" +SOURCES="test.c vec2.c world.c" output="build" for arg ; do @@ -7,3 +7,18 @@ 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 index afffd4f..0619c8a 100644 --- a/lib/types.h +++ b/lib/types.h @@ -2,3 +2,9 @@ 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); diff --git a/lib/vec2.c b/lib/vec2.c new file mode 100644 index 0000000..b417e9c --- /dev/null +++ b/lib/vec2.c @@ -0,0 +1,46 @@ +#include "types.h" + +vec2 +dp_vec2_new(float x, float y) +{ + vec2 new; + 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; +} diff --git a/lib/world.c b/lib/world.c new file mode 100644 index 0000000..954aa94 --- /dev/null +++ b/lib/world.c @@ -0,0 +1,104 @@ +#include <stdlib.h> +#include <string.h> + +#include "types.h" +#include "world.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 new file mode 100644 index 0000000..40c3fef --- /dev/null +++ b/lib/world.h @@ -0,0 +1,31 @@ +#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); |
