this repo has no description

work on npcs

+32 -11
Kestrel.Framework/Client/Client.cs
··· 92 92 _gl.Enable(EnableCap.DepthTest); 93 93 _gl.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); 94 94 // _gl.PolygonMode(GLEnum.FrontAndBack, GLEnum.Line); 95 - clientState.Window.GL.Enable(GLEnum.CullFace); 96 - clientState.Window.GL.CullFace(GLEnum.Back); 95 + // clientState.Window.GL.Enable(GLEnum.CullFace); 96 + // clientState.Window.GL.CullFace(GLEnum.Back); 97 97 98 98 // Tell GL which triangle winding is considered "front" 99 - clientState.Window.GL.FrontFace(GLEnum.Ccw); 99 + // clientState.Window.GL.FrontFace(GLEnum.Ccw); 100 100 101 101 // Networking 102 102 ··· 179 179 var playerServerId = clientState.Player.Get<ServerId>().Id; 180 180 if (packet.ServerId == playerServerId) 181 181 { 182 + Console.WriteLine("Ignoring player spawn"); 182 183 return; 183 184 } 184 185 185 186 if (!clientState.ServerIdToEntity.ContainsKey(packet.ServerId)) 186 187 { 188 + Console.WriteLine("adding entity"); 187 189 ArchEntity archEntity = clientState.Entities.Create(new ServerId(packet.ServerId)); 188 190 189 191 // entity.Key is the server ID so we add it to the dictionary ··· 196 198 { 197 199 clientState.Player = archEntity; 198 200 } 199 - clientState.Entities.Add(archEntity, component); 201 + 202 + switch (component) 203 + { 204 + case Location location: clientState.Entities.Add(archEntity, location); break; 205 + case Nametag nametag: clientState.Entities.Add(archEntity, nametag); break; 206 + case Player playerC: clientState.Entities.Add(archEntity, playerC); break; 207 + case Velocity velocity: clientState.Entities.Add(archEntity, velocity); break; 208 + case Physics physics: clientState.Entities.Add(archEntity, physics); break; 209 + case Collider collider: clientState.Entities.Add(archEntity, collider); break; 210 + } 200 211 } 201 212 } 202 213 else 203 214 { 215 + Console.WriteLine("Entity already exists"); 204 216 // If the entity already exists we just ignore it for now we might want to change this later 205 217 // to check for new components etc etc 206 218 } ··· 219 231 } 220 232 221 233 ArchEntity entity = clientState.ServerIdToEntity[packet.ServerId]; 222 - entity.Get<Location>().Postion = new Vector3(packet.Position.X, packet.Position.Y, packet.Position.Z); 234 + entity.Get<Location>().Position = new Vector3(packet.Position.X, packet.Position.Y, packet.Position.Z); 223 235 } 224 236 break; 225 237 case Packet.S2CChunkResponse: ··· 276 288 var _keyboard = _input.Keyboards[0]; 277 289 float cameraSpeed = 150.0f * (float)deltaTime; 278 290 279 - clientState.Entities.Query(new Arch.Core.QueryDescription().WithAll<Location>(), (ArchEntity entity, ref Location location) => 291 + clientState.Entities.Query(new Arch.Core.QueryDescription().WithAll<Location, Player>(), (ArchEntity entity, ref Location location, ref Player player) => 280 292 { 293 + if (player.Name != clientState.PlayerName) 294 + return; 295 + 281 296 bool playerMoved = false; 297 + 298 + var actualCameraSpeed = cameraSpeed; 299 + if (_keyboard.IsKeyPressed(Key.ShiftLeft)) 300 + actualCameraSpeed *= 0.2f; 301 + 282 302 if (_keyboard.IsKeyPressed(Key.W)) 283 303 { 284 304 playerMoved = true; 285 - location.Postion += cameraSpeed * clientState.Camera.front.ToVector3(); 305 + location.Position += actualCameraSpeed * clientState.Camera.front.ToVector3(); 286 306 } 287 307 if (_keyboard.IsKeyPressed(Key.S)) 288 308 { 289 309 playerMoved = true; 290 - location.Postion -= cameraSpeed * clientState.Camera.front.ToVector3(); 310 + location.Position -= actualCameraSpeed * clientState.Camera.front.ToVector3(); 291 311 } 292 312 if (_keyboard.IsKeyPressed(Key.A)) 293 313 { 294 314 playerMoved = true; 295 315 296 - location.Postion -= glm.Normalized(glm.Cross(clientState.Camera.front, clientState.Camera.up)).ToVector3() * cameraSpeed; 316 + location.Position -= glm.Normalized(glm.Cross(clientState.Camera.front, clientState.Camera.up)).ToVector3() * actualCameraSpeed; 297 317 } 298 318 if (_keyboard.IsKeyPressed(Key.D)) 299 319 { 300 - location.Postion += glm.Normalized(glm.Cross(clientState.Camera.front, clientState.Camera.up)).ToVector3() * cameraSpeed; 320 + location.Position += glm.Normalized(glm.Cross(clientState.Camera.front, clientState.Camera.up)).ToVector3() * actualCameraSpeed; 301 321 playerMoved = true; 302 322 } 303 323 ··· 338 358 clientState.RequestChunk(new(x, y, z)); 339 359 } 340 360 341 - clientState.NetServer.Send(IPacket.Serialize(new C2SPlayerMove(location.Postion)), DeliveryMethod.ReliableUnordered); 361 + Console.WriteLine("Sending player move packet"); 362 + clientState.NetServer.Send(IPacket.Serialize(new C2SPlayerMove(location.Position)), DeliveryMethod.ReliableUnordered); 342 363 } 343 364 }); 344 365
-1
Kestrel.Framework/Client/ClientState.cs
··· 6 6 using Kestrel.Framework.Client.Graphics.Camera; 7 7 using Kestrel.Framework.Networking.Packets; 8 8 using Kestrel.Framework.Networking.Packets.C2S; 9 - using Kestrel.Framework.Server.Player; 10 9 using Kestrel.Framework.Utils; 11 10 using KestrelWorld = Kestrel.Framework.World.World; 12 11 using LiteNetLib;
+13 -9
Kestrel.Framework/Client/Graphics/Buffers/ChunkRenderer.cs
··· 3 3 using GlmSharp; 4 4 using Kestrel.Framework.Client.Graphics.Shaders; 5 5 using Kestrel.Framework.Entity.Components; 6 - using Kestrel.Framework.Server.Player; 7 6 using Kestrel.Framework.Utils; 8 7 using Kestrel.Framework.World; 9 8 using Silk.NET.Maths; ··· 104 103 } 105 104 106 105 cube.Bind(); 106 + // Console.WriteLine("Before"); 107 107 clientState.Entities.Query(new Arch.Core.QueryDescription().WithAll<Location>(), (ArchEntity entity, ref Location location) => 108 108 { 109 - vec3 pos = location.Postion.ToVec3(); 109 + vec3 pos = location.Position.ToVec3(); 110 + if (entity.Has<Player>() && entity.Get<Player>().Name == clientState.PlayerName) 111 + { 112 + return; 113 + } 110 114 mat4 model = mat4.Identity * mat4.Translate(pos); 111 115 fixed (float* ptr = model.Values1D) 112 116 clientState.Window.GL.UniformMatrix4(_shader.GetUniformLocation("model"), 1, false, ptr); ··· 114 118 clientState.Window.GL.DrawArrays(PrimitiveType.Triangles, 0, 36); 115 119 }); 116 120 117 - { 118 - vec3 pos = clientState.Player.Get<Location>().Postion.ToVec3(); 119 - mat4 model = mat4.Identity * mat4.Translate(pos); 120 - fixed (float* ptr = model.Values1D) 121 - clientState.Window.GL.UniformMatrix4(_shader.GetUniformLocation("model"), 1, false, ptr); 121 + // { 122 + // vec3 pos = clientState.Player.Get<Location>().Postion.ToVec3(); 123 + // mat4 model = mat4.Identity * mat4.Translate(pos); 124 + // fixed (float* ptr = model.Values1D) 125 + // clientState.Window.GL.UniformMatrix4(_shader.GetUniformLocation("model"), 1, false, ptr); 122 126 123 - clientState.Window.GL.DrawArrays(PrimitiveType.Triangles, 0, 36); 124 - } 127 + // clientState.Window.GL.DrawArrays(PrimitiveType.Triangles, 0, 36); 128 + // } 125 129 } 126 130 }
+1 -1
Kestrel.Framework/Client/Graphics/Camera/Camera.cs
··· 32 32 get 33 33 { 34 34 vec3 playerLocation; 35 - playerLocation = clientState.Player.Get<Location>().Postion.ToVec3(); 35 + playerLocation = clientState.Player.Get<Location>().Position.ToVec3(); 36 36 37 37 return mat4.LookAt(playerLocation, playerLocation + front, up); 38 38 }
+1 -1
Kestrel.Framework/Client/Graphics/Camera/ThirdPersonCamera.cs
··· 44 44 get 45 45 { 46 46 // point we want to look at (player “head”) 47 - var target = clientState.Player.Get<Location>().Postion.ToVec3() + new vec3(0, ShoulderHeight, 0); 47 + var target = clientState.Player.Get<Location>().Position.ToVec3() + new vec3(0, ShoulderHeight, 0); 48 48 49 49 // spherical orbit offset from yaw/pitch/distance 50 50 float yawR = glm.Radians(yaw);
-13
Kestrel.Framework/Client/Player/ClientPlayer.cs
··· 1 - using System.Numerics; 2 - using GlmSharp; 3 - using Kestrel.Framework.Utils; 4 - using Kestrel.Framework.World; 5 - 6 - namespace Kestrel.Framework.Server.Player; 7 - 8 - public class ClientPlayer 9 - { 10 - public string Name { get; set; } 11 - public Vector3 Location { get; set; } 12 - public Vector3I LastFrameChunkPos { get; set; } 13 - }
+2
Kestrel.Framework/Entity/ComponentRegistry.cs
··· 15 15 Register(new Location()); 16 16 Register(new Player()); 17 17 Register(new Velocity()); 18 + Register(new Physics()); 19 + Register(new Collider()); 18 20 } 19 21 20 22 public static void Register(INetworkableComponent component)
+22
Kestrel.Framework/Entity/Components/Collider.cs
··· 1 + using LiteNetLib.Utils; 2 + 3 + namespace Kestrel.Framework.Entity.Components; 4 + 5 + public record struct Collider : INetworkableComponent 6 + { 7 + public readonly ushort PacketId => 6; 8 + 9 + public bool IsOnGround = false; 10 + 11 + public Collider() 12 + { 13 + } 14 + 15 + public void Deserialize(NetDataReader reader) 16 + { 17 + } 18 + 19 + public void Serialize(NetDataWriter writer) 20 + { 21 + } 22 + }
+40
Kestrel.Framework/Entity/Components/EntityAi.cs
··· 1 + using System.Numerics; 2 + using LiteNetLib.Utils; 3 + 4 + namespace Kestrel.Framework.Entity.Components; 5 + 6 + public abstract record EntityState 7 + { 8 + public float? StateTime = null; 9 + }; 10 + 11 + public record EntityIdle : EntityState 12 + { 13 + public EntityIdle() 14 + { 15 + StateTime = 5f; 16 + } 17 + }; 18 + 19 + public record EntityWalking : EntityState 20 + { 21 + public Vector3 TargetLocation; 22 + 23 + public EntityWalking(Vector3 location) 24 + { 25 + TargetLocation = location; 26 + StateTime = 5f; 27 + } 28 + }; 29 + 30 + public record struct EntityAi 31 + { 32 + public EntityState State; 33 + public DateTime LastStateChange; 34 + 35 + public EntityAi(EntityState State) 36 + { 37 + this.State = State; 38 + LastStateChange = DateTime.Now; 39 + } 40 + };
+4 -15
Kestrel.Framework/Entity/Components/Location.cs
··· 9 9 { 10 10 public readonly ushort PacketId => 1; 11 11 12 - public Vector3 Postion 13 - { 14 - get => new(X, Y, Z); 15 - set 16 - { 17 - X = value.X; 18 - Y = value.Y; 19 - Z = value.Z; 20 - } 21 - } 12 + public Vector3 Position = new(); 22 13 23 - public float X; 24 - public float Y; 25 - public float Z; 14 + public float X { readonly get { return Position.X; } set { Position.X = value; } } 15 + public float Y { readonly get { return Position.Y; } set { Position.Y = value; } } 16 + public float Z { readonly get { return Position.Z; } set { Position.Z = value; } } 26 17 27 18 public float LastUpdatedX; 28 19 public float LastUpdatedY; ··· 49 40 writer.Put(X); 50 41 writer.Put(Y); 51 42 writer.Put(Z); 52 - Console.WriteLine("Serialized X{0} Y{1} Z{2}", X, Y, Z); 53 43 } 54 44 55 45 public void Deserialize(NetDataReader reader) ··· 57 47 X = reader.GetFloat(); 58 48 Y = reader.GetFloat(); 59 49 Z = reader.GetFloat(); 60 - Console.WriteLine("Deserialized X{0} Y{1} Z{2}", X, Y, Z); 61 50 } 62 51 };
+19
Kestrel.Framework/Entity/Components/Physics.cs
··· 1 + using LiteNetLib.Utils; 2 + 3 + namespace Kestrel.Framework.Entity.Components; 4 + 5 + public record struct Physics : INetworkableComponent 6 + { 7 + public readonly ushort PacketId => 5; 8 + 9 + // 9.98 / 20, 20 = ticks/seconds, 9.92 = Gravity 10 + public const float GRAVITY = -0.491f; 11 + 12 + public void Deserialize(NetDataReader reader) 13 + { 14 + } 15 + 16 + public void Serialize(NetDataWriter writer) 17 + { 18 + } 19 + }
+33 -7
Kestrel.Framework/Entity/Components/Velocity.cs
··· 1 1 namespace Kestrel.Framework.Entity.Components; 2 2 3 + using System.Numerics; 3 4 using LiteNetLib.Utils; 4 5 5 - public record struct Velocity(float X, float Y, float Z) : INetworkableComponent 6 + public record struct Velocity : INetworkableComponent 6 7 { 7 8 public readonly ushort PacketId => 2; 9 + 10 + public Vector3 Vel = new(); 11 + public Vector3 WishVel = new(); 12 + 13 + public float X { readonly get { return Vel.X; } set { Vel.X = value; } } 14 + public float Y { readonly get { return Vel.Y; } set { Vel.Y = value; } } 15 + public float Z { readonly get { return Vel.Z; } set { Vel.Z = value; } } 16 + 17 + 18 + public float DistanceMoved = 0; 19 + 20 + public Velocity(float X, float Y, float Z) 21 + { 22 + Vel.X = X; 23 + Vel.Y = Y; 24 + Vel.Z = Z; 25 + } 8 26 9 27 public void Deserialize(NetDataReader reader) 10 28 { 11 - X = reader.GetFloat(); 12 - Y = reader.GetFloat(); 13 - Z = reader.GetFloat(); 29 + Vel.X = reader.GetFloat(); 30 + Vel.Y = reader.GetFloat(); 31 + Vel.Z = reader.GetFloat(); 32 + 33 + WishVel.X = reader.GetFloat(); 34 + WishVel.Y = reader.GetFloat(); 35 + WishVel.Z = reader.GetFloat(); 14 36 } 15 37 16 38 public void Serialize(NetDataWriter writer) 17 39 { 18 - writer.Put(X); 19 - writer.Put(Y); 20 - writer.Put(Z); 40 + writer.Put(Vel.X); 41 + writer.Put(Vel.Y); 42 + writer.Put(Vel.Z); 43 + 44 + writer.Put(WishVel.X); 45 + writer.Put(WishVel.Y); 46 + writer.Put(WishVel.Z); 21 47 } 22 48 }
-1
Kestrel.Framework/Networking/Packets/C2S/C2SChunkRequest.cs
··· 2 2 using System.Text; 3 3 using Kestrel.Framework.Networking.Packets.S2C; 4 4 using Kestrel.Framework.Server; 5 - using Kestrel.Framework.Server.Player; 6 5 using Kestrel.Framework.Utils; 7 6 using Kestrel.Framework.World; 8 7 using LiteNetLib;
-1
Kestrel.Framework/Networking/Packets/C2S/C2SPlayerLoginRequest.cs
··· 5 5 using Kestrel.Framework.Entity.Components; 6 6 using Kestrel.Framework.Networking.Packets.S2C; 7 7 using Kestrel.Framework.Server; 8 - using Kestrel.Framework.Server.Player; 9 8 using LiteNetLib; 10 9 using LiteNetLib.Utils; 11 10 using ArchEntity = Arch.Core.Entity;
-1
Kestrel.Framework/Networking/Packets/C2S/C2SPlayerMove.cs
··· 5 5 using Kestrel.Framework.Entity.Components; 6 6 using Kestrel.Framework.Networking.Packets.S2C; 7 7 using Kestrel.Framework.Server; 8 - using Kestrel.Framework.Server.Player; 9 8 using LiteNetLib; 10 9 using LiteNetLib.Utils; 11 10 using ArchEntity = Arch.Core.Entity;
-1
Kestrel.Framework/Networking/Packets/S2C/S2CBroadcastEntityMove.cs
··· 2 2 using Arch.Core.Extensions; 3 3 using GlmSharp; 4 4 using Kestrel.Framework.Entity.Components; 5 - using Kestrel.Framework.Server.Player; 6 5 using LiteNetLib; 7 6 using LiteNetLib.Utils; 8 7 using ArchEntity = Arch.Core.Entity;
-1
Kestrel.Framework/Networking/Packets/S2C/S2CBroadcastEntitySpawn.cs
··· 5 5 using GlmSharp; 6 6 using Kestrel.Framework.Entity; 7 7 using Kestrel.Framework.Entity.Components; 8 - using Kestrel.Framework.Server.Player; 9 8 using LiteNetLib; 10 9 using LiteNetLib.Utils; 11 10 using ArchEntity = Arch.Core.Entity;
+1 -2
Kestrel.Framework/Networking/Packets/S2C/S2CBroadcastPlayerJoin.cs
··· 1 1 // using System.Numerics; 2 2 // using GlmSharp; 3 - // using Kestrel.Framework.Server.Player; 4 - // using LiteNetLib; 3 + // // using LiteNetLib; 5 4 // using LiteNetLib.Utils; 6 5 7 6 // namespace Kestrel.Framework.Networking.Packets.S2C;
-1
Kestrel.Framework/Networking/Packets/S2C/S2CChunkResponse.cs
··· 1 1 using System.Numerics; 2 2 using GlmSharp; 3 3 using Kestrel.Framework.Client.Graphics.Buffers; 4 - using Kestrel.Framework.Server.Player; 5 4 using Kestrel.Framework.Utils; 6 5 using Kestrel.Framework.World; 7 6 using LiteNetLib;
-1
Kestrel.Framework/Networking/Packets/S2C/S2CPlayerLoginSuccess.cs
··· 4 4 using GlmSharp; 5 5 using Kestrel.Framework.Entity; 6 6 using Kestrel.Framework.Networking.Packets.C2S; 7 - using Kestrel.Framework.Server.Player; 8 7 using Kestrel.Framework.Utils; 9 8 using Kestrel.Framework.World; 10 9 using LiteNetLib;
+116 -20
Kestrel.Framework/Server/Server.cs
··· 14 14 using Kestrel.Framework.Networking.Packets.S2C; 15 15 using Arch.Core.Extensions; 16 16 using Kestrel.Framework.Entity; 17 + using System.Reflection.Metadata.Ecma335; 18 + using System.Net; 17 19 18 20 namespace Kestrel.Framework.Server; 19 21 ··· 75 77 else 76 78 { 77 79 var guid = Guid.NewGuid(); 78 - player = ServerState.Entities.Create(new ServerId(guid), new Entity.Components.Player(packet.PlayerName!), new Location(ServerState.World, -416, 100, 383), new Nametag(packet.PlayerName!), new Velocity(0, 0, 0)); 80 + player = ServerState.Entities.Create(new ServerId(guid), new Entity.Components.Player(packet.PlayerName!), new Location(ServerState.World, -416, 100, 383), new Nametag(packet.PlayerName!), new Velocity(0, 0, 0), new Physics()); 79 81 80 82 ServerState.PlayersByName.TryAdd(packet.PlayerName!, player); 81 83 ServerState.PlayersByConnection.TryAdd(client, player); ··· 86 88 Console.WriteLine("Found {0} eligible entities to send.", ServerState.NetworkableEntities.Count); 87 89 foreach (var entity in ServerState.NetworkableEntities) 88 90 { 89 - List<INetworkableComponent> serializedComponents = []; 90 - var entityComponents = ServerState.Entities.GetAllComponents(entity.Value); 91 - 92 - foreach (var _component in entityComponents) 93 - { 94 - if (_component is INetworkableComponent component) 95 - { 96 - serializedComponents.Add(component); 97 - } 98 - } 99 - serializedEntities.Add(entity.Key, [.. serializedComponents]); 91 + var serializedComponents = ServerState.GetNetworkableComponents(entity.Value); 92 + serializedEntities.Add(entity.Key, serializedComponents); 100 93 } 101 94 var loginSuccess = new S2CPlayerLoginSuccess 102 95 { ··· 120 113 }, (i) => 121 114 { 122 115 var chunkPos = chunks[i]; 123 - generatedChunks[i] = ServerState.World.GetChunkOrGenerate(chunkPos.X, chunkPos.Y, chunkPos.Z); 116 + var chunk = ServerState.World.GetChunkOrGenerate(chunkPos.X, chunkPos.Y, chunkPos.Z, out var generated); 117 + generatedChunks[i] = chunk; 118 + bool addedEntity = false; 119 + if (generated) 120 + { 121 + for (int lx = 0; lx < ServerState.World.ChunkSize && !addedEntity; lx++) 122 + { 123 + for (int ly = 0; ly < ServerState.World.ChunkSize && !addedEntity; ly++) 124 + { 125 + for (int lz = 0; lz < ServerState.World.ChunkSize && !addedEntity; lz++) 126 + { 127 + var block = chunk.GetBlock(lx, ly, lz); 128 + var below = chunk.GetBlock(lx, ly - 1, lz); 129 + 130 + if (below.HasValue && block.HasValue) 131 + { 132 + if (below.Value == World.BlockType.Grass && block.Value == World.BlockType.Air) 133 + { 134 + (int wx, int wy, int wz) = chunk.ChunkToWorld(lx, ly, lz); 135 + ServerState.SpawnEntity(wx, wy, wz); 136 + addedEntity = true; 137 + } 138 + } 139 + } 140 + } 141 + } 142 + } 124 143 }); 125 144 126 145 client.Send(IPacket.Serialize(new S2CChunkResponse(generatedChunks)), DeliveryMethod.ReliableUnordered); ··· 136 155 Console.WriteLine($"Client not found in context."); 137 156 return; 138 157 } 139 - player.Get<Location>().Postion = packet.Location; 158 + player.Get<Location>().Position = packet.Location; 140 159 var playerServerId = player.Get<ServerId>().Id; 141 160 142 161 ServerState.NetServer.SendToAll(IPacket.Serialize(new S2CBroadcastEntityMove() 143 162 { 144 163 ServerId = playerServerId, 145 164 Position = new Vector3(packet.Location.X, packet.Location.Y, packet.Location.Z) 146 - }), DeliveryMethod.ReliableOrdered); 165 + }), DeliveryMethod.Unreliable); 147 166 } 148 167 break; 149 168 } ··· 152 171 } 153 172 ; 154 173 174 + const int targetTickRate = 20; // 20 ticks per second 175 + const double tickInterval = 1000.0 / targetTickRate; // in ms 176 + var stopwatch = new System.Diagnostics.Stopwatch(); 177 + stopwatch.Start(); 178 + double accumulated = 0; 155 179 156 180 while (!Console.KeyAvailable) 157 181 { 158 182 ServerState.NetServer.PollEvents(); 159 183 160 - ServerState.Entities.Query(new QueryDescription().WithAll<Location>(), (ArchEntity entity, ref Location location) => 184 + accumulated += stopwatch.Elapsed.TotalMilliseconds; 185 + stopwatch.Restart(); 186 + 187 + while (accumulated >= tickInterval) 161 188 { 162 - if (LocationUtil.Distance(new(location.X, location.Y, location.Z), new(location.LastUpdatedX, location.LastUpdatedY, location.LastUpdatedZ)) > 5) 163 - { 189 + Tick(); 190 + accumulated -= tickInterval; 191 + } 164 192 165 - } 166 - }); 193 + // Thread.Sleep(1); 167 194 } 168 195 ServerState.NetServer.Stop(); 196 + } 197 + 198 + public void Tick() 199 + { 200 + ServerState.Entities.Query(new QueryDescription().WithAll<Velocity, Physics>(), (ArchEntity entity, ref Velocity velocity, ref Physics physics) => 201 + { 202 + if (entity.Has<Player>()) 203 + return; 204 + 205 + velocity.Y += Physics.GRAVITY; 206 + 207 + // Cap y velocity 208 + velocity.Y = MathF.Min(velocity.Y, 3); 209 + }); 210 + 211 + ServerState.Entities.Query(new QueryDescription().WithAll<EntityAi>(), (ArchEntity entity, ref EntityAi entityAi) => 212 + { 213 + var secondsSinceLastStateChange = (DateTime.Now - entityAi.LastStateChange).Milliseconds / 1000f; 214 + if (entityAi.State.StateTime < secondsSinceLastStateChange) 215 + { 216 + var random = new Random(); 217 + if (random.NextDouble() < 0.5) 218 + { 219 + entityAi.State = new EntityIdle(); 220 + } 221 + else 222 + { 223 + entityAi.State = new EntityWalking(); 224 + } 225 + } 226 + }); 227 + 228 + ServerState.Entities.Query( 229 + new QueryDescription().WithAll<Location, Velocity, Collider>(), 230 + (ArchEntity entity, ref Location location, ref Velocity velocity, ref Collider collider) => 231 + { 232 + if (entity.Has<Player>()) 233 + return; 234 + 235 + Vector3 startPos = location.Position; 236 + 237 + collider.IsOnGround = false; 238 + 239 + // TODO: Add deltatime 240 + Vector3 move = velocity.Vel; 241 + 242 + AxisResolver.MoveAxis(ServerState.World, ref location, ref velocity, ref collider, AxisResolver.Axis.Y, move.Y); 243 + AxisResolver.MoveAxis(ServerState.World, ref location, ref velocity, ref collider, AxisResolver.Axis.X, move.X); 244 + AxisResolver.MoveAxis(ServerState.World, ref location, ref velocity, ref collider, AxisResolver.Axis.Z, move.Z); 245 + 246 + Vector3 displacement = location.Position - startPos; 247 + float distanceMoved = displacement.Length(); 248 + velocity.DistanceMoved += distanceMoved; 249 + 250 + // velocity.DistanceMoved > 1f && 251 + if (entity.Has<ServerId>()) 252 + { 253 + var serverId = entity.Get<ServerId>(); 254 + 255 + var packet = new S2CBroadcastEntityMove 256 + { 257 + ServerId = serverId.Id, 258 + Position = location.Position 259 + }; 260 + 261 + velocity.DistanceMoved = 0f; 262 + ServerState.NetServer.SendToAll(IPacket.Serialize(packet), DeliveryMethod.Unreliable); 263 + } 264 + }); 169 265 } 170 266 }
+32 -2
Kestrel.Framework/Server/ServerState.cs
··· 1 1 using System.Collections.Concurrent; 2 - using Kestrel.Framework.Server.Player; 3 2 using Kestrel.Framework.World; 4 3 using LiteNetLib; 5 4 using ArchWorld = Arch.Core.World; 6 5 using ArchEntity = Arch.Core.Entity; 6 + using Kestrel.Framework.Entity.Components; 7 + using Arch.Core.Extensions; 8 + using Kestrel.Framework.Networking.Packets.S2C; 9 + using Kestrel.Framework.Entity; 10 + using System.Net; 11 + using Kestrel.Framework.Networking.Packets; 7 12 8 13 namespace Kestrel.Framework.Server; 9 14 ··· 16 21 public ArchWorld Entities; 17 22 public ConcurrentDictionary<Guid, ArchEntity> NetworkableEntities = []; 18 23 19 - public void SpawnEntity() 24 + public INetworkableComponent[] GetNetworkableComponents(ArchEntity entity) 20 25 { 26 + List<INetworkableComponent> serializedComponents = []; 27 + var entityComponents = Entities.GetAllComponents(entity); 21 28 29 + foreach (var _component in entityComponents) 30 + { 31 + if (_component is INetworkableComponent component) 32 + { 33 + serializedComponents.Add(component); 34 + } 35 + } 36 + 37 + return [.. serializedComponents]; 38 + } 39 + 40 + public void SpawnEntity(int x, int y, int z) 41 + { 42 + var guid = Guid.NewGuid(); 43 + ArchEntity entity = Entities.Create(new ServerId(guid), new Location(World, x, y + 20, z), new Velocity(0, 0, 0), new Physics(), new Collider(), new EntityAi(new EntityIdle())); 44 + NetworkableEntities.TryAdd(guid, entity); 45 + 46 + var spawnPacket = new S2CBroadcastEntitySpawn 47 + { 48 + ServerId = guid, 49 + Components = GetNetworkableComponents(entity) 50 + }; 51 + NetServer.SendToAll(IPacket.Serialize(spawnPacket), DeliveryMethod.Unreliable); 22 52 } 23 53 }
+75
Kestrel.Framework/Utils/AxisResolver.cs
··· 1 + using System.Numerics; 2 + using Kestrel.Framework.Entity.Components; 3 + using Kestrel.Framework.World; 4 + 5 + namespace Kestrel.Framework.Utils; 6 + 7 + public static class AxisResolver 8 + { 9 + public enum Axis { X, Y, Z } 10 + 11 + public static void MoveAxis(World.World world, ref Location pos, ref Velocity vel, ref Collider col, Axis axis, float delta) 12 + { 13 + if (delta == 0f) return; 14 + 15 + float sign = MathF.Sign(delta); 16 + float remaining = MathF.Abs(delta); 17 + 18 + while (remaining > 0f) 19 + { 20 + float step = MathF.Min(remaining, 1f); 21 + Vector3 newPos = pos.Position; 22 + 23 + switch (axis) 24 + { 25 + case Axis.X: newPos.X += sign * step; break; 26 + case Axis.Y: newPos.Y += sign * step; break; 27 + case Axis.Z: newPos.Z += sign * step; break; 28 + } 29 + 30 + (var isColliding, var collidingPos) = IsColliding(world, newPos); 31 + if (isColliding && collidingPos.HasValue) 32 + { 33 + switch (axis) 34 + { 35 + case Axis.X: 36 + vel.X = 0f; pos.X = collidingPos.Value.X; 37 + break; 38 + case Axis.Y: 39 + if (sign < 0) col.IsOnGround = true; 40 + pos.Y = collidingPos.Value.Y; 41 + vel.Y = 0f; break; 42 + case Axis.Z: 43 + vel.Z = 0f; pos.Z = collidingPos.Value.Z; 44 + break; 45 + } 46 + return; 47 + } 48 + 49 + pos.Position = newPos; 50 + remaining -= step; 51 + } 52 + } 53 + 54 + public static (bool, Vector3?) IsColliding(World.World world, Vector3 pos) 55 + { 56 + int minX = (int)MathF.Floor(pos.X); 57 + int maxX = (int)MathF.Floor(pos.X); 58 + int minY = (int)MathF.Floor(pos.Y); 59 + int maxY = (int)MathF.Floor(pos.Y); 60 + int minZ = (int)MathF.Floor(pos.Z); 61 + int maxZ = (int)MathF.Floor(pos.Z); 62 + 63 + for (int x = minX; x <= maxX; x++) 64 + for (int y = minY; y <= maxY; y++) 65 + for (int z = minZ; z <= maxZ; z++) 66 + { 67 + var block = world.GetBlock(x, y, z); 68 + if (block.IsSolid()) 69 + return (true, new(x + 1, y + 1, z + 1)); 70 + } 71 + 72 + return (false, null); 73 + } 74 + 75 + }
+6
Kestrel.Framework/World/BlockType.cs
··· 8 8 Grass = 3, 9 9 Water = 4, 10 10 Leaves = 5 11 + } 12 + 13 + public static class BlockTypeExtensions 14 + { 15 + public static bool IsSolid(this BlockType? blockType) => blockType != BlockType.Air; 16 + public static bool IsSolid(this BlockType blockType) => blockType != BlockType.Air; 11 17 }
+5 -5
Kestrel.Framework/World/Chunk.cs
··· 34 34 }); 35 35 } 36 36 37 - public BlockType? GetBlock(int x, int y, int z) 37 + public BlockType? GetBlock(int lx, int ly, int lz) 38 38 { 39 - if (x < 0 || x >= World.ChunkSize || 40 - y < 0 || y >= World.ChunkSize || 41 - z < 0 || z >= World.ChunkSize) 39 + if (lx < 0 || lx >= World.ChunkSize || 40 + ly < 0 || ly >= World.ChunkSize || 41 + lz < 0 || lz >= World.ChunkSize) 42 42 { 43 43 return null; 44 44 } 45 - return Blocks[ChunkToIndex(x, y, z)]; 45 + return Blocks[ChunkToIndex(lx, ly, lz)]; 46 46 } 47 47 48 48 public void SetBlock(int lx, int ly, int lz, BlockType block)
+13 -1
Kestrel.Framework/World/World.cs
··· 17 17 Generator = new(this); 18 18 } 19 19 20 - public Chunk GetChunkOrGenerate(int cx, int cy, int cz) 20 + public Chunk GetChunkOrGenerate(int cx, int cy, int cz, out bool generated) 21 21 { 22 22 Vector3I chunkPos = new(cx, cy, cz); 23 23 if (_chunks.TryGetValue(chunkPos, out var chunk)) 24 24 { 25 + generated = false; 25 26 return chunk; 26 27 } 27 28 28 29 Chunk newChunk = new(this, cx, cy, cz); 29 30 newChunk.Generate(); 30 31 _chunks.TryAdd(chunkPos, newChunk); 32 + generated = true; 31 33 return newChunk; 32 34 } 33 35 ··· 58 60 59 61 public void SetChunk(int cx, int cy, int cz, Chunk chunk) => 60 62 _chunks[new Vector3I(cx, cy, cz)] = chunk; 63 + 64 + public BlockType? GetBlock(int wx, int wy, int wz) 65 + { 66 + WorldToChunk(wx, wy, wz, out var chunkPos, out var localPos); 67 + var chunk = GetChunk(chunkPos.X, chunkPos.Y, chunkPos.Z); 68 + if (chunk == null) 69 + return null; 70 + 71 + return chunk.GetBlock(localPos.lx, localPos.ly, localPos.lz); 72 + } 61 73 62 74 public static void WorldToChunk(int wx, int wy, int wz, int chunkSize, 63 75 out Vector3I cpos, out (int lx, int ly, int lz) local)
+1 -1
Kestrel.Framework/obj/Debug/net8.0/Kestrel.Framework.AssemblyInfo.cs
··· 13 13 [assembly: System.Reflection.AssemblyCompanyAttribute("Kestrel.Framework")] 14 14 [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] 15 15 [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] 16 - [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+043631eed54cefe8c936a522f98924f0e5de1d39")] 16 + [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+03aefa68cc89324991f72372f45e23b26e6af377")] 17 17 [assembly: System.Reflection.AssemblyProductAttribute("Kestrel.Framework")] 18 18 [assembly: System.Reflection.AssemblyTitleAttribute("Kestrel.Framework")] 19 19 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
+1 -1
Kestrel.Framework/obj/Debug/net8.0/Kestrel.Framework.AssemblyInfoInputs.cache
··· 1 - 462c289644be15e5f833b48314aea9a303b052a9e228356a827b30e4e88e2338 1 + 7aa652efadbe480e9f1948dced67b3118a495d342ba47eacd7e935369f8c9db0
+1 -1
Kestrel.Framework/obj/Debug/net8.0/Kestrel.Framework.csproj.CoreCompileInputs.cache
··· 1 - f44061e7c827b18837aa748c268d8054bfe867690d340a6d687e1a022367c6a3 1 + e71efa90d0651cf4e94cb789845a4e38cb78f02265a6c43f946689478340b9ee
Kestrel.Framework/obj/Debug/net8.0/Kestrel.Framework.dll

