diff --git a/Obsidian.API/_Enums/EntityType.cs b/Obsidian.API/_Enums/EntityType.cs index edcab10e..3454637a 100644 --- a/Obsidian.API/_Enums/EntityType.cs +++ b/Obsidian.API/_Enums/EntityType.cs @@ -2,47 +2,58 @@ public enum EntityType : int { + AcaciaBoat, + AcaciaChestBoat, Allay, AreaEffectCloud, Armadillo, ArmorStand, Arrow, Axolotl, + BambooChestRaft, + BambooRaft, Bat, Bee, + BirchBoat, + BirchChestBoat, Blaze, BlockDisplay, - Boat, Bogged, Breeze, BreezeWindCharge, Camel, Cat, CaveSpider, - ChestBoat, + CherryBoat, + CherryChestBoat, ChestMinecart, Chicken, Cod, CommandBlockMinecart, Cow, + Creaking, + CreakingTransient, Creeper, + DarkOakBoat, + DarkOakChestBoat, Dolphin, Donkey, DragonFireball, Drowned, Egg, ElderGuardian, - EndCrystal, - EnderDragon, - EnderPearl, Enderman, Endermite, + EnderDragon, + EnderPearl, + EndCrystal, Evoker, EvokerFangs, ExperienceBottle, ExperienceOrb, EyeOfEnder, FallingBlock, + Fireball, FireworkRocket, Fox, Frog, @@ -63,19 +74,26 @@ public enum EntityType : int Item, ItemDisplay, ItemFrame, - OminousItemSpawner, - Fireball, + JungleBoat, + JungleChestBoat, LeashKnot, LightningBolt, Llama, LlamaSpit, MagmaCube, + MangroveBoat, + MangroveChestBoat, Marker, Minecart, Mooshroom, Mule, + OakBoat, + OakChestBoat, Ocelot, + OminousItemSpawner, Painting, + PaleOakBoat, + PaleOakChestBoat, Panda, Parrot, Phantom, @@ -98,11 +116,13 @@ public enum EntityType : int Slime, SmallFireball, Sniffer, - SnowGolem, Snowball, + SnowGolem, SpawnerMinecart, SpectralArrow, Spider, + SpruceBoat, + SpruceChestBoat, Squid, Stray, Strider, diff --git a/Obsidian.API/_Enums/Gamemode.cs b/Obsidian.API/_Enums/Gamemode.cs index e1af08cd..78958840 100644 --- a/Obsidian.API/_Enums/Gamemode.cs +++ b/Obsidian.API/_Enums/Gamemode.cs @@ -1,7 +1,8 @@ namespace Obsidian.API; -public enum Gamemode : byte +public enum Gamemode : sbyte { + None = -1, Survival, Creative, Adventure, diff --git a/Obsidian.API/_Types/CommonPlayerSpawnInfo.cs b/Obsidian.API/_Types/CommonPlayerSpawnInfo.cs new file mode 100644 index 00000000..5cf306f5 --- /dev/null +++ b/Obsidian.API/_Types/CommonPlayerSpawnInfo.cs @@ -0,0 +1,45 @@ +namespace Obsidian.API; +public readonly struct CommonPlayerSpawnInfo : INetworkSerializable +{ + public required int DimensionType { get; init; } + + public required string DimensionName { get; init; } + public long HashedSeed { get; init; } + public Gamemode Gamemode { get; init; } + public Gamemode PreviousGamemode { get; init; } + + public bool Debug { get; init; } + + public bool Flat { get; init; } + public bool HasDeathLocation => !string.IsNullOrEmpty(this.DeathDimensionName); + public string? DeathDimensionName { get; init; } + public Vector? DeathLocation { get; init; } + + public int PortalCooldown { get; init; } + + public int SeaLevel { get; init; } + + public static void Write(CommonPlayerSpawnInfo value, INetStreamWriter writer) + { + writer.WriteVarInt(value.DimensionType); + writer.WriteString(value.DimensionName); + + writer.WriteLong(value.HashedSeed); + + writer.WriteByte(value.Gamemode); + writer.WriteByte(value.PreviousGamemode); + + writer.WriteBoolean(value.Debug); + writer.WriteBoolean(value.Flat); + writer.WriteBoolean(value.HasDeathLocation); + + if (value.HasDeathLocation) + { + writer.WriteString(value.DeathDimensionName!); + writer.WritePosition(value.DeathLocation!.Value); + } + + writer.WriteVarInt(value.PortalCooldown); + writer.WriteVarInt(value.SeaLevel); + } +} diff --git a/Obsidian/Client.cs b/Obsidian/Client.cs index 8f275ae4..37ac50a5 100644 --- a/Obsidian/Client.cs +++ b/Obsidian/Client.cs @@ -571,22 +571,32 @@ internal async Task ConnectAsync() await QueuePacketAsync(new LoginPacket { EntityId = id, - Gamemode = Player.Gamemode, DimensionNames = CodecRegistry.Dimensions.All.Keys.ToList(), - DimensionType = codec.Id, - DimensionName = codec.Name, - HashedSeed = 0, + CommonPlayerSpawnInfo = new() + { + Gamemode = Player.Gamemode, + DimensionType = codec.Id, + DimensionName = codec.Name, + HashedSeed = 0, + Flat = false + }, ReducedDebugInfo = false, EnableRespawnScreen = true, - Flat = false }); + await QueuePacketAsync(new SetDefaultSpawnPositionPacket(Player.world.LevelData.SpawnPosition, 0)); + await SendTimeUpdateAsync(); + await SendWeatherUpdateAsync(); + await SendServerBrand(); await SendCommandsAsync(); + //Information has to be sent in a certain order or the client will auto throw a network protocol error. await SendPlayerInfoAsync(); - await this.QueuePacketAsync(new GameEventPacket(ChangeGameStateReason.StartWaitingForLevelChunks)); + await SendInfoAsync(); + + await QueuePacketAsync(new GameEventPacket(ChangeGameStateReason.StartWaitingForLevelChunks)); Player.TeleportId = Globals.Random.Next(0, 999); await QueuePacketAsync(new PlayerPositionPacket @@ -599,7 +609,6 @@ await QueuePacketAsync(new PlayerPositionPacket }); await Player.UpdateChunksAsync(distance: 7); - //await SendInfoAsync(); await this.server.EventDispatcher.ExecuteEventAsync(new PlayerJoinEventArgs(Player, this.server, DateTimeOffset.Now)); } @@ -608,22 +617,18 @@ internal async Task SendInfoAsync() { if (Player is null) throw new UnreachableException("Player is null, which means the client has not yet logged in."); + + await QueuePacketAsync(new ContainerSetContentPacket(0, Player.Inventory.ToList()) + { + StateId = Player.Inventory.StateId++, + CarriedItem = Player.GetHeldItem(), + }); - //await QueuePacketAsync(new SetDefaultSpawnPositionPacket(Player.world.LevelData.SpawnPosition, 0)); - - //await SendTimeUpdateAsync(); - //await SendWeatherUpdateAsync(); - //await QueuePacketAsync(new ContainerSetContentPacket(0, Player.Inventory.ToList()) - //{ - // StateId = Player.Inventory.StateId++, - // CarriedItem = Player.GetHeldItem(), - //}); - - //await QueuePacketAsync(new SetEntityDataPacket - //{ - // EntityId = this.Player.EntityId, - // Entity = this.Player - //}); + await QueuePacketAsync(new SetEntityDataPacket + { + EntityId = this.Player.EntityId, + Entity = this.Player + }); } internal async Task DisconnectAsync(ChatMessage reason) diff --git a/Obsidian/Entities/Player.cs b/Obsidian/Entities/Player.cs index 22335be0..08b63aba 100644 --- a/Obsidian/Entities/Player.cs +++ b/Obsidian/Entities/Player.cs @@ -363,13 +363,16 @@ public async Task RespawnAsync(DataKept dataKept = DataKept.Metadata) await client.QueuePacketAsync(new RespawnPacket { - DimensionType = codec.Name, - DimensionName = world.DimensionName, - Gamemode = Gamemode, - PreviousGamemode = Gamemode, - HashedSeed = 0, - IsFlat = false, - IsDebug = false, + CommonPlayerSpawnInfo = new() + { + DimensionType = codec.Id, + DimensionName = world.DimensionName, + Gamemode = Gamemode, + PreviousGamemode = Gamemode, + HashedSeed = 0, + Flat = false, + Debug = false, + }, DataKept = dataKept, }); diff --git a/Obsidian/Net/MinecraftStream.Writing.cs b/Obsidian/Net/MinecraftStream.Writing.cs index bad8912b..3b2f5d15 100644 --- a/Obsidian/Net/MinecraftStream.Writing.cs +++ b/Obsidian/Net/MinecraftStream.Writing.cs @@ -1,7 +1,6 @@ using Microsoft.CodeAnalysis; using Obsidian.API.Advancements; using Obsidian.API.Crafting; -using Obsidian.API.Events; using Obsidian.API.Inventory; using Obsidian.API.Registry.Codecs.ArmorTrims.TrimMaterial; using Obsidian.API.Registry.Codecs.ArmorTrims.TrimPattern; @@ -20,7 +19,6 @@ using Obsidian.Net.WindowProperties; using Obsidian.Registries; using Obsidian.Serialization.Attributes; -using Org.BouncyCastle.Bcpg; using System.Buffers.Binary; using System.Text; using System.Text.Json; diff --git a/Obsidian/Net/Packets/Common/ClientInformationPacket.cs b/Obsidian/Net/Packets/Common/ClientInformationPacket.cs index 1cfaaaf7..968efce5 100644 --- a/Obsidian/Net/Packets/Common/ClientInformationPacket.cs +++ b/Obsidian/Net/Packets/Common/ClientInformationPacket.cs @@ -59,6 +59,6 @@ public async override ValueTask HandleAsync(Server server, Player player) ParticleStatus = ParticleStatus }; - await player.client.SendInfoAsync(); + } } diff --git a/Obsidian/Net/Packets/Play/Clientbound/LoginPacket.cs b/Obsidian/Net/Packets/Play/Clientbound/LoginPacket.cs index 0c89995a..25aa80a5 100644 --- a/Obsidian/Net/Packets/Play/Clientbound/LoginPacket.cs +++ b/Obsidian/Net/Packets/Play/Clientbound/LoginPacket.cs @@ -5,13 +5,13 @@ namespace Obsidian.Net.Packets.Play.Clientbound; public partial class LoginPacket { [Field(0)] - public int EntityId { get; init; } + public required int EntityId { get; init; } [Field(1)] public bool Hardcore { get; init; } = false; [Field(2)] - public List DimensionNames { get; init; } + public required List DimensionNames { get; init; } [Field(3), VarLength] private const int MaxPlayers = 0; @@ -31,43 +31,10 @@ public partial class LoginPacket [Field(8)] public bool DoLimitedCrafting { get; init; } = false; - [Field(9), VarLength] - public int DimensionType { get; init; } + [Field(9)] + public required CommonPlayerSpawnInfo CommonPlayerSpawnInfo { get; init; } [Field(10)] - public string DimensionName { get; init; } - - [Field(11)] - public long HashedSeed { get; init; } - - [Field(12), ActualType(typeof(byte))] - public Gamemode Gamemode { get; init; } = Gamemode.Survival; - - [Field(13), ActualType(typeof(sbyte))] - public Gamemode PreviousGamemode { get; init; } = Gamemode.Survival; - - [Field(14)] - public bool Debug { get; init; } = false; - - [Field(15)] - public bool Flat { get; init; } = false; - - [Field(16)] - public bool HasDeathLocation { get; init; } - - [Field(17), Condition("HasDeathLocation")] - public string? DeathDimensionName { get; init; } - - [Field(18), Condition("HasDeathLocation")] - public Vector? DeathLocation { get; init; } - - [Field(19), VarLength] - public int PortalCooldown { get; init; } - - [Field(20), VarLength] - public int SeaLevel { get; init; } - - [Field(21)] public bool EnforcesSecureChat { get; init; } public override void Serialize(INetStreamWriter writer) @@ -76,7 +43,7 @@ public override void Serialize(INetStreamWriter writer) writer.WriteBoolean(this.Hardcore); writer.WriteVarInt(this.DimensionNames.Count); - foreach(var dimName in this.DimensionNames) + foreach (var dimName in this.DimensionNames) writer.WriteString(dimName); writer.WriteVarInt(MaxPlayers); @@ -87,26 +54,7 @@ public override void Serialize(INetStreamWriter writer) writer.WriteBoolean(this.EnableRespawnScreen); writer.WriteBoolean(this.DoLimitedCrafting); - writer.WriteVarInt(this.DimensionType); - writer.WriteString(this.DimensionName); - - writer.WriteLong(this.HashedSeed); - - writer.WriteByte(this.Gamemode); - writer.WriteByte(this.PreviousGamemode); - - writer.WriteBoolean(this.Debug); - writer.WriteBoolean(this.Flat); - writer.WriteBoolean(this.HasDeathLocation); - - if (this.HasDeathLocation) - { - writer.WriteString(this.DeathDimensionName!); - writer.WritePosition(this.DeathLocation!.Value); - } - - writer.WriteVarInt(this.PortalCooldown); - writer.WriteVarInt(this.SeaLevel); + CommonPlayerSpawnInfo.Write(this.CommonPlayerSpawnInfo, writer); writer.WriteBoolean(this.EnforcesSecureChat); } diff --git a/Obsidian/Net/Packets/Play/Clientbound/RespawnPacket.cs b/Obsidian/Net/Packets/Play/Clientbound/RespawnPacket.cs index b4f202e5..2e8ac475 100644 --- a/Obsidian/Net/Packets/Play/Clientbound/RespawnPacket.cs +++ b/Obsidian/Net/Packets/Play/Clientbound/RespawnPacket.cs @@ -5,56 +5,14 @@ namespace Obsidian.Net.Packets.Play.Clientbound; public partial class RespawnPacket { [Field(0)] - public required string DimensionType { get; init; } + public required CommonPlayerSpawnInfo CommonPlayerSpawnInfo { get; set; } [Field(1)] - public required string DimensionName { get; init; } - - [Field(2)] - public long HashedSeed { get; init; } - - [Field(3), ActualType(typeof(byte))] - public Gamemode Gamemode { get; init; } - - [Field(4), ActualType(typeof(sbyte))] - public Gamemode PreviousGamemode { get; init; } - - [Field(5)] - public bool IsDebug { get; init; } - - [Field(6)] - public bool IsFlat { get; init; } - - - [Field(9)] - public GlobalPosition? DeathPosition { get; init; } - - [Field(10), VarLength] - public int PortalCooldown { get; init; } - - [Field(11), VarLength] - public int SeaLevel { get; init; } - - [Field(12)] public DataKept DataKept { get; init; } public override void Serialize(INetStreamWriter writer) { - writer.WriteString(this.DimensionType); - writer.WriteString(this.DimensionName); - - writer.WriteLong(this.HashedSeed); - - writer.WriteByte(this.Gamemode); - writer.WriteByte(this.PreviousGamemode); - - writer.WriteBoolean(this.IsDebug); - writer.WriteBoolean(this.IsFlat); - - writer.WriteOptional(this.DeathPosition); - - writer.WriteVarInt(this.PortalCooldown); - writer.WriteVarInt(this.SeaLevel); + CommonPlayerSpawnInfo.Write(this.CommonPlayerSpawnInfo, writer); writer.WriteByte(this.DataKept); } diff --git a/Obsidian/Utilities/Extensions.cs b/Obsidian/Utilities/Extensions.cs index d55e429c..439e7c57 100644 --- a/Obsidian/Utilities/Extensions.cs +++ b/Obsidian/Utilities/Extensions.cs @@ -2,14 +2,12 @@ using Obsidian.API.Plugins; using Obsidian.Entities; using Obsidian.Net; -using Obsidian.Net.Packets; using Obsidian.Registries; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq.Expressions; using System.Reflection; -using System.Numerics; using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text.Json; @@ -24,7 +22,7 @@ public static partial class Extensions internal readonly static EntityType[] nonLiving = [ EntityType.Arrow, EntityType.SpectralArrow, - EntityType.Boat, + //EntityType.Boat, EntityType.DragonFireball, EntityType.AreaEffectCloud, EntityType.EndCrystal,