This is a binary file and will not be displayed.

Kestrel.Framework/obj/Debug/net8.0/Kestrel.Framework.pdb

This is a binary file and will not be displayed.

Kestrel.Framework/obj/Debug/net8.0/ref/Kestrel.Framework.dll

This is a binary file and will not be displayed.

Kestrel.Framework/obj/Debug/net8.0/refint/Kestrel.Framework.dll

This is a binary file and will not be displayed.

+1 -1
Kestrel.Game.Client/obj/Debug/net8.0/Kestrel.Game.Client.AssemblyInfo.cs
··· 13 13 [assembly: System.Reflection.AssemblyCompanyAttribute("Kestrel.Game.Client")] 14 14 [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] 15 15 [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] 16 - [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+043631eed54cefe8c936a522f98924f0e5de1d39")] 16 + [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+03aefa68cc89324991f72372f45e23b26e6af377")] 17 17 [assembly: System.Reflection.AssemblyProductAttribute("Kestrel.Game.Client")] 18 18 [assembly: System.Reflection.AssemblyTitleAttribute("Kestrel.Game.Client")] 19 19 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
+1 -1
Kestrel.Game.Client/obj/Debug/net8.0/Kestrel.Game.Client.AssemblyInfoInputs.cache
··· 1 - 914ddb5c62ff20df059e345c93e4dd890feb5d6fe17f883656005c68a571063f 1 + 6d2445ecad05f08d23a7362331f26c836109134a3faaed7bd91a21d873132553
Kestrel.Game.Client/obj/Debug/net8.0/Kestrel.Game.Client.csproj.AssemblyReference.cache

This is a binary file and will not be displayed.

Kestrel.Game.Client/obj/Debug/net8.0/Kestrel.Game.Client.dll

This is a binary file and will not be displayed.

Kestrel.Game.Client/obj/Debug/net8.0/Kestrel.Game.Client.pdb

This is a binary file and will not be displayed.

+1 -1
Kestrel.Game.Client/obj/Debug/net8.0/Kestrel.Game.Client.sourcelink.json
··· 1 - {"documents":{"/home/keii/Coding/voxel/*":"https://raw.githubusercontent.com/dinkelspiel/kestrel/043631eed54cefe8c936a522f98924f0e5de1d39/*"}} 1 + {"documents":{"/home/keii/Coding/voxel/*":"https://raw.githubusercontent.com/dinkelspiel/kestrel/03aefa68cc89324991f72372f45e23b26e6af377/*"}}
Kestrel.Game.Client/obj/Debug/net8.0/ref/Kestrel.Game.Client.dll

This is a binary file and will not be displayed.

Kestrel.Game.Client/obj/Debug/net8.0/refint/Kestrel.Game.Client.dll

This is a binary file and will not be displayed.