From 46649adfd8761d47830bd9e4d19175b8fc55dafd Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Wed, 15 Nov 2023 20:00:08 +0100 Subject: [PATCH 01/60] feat: character creation works requires base stats and parsing of values provided by user --- Server/DB/Documents/Account.cs | 18 +- Server/DB/Documents/Character.cs | 15 +- Server/DB/Documents/Guild.cs | 8 +- Server/DB/Documents/GuildMember.cs | 8 +- Server/DB/Documents/InventoryItem.cs | 2 +- .../{ItemType.cs => InventoryTab.cs} | 2 +- ...31115174714_GuildIsNotRequired.Designer.cs | 333 ++++++++++++++++++ .../20231115174714_GuildIsNotRequired.cs | 27 ++ ...183824_SwitchToDataAnnotations.Designer.cs | 322 +++++++++++++++++ .../20231115183824_SwitchToDataAnnotations.cs | 230 ++++++++++++ .../WonderkingContextModelSnapshot.cs | 41 +-- Server/DB/WonderkingContext.cs | 26 -- .../PacketHandlers/ChannelSelectionHandler.cs | 8 +- .../CharacterCreationHandler..cs | 89 +++++ .../CharacterNameCheckHandler.cs | 25 ++ Server/Program.cs | 3 +- Server/Services/ItemObjectPoolService.cs | 42 ++- Server/docker-compose.yml | 4 +- Server/settings.Development.json | 2 +- Wonderking/Game/DataReader.cs | 4 +- Wonderking/Game/Reader/ItemReader.cs | 12 +- .../Incoming/CharacterCreationPacket.cs | 39 ++ .../Incoming/CharacterNameCheckPacket.cs | 19 + Wonderking/Packets/OperationCode.cs | 6 + .../CharacterCreationResponsePacket.cs | 58 +++ .../CharacterNameCheckPacketResponse.cs | 19 + Wonderking/Packets/Outgoing/Data/BaseStats.cs | 2 + Wonderking/Packets/Outgoing/Data/JobData.cs | 2 + Wonderking/Wonderking.csproj | 1 + 29 files changed, 1276 insertions(+), 91 deletions(-) rename Server/DB/Documents/{ItemType.cs => InventoryTab.cs} (83%) create mode 100644 Server/DB/Migrations/20231115174714_GuildIsNotRequired.Designer.cs create mode 100644 Server/DB/Migrations/20231115174714_GuildIsNotRequired.cs create mode 100644 Server/DB/Migrations/20231115183824_SwitchToDataAnnotations.Designer.cs create mode 100644 Server/DB/Migrations/20231115183824_SwitchToDataAnnotations.cs create mode 100644 Server/PacketHandlers/CharacterCreationHandler..cs create mode 100644 Server/PacketHandlers/CharacterNameCheckHandler.cs create mode 100644 Wonderking/Packets/Incoming/CharacterCreationPacket.cs create mode 100644 Wonderking/Packets/Incoming/CharacterNameCheckPacket.cs create mode 100644 Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs create mode 100644 Wonderking/Packets/Outgoing/CharacterNameCheckPacketResponse.cs diff --git a/Server/DB/Documents/Account.cs b/Server/DB/Documents/Account.cs index 7d66d36..20c8bc0 100644 --- a/Server/DB/Documents/Account.cs +++ b/Server/DB/Documents/Account.cs @@ -1,5 +1,10 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + namespace Server.DB.Documents; +[Index(nameof(Username), IsUnique = true), Index(nameof(Id), IsUnique = true)] public class Account { public Account(string username, byte[] password, string email, byte permissionLevel, byte[] salt) @@ -11,12 +16,15 @@ public class Account this.Salt = salt; } + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } - public string Username { get; set; } - public byte[] Password { get; set; } - public string Email { get; set; } + [Column(TypeName = "varchar(20)")] public string Username { get; set; } + [Column(TypeName = "bytea")] public byte[] Password { get; set; } + + [EmailAddress] public string Email { get; set; } public byte PermissionLevel { get; set; } - public byte[] Salt { get; set; } - public ICollection Characters { get; } = new List(); + [Column(TypeName = "bytea")] public byte[] Salt { get; set; } + public virtual ICollection Characters { get; } = new List(); } diff --git a/Server/DB/Documents/Character.cs b/Server/DB/Documents/Character.cs index bb507d5..c286fff 100644 --- a/Server/DB/Documents/Character.cs +++ b/Server/DB/Documents/Character.cs @@ -1,3 +1,5 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; using Wonderking.Game.Data.Character; using Wonderking.Packets.Outgoing.Data; @@ -5,10 +7,12 @@ namespace Server.DB.Documents; public class Character { - public byte ServerId { get; set; } - public Guid AccountId { get; set; } - public Account Account { get; set; } + public virtual Account Account { get; set; } + + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } + public ushort MapId { get; set; } public string Name { get; set; } public short LastXCoordinate { get; set; } @@ -17,13 +21,12 @@ public class Character public Gender Gender { get; set; } public long Experience { get; set; } public byte Level { get; set; } - public ICollection InventoryItems { get; set; } + public virtual ICollection InventoryItems { get; set; } public BaseStats BaseStats { get; set; } public JobData JobData { get; set; } public int Health { get; set; } public int Mana { get; set; } - public Guid GuildId { get; set; } - public Guild Guild { get; set; } + public virtual Guild Guild { get; set; } } diff --git a/Server/DB/Documents/Guild.cs b/Server/DB/Documents/Guild.cs index a907566..3109d00 100644 --- a/Server/DB/Documents/Guild.cs +++ b/Server/DB/Documents/Guild.cs @@ -1,9 +1,15 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + namespace Server.DB.Documents; public class Guild { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } + public string Name { get; set; } public string Notice { get; set; } - public ICollection GuildMembers { get; set; } + public virtual ICollection GuildMembers { get; set; } } diff --git a/Server/DB/Documents/GuildMember.cs b/Server/DB/Documents/GuildMember.cs index 44cb91b..557aa2d 100644 --- a/Server/DB/Documents/GuildMember.cs +++ b/Server/DB/Documents/GuildMember.cs @@ -1,11 +1,15 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + namespace Server.DB.Documents; public class GuildMember { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } - public Guid CharacterId { get; set; } + public Character Character { get; set; } - public Guid GuildId { get; set; } public Guild Guild { get; set; } public GuildRank Rank { get; set; } } diff --git a/Server/DB/Documents/InventoryItem.cs b/Server/DB/Documents/InventoryItem.cs index 58ddd55..08f5078 100644 --- a/Server/DB/Documents/InventoryItem.cs +++ b/Server/DB/Documents/InventoryItem.cs @@ -8,7 +8,7 @@ public class InventoryItem public ushort ItemId { get; set; } public ushort Count { get; set; } public byte Slot { get; set; } - public ItemType ItemType { get; set; } + public InventoryTab InventoryTab { get; set; } public byte Level { get; set; } public byte Rarity { get; set; } public byte AddOption { get; set; } diff --git a/Server/DB/Documents/ItemType.cs b/Server/DB/Documents/InventoryTab.cs similarity index 83% rename from Server/DB/Documents/ItemType.cs rename to Server/DB/Documents/InventoryTab.cs index 2b12c16..cbcdae1 100644 --- a/Server/DB/Documents/ItemType.cs +++ b/Server/DB/Documents/InventoryTab.cs @@ -1,6 +1,6 @@ namespace Server.DB.Documents; -public enum ItemType : byte +public enum InventoryTab : byte { WornEquipment = 0, WornCashEquipment = 1, diff --git a/Server/DB/Migrations/20231115174714_GuildIsNotRequired.Designer.cs b/Server/DB/Migrations/20231115174714_GuildIsNotRequired.Designer.cs new file mode 100644 index 0000000..c5b763c --- /dev/null +++ b/Server/DB/Migrations/20231115174714_GuildIsNotRequired.Designer.cs @@ -0,0 +1,333 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Server.DB; + +#nullable disable + +namespace Server.DB.Migrations +{ + [DbContext(typeof(WonderkingContext))] + [Migration("20231115174714_GuildIsNotRequired")] + partial class GuildIsNotRequired + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Server.DB.Documents.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Password") + .HasColumnType("bytea"); + + b.Property("PermissionLevel") + .HasColumnType("smallint"); + + b.Property("Salt") + .HasColumnType("bytea"); + + b.Property("Username") + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("Accounts"); + }); + + modelBuilder.Entity("Server.DB.Documents.Character", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("Experience") + .HasColumnType("bigint"); + + b.Property("Gender") + .HasColumnType("smallint"); + + b.Property("GuildId") + .HasColumnType("uuid"); + + b.Property("Health") + .HasColumnType("integer"); + + b.Property("LastXCoordinate") + .HasColumnType("smallint"); + + b.Property("LastYCoordinate") + .HasColumnType("smallint"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.Property("Mana") + .HasColumnType("integer"); + + b.Property("MapId") + .HasColumnType("integer"); + + b.Property("Name") + .HasColumnType("varchar(20)"); + + b.Property("PvPLevel") + .HasColumnType("smallint"); + + b.Property("ServerId") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("GuildId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Characters"); + }); + + modelBuilder.Entity("Server.DB.Documents.Guild", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Notice") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Guild"); + }); + + modelBuilder.Entity("Server.DB.Documents.GuildMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("GuildId") + .HasColumnType("uuid"); + + b.Property("Rank") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.HasIndex("GuildId"); + + b.ToTable("GuildMember"); + }); + + modelBuilder.Entity("Server.DB.Documents.InventoryItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddOption") + .HasColumnType("smallint"); + + b.Property("AddOption2") + .HasColumnType("smallint"); + + b.Property("AddOption3") + .HasColumnType("smallint"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("Count") + .HasColumnType("integer"); + + b.Property("InventoryTab") + .HasColumnType("smallint"); + + b.Property("ItemId") + .HasColumnType("integer"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.Property("Option") + .HasColumnType("smallint"); + + b.Property("Option2") + .HasColumnType("smallint"); + + b.Property("Option3") + .HasColumnType("smallint"); + + b.Property("Rarity") + .HasColumnType("smallint"); + + b.Property("Slot") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("InventoryItem"); + }); + + modelBuilder.Entity("Server.DB.Documents.Character", b => + { + b.HasOne("Server.DB.Documents.Account", "Account") + .WithMany("Characters") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.DB.Documents.Guild", "Guild") + .WithMany() + .HasForeignKey("GuildId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Wonderking.Packets.Outgoing.Data.BaseStats", "BaseStats", b1 => + { + b1.Property("CharacterId") + .HasColumnType("uuid"); + + b1.Property("Dexterity") + .HasColumnType("smallint"); + + b1.Property("Intelligence") + .HasColumnType("smallint"); + + b1.Property("Luck") + .HasColumnType("smallint"); + + b1.Property("Strength") + .HasColumnType("smallint"); + + b1.Property("Vitality") + .HasColumnType("smallint"); + + b1.Property("Wisdom") + .HasColumnType("smallint"); + + b1.HasKey("CharacterId"); + + b1.ToTable("Characters"); + + b1.WithOwner() + .HasForeignKey("CharacterId"); + }); + + b.OwnsOne("Wonderking.Packets.Outgoing.Data.JobData", "JobData", b1 => + { + b1.Property("CharacterId") + .HasColumnType("uuid"); + + b1.Property("FirstJob") + .HasColumnType("smallint"); + + b1.Property("FourthJob") + .HasColumnType("smallint"); + + b1.Property("SecondJob") + .HasColumnType("smallint"); + + b1.Property("ThirdJob") + .HasColumnType("smallint"); + + b1.HasKey("CharacterId"); + + b1.ToTable("Characters"); + + b1.WithOwner() + .HasForeignKey("CharacterId"); + }); + + b.Navigation("Account"); + + b.Navigation("BaseStats"); + + b.Navigation("Guild"); + + b.Navigation("JobData"); + }); + + modelBuilder.Entity("Server.DB.Documents.GuildMember", b => + { + b.HasOne("Server.DB.Documents.Character", "Character") + .WithMany() + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.DB.Documents.Guild", "Guild") + .WithMany("GuildMembers") + .HasForeignKey("GuildId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + + b.Navigation("Guild"); + }); + + modelBuilder.Entity("Server.DB.Documents.InventoryItem", b => + { + b.HasOne("Server.DB.Documents.Character", "Character") + .WithMany("InventoryItems") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Server.DB.Documents.Account", b => + { + b.Navigation("Characters"); + }); + + modelBuilder.Entity("Server.DB.Documents.Character", b => + { + b.Navigation("InventoryItems"); + }); + + modelBuilder.Entity("Server.DB.Documents.Guild", b => + { + b.Navigation("GuildMembers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Server/DB/Migrations/20231115174714_GuildIsNotRequired.cs b/Server/DB/Migrations/20231115174714_GuildIsNotRequired.cs new file mode 100644 index 0000000..27c1de3 --- /dev/null +++ b/Server/DB/Migrations/20231115174714_GuildIsNotRequired.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Server.DB.Migrations; + +/// +public partial class GuildIsNotRequired : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "ItemType", + table: "InventoryItem", + newName: "InventoryTab"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "InventoryTab", + table: "InventoryItem", + newName: "ItemType"); + } +} diff --git a/Server/DB/Migrations/20231115183824_SwitchToDataAnnotations.Designer.cs b/Server/DB/Migrations/20231115183824_SwitchToDataAnnotations.Designer.cs new file mode 100644 index 0000000..907419e --- /dev/null +++ b/Server/DB/Migrations/20231115183824_SwitchToDataAnnotations.Designer.cs @@ -0,0 +1,322 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Server.DB; + +#nullable disable + +namespace Server.DB.Migrations +{ + [DbContext(typeof(WonderkingContext))] + [Migration("20231115183824_SwitchToDataAnnotations")] + partial class SwitchToDataAnnotations + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Server.DB.Documents.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Password") + .HasColumnType("bytea"); + + b.Property("PermissionLevel") + .HasColumnType("smallint"); + + b.Property("Salt") + .HasColumnType("bytea"); + + b.Property("Username") + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("Accounts"); + }); + + modelBuilder.Entity("Server.DB.Documents.Character", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("Experience") + .HasColumnType("bigint"); + + b.Property("Gender") + .HasColumnType("smallint"); + + b.Property("GuildId") + .HasColumnType("uuid"); + + b.Property("Health") + .HasColumnType("integer"); + + b.Property("LastXCoordinate") + .HasColumnType("smallint"); + + b.Property("LastYCoordinate") + .HasColumnType("smallint"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.Property("Mana") + .HasColumnType("integer"); + + b.Property("MapId") + .HasColumnType("integer"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PvPLevel") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("GuildId"); + + b.ToTable("Characters"); + }); + + modelBuilder.Entity("Server.DB.Documents.Guild", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Notice") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Guild"); + }); + + modelBuilder.Entity("Server.DB.Documents.GuildMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("GuildId") + .HasColumnType("uuid"); + + b.Property("Rank") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.HasIndex("GuildId"); + + b.ToTable("GuildMember"); + }); + + modelBuilder.Entity("Server.DB.Documents.InventoryItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddOption") + .HasColumnType("smallint"); + + b.Property("AddOption2") + .HasColumnType("smallint"); + + b.Property("AddOption3") + .HasColumnType("smallint"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("Count") + .HasColumnType("integer"); + + b.Property("InventoryTab") + .HasColumnType("smallint"); + + b.Property("ItemId") + .HasColumnType("integer"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.Property("Option") + .HasColumnType("smallint"); + + b.Property("Option2") + .HasColumnType("smallint"); + + b.Property("Option3") + .HasColumnType("smallint"); + + b.Property("Rarity") + .HasColumnType("smallint"); + + b.Property("Slot") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("InventoryItem"); + }); + + modelBuilder.Entity("Server.DB.Documents.Character", b => + { + b.HasOne("Server.DB.Documents.Account", "Account") + .WithMany("Characters") + .HasForeignKey("AccountId"); + + b.HasOne("Server.DB.Documents.Guild", "Guild") + .WithMany() + .HasForeignKey("GuildId"); + + b.OwnsOne("Wonderking.Packets.Outgoing.Data.BaseStats", "BaseStats", b1 => + { + b1.Property("CharacterId") + .HasColumnType("uuid"); + + b1.Property("Dexterity") + .HasColumnType("smallint"); + + b1.Property("Intelligence") + .HasColumnType("smallint"); + + b1.Property("Luck") + .HasColumnType("smallint"); + + b1.Property("Strength") + .HasColumnType("smallint"); + + b1.Property("Vitality") + .HasColumnType("smallint"); + + b1.Property("Wisdom") + .HasColumnType("smallint"); + + b1.HasKey("CharacterId"); + + b1.ToTable("Characters"); + + b1.WithOwner() + .HasForeignKey("CharacterId"); + }); + + b.OwnsOne("Wonderking.Packets.Outgoing.Data.JobData", "JobData", b1 => + { + b1.Property("CharacterId") + .HasColumnType("uuid"); + + b1.Property("FirstJob") + .HasColumnType("smallint"); + + b1.Property("FourthJob") + .HasColumnType("smallint"); + + b1.Property("SecondJob") + .HasColumnType("smallint"); + + b1.Property("ThirdJob") + .HasColumnType("smallint"); + + b1.HasKey("CharacterId"); + + b1.ToTable("Characters"); + + b1.WithOwner() + .HasForeignKey("CharacterId"); + }); + + b.Navigation("Account"); + + b.Navigation("BaseStats"); + + b.Navigation("Guild"); + + b.Navigation("JobData"); + }); + + modelBuilder.Entity("Server.DB.Documents.GuildMember", b => + { + b.HasOne("Server.DB.Documents.Character", "Character") + .WithMany() + .HasForeignKey("CharacterId"); + + b.HasOne("Server.DB.Documents.Guild", "Guild") + .WithMany("GuildMembers") + .HasForeignKey("GuildId"); + + b.Navigation("Character"); + + b.Navigation("Guild"); + }); + + modelBuilder.Entity("Server.DB.Documents.InventoryItem", b => + { + b.HasOne("Server.DB.Documents.Character", "Character") + .WithMany("InventoryItems") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Server.DB.Documents.Account", b => + { + b.Navigation("Characters"); + }); + + modelBuilder.Entity("Server.DB.Documents.Character", b => + { + b.Navigation("InventoryItems"); + }); + + modelBuilder.Entity("Server.DB.Documents.Guild", b => + { + b.Navigation("GuildMembers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Server/DB/Migrations/20231115183824_SwitchToDataAnnotations.cs b/Server/DB/Migrations/20231115183824_SwitchToDataAnnotations.cs new file mode 100644 index 0000000..7b9c842 --- /dev/null +++ b/Server/DB/Migrations/20231115183824_SwitchToDataAnnotations.cs @@ -0,0 +1,230 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Server.DB.Migrations; + +/// +public partial class SwitchToDataAnnotations : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Characters_Accounts_AccountId", + table: "Characters"); + + migrationBuilder.DropForeignKey( + name: "FK_Characters_Guild_GuildId", + table: "Characters"); + + migrationBuilder.DropForeignKey( + name: "FK_GuildMember_Characters_CharacterId", + table: "GuildMember"); + + migrationBuilder.DropForeignKey( + name: "FK_GuildMember_Guild_GuildId", + table: "GuildMember"); + + migrationBuilder.DropIndex( + name: "IX_Characters_Name", + table: "Characters"); + + migrationBuilder.DropColumn( + name: "ServerId", + table: "Characters"); + + migrationBuilder.AlterColumn( + name: "GuildId", + table: "GuildMember", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AlterColumn( + name: "CharacterId", + table: "GuildMember", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Characters", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "varchar(20)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "GuildId", + table: "Characters", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AlterColumn( + name: "AccountId", + table: "Characters", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.CreateIndex( + name: "IX_Accounts_Id", + table: "Accounts", + column: "Id", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_Characters_Accounts_AccountId", + table: "Characters", + column: "AccountId", + principalTable: "Accounts", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_Characters_Guild_GuildId", + table: "Characters", + column: "GuildId", + principalTable: "Guild", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_GuildMember_Characters_CharacterId", + table: "GuildMember", + column: "CharacterId", + principalTable: "Characters", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_GuildMember_Guild_GuildId", + table: "GuildMember", + column: "GuildId", + principalTable: "Guild", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Characters_Accounts_AccountId", + table: "Characters"); + + migrationBuilder.DropForeignKey( + name: "FK_Characters_Guild_GuildId", + table: "Characters"); + + migrationBuilder.DropForeignKey( + name: "FK_GuildMember_Characters_CharacterId", + table: "GuildMember"); + + migrationBuilder.DropForeignKey( + name: "FK_GuildMember_Guild_GuildId", + table: "GuildMember"); + + migrationBuilder.DropIndex( + name: "IX_Accounts_Id", + table: "Accounts"); + + migrationBuilder.AlterColumn( + name: "GuildId", + table: "GuildMember", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CharacterId", + table: "GuildMember", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Characters", + type: "varchar(20)", + nullable: true, + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "GuildId", + table: "Characters", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "AccountId", + table: "Characters", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AddColumn( + name: "ServerId", + table: "Characters", + type: "smallint", + nullable: false, + defaultValue: (byte)0); + + migrationBuilder.CreateIndex( + name: "IX_Characters_Name", + table: "Characters", + column: "Name", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_Characters_Accounts_AccountId", + table: "Characters", + column: "AccountId", + principalTable: "Accounts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Characters_Guild_GuildId", + table: "Characters", + column: "GuildId", + principalTable: "Guild", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_GuildMember_Characters_CharacterId", + table: "GuildMember", + column: "CharacterId", + principalTable: "Characters", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_GuildMember_Guild_GuildId", + table: "GuildMember", + column: "GuildId", + principalTable: "Guild", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } +} diff --git a/Server/DB/Migrations/WonderkingContextModelSnapshot.cs b/Server/DB/Migrations/WonderkingContextModelSnapshot.cs index 5cff8b9..28aea68 100644 --- a/Server/DB/Migrations/WonderkingContextModelSnapshot.cs +++ b/Server/DB/Migrations/WonderkingContextModelSnapshot.cs @@ -45,6 +45,9 @@ namespace Server.DB.Migrations b.HasKey("Id"); + b.HasIndex("Id") + .IsUnique(); + b.HasIndex("Username") .IsUnique(); @@ -57,7 +60,7 @@ namespace Server.DB.Migrations .ValueGeneratedOnAdd() .HasColumnType("uuid"); - b.Property("AccountId") + b.Property("AccountId") .HasColumnType("uuid"); b.Property("Experience") @@ -66,7 +69,7 @@ namespace Server.DB.Migrations b.Property("Gender") .HasColumnType("smallint"); - b.Property("GuildId") + b.Property("GuildId") .HasColumnType("uuid"); b.Property("Health") @@ -88,23 +91,17 @@ namespace Server.DB.Migrations .HasColumnType("integer"); b.Property("Name") - .HasColumnType("varchar(20)"); + .HasColumnType("text"); b.Property("PvPLevel") .HasColumnType("smallint"); - b.Property("ServerId") - .HasColumnType("smallint"); - b.HasKey("Id"); b.HasIndex("AccountId"); b.HasIndex("GuildId"); - b.HasIndex("Name") - .IsUnique(); - b.ToTable("Characters"); }); @@ -131,10 +128,10 @@ namespace Server.DB.Migrations .ValueGeneratedOnAdd() .HasColumnType("uuid"); - b.Property("CharacterId") + b.Property("CharacterId") .HasColumnType("uuid"); - b.Property("GuildId") + b.Property("GuildId") .HasColumnType("uuid"); b.Property("Rank") @@ -170,12 +167,12 @@ namespace Server.DB.Migrations b.Property("Count") .HasColumnType("integer"); + b.Property("InventoryTab") + .HasColumnType("smallint"); + b.Property("ItemId") .HasColumnType("integer"); - b.Property("ItemType") - .HasColumnType("smallint"); - b.Property("Level") .HasColumnType("smallint"); @@ -205,15 +202,11 @@ namespace Server.DB.Migrations { b.HasOne("Server.DB.Documents.Account", "Account") .WithMany("Characters") - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("AccountId"); b.HasOne("Server.DB.Documents.Guild", "Guild") .WithMany() - .HasForeignKey("GuildId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("GuildId"); b.OwnsOne("Wonderking.Packets.Outgoing.Data.BaseStats", "BaseStats", b1 => { @@ -284,15 +277,11 @@ namespace Server.DB.Migrations { b.HasOne("Server.DB.Documents.Character", "Character") .WithMany() - .HasForeignKey("CharacterId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("CharacterId"); b.HasOne("Server.DB.Documents.Guild", "Guild") .WithMany("GuildMembers") - .HasForeignKey("GuildId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("GuildId"); b.Navigation("Character"); diff --git a/Server/DB/WonderkingContext.cs b/Server/DB/WonderkingContext.cs index af4e97e..8ec7cf6 100644 --- a/Server/DB/WonderkingContext.cs +++ b/Server/DB/WonderkingContext.cs @@ -24,30 +24,4 @@ public class WonderkingContext : DbContext .UseNpgsql( $"Host={this._configuration["DB:Host"]};Username={this._configuration["DB:Username"]};Password={this._configuration["DB:Password"]};Database={this._configuration["DB:Database"]};Port={this._configuration["DB:Port"]}") .EnableSensitiveDataLogging().UseLoggerFactory(this._loggerFactory); - - protected override void OnModelCreating(ModelBuilder modelBuilder) => - modelBuilder.Entity(builder => - { - builder.Property(b => b.Username).HasColumnType("varchar(20)"); - builder.HasIndex(b => b.Username).IsUnique(); - builder.Property(b => b.Password).HasColumnType("bytea"); - builder.Property(b => b.Salt).HasColumnType("bytea"); - builder.HasKey(b => b.Id); - builder.HasMany(e => e.Characters).WithOne(e => e.Account).HasForeignKey(e => e.AccountId) - .IsRequired(); - }).Entity(builder => - { - builder.HasKey(c => c.Id); - builder.Property(c => c.Name).HasColumnType("varchar(20)"); - builder.HasIndex(c => c.Name).IsUnique(); - builder.HasMany(e => e.InventoryItems).WithOne(e => e.Character) - .HasForeignKey(e => e.CharacterId).IsRequired(); - builder.OwnsOne(p => p.BaseStats); - builder.OwnsOne(p => p.JobData); - }).Entity(builder => { builder.HasKey(i => i.Id); }).Entity(builder => - { - builder.HasKey(g => g.Id); - builder.HasMany(g => g.GuildMembers).WithOne(g => g.Guild).HasForeignKey(g => g.GuildId) - .IsRequired(); - }); } diff --git a/Server/PacketHandlers/ChannelSelectionHandler.cs b/Server/PacketHandlers/ChannelSelectionHandler.cs index e67ca30..7e66231 100644 --- a/Server/PacketHandlers/ChannelSelectionHandler.cs +++ b/Server/PacketHandlers/ChannelSelectionHandler.cs @@ -46,7 +46,7 @@ public class ChannelSelectionHandler : IPacketHandler ChannelIsFullFlag = 0, Endpoint = "127.0.0.1", Port = 12345, - Characters = this._wonderkingContext.Characters.Where(c => c.AccountId == authSession.AccountId) + Characters = this._wonderkingContext.Characters.AsNoTracking().Where(c => c.Account.Id == authSession.AccountId) .Select(c => new CharacterData { @@ -59,11 +59,11 @@ public class ChannelSelectionHandler : IPacketHandler Health = c.Health, Mana = c.Mana, EquippedItems = - c.InventoryItems.Where(item => item.ItemType == ItemType.WornEquipment) + c.InventoryItems.Where(item => item.InventoryTab == InventoryTab.WornEquipment) .Select(item => item.ItemId) .ToArray(), EquippedCashItems = c.InventoryItems - .Where(item => item.ItemType == ItemType.WornCashEquipment) + .Where(item => item.InventoryTab == InventoryTab.WornCashEquipment) .Select(item => item.ItemId) .ToArray(), }) @@ -72,7 +72,7 @@ public class ChannelSelectionHandler : IPacketHandler guildNameResponsePacket = new CharacterSelectionSetGuildNamePacket { - GuildNames = this._wonderkingContext.Characters.Where(c => c.AccountId == authSession.AccountId) + GuildNames = this._wonderkingContext.Characters.Where(c => c.Account.Id == authSession.AccountId) .Select(character => character.Guild.Name).ToArray() }; } diff --git a/Server/PacketHandlers/CharacterCreationHandler..cs b/Server/PacketHandlers/CharacterCreationHandler..cs new file mode 100644 index 0000000..5ee5af6 --- /dev/null +++ b/Server/PacketHandlers/CharacterCreationHandler..cs @@ -0,0 +1,89 @@ +using Microsoft.EntityFrameworkCore; +using NetCoreServer; +using Server.DB; +using Server.DB.Documents; +using Server.Services; +using Wonderking.Game.Data.Character; +using Wonderking.Packets.Incoming; +using Wonderking.Packets.Outgoing; +using Wonderking.Packets.Outgoing.Data; + +namespace Server.PacketHandlers; + +public class CharacterCreationHandler : IPacketHandler +{ + private readonly WonderkingContext _wonderkingContext; + private readonly ItemObjectPoolService _itemObjectPoolService; + + public CharacterCreationHandler(WonderkingContext wonderkingContext, ItemObjectPoolService itemObjectPoolService) + { + _wonderkingContext = wonderkingContext; + _itemObjectPoolService = itemObjectPoolService; + } + + public Task HandleAsync(CharacterCreationPacket packet, TcpSession session) + { + var authSession = session as AuthSession; + var account = + _wonderkingContext.Accounts.FirstOrDefault(a => authSession != null && a.Id == authSession.AccountId); + var items = new List + { + _itemObjectPoolService.GetBaseInventoryItem(25), + _itemObjectPoolService.GetBaseInventoryItem(764), + _itemObjectPoolService.GetBaseInventoryItem(766), + _itemObjectPoolService.GetBaseInventoryItem(763), + _itemObjectPoolService.GetBaseInventoryItem(767) + }; + account?.Characters.Add(new Character + { + Account = account, + MapId = 300, + Name = packet.Name, + LastXCoordinate = 113, + LastYCoordinate = 0, + PvPLevel = PvPLevel.None, + Gender = Gender.None, + Experience = 0, + Level = 1, + InventoryItems = items, + BaseStats = new BaseStats + { + Strength = 5, + Dexterity = 5, + Intelligence = 5, + Vitality = 5, + Luck = 5, + Wisdom = 5 + }, + JobData = new JobData { FirstJob = packet.FirstJob, SecondJob = 0, ThirdJob = 0, FourthJob = 0 }, + Health = 250, + Mana = 250, + }); + _wonderkingContext.SaveChanges(); + + var character = this._wonderkingContext.Characters.AsNoTracking() + .Where(c => authSession != null && c.Account.Id == authSession.AccountId && c.Name == packet.Name) + .Select(c => + new CharacterData + { + Name = c.Name, + Job = c.JobData, + Gender = c.Gender, + Level = c.Level, + Experience = 0, + Stats = c.BaseStats, + Health = c.Health, + Mana = c.Mana, + EquippedItems = + c.InventoryItems.Where(item => item.InventoryTab == InventoryTab.WornEquipment) + .Select(item => item.ItemId) + .ToArray(), + EquippedCashItems = c.InventoryItems + .Where(item => item.InventoryTab == InventoryTab.WornCashEquipment) + .Select(item => item.ItemId) + .ToArray(), + }).FirstOrDefault(); + authSession?.Send(new CharacterCreationResponsePacket { Character = character }); + return Task.CompletedTask; + } +} diff --git a/Server/PacketHandlers/CharacterNameCheckHandler.cs b/Server/PacketHandlers/CharacterNameCheckHandler.cs new file mode 100644 index 0000000..c97af26 --- /dev/null +++ b/Server/PacketHandlers/CharacterNameCheckHandler.cs @@ -0,0 +1,25 @@ +using NetCoreServer; +using Server.DB; +using Wonderking.Packets.Incoming; +using Wonderking.Packets.Outgoing; + +namespace Server.PacketHandlers; + +public class CharacterNameCheckHandler : IPacketHandler +{ + private readonly WonderkingContext _wonderkingContext; + + public CharacterNameCheckHandler(WonderkingContext wonderkingContext) + { + _wonderkingContext = wonderkingContext; + } + + public Task HandleAsync(CharacterNameCheckPacket packet, TcpSession session) + { + var isTaken = _wonderkingContext.Characters.Any(c => c.Name == packet.Name); + var responsePacket = new CharacterNameCheckPacketResponse { IsTaken = isTaken }; + var authSession = session as AuthSession; + authSession?.Send(responsePacket); + return Task.CompletedTask; + } +} diff --git a/Server/Program.cs b/Server/Program.cs index 70ff82c..a092fe9 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -22,9 +22,10 @@ builder.Logging.AddFile("Logs/Server-{Date}.json.log", LogLevel.Trace, isJson: t builder.Services.AddEntityFrameworkNpgsql(); builder.Services.AddDbContext(); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddHostedService(); builder.Services.AddHostedService(provider => provider.GetService() ?? throw new InvalidOperationException()); -builder.Services.AddSingleton(); builder.Services.AddMassTransit(x => { x.UsingInMemory((context, configurator) => configurator.ConfigureEndpoints(context)); diff --git a/Server/Services/ItemObjectPoolService.cs b/Server/Services/ItemObjectPoolService.cs index 3a8ac45..2bce29b 100644 --- a/Server/Services/ItemObjectPoolService.cs +++ b/Server/Services/ItemObjectPoolService.cs @@ -1,6 +1,8 @@ using System.Collections.Concurrent; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Server.DB.Documents; using Wonderking.Game.Data; using Wonderking.Game.Reader; @@ -10,9 +12,11 @@ public class ItemObjectPoolService : IHostedService { readonly ConcurrentDictionary _itemObjectPool = new(); private readonly ItemReader _itemReader; + private readonly ILogger _logger; - public ItemObjectPoolService(IConfiguration configuration) + public ItemObjectPoolService(IConfiguration configuration, ILogger logger) { + _logger = logger; _itemReader = new ItemReader(configuration.GetSection("Game").GetSection("Data").GetValue("Path") ?? string.Empty); } @@ -20,23 +24,31 @@ public class ItemObjectPoolService : IHostedService public Task StartAsync(CancellationToken cancellationToken) { var amountOfEntries = _itemReader.GetAmountOfEntries(); - ParallelEnumerable.Range(0, (int)amountOfEntries).AsParallel().ForAll(i => + Parallel.For(0, (int)amountOfEntries, i => { var itemObject = _itemReader.GetEntry((uint)i); - _itemObjectPool.TryAdd(itemObject.ItemID, itemObject); + var result = _itemObjectPool.TryAdd(itemObject.ItemID, itemObject); + if (!result) + { + throw new Exception($"Failed to add item {itemObject.ItemID} to the item object pool"); + } + + _logger.LogTrace("Item with {ID} has been added", itemObject.ItemID); }); + _logger.LogInformation("A total of {AmountOfEntries} items have been added to the item object pool", + _itemObjectPool.Count); return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { - _itemReader.Dispose(); return Task.CompletedTask; } public ItemObject GetItem(ushort itemId) { - return _itemObjectPool[itemId]; + _ = _itemObjectPool.TryGetValue(itemId, out var itemObject); + return itemObject; } public bool ContainsItem(ushort itemId) @@ -48,4 +60,24 @@ public class ItemObjectPoolService : IHostedService { return _itemObjectPool.AsReadOnly().Values.AsQueryable(); } + + public InventoryItem GetBaseInventoryItem(ushort itemId, ushort count = 1, bool isWorn = false) + { + var item = this.GetItem(itemId); + return new InventoryItem + { + ItemId = itemId, + Count = count, + Slot = 0, + InventoryTab = InventoryTab.WornEquipment, + Level = item.MinimumLevelRequirement, + Rarity = 0, + AddOption = 0, + AddOption2 = 0, + AddOption3 = 0, + Option = 0, + Option2 = 0, + Option3 = 0 + }; + } } diff --git a/Server/docker-compose.yml b/Server/docker-compose.yml index bd3cc51..657f148 100644 --- a/Server/docker-compose.yml +++ b/Server/docker-compose.yml @@ -12,14 +12,14 @@ - DB:Port=5432 - DB:Username=continuity - DB:Password=continuity - - Game:Data:Path=/app/data + - Game:Data:Path=/app/data/ networks: - continuity ports: - "10001:10001" volumes: - type: bind - source: game-data + source: ../wk-data target: /app/data read_only: true diff --git a/Server/settings.Development.json b/Server/settings.Development.json index ded1040..3d15198 100644 --- a/Server/settings.Development.json +++ b/Server/settings.Development.json @@ -11,7 +11,7 @@ }, "Game":{ "Data":{ - "Path": "../wk-data" + "Path": "../wk-data/" } } } diff --git a/Wonderking/Game/DataReader.cs b/Wonderking/Game/DataReader.cs index e67c8a4..6563c85 100644 --- a/Wonderking/Game/DataReader.cs +++ b/Wonderking/Game/DataReader.cs @@ -7,7 +7,7 @@ public abstract class DataReader protected DataReader(string path) { Path = path; - DatFileContent = new(GetDatFileContent(path).ToArray()); + DatFileContent = GetDatFileContent(path).ToArray(); } private protected string Path { get; init; } @@ -33,7 +33,7 @@ public abstract class DataReader throw new NotSupportedException("XorKey is null"); } - protected MemoryStream DatFileContent { get; } + protected byte[] DatFileContent { get; } private static Span GetDatFileContent(string path) { diff --git a/Wonderking/Game/Reader/ItemReader.cs b/Wonderking/Game/Reader/ItemReader.cs index 38ae8a3..8b00ff0 100644 --- a/Wonderking/Game/Reader/ItemReader.cs +++ b/Wonderking/Game/Reader/ItemReader.cs @@ -1,9 +1,8 @@ using Wonderking.Game.Data; -using Wonderking.Game.Data.Item; namespace Wonderking.Game.Reader; -public class ItemReader(string path) : DataReader(path), IDisposable +public class ItemReader(string path) : DataReader(path) { public override uint GetAmountOfEntries() { @@ -13,8 +12,9 @@ public class ItemReader(string path) : DataReader(path), IDisposable public override ItemObject GetEntry(uint entryId) { var item = new ItemObject(); - this.DatFileContent.Position = 9 + entryId * this.GetSizeOfEntry(); - var reader = new BinaryReader(this.DatFileContent); + var arraySegment = new ArraySegment(DatFileContent, + 9 + (int)entryId * this.GetSizeOfEntry(), this.GetSizeOfEntry()); + var reader = new BinaryReader(new MemoryStream(arraySegment.ToArray())); item.ItemID = reader.ReadUInt32(); //9 item.Disabled = reader.ReadUInt32() == 1; //13 item.ItemType = reader.ReadUInt32(); //17 @@ -100,8 +100,4 @@ public class ItemReader(string path) : DataReader(path), IDisposable return item; } - public void Dispose() - { - this.DatFileContent.Dispose(); - } } diff --git a/Wonderking/Packets/Incoming/CharacterCreationPacket.cs b/Wonderking/Packets/Incoming/CharacterCreationPacket.cs new file mode 100644 index 0000000..105f6db --- /dev/null +++ b/Wonderking/Packets/Incoming/CharacterCreationPacket.cs @@ -0,0 +1,39 @@ +using System.Text; +using Wonderking.Game.Data.Character; + +namespace Wonderking.Packets.Incoming; + +[PacketId(OperationCode.CharacterCreation)] +public class CharacterCreationPacket : IPacket +{ + public byte Slot { get; set; } + public byte Unknown { get; set; } + public ushort Id { get; set; } + + public string Name { get; set; } + public byte FirstJob { get; set; } + public Gender Gender { get; set; } + public byte Hair { get; set; } + public byte Eyes { get; set; } + public byte Shirt { get; set; } + public byte Pants { get; set; } + + public void Deserialize(byte[] data) + { + Slot = data[0]; + Unknown = data[1]; + Id = BitConverter.ToUInt16(data, 2); + Name = Encoding.ASCII.GetString(data, 4, 20).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0'); + FirstJob = data[24]; + Gender = (Gender)data[25]; + Hair = data[26]; + Eyes = data[27]; + Shirt = data[28]; + Pants = data[29]; + } + + public byte[] Serialize() + { + throw new NotSupportedException(); + } +} diff --git a/Wonderking/Packets/Incoming/CharacterNameCheckPacket.cs b/Wonderking/Packets/Incoming/CharacterNameCheckPacket.cs new file mode 100644 index 0000000..6799961 --- /dev/null +++ b/Wonderking/Packets/Incoming/CharacterNameCheckPacket.cs @@ -0,0 +1,19 @@ +using System.Text; + +namespace Wonderking.Packets.Incoming; + +[PacketId(OperationCode.CharacterNameCheck)] +public class CharacterNameCheckPacket : IPacket +{ + public required string Name { get; set; } + + public void Deserialize(byte[] data) + { + Encoding.ASCII.GetString(data, 0, 20); + } + + public byte[] Serialize() + { + throw new NotSupportedException(); + } +} diff --git a/Wonderking/Packets/OperationCode.cs b/Wonderking/Packets/OperationCode.cs index 97e5584..28967dd 100644 --- a/Wonderking/Packets/OperationCode.cs +++ b/Wonderking/Packets/OperationCode.cs @@ -6,5 +6,11 @@ public enum OperationCode : ushort LoginResponse = 12, ChannelSelection = 13, ChannelSelectionResponse = 13, + CharacterNameCheck = 14, + CharacterNameCheckResponse = 14, + CharacterCreation = 15, + CharacterCreationResponse = 15, + CharacterDeletion = 16, + CharacterSelection = 17, CharacterSelectionSetGuildName = 19, } diff --git a/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs b/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs new file mode 100644 index 0000000..c38e7a5 --- /dev/null +++ b/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs @@ -0,0 +1,58 @@ +using System.Buffers.Binary; +using System.Text; +using Wonderking.Packets.Outgoing.Data; + +namespace Wonderking.Packets.Outgoing; + +[PacketId(OperationCode.CharacterCreationResponse)] +public class CharacterCreationResponsePacket : IPacket +{ + public required CharacterData Character { get; set; } + + public void Deserialize(byte[] data) + { + throw new NotSupportedException(); + } + + public byte[] Serialize() + { + Span data = stackalloc byte[132]; + + BinaryPrimitives.WriteInt32LittleEndian(data.Slice(0 + 132, 4), 0); + Encoding.ASCII.GetBytes(Character.Name, data.Slice(4 + 132, 20)); + + // Job Data + data[24 + 132] = Character.Job.FirstJob; + data[25 + 132] = Character.Job.SecondJob; + data[26 + 132] = Character.Job.ThirdJob; + data[27 + 132] = Character.Job.FourthJob; + + data[28 + 132] = (byte)Character.Gender; + BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(49 + 132, 2), Character.Level); + data[31 + 132] = (byte)Character.Experience; + + // Stats + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(32 + 132, 2), Character.Stats.Strength); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(34 + 132, 2), Character.Stats.Dexterity); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(36 + 132, 2), Character.Stats.Intelligence); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(38 + 132, 2), Character.Stats.Vitality); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(40 + 132, 2), Character.Stats.Luck); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(42 + 132, 2), Character.Stats.Wisdom); + + BinaryPrimitives.WriteInt32LittleEndian(data.Slice(44 + 132, 4), Character.Health); + BinaryPrimitives.WriteInt32LittleEndian(data.Slice(48 + 132, 4), Character.Mana); + + for (var i = 0; i < 20; i++) + { + // Equipped Items + BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(52 + 132 + i * 2, 2), + Character.EquippedItems.Length > i ? Character.EquippedItems[i] : (ushort)0); + + // Equipped Cash Items + BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(92 + 132 + i * 2, 2), + Character.EquippedCashItems.Length > i ? Character.EquippedCashItems[i] : (ushort)0); + } + + return data.ToArray(); + } +} diff --git a/Wonderking/Packets/Outgoing/CharacterNameCheckPacketResponse.cs b/Wonderking/Packets/Outgoing/CharacterNameCheckPacketResponse.cs new file mode 100644 index 0000000..ac9e6a0 --- /dev/null +++ b/Wonderking/Packets/Outgoing/CharacterNameCheckPacketResponse.cs @@ -0,0 +1,19 @@ +namespace Wonderking.Packets.Outgoing; + +[PacketId(OperationCode.CharacterNameCheckResponse)] +public class CharacterNameCheckPacketResponse : IPacket +{ + public required bool IsTaken { get; set; } + + public void Deserialize(byte[] data) + { + throw new NotSupportedException(); + } + + public byte[] Serialize() + { + Span data = stackalloc byte[1]; + data[0] = this.IsTaken ? (byte)1 : (byte)0; + return data.ToArray(); + } +} diff --git a/Wonderking/Packets/Outgoing/Data/BaseStats.cs b/Wonderking/Packets/Outgoing/Data/BaseStats.cs index cb5cbbe..1dfba40 100644 --- a/Wonderking/Packets/Outgoing/Data/BaseStats.cs +++ b/Wonderking/Packets/Outgoing/Data/BaseStats.cs @@ -1,8 +1,10 @@ using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; namespace Wonderking.Packets.Outgoing.Data; [UsedImplicitly] +[Owned] public class BaseStats { public required short Strength { get; set; } diff --git a/Wonderking/Packets/Outgoing/Data/JobData.cs b/Wonderking/Packets/Outgoing/Data/JobData.cs index fb9578a..bb385cc 100644 --- a/Wonderking/Packets/Outgoing/Data/JobData.cs +++ b/Wonderking/Packets/Outgoing/Data/JobData.cs @@ -1,8 +1,10 @@ using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; namespace Wonderking.Packets.Outgoing.Data; [UsedImplicitly] +[Owned] public class JobData { public required byte FirstJob { get; set; } diff --git a/Wonderking/Wonderking.csproj b/Wonderking/Wonderking.csproj index f1e377f..97a08ea 100644 --- a/Wonderking/Wonderking.csproj +++ b/Wonderking/Wonderking.csproj @@ -22,6 +22,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive From 76991338f3cecda8e510ce47ee7e3d8df7cb8992 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Wed, 15 Nov 2023 22:12:59 +0100 Subject: [PATCH 02/60] feat: memory and cpu usage reduction + startup time improved --- Server/Services/ItemObjectPoolService.cs | 2 + Wonderking/Game/Data/Item/ItemOptions.cs | 2 +- Wonderking/Game/DataReader.cs | 15 +- Wonderking/Game/Reader/ItemReader.cs | 264 ++++++++++++------ .../Game/Reader/ItemReaderExtensions.cs | 6 +- 5 files changed, 195 insertions(+), 94 deletions(-) diff --git a/Server/Services/ItemObjectPoolService.cs b/Server/Services/ItemObjectPoolService.cs index 2bce29b..771ad27 100644 --- a/Server/Services/ItemObjectPoolService.cs +++ b/Server/Services/ItemObjectPoolService.cs @@ -37,11 +37,13 @@ public class ItemObjectPoolService : IHostedService }); _logger.LogInformation("A total of {AmountOfEntries} items have been added to the item object pool", _itemObjectPool.Count); + return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { + return Task.CompletedTask; } diff --git a/Wonderking/Game/Data/Item/ItemOptions.cs b/Wonderking/Game/Data/Item/ItemOptions.cs index 3b52fb1..9274e4f 100644 --- a/Wonderking/Game/Data/Item/ItemOptions.cs +++ b/Wonderking/Game/Data/Item/ItemOptions.cs @@ -2,6 +2,6 @@ namespace Wonderking.Game.Data.Item; public struct ItemOptions { - public ICollection OptionIDs { get; internal set; } + public uint[] OptionIDs { get; internal set; } public bool OptionAvailable { get; internal set; } } diff --git a/Wonderking/Game/DataReader.cs b/Wonderking/Game/DataReader.cs index 6563c85..b43c2b9 100644 --- a/Wonderking/Game/DataReader.cs +++ b/Wonderking/Game/DataReader.cs @@ -7,6 +7,9 @@ public abstract class DataReader protected DataReader(string path) { Path = path; + _xorKey = GetXorKey(); + SizeOfEntry = GetSizeOfEntry(); + _datFileName = GetDatFileName(); DatFileContent = GetDatFileContent(path).ToArray(); } @@ -15,7 +18,7 @@ public abstract class DataReader public abstract uint GetAmountOfEntries(); public abstract T GetEntry(uint entryId); - protected ushort GetSizeOfEntry() + private static ushort GetSizeOfEntry() { return typeof(T).GetCustomAttribute()?.DataEntrySize ?? throw new NotSupportedException("DataEntrySize is null"); @@ -33,16 +36,20 @@ public abstract class DataReader throw new NotSupportedException("XorKey is null"); } + private readonly byte _xorKey; + protected readonly ushort SizeOfEntry; + private readonly string _datFileName; + protected byte[] DatFileContent { get; } - private static Span GetDatFileContent(string path) + private Span GetDatFileContent(string path) { - var fileData = File.ReadAllBytes(path + GetDatFileName()); + var fileData = File.ReadAllBytes(path + this._datFileName); var data = new byte[fileData.Length]; for (var i = 0; i < fileData.Length; i++) { - data[i] = (byte)(fileData[i] ^ GetXorKey()); + data[i] = (byte)(fileData[i] ^ this._xorKey); } return data; diff --git a/Wonderking/Game/Reader/ItemReader.cs b/Wonderking/Game/Reader/ItemReader.cs index 8b00ff0..4bc4eef 100644 --- a/Wonderking/Game/Reader/ItemReader.cs +++ b/Wonderking/Game/Reader/ItemReader.cs @@ -1,4 +1,7 @@ +using System.Buffers.Binary; +using System.Text; using Wonderking.Game.Data; +using Wonderking.Game.Data.Item; namespace Wonderking.Game.Reader; @@ -6,98 +9,187 @@ public class ItemReader(string path) : DataReader(path) { public override uint GetAmountOfEntries() { - return (uint)((this.DatFileContent.Length - 9) / this.GetSizeOfEntry()); + return (uint)((this.DatFileContent.Length - 9) / this.SizeOfEntry); } public override ItemObject GetEntry(uint entryId) { var item = new ItemObject(); var arraySegment = new ArraySegment(DatFileContent, - 9 + (int)entryId * this.GetSizeOfEntry(), this.GetSizeOfEntry()); - var reader = new BinaryReader(new MemoryStream(arraySegment.ToArray())); - item.ItemID = reader.ReadUInt32(); //9 - item.Disabled = reader.ReadUInt32() == 1; //13 - item.ItemType = reader.ReadUInt32(); //17 - item.Unknown2 = reader.ReadBytes(4); //21 - item.Unknown3 = reader.ReadBytes(4); //25 - item.ClassNo1 = reader.ReadUInt32(); //29 - item.ClassNo2 = reader.ReadUInt32(); //33 - item.ClassNo3 = reader.ReadUInt32(); //37 - item.ClassNo4 = reader.ReadUInt32(); //41 - item.SlotNo1 = reader.ReadUInt32(); //45 - item.SlotNo2 = reader.ReadUInt32(); //49 - item.Unknown4 = reader.ReadBytes(4); //53 - item.IsCash = reader.ReadUInt32(); //57 - item.Unknown5 = reader.ReadBytes(4); //61 - item.Price = reader.ReadUInt32(); //65 - item.Unknown7 = reader.ReadBytes(4); //69 - item.MaxNumber = reader.ReadUInt32(); //73 - item.Unknown17 = reader.ReadBytes(12); //77 - item.MaximumLevelRequirement = reader.ReadUInt32(); //89 - item.SexNo = reader.ReadUInt32(); //93 - item.WeaponSomething = reader.ReadUInt32(); //97 - item.Unknown8 = reader.ReadBytes(4); //101 - item.R2C = reader.ReadBytes(16); //105 - item.Unknown9 = reader.ReadBytes(4); //121 - item.Stats = reader.ReadStats(); //125 - item.ElementalStats = reader.ReadElementalStats(); //149 - item.R7C = reader.ReadBytes(4); //213 - item.R8C = reader.ReadBytes(8); //217 - item.Speed = reader.ReadSingle(); //225 - item.Jump = reader.ReadSingle(); //229 - item.StatDefense = reader.ReadInt32(); //233 - item.MagicID = reader.ReadUInt32(); //237 - item.Unknown13 = reader.ReadBytes(4); //241 - item.Unknown14 = reader.ReadBytes(4); //245 - item.AdditionalHealthRecoveryVolume = reader.ReadInt32(); //249 - item.R9C_1 = reader.ReadBytes(4); //253 - item.AdditionalManaRecoveryVolume = reader.ReadInt32(); //257 - item.R9C_2 = reader.ReadBytes(4); //261 - item.R10C = reader.ReadBytes(8); //265 - item.AdditionalHealthPoints = reader.ReadInt32(); //273 - item.AdditionalManaPoints = reader.ReadInt32(); //277 - item.IsArrow = reader.ReadBoolean(); //281 - item.Unknown18 = reader.ReadBytes(7); //282 - item.AdditionalEvasionRate = reader.ReadInt32(); //289 - item.HitRate = reader.ReadInt32(); //293 - item.ChanceToHit = reader.ReadInt32(); //297 - item.MagicalDamage = reader.ReadInt32(); //301 - item.CriticalHitChance = reader.ReadInt32(); //305 - item.R12C = reader.ReadBytes(4); //309 - item.Unknown16 = reader.ReadBytes(4); //313 - item.MinimalAttackDamage = reader.ReadInt32(); //317 - item.MaximalAttackDamage = reader.ReadInt32(); //321 - item.PhysicalDamage = reader.ReadInt32(); //325 - item.CraftMaterial = reader.ReadCraftMaterial(); //329 - item.CraftResultAmount = reader.ReadUInt32(); //361 - item.R14C = reader.ReadBytes(4); //365 - item.CraftResultItem = reader.ReadUInt32(); //369 - item.R15C = reader.ReadBytes(4); //373 - item.R16C = reader.ReadBytes(20); //377 - item.InventoryX = reader.ReadInt32(); //397 - item.InventoryY = reader.ReadInt32(); //401 - item.InventoryWidth = reader.ReadInt32(); //405 - item.InventoryHeight = reader.ReadInt32(); //409 - item.SheetID = reader.ReadInt32(); //413 - item.Name = reader.ReadString(20); //417 - item.Description = reader.ReadString(85); //427 - item.Unknown1 = reader.ReadBytes(175); //493 - item.IsEnchantable = reader.ReadUInt32() == 1; //687 - item.Unknown1_2 = reader.ReadBytes(104); //687 - item.SetItems = reader.ReadArray(5); - item.SetID = reader.ReadUInt32(); //691 - item.Options = reader.ReadItemOptions(); //819 - item.Unknown19 = reader.ReadBytes(23); //835 - item.PetID = reader.ReadByte(); //858 - item.Unknown20 = reader.ReadBytes(20); //859 - item.HitBoxScaling = reader.ReadByte(); //879 - item.Unknown20_2 = reader.ReadBytes(13); //880 - item.ContainedItems = reader.ReadContainedItems(); //893 - item.IsQuestItem = reader.ReadBoolean(); //923 - item.MinimumLevelRequirement = reader.ReadByte(); //924 - item.Unknown21_2 = reader.ReadBytes(6); //925 - reader.Dispose(); //931 + 9 + (int)entryId * this.SizeOfEntry, this.SizeOfEntry); + var data = new Span(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + item.ItemID = BitConverter.ToUInt32(data.Slice(0, 4)); // 0 -> 4 + item.Disabled = BitConverter.ToBoolean(data.Slice(4, 4)); // 4 -> 8 + item.ItemType = BitConverter.ToUInt32(data.Slice(8, 4)); // 8 -> 12 + item.Unknown2 = data.Slice(12, 4).ToArray(); // 12 -> 16 + item.Unknown3 = data.Slice(16, 4).ToArray(); // 16 -> 20 + item.ClassNo1 = BitConverter.ToUInt32(data.Slice(20, 4)); // 20 -> 24 + item.ClassNo2 = BitConverter.ToUInt32(data.Slice(24, 4)); // 24 -> 28 + item.ClassNo3 = BitConverter.ToUInt32(data.Slice(28, 4)); // 28 -> 32 + item.ClassNo4 = BitConverter.ToUInt32(data.Slice(32, 4)); // 32 -> 36 + item.SlotNo1 = BitConverter.ToUInt32(data.Slice(36, 4)); // 36 -> 40 + item.SlotNo2 = BitConverter.ToUInt32(data.Slice(40, 4)); // 40 -> 44 + item.Unknown4 = data.Slice(44, 4).ToArray(); // 44 -> 48 + item.IsCash = BitConverter.ToUInt32(data.Slice(48, 4)); // 48 -> 52 + item.Unknown5 = data.Slice(52, 4).ToArray(); // 52 -> 56 + item.Price = BitConverter.ToUInt32(data.Slice(56, 4)); // 56 -> 60 + item.Unknown7 = data.Slice(60, 4).ToArray(); // 60 -> 64 + item.MaxNumber = BitConverter.ToUInt32(data.Slice(64, 4)); // 64 -> 68 + item.Unknown17 = data.Slice(68, 12).ToArray(); // 68 -> 80 + item.MaximumLevelRequirement = BitConverter.ToUInt32(data.Slice(80, 4)); // 80 -> 84 + item.SexNo = BitConverter.ToUInt32(data.Slice(84, 4)); // 84 -> 88 + item.WeaponSomething = BitConverter.ToUInt32(data.Slice(88, 4)); // 88 -> 92 + item.Unknown8 = data.Slice(92, 4).ToArray(); // 92 -> 96 + item.R2C = data.Slice(96, 16).ToArray(); // 96 -> 112 + item.Unknown9 = data.Slice(112, 4).ToArray(); // 112 -> 116 + item.Stats = new Stats + { + Strength = BitConverter.ToInt32(data.Slice(116, 4)), // 116 -> 120 + Dexterity = BitConverter.ToInt32(data.Slice(120, 4)), // 120 -> 124 + Intelligence = BitConverter.ToInt32(data.Slice(124, 4)), // 124 -> 128 + Vitality = BitConverter.ToInt32(data.Slice(128, 4)), // 128 -> 132 + Luck = BitConverter.ToInt32(data.Slice(132, 4)), // 132 -> 136 + Wisdom = BitConverter.ToInt32(data.Slice(136, 4)), // 136 -> 140 + }; // 116 -> 140 + item.ElementalStats = new ElementalStats + { + MinimumFireDamage = BitConverter.ToInt32(data.Slice(140, 4)), // 140 -> 144 + MinimumWaterDamage = BitConverter.ToInt32(data.Slice(144, 4)), // 144 -> 148 + MinimumDarkDamage = BitConverter.ToInt32(data.Slice(148, 4)), // 148 -> 152 + MinimumHolyDamage = BitConverter.ToInt32(data.Slice(152, 4)), // 152 -> 156 + MaximumFireDamage = BitConverter.ToInt32(data.Slice(156, 4)), // 156 -> 160 + MaximumWaterDamage = BitConverter.ToInt32(data.Slice(160, 4)), // 160 -> 164 + MaximumDarkDamage = BitConverter.ToInt32(data.Slice(164, 4)), // 164 -> 168 + MaximumHolyDamage = BitConverter.ToInt32(data.Slice(168, 4)), // 168 -> 172 + ElementFire = BitConverter.ToUInt32(data.Slice(172, 4)), // 172 -> 176 + ElementWater = BitConverter.ToUInt32(data.Slice(176, 4)), // 176 -> 180 + ElementDark = BitConverter.ToUInt32(data.Slice(180, 4)), // 180 -> 184 + ElementHoly = BitConverter.ToUInt32(data.Slice(184, 4)), // 184 -> 188 + FireResistance = BitConverter.ToInt32(data.Slice(188, 4)), // 188 -> 192 + WaterResistance = BitConverter.ToInt32(data.Slice(192, 4)), // 192 -> 196 + DarkResistance = BitConverter.ToInt32(data.Slice(196, 4)), // 196 -> 200 + HolyResistance = BitConverter.ToInt32(data.Slice(200, 4)), // 200 -> 204 + }; // 140 -> 204 + item.R7C = data.Slice(204, 4).ToArray(); // 204 -> 208 + item.R8C = data.Slice(208, 8).ToArray(); // 208 -> 216 + item.Speed = BinaryPrimitives.ReadSingleLittleEndian(data.Slice(216, 4)); // 216 -> 220 + item.Jump = BinaryPrimitives.ReadSingleLittleEndian(data.Slice(220, 4)); // 220 -> 224 + item.StatDefense = BitConverter.ToInt32(data.Slice(224, 4)); // 224 -> 228 + item.MagicID = BitConverter.ToUInt32(data.Slice(228, 4)); // 228 -> 232 + item.Unknown13 = data.Slice(232, 4).ToArray(); // 232 -> 236 + item.Unknown14 = data.Slice(236, 4).ToArray(); // 236 -> 240 + item.AdditionalHealthRecoveryVolume = BitConverter.ToInt32(data.Slice(240, 4)); // 240 -> 244 + item.R9C_1 = data.Slice(244, 4).ToArray(); // 244 -> 248 + item.AdditionalManaRecoveryVolume = BitConverter.ToInt32(data.Slice(248, 4)); // 248 -> 252 + item.R9C_2 = data.Slice(252, 4).ToArray(); // 252 -> 256 + item.R10C = data.Slice(256, 8).ToArray(); // 256 -> 264 + item.AdditionalHealthPoints = BitConverter.ToInt32(data.Slice(264, 4)); // 264 -> 268 + item.AdditionalManaPoints = BitConverter.ToInt32(data.Slice(268, 4)); // 268 -> 272 + item.IsArrow = BitConverter.ToBoolean(data.Slice(272, 1)); // 272 -> 273 + item.Unknown18 = data.Slice(273, 7).ToArray(); // 273 -> 280 + item.AdditionalEvasionRate = BitConverter.ToInt32(data.Slice(280, 4)); // 280 -> 284 + item.HitRate = BitConverter.ToInt32(data.Slice(284, 4)); // 284 -> 288 + item.ChanceToHit = BitConverter.ToInt32(data.Slice(288, 4)); // 288 -> 292 + item.MagicalDamage = BitConverter.ToInt32(data.Slice(292, 4)); // 292 -> 296 + item.CriticalHitChance = BitConverter.ToInt32(data.Slice(296, 4)); // 296 -> 300 + item.R12C = data.Slice(300, 4).ToArray(); // 300 -> 304 + item.Unknown16 = data.Slice(304, 4).ToArray(); // 304 -> 308 + item.MinimalAttackDamage = BitConverter.ToInt32(data.Slice(308, 4)); // 308 -> 312 + item.MaximalAttackDamage = BitConverter.ToInt32(data.Slice(312, 4)); // 312 -> 316 + item.PhysicalDamage = BitConverter.ToInt32(data.Slice(316, 4)); // 316 -> 320 + item.CraftMaterial = new CraftMaterial[] + { + new() + { + ID = BitConverter.ToUInt32(data.Slice(320, 4)), // 320 -> 324 + Amount = BitConverter.ToUInt32(data.Slice(336, 4)) // 336 -> 340 + }, + new() + { + ID = BitConverter.ToUInt32(data.Slice(324, 4)), // 324 -> 328 + Amount = BitConverter.ToUInt32(data.Slice(340, 4)) // 340 -> 344 + }, + new() + { + ID = BitConverter.ToUInt32(data.Slice(328, 4)), // 328 -> 332 + Amount = BitConverter.ToUInt32(data.Slice(344, 4)) // 344 -> 348 + }, + new() + { + ID = BitConverter.ToUInt32(data.Slice(332, 4)), // 332 -> 336 + Amount = BitConverter.ToUInt32(data.Slice(348, 4)) // 348 -> 352 + }, + }; // 320 -> 352 + item.CraftResultAmount = BitConverter.ToUInt32(data.Slice(352, 4)); // 352 -> 356 + item.R14C = data.Slice(356, 4).ToArray(); // 356 -> 360 + item.CraftResultItem = BitConverter.ToUInt32(data.Slice(360, 4)); // 360 -> 364 + item.R15C = data.Slice(364, 4).ToArray(); // 364 -> 368 + item.R16C = data.Slice(368, 20).ToArray(); // 368 -> 388 + item.InventoryX = BitConverter.ToInt32(data.Slice(388, 4)); // 388 -> 392 + item.InventoryY = BitConverter.ToInt32(data.Slice(392, 4)); // 392 -> 396 + item.InventoryWidth = BitConverter.ToInt32(data.Slice(396, 4)); // 396 -> 400 + item.InventoryHeight = BitConverter.ToInt32(data.Slice(400, 4)); // 400 -> 404 + item.SheetID = BitConverter.ToInt32(data.Slice(404, 4)); // 404 -> 408 + item.Name = Encoding.ASCII.GetString(data.Slice(408, 20)); // 408 -> 428 + item.Description = Encoding.ASCII.GetString(data.Slice(428, 85)); // 428 -> 513 + item.Unknown1 = data.Slice(513, 175).ToArray(); // 513 -> 688 + item.IsEnchantable = BitConverter.ToBoolean(data.Slice(688, 4)); // 688 -> 672 + item.Unknown1_2 = data.Slice(692, 104).ToArray(); // 692 -> 796 + item.SetItems = new[] + { + BitConverter.ToUInt32(data.Slice(796, 4)), // 796 -> 800 + BitConverter.ToUInt32(data.Slice(800, 4)), // 800 -> 804 + BitConverter.ToUInt32(data.Slice(804, 4)), // 804 -> 808 + BitConverter.ToUInt32(data.Slice(808, 4)), // 808 -> 812 + BitConverter.ToUInt32(data.Slice(812, 4)), // 812 -> 816 + }; // 796 -> 816 + item.SetID = BitConverter.ToUInt32(data.Slice(816, 4)); // 816 -> 820 + item.Options = new ItemOptions + { + OptionIDs = new[] + { + BitConverter.ToUInt32(data.Slice(824, 4)), // 824 -> 828 + BitConverter.ToUInt32(data.Slice(828, 4)), // 828 -> 832 + BitConverter.ToUInt32(data.Slice(832, 4)), // 832 -> 836 + BitConverter.ToUInt32(data.Slice(836, 4)), // 836 -> 840 + }, + OptionAvailable = BitConverter.ToBoolean(data.Slice(820, 4)), // 820 -> 824 + }; // 820 -> 840 + item.Unknown19 = data.Slice(840, 23).ToArray(); // 840 -> 863 + item.PetID = data[863]; // 863 -> 864 + item.Unknown20 = data.Slice(864, 20).ToArray(); // 864 -> 884 + item.HitBoxScaling = data[884]; // 884 -> 885 + item.Unknown20_2 = data.Slice(885, 13).ToArray(); // 885 -> 898 + item.ContainedItems = new[] + { + new ContainedItem + { + ID = BitConverter.ToInt16(data.Slice(898, 2)), // 898 -> 900 + ObtainChance = BitConverter.ToSingle(data.Slice(908, 4)) // 908 -> 912 + }, + new ContainedItem + { + ID = BitConverter.ToInt16(data.Slice(900, 2)), // 900 -> 902 + ObtainChance = BitConverter.ToSingle(data.Slice(912, 4)) // 912 -> 916 + }, + new ContainedItem + { + ID = BitConverter.ToInt16(data.Slice(902, 2)), // 902 -> 904 + ObtainChance = BitConverter.ToSingle(data.Slice(916, 4)) // 916 -> 920 + }, + new ContainedItem + { + ID = BitConverter.ToInt16(data.Slice(904, 2)), // 904 -> 906 + ObtainChance = BitConverter.ToSingle(data.Slice(920, 4)) // 920 -> 924 + }, + new ContainedItem + { + ID = BitConverter.ToInt16(data.Slice(906, 2)), // 906 -> 908 + ObtainChance = BitConverter.ToSingle(data.Slice(924, 4)) // 924 -> 928 + }, + }; + item.MinimumLevelRequirement = data[928]; // 928 -> 929 + item.Unknown21_2 = data.Slice(929, 3).ToArray(); // 929 -> 932 return item; } - } diff --git a/Wonderking/Game/Reader/ItemReaderExtensions.cs b/Wonderking/Game/Reader/ItemReaderExtensions.cs index 97a8b72..6fe699c 100644 --- a/Wonderking/Game/Reader/ItemReaderExtensions.cs +++ b/Wonderking/Game/Reader/ItemReaderExtensions.cs @@ -82,14 +82,14 @@ public static class ItemReaderExtensions options.OptionAvailable = reader.ReadInt32() == 1; //819 - var optionIDs = new List(4); + var optionIDs = new List(4); //823 for (var i = 0; i < 3; i++) { - optionIDs.Add((byte)reader.ReadUInt32()); + optionIDs.Add(reader.ReadUInt32()); } - options.OptionIDs = optionIDs; + options.OptionIDs = optionIDs.ToArray(); return options; } From 3602d74eb40dfe03e0ec425eb928553fa269b65d Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 06:47:28 +0100 Subject: [PATCH 03/60] refactor: Exception and init of dictionary --- Server/Services/ItemObjectPoolService.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Server/Services/ItemObjectPoolService.cs b/Server/Services/ItemObjectPoolService.cs index 771ad27..6b190e5 100644 --- a/Server/Services/ItemObjectPoolService.cs +++ b/Server/Services/ItemObjectPoolService.cs @@ -10,7 +10,7 @@ namespace Server.Services; public class ItemObjectPoolService : IHostedService { - readonly ConcurrentDictionary _itemObjectPool = new(); + readonly ConcurrentDictionary _itemObjectPool; private readonly ItemReader _itemReader; private readonly ILogger _logger; @@ -19,6 +19,8 @@ public class ItemObjectPoolService : IHostedService _logger = logger; _itemReader = new ItemReader(configuration.GetSection("Game").GetSection("Data").GetValue("Path") ?? string.Empty); + + _itemObjectPool = new ConcurrentDictionary(); } public Task StartAsync(CancellationToken cancellationToken) @@ -30,7 +32,7 @@ public class ItemObjectPoolService : IHostedService var result = _itemObjectPool.TryAdd(itemObject.ItemID, itemObject); if (!result) { - throw new Exception($"Failed to add item {itemObject.ItemID} to the item object pool"); + throw new KeyNotFoundException($"Failed to add item {itemObject.ItemID} to the item object pool"); } _logger.LogTrace("Item with {ID} has been added", itemObject.ItemID); @@ -43,7 +45,6 @@ public class ItemObjectPoolService : IHostedService public Task StopAsync(CancellationToken cancellationToken) { - return Task.CompletedTask; } From 2d391834c45fd23006aa24a1b509df2b2c3a65b0 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 06:51:25 +0100 Subject: [PATCH 04/60] ci: let's test setup-node --- .gitea/workflows/server.yaml | 82 +++++++++++++++--------------------- 1 file changed, 33 insertions(+), 49 deletions(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 9e97765..db0db3c 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -22,55 +22,39 @@ jobs: id: sanitize run: echo "::set-output name=sanitized_branch_name::$(echo ${{ github.ref_name }} | sed 's/\//-/g')" -# docs: -# runs-on: ubuntu-latest -# container: registry.jetbrains.team/p/writerside/builder/writerside-builder:${{env.DOCKER_VERSION}} -# steps: -# - name: Install basic dependencies -# run: | -# wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash -# echo "::add-path::$HOME/.nvm" -# export PATH="$HOME/.nvm:$PATH" -# export NVM_DIR="$HOME/.nvm" -# [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" -# echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> ~/.bashrc -# echo "$HOME/.nvm" >> $GITHUB_PATH -# nvm --version -# - name: Install Node -# run: | -# echo $PATH -# echo $GITHUB_PATH -# cat $GITHUB_PATH -# echo $NVM_DIR -# ls -la $HOME/.nvm -# nvm install 18 -# nvm use 18 -# node -v -# - name: Checkout repository -# uses: https://github.com/actions/checkout@v3 -# - name: Build docs -# run: | -# set -e -# export DISPLAY=:99 -# Xvfb :99 & -# /opt/builder/bin/idea.sh helpbuilderinspect -source-dir . -product ${{env.INSTANCE}} -output-dir artifacts/ || true -# echo "Test existing of ${{ env.ARTIFACT }} artifact" -# test -e artifacts/${{ env.ARTIFACT }} -# - name: rename artifact -# run: | -# mv artifacts/${{ env.ARTIFACT }} artifacts/wiki.zip -# - name: Upload documentation -# uses: actions/upload-artifact@v3 -# with: -# name: docs -# path: artifacts/wiki.zip -# retention-days: 14 -# - name: Upload algolia-indexes -# uses: actions/upload-artifact@v3 -# with: -# name: algolia-indexes -# path: artifacts/${{ env.ALGOLIA_ARTIFACT }} -# retention-days: 14 + docs: + runs-on: ubuntu-latest + container: registry.jetbrains.team/p/writerside/builder/writerside-builder:${{env.DOCKER_VERSION}} + steps: + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: 18 + - name: Checkout repository + uses: https://github.com/actions/checkout@v3 + - name: Build docs + run: | + set -e + export DISPLAY=:99 + Xvfb :99 & + /opt/builder/bin/idea.sh helpbuilderinspect -source-dir . -product ${{env.INSTANCE}} -output-dir artifacts/ || true + echo "Test existing of ${{ env.ARTIFACT }} artifact" + test -e artifacts/${{ env.ARTIFACT }} + - name: rename artifact + run: | + mv artifacts/${{ env.ARTIFACT }} artifacts/wiki.zip + - name: Upload documentation + uses: actions/upload-artifact@v3 + with: + name: docs + path: artifacts/wiki.zip + retention-days: 14 + - name: Upload algolia-indexes + uses: actions/upload-artifact@v3 + with: + name: algolia-indexes + path: artifacts/${{ env.ALGOLIA_ARTIFACT }} + retention-days: 14 build: runs-on: ubuntu-latest From 01702e94f776cceff3944e648ccf105197cc5619 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 06:56:03 +0100 Subject: [PATCH 05/60] ci: nvm attempt --- .gitea/workflows/server.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index db0db3c..fe45f72 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -26,6 +26,21 @@ jobs: runs-on: ubuntu-latest container: registry.jetbrains.team/p/writerside/builder/writerside-builder:${{env.DOCKER_VERSION}} steps: + - name: Install basic dependencies + run: | + wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash + echo "::add-path::$HOME/.nvm" + export PATH="$HOME/.nvm:$PATH" + export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> ~/.bashrc + echo "$HOME/.nvm" >> $GITHUB_PATH + nvm --version + - name: Check Node.js version + run: | + nvm --version + node -v + npm -v - name: Install Node uses: actions/setup-node@v4 with: From 2983c84bd8d0bfa8095d2fcc4e97cdf6be6f378d Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 06:58:24 +0100 Subject: [PATCH 06/60] ci: github env attempt --- .gitea/workflows/server.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index fe45f72..96d2598 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -34,8 +34,10 @@ jobs: export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> ~/.bashrc + echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $GITHUB_PATH echo "$HOME/.nvm" >> $GITHUB_PATH nvm --version + echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV - name: Check Node.js version run: | nvm --version From b4a0681c5708098f5eb9d24c3f9a4e68bf82cb80 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:00:54 +0100 Subject: [PATCH 07/60] ci: test --- .gitea/workflows/server.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 96d2598..1f29139 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -40,6 +40,8 @@ jobs: echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV - name: Check Node.js version run: | + cat $GITHUB_PATH + cat $GITHUB_ENV nvm --version node -v npm -v From 0bf8fa241e5e8e6bbc90a849c196df01593a8d57 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:04:07 +0100 Subject: [PATCH 08/60] ci: weird stuff --- .gitea/workflows/server.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 1f29139..77b982c 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -42,6 +42,9 @@ jobs: run: | cat $GITHUB_PATH cat $GITHUB_ENV + echo $PATH + echo $GITHUB_PATH + echo $GITHUB_ENV nvm --version node -v npm -v From fe99ae1b8df3779565c2296a003b270de82ed048 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:05:07 +0100 Subject: [PATCH 09/60] ci: set path for node/npm --- .gitea/workflows/server.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 77b982c..b9e2547 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -38,6 +38,8 @@ jobs: echo "$HOME/.nvm" >> $GITHUB_PATH nvm --version echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV + echo "::add-path::$(which node)" + echo "::add-path::$(which npm)" - name: Check Node.js version run: | cat $GITHUB_PATH From 70d173199c99310d5f04d0f62d6f93a17e0d9ffd Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:06:14 +0100 Subject: [PATCH 10/60] ci: again --- .gitea/workflows/server.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index b9e2547..de9b2e6 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -34,7 +34,6 @@ jobs: export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> ~/.bashrc - echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $GITHUB_PATH echo "$HOME/.nvm" >> $GITHUB_PATH nvm --version echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV @@ -47,7 +46,6 @@ jobs: echo $PATH echo $GITHUB_PATH echo $GITHUB_ENV - nvm --version node -v npm -v - name: Install Node From dbc3ff3c6231aeea77aee8733b240b2b34af5fff Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:07:35 +0100 Subject: [PATCH 11/60] ci: again --- .gitea/workflows/server.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index de9b2e6..3574125 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -35,10 +35,10 @@ jobs: [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> ~/.bashrc echo "$HOME/.nvm" >> $GITHUB_PATH + echo $(which node) >> $GITHUB_PATH + echo $(which npm) >> $GITHUB_PATH nvm --version echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV - echo "::add-path::$(which node)" - echo "::add-path::$(which npm)" - name: Check Node.js version run: | cat $GITHUB_PATH From cdac9fdf37ae2e6c2d5009414f85e2e2fa664081 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:11:41 +0100 Subject: [PATCH 12/60] ci: another one --- .gitea/workflows/server.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 3574125..c6de7aa 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -34,9 +34,9 @@ jobs: export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> ~/.bashrc - echo "$HOME/.nvm" >> $GITHUB_PATH - echo $(which node) >> $GITHUB_PATH - echo $(which npm) >> $GITHUB_PATH + export PATH="$(which node):$PATH" + export PATH="$(which npm):$PATH" + export PATH="$(which nvm):$PATH" nvm --version echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV - name: Check Node.js version From e1e6268e90ffbd3d364c01fed0fafc41cad3f60d Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:12:48 +0100 Subject: [PATCH 13/60] ci: add-path --- .gitea/workflows/server.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index c6de7aa..1221b8c 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -37,6 +37,9 @@ jobs: export PATH="$(which node):$PATH" export PATH="$(which npm):$PATH" export PATH="$(which nvm):$PATH" + echo "::add-path::$(which nvm)" + echo "::add-path::$(which node)" + echo "::add-path::$(which npm)" nvm --version echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV - name: Check Node.js version From 803a19765f34da58f7a4182adfbdff09cce3e2ce Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:13:44 +0100 Subject: [PATCH 14/60] ci: what --- .gitea/workflows/server.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 1221b8c..6d5eb5a 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -37,6 +37,7 @@ jobs: export PATH="$(which node):$PATH" export PATH="$(which npm):$PATH" export PATH="$(which nvm):$PATH" + echo $(which node) echo "::add-path::$(which nvm)" echo "::add-path::$(which node)" echo "::add-path::$(which npm)" From 973bbe9803c1db4c75c369e03b27daaeae9bd987 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:14:38 +0100 Subject: [PATCH 15/60] ci: stupid --- .gitea/workflows/server.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 6d5eb5a..2399f94 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -33,6 +33,8 @@ jobs: export PATH="$HOME/.nvm:$PATH" export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + nvm install 18 + nvm use 18 echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> ~/.bashrc export PATH="$(which node):$PATH" export PATH="$(which npm):$PATH" From a2252efd3af7a0c70161abd0f4fc527d533e534f Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:18:43 +0100 Subject: [PATCH 16/60] ci: maybe --- .gitea/workflows/server.yaml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 2399f94..b1a9eb8 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -36,13 +36,8 @@ jobs: nvm install 18 nvm use 18 echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> ~/.bashrc - export PATH="$(which node):$PATH" - export PATH="$(which npm):$PATH" - export PATH="$(which nvm):$PATH" echo $(which node) - echo "::add-path::$(which nvm)" - echo "::add-path::$(which node)" - echo "::add-path::$(which npm)" + echo "::add-path::$(dirname $(which npm))" nvm --version echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV - name: Check Node.js version @@ -54,10 +49,6 @@ jobs: echo $GITHUB_ENV node -v npm -v - - name: Install Node - uses: actions/setup-node@v4 - with: - node-version: 18 - name: Checkout repository uses: https://github.com/actions/checkout@v3 - name: Build docs From 40c9224158fdee4fb4e06c75f97cc58ff482174e Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:21:10 +0100 Subject: [PATCH 17/60] ci: cleanup --- .gitea/workflows/server.yaml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index b1a9eb8..a1b18b3 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -30,23 +30,14 @@ jobs: run: | wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash echo "::add-path::$HOME/.nvm" - export PATH="$HOME/.nvm:$PATH" export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" nvm install 18 nvm use 18 - echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> ~/.bashrc - echo $(which node) echo "::add-path::$(dirname $(which npm))" nvm --version - echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV - name: Check Node.js version run: | - cat $GITHUB_PATH - cat $GITHUB_ENV - echo $PATH - echo $GITHUB_PATH - echo $GITHUB_ENV node -v npm -v - name: Checkout repository @@ -65,13 +56,13 @@ jobs: - name: Upload documentation uses: actions/upload-artifact@v3 with: - name: docs + name: wiki.zip path: artifacts/wiki.zip retention-days: 14 - name: Upload algolia-indexes uses: actions/upload-artifact@v3 with: - name: algolia-indexes + name: algolia-indexes.zip path: artifacts/${{ env.ALGOLIA_ARTIFACT }} retention-days: 14 From 6ebc360d1baa935953d64d0c6428fa4e1a90fa60 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:29:36 +0100 Subject: [PATCH 18/60] ci: simple image setup --- .gitea/workflows/server.yaml | 43 ++++++++++++++++++++++++++++++++++++ Wiki.Dockerfile | 2 ++ 2 files changed, 45 insertions(+) create mode 100644 Wiki.Dockerfile diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index a1b18b3..f93f9b7 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -66,6 +66,49 @@ jobs: path: artifacts/${{ env.ALGOLIA_ARTIFACT }} retention-days: 14 + build-docs-container: + runs-on: ubuntu-latest + container: catthehacker/ubuntu:act-latest + needs: [ docs ] + steps: + - name: Checkout repository + uses: https://github.com/actions/checkout@v3 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: ${{ github.server_url }} + username: ${{ github.actor }} + password: ${{ secrets.REGISTRY_TOKEN }} + - name: Retrieve docs artifact + uses: actions/download-artifact@v2 + with: + name: wiki.zip + - name: Unzip wiki.zip into .public + run: | + mkdir .public + unzip wiki.zip -d .public + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + file: Wiki.Dockerfile + push: true + tags: forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}-wiki + platforms: linux/amd64,linux/arm64 + - name: Build and push to latest + if: github.ref_name == 'master' + uses: docker/build-push-action@v5 + with: + context: . + file: Wiki.Dockerfile + push: true + tags: forge.rainote.dev/${{ github.repository }}:latest-wiki + platforms: linux/amd64, linux/arm64 + build: runs-on: ubuntu-latest steps: diff --git a/Wiki.Dockerfile b/Wiki.Dockerfile new file mode 100644 index 0000000..eb4f5a6 --- /dev/null +++ b/Wiki.Dockerfile @@ -0,0 +1,2 @@ +FROM nginx +COPY .public /usr/share/nginx/html From e16bb57c882229c26f43c1124a5cf2f9e92eef64 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:31:55 +0100 Subject: [PATCH 19/60] ci: again? --- .gitea/workflows/server.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index f93f9b7..e97573c 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -84,9 +84,9 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.REGISTRY_TOKEN }} - name: Retrieve docs artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: - name: wiki.zip + name: wiki - name: Unzip wiki.zip into .public run: | mkdir .public From 3cb1e23e232a3d0fec9c89de007851119a0db3d4 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:33:28 +0100 Subject: [PATCH 20/60] ci: so? --- .gitea/workflows/server.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index e97573c..c8ffb72 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -86,7 +86,7 @@ jobs: - name: Retrieve docs artifact uses: actions/download-artifact@v3 with: - name: wiki + name: wiki.zip - name: Unzip wiki.zip into .public run: | mkdir .public From 8ee8bce13aa08390f44fbe75d4dcb1db13ddafd9 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:35:18 +0100 Subject: [PATCH 21/60] ci: file not found --- .gitea/workflows/server.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index c8ffb72..cce2665 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -90,6 +90,7 @@ jobs: - name: Unzip wiki.zip into .public run: | mkdir .public + ls unzip wiki.zip -d .public - name: Build and push uses: docker/build-push-action@v5 From 08c8539e35fb88f905e4ee02f5e9995ee1712e18 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:39:22 +0100 Subject: [PATCH 22/60] ci: maybe --- .gitea/workflows/server.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index cce2665..14c49c4 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -87,10 +87,12 @@ jobs: uses: actions/download-artifact@v3 with: name: wiki.zip + path: ${{ github.workspace }}/wiki.zip - name: Unzip wiki.zip into .public run: | mkdir .public ls + cd ${{ github.workspace }} unzip wiki.zip -d .public - name: Build and push uses: docker/build-push-action@v5 From 94cf988d935ac60243097b6cef1533d982038836 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:41:00 +0100 Subject: [PATCH 23/60] ci: ah --- .gitea/workflows/server.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 14c49c4..0899ade 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -91,9 +91,7 @@ jobs: - name: Unzip wiki.zip into .public run: | mkdir .public - ls - cd ${{ github.workspace }} - unzip wiki.zip -d .public + unzip ${{ github.workspace }}/wiki.zip -d .public - name: Build and push uses: docker/build-push-action@v5 with: From bc5351dbf7d0d97d2d28366013e6258d944fd3e2 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:46:14 +0100 Subject: [PATCH 24/60] ci: again --- .gitea/workflows/server.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 0899ade..ed84444 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -91,7 +91,7 @@ jobs: - name: Unzip wiki.zip into .public run: | mkdir .public - unzip ${{ github.workspace }}/wiki.zip -d .public + unzip -jo -qq ./wiki.zip -d .public - name: Build and push uses: docker/build-push-action@v5 with: From b40cfe00c1faa13c0e8b1b9e4911b763a337a7b6 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:47:44 +0100 Subject: [PATCH 25/60] ci: makes no sense --- .gitea/workflows/server.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index ed84444..c94af4f 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -91,6 +91,10 @@ jobs: - name: Unzip wiki.zip into .public run: | mkdir .public + pwd + cd ${{ github.workspace }} + pwd + ls unzip -jo -qq ./wiki.zip -d .public - name: Build and push uses: docker/build-push-action@v5 From 9f6d64f73d5299e175330c3f7818a20046adde55 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:51:25 +0100 Subject: [PATCH 26/60] ci: maybe? --- .gitea/workflows/server.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index c94af4f..adbe400 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -95,6 +95,7 @@ jobs: cd ${{ github.workspace }} pwd ls + file wiki.zip unzip -jo -qq ./wiki.zip -d .public - name: Build and push uses: docker/build-push-action@v5 From f65961fe94786698fc8c02e526399e2412fd0d9b Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:52:55 +0100 Subject: [PATCH 27/60] ci: again --- .gitea/workflows/server.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index adbe400..5b323d2 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -87,7 +87,7 @@ jobs: uses: actions/download-artifact@v3 with: name: wiki.zip - path: ${{ github.workspace }}/wiki.zip + path: ${{ github.workspace }} - name: Unzip wiki.zip into .public run: | mkdir .public From 7a98cdb89a2f061f77c335aef5c502c829fd38ff Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:54:32 +0100 Subject: [PATCH 28/60] ci: wtf? --- .gitea/workflows/server.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 5b323d2..76fbf84 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -95,6 +95,7 @@ jobs: cd ${{ github.workspace }} pwd ls + ls wiki.zip file wiki.zip unzip -jo -qq ./wiki.zip -d .public - name: Build and push From 6768fbf2a99aa7353d469be1f0c66911e0e19810 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:57:30 +0100 Subject: [PATCH 29/60] ci: why? --- .gitea/workflows/server.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 76fbf84..5b514dd 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -96,8 +96,8 @@ jobs: pwd ls ls wiki.zip - file wiki.zip - unzip -jo -qq ./wiki.zip -d .public + file wiki.zip/wiki.zip + unzip -jo -qq ./wiki.zip/wiki.zip -d ../.public - name: Build and push uses: docker/build-push-action@v5 with: From 580871ccb26504fa8e4933b7ae45289e41883aea Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 07:58:56 +0100 Subject: [PATCH 30/60] ci: cleanup --- .gitea/workflows/server.yaml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 5b514dd..82bf7f5 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -69,7 +69,7 @@ jobs: build-docs-container: runs-on: ubuntu-latest container: catthehacker/ubuntu:act-latest - needs: [ docs ] + needs: [ docs, preprocess ] steps: - name: Checkout repository uses: https://github.com/actions/checkout@v3 @@ -91,12 +91,6 @@ jobs: - name: Unzip wiki.zip into .public run: | mkdir .public - pwd - cd ${{ github.workspace }} - pwd - ls - ls wiki.zip - file wiki.zip/wiki.zip unzip -jo -qq ./wiki.zip/wiki.zip -d ../.public - name: Build and push uses: docker/build-push-action@v5 From c6552c7c5a5a6c57fd7765bcd0a7e0a2fd8c54c8 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 08:06:17 +0100 Subject: [PATCH 31/60] ci: am stupid --- .gitea/workflows/server.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 82bf7f5..a5acf09 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -91,7 +91,7 @@ jobs: - name: Unzip wiki.zip into .public run: | mkdir .public - unzip -jo -qq ./wiki.zip/wiki.zip -d ../.public + unzip -jo -qq ./wiki.zip/wiki.zip -d .public - name: Build and push uses: docker/build-push-action@v5 with: From 6a1abfeaf917ba714aea964ee6ee0ddf1b01de36 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 08:13:12 +0100 Subject: [PATCH 32/60] ci: automatically deploy wiki --- .gitea/workflows/server.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index a5acf09..862de2b 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -110,6 +110,19 @@ jobs: tags: forge.rainote.dev/${{ github.repository }}:latest-wiki platforms: linux/amd64, linux/arm64 + deploy-wiki: + runs-on: ubuntu-latest + container: catthehacker/ubuntu:act-latest + needs: [ build-docs-container, docs, preprocess ] + steps: + - name: Deploy Image to CapRrover + uses: caprover/deploy-from-github@v1.1.2 + with: + server: "${{ secrets.CAPROVER_SERVER }}" + app: "${{ secrets.WIKI_APP_NAME }}" + token: "${{ secrets.WIKI_APP_TOKEN }}" + image: forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}-wiki + build: runs-on: ubuntu-latest steps: From 1f9acf009d666fa3675e977b9dba1dfaf700cd61 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 08:14:04 +0100 Subject: [PATCH 33/60] ci: specify action for deployment --- .gitea/workflows/server.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 862de2b..b974793 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -116,7 +116,7 @@ jobs: needs: [ build-docs-container, docs, preprocess ] steps: - name: Deploy Image to CapRrover - uses: caprover/deploy-from-github@v1.1.2 + uses: https://github.com/caprover/deploy-from-github@v1.1.2 with: server: "${{ secrets.CAPROVER_SERVER }}" app: "${{ secrets.WIKI_APP_NAME }}" From a11675df456073adfdca15485494599fadfb554b Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 08:44:03 +0100 Subject: [PATCH 34/60] ci: manual deploy --- .gitea/workflows/server.yaml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index b974793..018ea5a 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -116,12 +116,8 @@ jobs: needs: [ build-docs-container, docs, preprocess ] steps: - name: Deploy Image to CapRrover - uses: https://github.com/caprover/deploy-from-github@v1.1.2 - with: - server: "${{ secrets.CAPROVER_SERVER }}" - app: "${{ secrets.WIKI_APP_NAME }}" - token: "${{ secrets.WIKI_APP_TOKEN }}" - image: forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}-wiki + run: | + docker run caprover/cli-caprover:2.2.3 caprover deploy --caproverUrl ${{ secrets.CAPROVER_SERVER }} --appToken ${{ secrets.WIKI_APP_TOKEN }} --imageName forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}-wiki -a ${{ secrets.WIKI_APP_NAME }} build: runs-on: ubuntu-latest From b68e05842969c88a77db084d047d07e8ccf09c89 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 10:25:10 +0100 Subject: [PATCH 35/60] refactor: remove testing code --- .../PacketHandlers/ChannelSelectionHandler.cs | 119 +++--------------- 1 file changed, 16 insertions(+), 103 deletions(-) diff --git a/Server/PacketHandlers/ChannelSelectionHandler.cs b/Server/PacketHandlers/ChannelSelectionHandler.cs index 7e66231..96f3129 100644 --- a/Server/PacketHandlers/ChannelSelectionHandler.cs +++ b/Server/PacketHandlers/ChannelSelectionHandler.cs @@ -1,6 +1,5 @@ using Microsoft.EntityFrameworkCore; using Server.DB.Documents; -using Wonderking.Game.Data.Character; using Wonderking.Packets.Incoming; using Wonderking.Packets.Outgoing; using Wonderking.Packets.Outgoing.Data; @@ -34,19 +33,19 @@ public class ChannelSelectionHandler : IPacketHandler { var authSession = (AuthSession)session; ChannelSelectionResponsePacket responsePacket; - CharacterSelectionSetGuildNamePacket guildNameResponsePacket; + var guildNameResponsePacket = new CharacterSelectionSetGuildNamePacket { GuildNames = Array.Empty() }; var hasCharacters = this._wonderkingContext.Accounts.Include(account => account.Characters) .FirstOrDefault(a => a.Id == authSession.AccountId)?.Characters.Count > 0; - var testingChars = false; - if (hasCharacters && !testingChars) + if (hasCharacters) { responsePacket = new ChannelSelectionResponsePacket { ChannelIsFullFlag = 0, Endpoint = "127.0.0.1", Port = 12345, - Characters = this._wonderkingContext.Characters.AsNoTracking().Where(c => c.Account.Id == authSession.AccountId) + Characters = this._wonderkingContext.Characters.AsNoTracking() + .Where(c => c.Account.Id == authSession.AccountId) .Select(c => new CharacterData { @@ -54,7 +53,8 @@ public class ChannelSelectionHandler : IPacketHandler Job = c.JobData, Gender = c.Gender, Level = c.Level, - Experience = 0, + // TODO: Calculate instead of clamping based on max experience for level + Experience = Math.Clamp(c.Experience, 0, 100), Stats = c.BaseStats, Health = c.Health, Mana = c.Mana, @@ -65,116 +65,29 @@ public class ChannelSelectionHandler : IPacketHandler EquippedCashItems = c.InventoryItems .Where(item => item.InventoryTab == InventoryTab.WornCashEquipment) .Select(item => item.ItemId) - .ToArray(), + .ToArray() }) .ToArray(), }; - guildNameResponsePacket = new CharacterSelectionSetGuildNamePacket - { - GuildNames = this._wonderkingContext.Characters.Where(c => c.Account.Id == authSession.AccountId) - .Select(character => character.Guild.Name).ToArray() - }; + guildNameResponsePacket.GuildNames = this._wonderkingContext.Characters + .Where(c => c.Account.Id == authSession.AccountId) + .Select(character => character.Guild.Name).ToArray(); } else { - responsePacket = testingChars - ? CreateTestChannelSelectionResponsePacket() - : new ChannelSelectionResponsePacket - { - ChannelIsFullFlag = 0, - Endpoint = "127.0.0.1", - Port = 12345, - Characters = Array.Empty() - }; - guildNameResponsePacket = new CharacterSelectionSetGuildNamePacket + responsePacket = new ChannelSelectionResponsePacket { - GuildNames = new[] { "ABCDEFGHIJKLMNOP", "QRSTUVWXYZ123456", "A Guild Name For" } + ChannelIsFullFlag = 0, + Endpoint = "127.0.0.1", + Port = 12345, + Characters = Array.Empty() }; } authSession.Send(responsePacket); - if (guildNameResponsePacket.GuildNames.Length > 0) - { - authSession.Send(guildNameResponsePacket); - } + authSession.Send(guildNameResponsePacket); return Task.CompletedTask; } - - private static ChannelSelectionResponsePacket CreateTestChannelSelectionResponsePacket() - { - return new ChannelSelectionResponsePacket - { - ChannelIsFullFlag = 0, - Endpoint = "127.0.0.1", - Port = 12345, - Characters = new[] - { - new CharacterData - { - Name = "1", - Job = new JobData { FirstJob = 1, SecondJob = 0, ThirdJob = 0, FourthJob = 0 }, - Gender = Gender.Female, - Level = ushort.MaxValue - 1, - Experience = 255, - Stats = new BaseStats - { - Strength = 5, - Dexterity = 5, - Intelligence = 5, - Vitality = 5, - Luck = 5, - Wisdom = 5 - }, - Health = int.MaxValue - 1, - Mana = int.MaxValue - 1, - EquippedItems = Enumerable.Repeat((ushort)25, 20).ToArray(), - EquippedCashItems = Enumerable.Repeat((ushort)70, 20).ToArray() - }, - new CharacterData - { - Name = "2", - Job = new JobData { FirstJob = 1, SecondJob = 0, ThirdJob = 0, FourthJob = 0 }, - Gender = Gender.Female, - Level = ushort.MaxValue - 1, - Experience = 255, - Stats = new BaseStats - { - Strength = 5, - Dexterity = 5, - Intelligence = 5, - Vitality = 5, - Luck = 5, - Wisdom = 5 - }, - Health = int.MaxValue - 1, - Mana = int.MaxValue - 1, - EquippedItems = Enumerable.Repeat((ushort)35, 20).ToArray(), - EquippedCashItems = Enumerable.Repeat((ushort)55, 20).ToArray() - }, - new CharacterData - { - Name = "3", - Job = new JobData { FirstJob = 1, SecondJob = 0, ThirdJob = 0, FourthJob = 0 }, - Gender = Gender.Female, - Level = ushort.MaxValue - 1, - Experience = 255, - Stats = new BaseStats - { - Strength = 5, - Dexterity = 5, - Intelligence = 5, - Vitality = 5, - Luck = 5, - Wisdom = 5 - }, - Health = int.MaxValue - 1, - Mana = int.MaxValue - 1, - EquippedItems = Enumerable.Repeat((ushort)45, 20).ToArray(), - EquippedCashItems = Enumerable.Repeat((ushort)65, 20).ToArray() - } - }, - }; - } } From 5abe7f75647cba4072e9ad9e1da6d10391a41f03 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 10:30:01 +0100 Subject: [PATCH 36/60] refactor: make HandleAsync async --- .../PacketHandlers/ChannelSelectionHandler.cs | 18 ++++++++---------- .../CharacterCreationHandler..cs | 9 ++++----- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/Server/PacketHandlers/ChannelSelectionHandler.cs b/Server/PacketHandlers/ChannelSelectionHandler.cs index 96f3129..3f64d74 100644 --- a/Server/PacketHandlers/ChannelSelectionHandler.cs +++ b/Server/PacketHandlers/ChannelSelectionHandler.cs @@ -29,22 +29,22 @@ public class ChannelSelectionHandler : IPacketHandler { } - public Task HandleAsync(ChannelSelectionPacket packet, TcpSession session) + public async Task HandleAsync(ChannelSelectionPacket packet, TcpSession session) { var authSession = (AuthSession)session; ChannelSelectionResponsePacket responsePacket; var guildNameResponsePacket = new CharacterSelectionSetGuildNamePacket { GuildNames = Array.Empty() }; - var hasCharacters = this._wonderkingContext.Accounts.Include(account => account.Characters) - .FirstOrDefault(a => a.Id == authSession.AccountId)?.Characters.Count > 0; - if (hasCharacters) + var account = await this._wonderkingContext.Accounts.Include(account => account.Characters) + .FirstOrDefaultAsync(a => a.Id == authSession.AccountId).ConfigureAwait(true); + if (account != null && account.Characters.Count > 0) { responsePacket = new ChannelSelectionResponsePacket { ChannelIsFullFlag = 0, Endpoint = "127.0.0.1", Port = 12345, - Characters = this._wonderkingContext.Characters.AsNoTracking() + Characters = await _wonderkingContext.Characters.AsNoTracking() .Where(c => c.Account.Id == authSession.AccountId) .Select(c => new CharacterData @@ -67,12 +67,12 @@ public class ChannelSelectionHandler : IPacketHandler .Select(item => item.ItemId) .ToArray() }) - .ToArray(), + .ToArrayAsync().ConfigureAwait(true), }; - guildNameResponsePacket.GuildNames = this._wonderkingContext.Characters + guildNameResponsePacket.GuildNames = await _wonderkingContext.Characters .Where(c => c.Account.Id == authSession.AccountId) - .Select(character => character.Guild.Name).ToArray(); + .Select(character => character.Guild.Name).ToArrayAsync().ConfigureAwait(true); } else { @@ -87,7 +87,5 @@ public class ChannelSelectionHandler : IPacketHandler authSession.Send(responsePacket); authSession.Send(guildNameResponsePacket); - - return Task.CompletedTask; } } diff --git a/Server/PacketHandlers/CharacterCreationHandler..cs b/Server/PacketHandlers/CharacterCreationHandler..cs index 5ee5af6..52bb1b3 100644 --- a/Server/PacketHandlers/CharacterCreationHandler..cs +++ b/Server/PacketHandlers/CharacterCreationHandler..cs @@ -21,7 +21,7 @@ public class CharacterCreationHandler : IPacketHandler _itemObjectPoolService = itemObjectPoolService; } - public Task HandleAsync(CharacterCreationPacket packet, TcpSession session) + public async Task HandleAsync(CharacterCreationPacket packet, TcpSession session) { var authSession = session as AuthSession; var account = @@ -59,9 +59,9 @@ public class CharacterCreationHandler : IPacketHandler Health = 250, Mana = 250, }); - _wonderkingContext.SaveChanges(); + await _wonderkingContext.SaveChangesAsync().ConfigureAwait(false); - var character = this._wonderkingContext.Characters.AsNoTracking() + var character = await _wonderkingContext.Characters.AsNoTracking() .Where(c => authSession != null && c.Account.Id == authSession.AccountId && c.Name == packet.Name) .Select(c => new CharacterData @@ -82,8 +82,7 @@ public class CharacterCreationHandler : IPacketHandler .Where(item => item.InventoryTab == InventoryTab.WornCashEquipment) .Select(item => item.ItemId) .ToArray(), - }).FirstOrDefault(); + }).FirstOrDefaultAsync().ConfigureAwait(true); authSession?.Send(new CharacterCreationResponsePacket { Character = character }); - return Task.CompletedTask; } } From 316881266b8a129f645e39a1fdc157db8db13612 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 12:06:36 +0100 Subject: [PATCH 37/60] feat: dbcontext pooling & lazy loading --- Server/DB/Documents/Account.cs | 5 +- Server/DB/Documents/Character.cs | 6 + Server/DB/Documents/GuildMember.cs | 4 +- Server/DB/Documents/InventoryItem.cs | 2 +- ...DBPoolingAndLazyLoadingSupport.Designer.cs | 333 ++++++++++++++++++ ...16110504_DBPoolingAndLazyLoadingSupport.cs | 57 +++ .../WonderkingContextModelSnapshot.cs | 13 +- Server/DB/WonderkingContext.cs | 17 +- Server/Program.cs | 17 +- Server/Server.csproj | 1 + 10 files changed, 433 insertions(+), 22 deletions(-) create mode 100644 Server/DB/Migrations/20231116110504_DBPoolingAndLazyLoadingSupport.Designer.cs create mode 100644 Server/DB/Migrations/20231116110504_DBPoolingAndLazyLoadingSupport.cs diff --git a/Server/DB/Documents/Account.cs b/Server/DB/Documents/Account.cs index 20c8bc0..80ede30 100644 --- a/Server/DB/Documents/Account.cs +++ b/Server/DB/Documents/Account.cs @@ -20,7 +20,10 @@ public class Account [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } - [Column(TypeName = "varchar(20)")] public string Username { get; set; } + [Column(TypeName = "varchar(20)")] + [MaxLength(20)] + public string Username { get; set; } + [Column(TypeName = "bytea")] public byte[] Password { get; set; } [EmailAddress] public string Email { get; set; } diff --git a/Server/DB/Documents/Character.cs b/Server/DB/Documents/Character.cs index c286fff..16e18e6 100644 --- a/Server/DB/Documents/Character.cs +++ b/Server/DB/Documents/Character.cs @@ -1,10 +1,12 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; using Wonderking.Game.Data.Character; using Wonderking.Packets.Outgoing.Data; namespace Server.DB.Documents; +[Index(nameof(Name), IsUnique = true), Index(nameof(Id), IsUnique = true)] public class Character { public virtual Account Account { get; set; } @@ -14,7 +16,11 @@ public class Character public Guid Id { get; set; } public ushort MapId { get; set; } + + [Column(TypeName = "varchar(20)")] + [MaxLength(20)] public string Name { get; set; } + public short LastXCoordinate { get; set; } public short LastYCoordinate { get; set; } public PvPLevel PvPLevel { get; set; } diff --git a/Server/DB/Documents/GuildMember.cs b/Server/DB/Documents/GuildMember.cs index 557aa2d..07e0edd 100644 --- a/Server/DB/Documents/GuildMember.cs +++ b/Server/DB/Documents/GuildMember.cs @@ -9,7 +9,7 @@ public class GuildMember [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } - public Character Character { get; set; } - public Guild Guild { get; set; } + public virtual Character Character { get; set; } + public virtual Guild Guild { get; set; } public GuildRank Rank { get; set; } } diff --git a/Server/DB/Documents/InventoryItem.cs b/Server/DB/Documents/InventoryItem.cs index 08f5078..57754f0 100644 --- a/Server/DB/Documents/InventoryItem.cs +++ b/Server/DB/Documents/InventoryItem.cs @@ -3,7 +3,7 @@ namespace Server.DB.Documents; public class InventoryItem { public Guid CharacterId { get; set; } - public Character Character { get; set; } + public virtual Character Character { get; set; } public Guid Id { get; set; } public ushort ItemId { get; set; } public ushort Count { get; set; } diff --git a/Server/DB/Migrations/20231116110504_DBPoolingAndLazyLoadingSupport.Designer.cs b/Server/DB/Migrations/20231116110504_DBPoolingAndLazyLoadingSupport.Designer.cs new file mode 100644 index 0000000..932bf0b --- /dev/null +++ b/Server/DB/Migrations/20231116110504_DBPoolingAndLazyLoadingSupport.Designer.cs @@ -0,0 +1,333 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Server.DB; + +#nullable disable + +namespace Server.DB.Migrations +{ + [DbContext(typeof(WonderkingContext))] + [Migration("20231116110504_DBPoolingAndLazyLoadingSupport")] + partial class DBPoolingAndLazyLoadingSupport + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Proxies:ChangeTracking", false) + .HasAnnotation("Proxies:CheckEquality", false) + .HasAnnotation("Proxies:LazyLoading", true) + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Server.DB.Documents.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Password") + .HasColumnType("bytea"); + + b.Property("PermissionLevel") + .HasColumnType("smallint"); + + b.Property("Salt") + .HasColumnType("bytea"); + + b.Property("Username") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("Accounts"); + }); + + modelBuilder.Entity("Server.DB.Documents.Character", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("Experience") + .HasColumnType("bigint"); + + b.Property("Gender") + .HasColumnType("smallint"); + + b.Property("GuildId") + .HasColumnType("uuid"); + + b.Property("Health") + .HasColumnType("integer"); + + b.Property("LastXCoordinate") + .HasColumnType("smallint"); + + b.Property("LastYCoordinate") + .HasColumnType("smallint"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.Property("Mana") + .HasColumnType("integer"); + + b.Property("MapId") + .HasColumnType("integer"); + + b.Property("Name") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("PvPLevel") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("GuildId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Characters"); + }); + + modelBuilder.Entity("Server.DB.Documents.Guild", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Notice") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Guild"); + }); + + modelBuilder.Entity("Server.DB.Documents.GuildMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("GuildId") + .HasColumnType("uuid"); + + b.Property("Rank") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.HasIndex("GuildId"); + + b.ToTable("GuildMember"); + }); + + modelBuilder.Entity("Server.DB.Documents.InventoryItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddOption") + .HasColumnType("smallint"); + + b.Property("AddOption2") + .HasColumnType("smallint"); + + b.Property("AddOption3") + .HasColumnType("smallint"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("Count") + .HasColumnType("integer"); + + b.Property("InventoryTab") + .HasColumnType("smallint"); + + b.Property("ItemId") + .HasColumnType("integer"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.Property("Option") + .HasColumnType("smallint"); + + b.Property("Option2") + .HasColumnType("smallint"); + + b.Property("Option3") + .HasColumnType("smallint"); + + b.Property("Rarity") + .HasColumnType("smallint"); + + b.Property("Slot") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("InventoryItem"); + }); + + modelBuilder.Entity("Server.DB.Documents.Character", b => + { + b.HasOne("Server.DB.Documents.Account", "Account") + .WithMany("Characters") + .HasForeignKey("AccountId"); + + b.HasOne("Server.DB.Documents.Guild", "Guild") + .WithMany() + .HasForeignKey("GuildId"); + + b.OwnsOne("Wonderking.Packets.Outgoing.Data.BaseStats", "BaseStats", b1 => + { + b1.Property("CharacterId") + .HasColumnType("uuid"); + + b1.Property("Dexterity") + .HasColumnType("smallint"); + + b1.Property("Intelligence") + .HasColumnType("smallint"); + + b1.Property("Luck") + .HasColumnType("smallint"); + + b1.Property("Strength") + .HasColumnType("smallint"); + + b1.Property("Vitality") + .HasColumnType("smallint"); + + b1.Property("Wisdom") + .HasColumnType("smallint"); + + b1.HasKey("CharacterId"); + + b1.ToTable("Characters"); + + b1.WithOwner() + .HasForeignKey("CharacterId"); + }); + + b.OwnsOne("Wonderking.Packets.Outgoing.Data.JobData", "JobData", b1 => + { + b1.Property("CharacterId") + .HasColumnType("uuid"); + + b1.Property("FirstJob") + .HasColumnType("smallint"); + + b1.Property("FourthJob") + .HasColumnType("smallint"); + + b1.Property("SecondJob") + .HasColumnType("smallint"); + + b1.Property("ThirdJob") + .HasColumnType("smallint"); + + b1.HasKey("CharacterId"); + + b1.ToTable("Characters"); + + b1.WithOwner() + .HasForeignKey("CharacterId"); + }); + + b.Navigation("Account"); + + b.Navigation("BaseStats"); + + b.Navigation("Guild"); + + b.Navigation("JobData"); + }); + + modelBuilder.Entity("Server.DB.Documents.GuildMember", b => + { + b.HasOne("Server.DB.Documents.Character", "Character") + .WithMany() + .HasForeignKey("CharacterId"); + + b.HasOne("Server.DB.Documents.Guild", "Guild") + .WithMany("GuildMembers") + .HasForeignKey("GuildId"); + + b.Navigation("Character"); + + b.Navigation("Guild"); + }); + + modelBuilder.Entity("Server.DB.Documents.InventoryItem", b => + { + b.HasOne("Server.DB.Documents.Character", "Character") + .WithMany("InventoryItems") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Server.DB.Documents.Account", b => + { + b.Navigation("Characters"); + }); + + modelBuilder.Entity("Server.DB.Documents.Character", b => + { + b.Navigation("InventoryItems"); + }); + + modelBuilder.Entity("Server.DB.Documents.Guild", b => + { + b.Navigation("GuildMembers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Server/DB/Migrations/20231116110504_DBPoolingAndLazyLoadingSupport.cs b/Server/DB/Migrations/20231116110504_DBPoolingAndLazyLoadingSupport.cs new file mode 100644 index 0000000..9b9cb8c --- /dev/null +++ b/Server/DB/Migrations/20231116110504_DBPoolingAndLazyLoadingSupport.cs @@ -0,0 +1,57 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Server.DB.Migrations; + +/// +public partial class DBPoolingAndLazyLoadingSupport : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Name", + table: "Characters", + type: "varchar(20)", + maxLength: 20, + nullable: true, + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Characters_Id", + table: "Characters", + column: "Id", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Characters_Name", + table: "Characters", + column: "Name", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Characters_Id", + table: "Characters"); + + migrationBuilder.DropIndex( + name: "IX_Characters_Name", + table: "Characters"); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Characters", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "varchar(20)", + oldMaxLength: 20, + oldNullable: true); + } +} diff --git a/Server/DB/Migrations/WonderkingContextModelSnapshot.cs b/Server/DB/Migrations/WonderkingContextModelSnapshot.cs index 28aea68..8e5308f 100644 --- a/Server/DB/Migrations/WonderkingContextModelSnapshot.cs +++ b/Server/DB/Migrations/WonderkingContextModelSnapshot.cs @@ -18,6 +18,9 @@ namespace Server.DB.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Proxies:ChangeTracking", false) + .HasAnnotation("Proxies:CheckEquality", false) + .HasAnnotation("Proxies:LazyLoading", true) .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -41,6 +44,7 @@ namespace Server.DB.Migrations .HasColumnType("bytea"); b.Property("Username") + .HasMaxLength(20) .HasColumnType("varchar(20)"); b.HasKey("Id"); @@ -91,7 +95,8 @@ namespace Server.DB.Migrations .HasColumnType("integer"); b.Property("Name") - .HasColumnType("text"); + .HasMaxLength(20) + .HasColumnType("varchar(20)"); b.Property("PvPLevel") .HasColumnType("smallint"); @@ -102,6 +107,12 @@ namespace Server.DB.Migrations b.HasIndex("GuildId"); + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("Name") + .IsUnique(); + b.ToTable("Characters"); }); diff --git a/Server/DB/WonderkingContext.cs b/Server/DB/WonderkingContext.cs index 8ec7cf6..8251da3 100644 --- a/Server/DB/WonderkingContext.cs +++ b/Server/DB/WonderkingContext.cs @@ -1,27 +1,16 @@ +using JetBrains.Annotations; + namespace Server.DB; using Documents; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; public class WonderkingContext : DbContext { - private readonly IConfiguration _configuration; - private readonly ILoggerFactory _loggerFactory; - - public WonderkingContext(ILoggerFactory loggerFactory, IConfiguration configuration) + public WonderkingContext([NotNull] DbContextOptions options) : base(options) { - this._loggerFactory = loggerFactory; - this._configuration = configuration; } public DbSet Accounts { get; set; } public DbSet Characters { get; set; } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => - optionsBuilder - .UseNpgsql( - $"Host={this._configuration["DB:Host"]};Username={this._configuration["DB:Username"]};Password={this._configuration["DB:Password"]};Database={this._configuration["DB:Database"]};Port={this._configuration["DB:Port"]}") - .EnableSensitiveDataLogging().UseLoggerFactory(this._loggerFactory); } diff --git a/Server/Program.cs b/Server/Program.cs index a092fe9..3a3659d 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -17,10 +17,21 @@ builder.Configuration.AddJsonFile("settings.json", true, true) .AddJsonFile($"settings.{builder.Environment.EnvironmentName}.json", true) .AddEnvironmentVariables().Build(); builder.Services.AddLogging(); -builder.Logging.AddFile("Logs/Server-{Date}.log", LogLevel.Trace); -builder.Logging.AddFile("Logs/Server-{Date}.json.log", LogLevel.Trace, isJson: true); +var loggerFactory = LoggerFactory.Create(loggingBuilder => +{ + loggingBuilder.AddFile("Logs/Server-{Date}.log", LogLevel.Trace); + loggingBuilder.AddFile("Logs/Server-{Date}.json.log", LogLevel.Trace, isJson: true); + loggingBuilder.AddConsole(); +}); builder.Services.AddEntityFrameworkNpgsql(); -builder.Services.AddDbContext(); +builder.Services.AddDbContextPool(o => +{ + using var configuration = builder.Configuration; + o.UseNpgsql( + $"Host={configuration["DB:Host"]};Username={configuration["DB:Username"]};Password={configuration["DB:Password"]};Database={configuration["DB:Database"]};Port={configuration["DB:Port"]}") + .EnableSensitiveDataLogging().UseLazyLoadingProxies().UseLoggerFactory(loggerFactory); +}); +builder.Services.AddSingleton(loggerFactory); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddHostedService(); diff --git a/Server/Server.csproj b/Server/Server.csproj index 8342b6d..5745021 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -66,6 +66,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive From 797ea61f36f3903c6cf76270cab36c7d93b4bc1d Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 12:22:18 +0100 Subject: [PATCH 38/60] fix: incorrect offset --- .../CharacterCreationResponsePacket.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs b/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs index c38e7a5..06b0b3f 100644 --- a/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs +++ b/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs @@ -18,38 +18,38 @@ public class CharacterCreationResponsePacket : IPacket { Span data = stackalloc byte[132]; - BinaryPrimitives.WriteInt32LittleEndian(data.Slice(0 + 132, 4), 0); - Encoding.ASCII.GetBytes(Character.Name, data.Slice(4 + 132, 20)); + BinaryPrimitives.WriteInt32LittleEndian(data.Slice(0, 4), 0); + Encoding.ASCII.GetBytes(Character.Name, data.Slice(4, 20)); // Job Data - data[24 + 132] = Character.Job.FirstJob; - data[25 + 132] = Character.Job.SecondJob; - data[26 + 132] = Character.Job.ThirdJob; - data[27 + 132] = Character.Job.FourthJob; + data[24] = Character.Job.FirstJob; + data[25] = Character.Job.SecondJob; + data[26] = Character.Job.ThirdJob; + data[27] = Character.Job.FourthJob; - data[28 + 132] = (byte)Character.Gender; - BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(49 + 132, 2), Character.Level); - data[31 + 132] = (byte)Character.Experience; + data[28] = (byte)Character.Gender; + BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(49, 2), Character.Level); + data[31] = (byte)Character.Experience; // Stats - BinaryPrimitives.WriteInt16LittleEndian(data.Slice(32 + 132, 2), Character.Stats.Strength); - BinaryPrimitives.WriteInt16LittleEndian(data.Slice(34 + 132, 2), Character.Stats.Dexterity); - BinaryPrimitives.WriteInt16LittleEndian(data.Slice(36 + 132, 2), Character.Stats.Intelligence); - BinaryPrimitives.WriteInt16LittleEndian(data.Slice(38 + 132, 2), Character.Stats.Vitality); - BinaryPrimitives.WriteInt16LittleEndian(data.Slice(40 + 132, 2), Character.Stats.Luck); - BinaryPrimitives.WriteInt16LittleEndian(data.Slice(42 + 132, 2), Character.Stats.Wisdom); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(32, 2), Character.Stats.Strength); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(34, 2), Character.Stats.Dexterity); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(36, 2), Character.Stats.Intelligence); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(38, 2), Character.Stats.Vitality); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(40, 2), Character.Stats.Luck); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(42, 2), Character.Stats.Wisdom); - BinaryPrimitives.WriteInt32LittleEndian(data.Slice(44 + 132, 4), Character.Health); - BinaryPrimitives.WriteInt32LittleEndian(data.Slice(48 + 132, 4), Character.Mana); + BinaryPrimitives.WriteInt32LittleEndian(data.Slice(44, 4), Character.Health); + BinaryPrimitives.WriteInt32LittleEndian(data.Slice(48, 4), Character.Mana); for (var i = 0; i < 20; i++) { // Equipped Items - BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(52 + 132 + i * 2, 2), + BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(52 + i * 2, 2), Character.EquippedItems.Length > i ? Character.EquippedItems[i] : (ushort)0); // Equipped Cash Items - BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(92 + 132 + i * 2, 2), + BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(92 + i * 2, 2), Character.EquippedCashItems.Length > i ? Character.EquippedCashItems[i] : (ushort)0); } From 854ac06de6f98409e2064e163c48b9d5a6e07cf7 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 12:23:22 +0100 Subject: [PATCH 39/60] chore: slight adjustments to async --- Server/AuthSession.cs | 6 +++--- Server/PacketHandlers/CharacterCreationHandler..cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Server/AuthSession.cs b/Server/AuthSession.cs index 443d3ae..d871bb0 100644 --- a/Server/AuthSession.cs +++ b/Server/AuthSession.cs @@ -31,7 +31,7 @@ public class AuthSession : TcpSession public void Send(IPacket packet) { var type = packet.GetType(); - this._logger.LogTrace("Packet of type {Type} is being serialized", type.Name); + this._logger.LogInformation("Packet of type {Type} is being serialized", type.Name); var packetIdAttribute = type.GetCustomAttribute(); if (packetIdAttribute == null) { @@ -59,8 +59,8 @@ public class AuthSession : TcpSession buffer[2 + i] = bytesOfOpcode[i]; } - this._logger.LogTrace("Packet data being parsed is: {Data}", BitConverter.ToString(packetData.ToArray())); - this._logger.LogTrace("Packet being parsed is: {Data}", BitConverter.ToString(buffer.ToArray())); + this._logger.LogInformation("Packet data being parsed is: {Data}", BitConverter.ToString(packetData.ToArray())); + this._logger.LogInformation("Packet being parsed is: {Data}", BitConverter.ToString(buffer.ToArray())); this.Send(buffer); } diff --git a/Server/PacketHandlers/CharacterCreationHandler..cs b/Server/PacketHandlers/CharacterCreationHandler..cs index 52bb1b3..b894f87 100644 --- a/Server/PacketHandlers/CharacterCreationHandler..cs +++ b/Server/PacketHandlers/CharacterCreationHandler..cs @@ -59,9 +59,9 @@ public class CharacterCreationHandler : IPacketHandler Health = 250, Mana = 250, }); - await _wonderkingContext.SaveChangesAsync().ConfigureAwait(false); + await _wonderkingContext.SaveChangesAsync().ConfigureAwait(true); - var character = await _wonderkingContext.Characters.AsNoTracking() + var character = await _wonderkingContext.Characters.AsNoTrackingWithIdentityResolution() .Where(c => authSession != null && c.Account.Id == authSession.AccountId && c.Name == packet.Name) .Select(c => new CharacterData @@ -82,7 +82,7 @@ public class CharacterCreationHandler : IPacketHandler .Where(item => item.InventoryTab == InventoryTab.WornCashEquipment) .Select(item => item.ItemId) .ToArray(), - }).FirstOrDefaultAsync().ConfigureAwait(true); + }).FirstAsync().ConfigureAwait(true); authSession?.Send(new CharacterCreationResponsePacket { Character = character }); } } From dd4ed0a67b4b2873019fc33a3c5550da8690ae2f Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 12:48:33 +0100 Subject: [PATCH 40/60] fix: name check packet not setting name --- Wonderking/Packets/Incoming/CharacterNameCheckPacket.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Wonderking/Packets/Incoming/CharacterNameCheckPacket.cs b/Wonderking/Packets/Incoming/CharacterNameCheckPacket.cs index 6799961..2122db9 100644 --- a/Wonderking/Packets/Incoming/CharacterNameCheckPacket.cs +++ b/Wonderking/Packets/Incoming/CharacterNameCheckPacket.cs @@ -9,7 +9,7 @@ public class CharacterNameCheckPacket : IPacket public void Deserialize(byte[] data) { - Encoding.ASCII.GetString(data, 0, 20); + Name = Encoding.ASCII.GetString(data, 0, 20).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0'); } public byte[] Serialize() From 873ab306a59dd27ad4dae43ec029219286f2c09c Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 12:49:28 +0100 Subject: [PATCH 41/60] fix: adjustment to packet offsets --- .../CharacterCreationHandler..cs | 10 ++++- .../CharacterCreationResponsePacket.cs | 44 ++++++++++--------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/Server/PacketHandlers/CharacterCreationHandler..cs b/Server/PacketHandlers/CharacterCreationHandler..cs index b894f87..7bd5485 100644 --- a/Server/PacketHandlers/CharacterCreationHandler..cs +++ b/Server/PacketHandlers/CharacterCreationHandler..cs @@ -61,6 +61,9 @@ public class CharacterCreationHandler : IPacketHandler }); await _wonderkingContext.SaveChangesAsync().ConfigureAwait(true); + var amountOfCharacters = await _wonderkingContext.Characters.AsNoTrackingWithIdentityResolution() + .CountAsync(c => authSession != null && c.Account.Id == authSession.AccountId).ConfigureAwait(true); + var character = await _wonderkingContext.Characters.AsNoTrackingWithIdentityResolution() .Where(c => authSession != null && c.Account.Id == authSession.AccountId && c.Name == packet.Name) .Select(c => @@ -83,6 +86,11 @@ public class CharacterCreationHandler : IPacketHandler .Select(item => item.ItemId) .ToArray(), }).FirstAsync().ConfigureAwait(true); - authSession?.Send(new CharacterCreationResponsePacket { Character = character }); + authSession?.Send(new CharacterCreationResponsePacket + { + Character = character, + Slot = amountOfCharacters - 1, + isDuplicate = false, + }); } } diff --git a/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs b/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs index 06b0b3f..6419417 100644 --- a/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs +++ b/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs @@ -8,6 +8,8 @@ namespace Wonderking.Packets.Outgoing; public class CharacterCreationResponsePacket : IPacket { public required CharacterData Character { get; set; } + public required int Slot { get; set; } + public required bool isDuplicate { get; set; } public void Deserialize(byte[] data) { @@ -16,40 +18,40 @@ public class CharacterCreationResponsePacket : IPacket public byte[] Serialize() { - Span data = stackalloc byte[132]; - - BinaryPrimitives.WriteInt32LittleEndian(data.Slice(0, 4), 0); - Encoding.ASCII.GetBytes(Character.Name, data.Slice(4, 20)); + Span data = stackalloc byte[1 + 132]; + data[0] = isDuplicate ? (byte)1 : (byte)0; + BinaryPrimitives.WriteInt32LittleEndian(data.Slice(1, 4), Slot); + Encoding.ASCII.GetBytes(Character.Name, data.Slice(5, 20)); // Job Data - data[24] = Character.Job.FirstJob; - data[25] = Character.Job.SecondJob; - data[26] = Character.Job.ThirdJob; - data[27] = Character.Job.FourthJob; + data[25] = Character.Job.FirstJob; + data[26] = Character.Job.SecondJob; + data[27] = Character.Job.ThirdJob; + data[28] = Character.Job.FourthJob; - data[28] = (byte)Character.Gender; - BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(49, 2), Character.Level); - data[31] = (byte)Character.Experience; + data[29] = (byte)Character.Gender; + BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(30, 2), Character.Level); + data[32] = (byte)Character.Experience; // Stats - BinaryPrimitives.WriteInt16LittleEndian(data.Slice(32, 2), Character.Stats.Strength); - BinaryPrimitives.WriteInt16LittleEndian(data.Slice(34, 2), Character.Stats.Dexterity); - BinaryPrimitives.WriteInt16LittleEndian(data.Slice(36, 2), Character.Stats.Intelligence); - BinaryPrimitives.WriteInt16LittleEndian(data.Slice(38, 2), Character.Stats.Vitality); - BinaryPrimitives.WriteInt16LittleEndian(data.Slice(40, 2), Character.Stats.Luck); - BinaryPrimitives.WriteInt16LittleEndian(data.Slice(42, 2), Character.Stats.Wisdom); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(33, 2), Character.Stats.Strength); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(35, 2), Character.Stats.Dexterity); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(37, 2), Character.Stats.Intelligence); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(39, 2), Character.Stats.Vitality); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(41, 2), Character.Stats.Luck); + BinaryPrimitives.WriteInt16LittleEndian(data.Slice(43, 2), Character.Stats.Wisdom); - BinaryPrimitives.WriteInt32LittleEndian(data.Slice(44, 4), Character.Health); - BinaryPrimitives.WriteInt32LittleEndian(data.Slice(48, 4), Character.Mana); + BinaryPrimitives.WriteInt32LittleEndian(data.Slice(45, 4), Character.Health); + BinaryPrimitives.WriteInt32LittleEndian(data.Slice(49, 4), Character.Mana); for (var i = 0; i < 20; i++) { // Equipped Items - BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(52 + i * 2, 2), + BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(53 + i * 2, 2), Character.EquippedItems.Length > i ? Character.EquippedItems[i] : (ushort)0); // Equipped Cash Items - BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(92 + i * 2, 2), + BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(93 + i * 2, 2), Character.EquippedCashItems.Length > i ? Character.EquippedCashItems[i] : (ushort)0); } From 7fde237434e3df5ebd9132b8681d8810918cb86d Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 12:49:47 +0100 Subject: [PATCH 42/60] chore: restructuring --- .../{ => Packets}/Character-Selection-Set-Guild-Name-Packet.md | 0 Wiki/wiki.tree | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename Wiki/topics/{ => Packets}/Character-Selection-Set-Guild-Name-Packet.md (100%) diff --git a/Wiki/topics/Character-Selection-Set-Guild-Name-Packet.md b/Wiki/topics/Packets/Character-Selection-Set-Guild-Name-Packet.md similarity index 100% rename from Wiki/topics/Character-Selection-Set-Guild-Name-Packet.md rename to Wiki/topics/Packets/Character-Selection-Set-Guild-Name-Packet.md diff --git a/Wiki/wiki.tree b/Wiki/wiki.tree index 7304106..0e3a3fb 100644 --- a/Wiki/wiki.tree +++ b/Wiki/wiki.tree @@ -11,6 +11,6 @@ + - \ No newline at end of file From c2ba247dbe37751987890bc18db30af94dfcc4ae Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 14:26:55 +0100 Subject: [PATCH 43/60] chore: added hooks --- .pre-commit-config.yaml | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 57b7d26..5f35186 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,9 +1,19 @@ -repos: - - repo: local - hooks: +repos: +- repo: local + hooks: #Use dotnet format already installed on your machine - - id: dotnet-format - name: dotnet-format - language: system - entry: dotnet format --include - types_or: ["c#", "vb"] + - id: dotnet-format + name: dotnet-format + language: system + entry: dotnet format --include + types_or: [c#, vb] +- repo: https://github.com/Mateusz-Grzelinski/actionlint-py + rev: v1.6.26.11 + hooks: + - id: actionlint + additional_dependencies: [pyflakes>=3.0.1, shellcheck-py>=0.9.0.5] +- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks + rev: v2.11.0 + hooks: + - id: pretty-format-yaml + args: [--autofix, --indent, '2'] From 9ec8039c998343768967213b9b0e3a435d948d3e Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 14:27:17 +0100 Subject: [PATCH 44/60] chore: using lazy loading --- Server/PacketHandlers/ChannelSelectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/PacketHandlers/ChannelSelectionHandler.cs b/Server/PacketHandlers/ChannelSelectionHandler.cs index 3f64d74..8a66499 100644 --- a/Server/PacketHandlers/ChannelSelectionHandler.cs +++ b/Server/PacketHandlers/ChannelSelectionHandler.cs @@ -35,7 +35,7 @@ public class ChannelSelectionHandler : IPacketHandler ChannelSelectionResponsePacket responsePacket; var guildNameResponsePacket = new CharacterSelectionSetGuildNamePacket { GuildNames = Array.Empty() }; - var account = await this._wonderkingContext.Accounts.Include(account => account.Characters) + var account = await this._wonderkingContext.Accounts .FirstOrDefaultAsync(a => a.Id == authSession.AccountId).ConfigureAwait(true); if (account != null && account.Characters.Count > 0) { From 9900d3611b90a5216e240ec3900f844fd650be3a Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 14:27:49 +0100 Subject: [PATCH 45/60] ci: workflows separated --- .gitea/workflows/docs.yaml | 120 +++++++++++ .gitea/workflows/server.yaml | 373 +++++++++++++---------------------- 2 files changed, 253 insertions(+), 240 deletions(-) create mode 100644 .gitea/workflows/docs.yaml diff --git a/.gitea/workflows/docs.yaml b/.gitea/workflows/docs.yaml new file mode 100644 index 0000000..56ee0b3 --- /dev/null +++ b/.gitea/workflows/docs.yaml @@ -0,0 +1,120 @@ +name: Build documentation +run-name: ${{ gitea.actor }} is building the Wiki documentation +on: [push] + +env: + # Name of module and id separated by a slash + INSTANCE: Wiki/wiki + # Replace HI with the ID of the instance in capital letters + ARTIFACT: webHelpWIKI2-all.zip + # Writerside docker image version + DOCKER_VERSION: 232.10165.1 + ALGOLIA_ARTIFACT: algolia-indexes-wiki.zip + + +jobs: + preprocess: + runs-on: ubuntu-latest + outputs: + sanitized_branch_name: ${{ steps.sanitize.outputs.sanitized_branch_name }} + steps: + - name: Sanitize branch name + id: sanitize + run: echo "::set-output name=sanitized_branch_name::$(echo ${{ github.ref_name }} | sed 's/\//-/g')" + + docs: + runs-on: ubuntu-latest + container: registry.jetbrains.team/p/writerside/builder/writerside-builder:${{env.DOCKER_VERSION}} + steps: + - name: Install basic dependencies + run: | + wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash + echo "::add-path::$HOME/.nvm" + export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + nvm install 18 + nvm use 18 + echo "::add-path::$(dirname $(which npm))" + nvm --version + - name: Check Node.js version + run: | + node -v + npm -v + - name: Checkout repository + uses: https://github.com/actions/checkout@v3 + - name: Build docs + run: | + set -e + export DISPLAY=:99 + Xvfb :99 & + /opt/builder/bin/idea.sh helpbuilderinspect -source-dir . -product ${{env.INSTANCE}} -output-dir artifacts/ || true + echo "Test existing of ${{ env.ARTIFACT }} artifact" + test -e artifacts/${{ env.ARTIFACT }} + - name: rename artifact + run: | + mv artifacts/${{ env.ARTIFACT }} artifacts/wiki.zip + - name: Upload documentation + uses: actions/upload-artifact@v3 + with: + name: wiki.zip + path: artifacts/wiki.zip + retention-days: 14 + - name: Upload algolia-indexes + uses: actions/upload-artifact@v3 + with: + name: algolia-indexes.zip + path: artifacts/${{ env.ALGOLIA_ARTIFACT }} + retention-days: 14 + + build-docs-container: + runs-on: ubuntu-latest + container: catthehacker/ubuntu:act-latest + needs: [docs, preprocess] + steps: + - name: Checkout repository + uses: https://github.com/actions/checkout@v3 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: ${{ github.server_url }} + username: ${{ github.actor }} + password: ${{ secrets.REGISTRY_TOKEN }} + - name: Retrieve docs artifact + uses: actions/download-artifact@v3 + with: + name: wiki.zip + path: ${{ github.workspace }} + - name: Unzip wiki.zip into .public + run: | + mkdir .public + unzip -jo -qq ./wiki.zip/wiki.zip -d .public + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + file: Wiki.Dockerfile + push: true + tags: forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}-wiki + platforms: linux/amd64,linux/arm64 + - name: Build and push to latest + if: github.ref_name == 'master' + uses: docker/build-push-action@v5 + with: + context: . + file: Wiki.Dockerfile + push: true + tags: forge.rainote.dev/${{ github.repository }}:latest-wiki + platforms: linux/amd64, linux/arm64 + + deploy-wiki: + runs-on: ubuntu-latest + container: catthehacker/ubuntu:act-latest + needs: [build-docs-container, docs, preprocess] + steps: + - name: Deploy Image to CapRrover + run: | + docker run caprover/cli-caprover:2.2.3 caprover deploy --caproverUrl ${{ secrets.CAPROVER_SERVER }} --appToken ${{ secrets.WIKI_APP_TOKEN }} --imageName forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}-wiki -a ${{ secrets.WIKI_APP_NAME }} diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index 018ea5a..f71cf3d 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -1,16 +1,6 @@ -name: Build, Package and Push Images +name: Build, Package and Push Images run-name: ${{ gitea.actor }} is building the Server application -on: [ push ] - -env: - # Name of module and id separated by a slash - INSTANCE: Wiki/wiki - # Replace HI with the ID of the instance in capital letters - ARTIFACT: webHelpWIKI2-all.zip - # Writerside docker image version - DOCKER_VERSION: 232.10165.1 - ALGOLIA_ARTIFACT: algolia-indexes-wiki.zip - +on: [push] jobs: preprocess: @@ -18,120 +8,23 @@ jobs: outputs: sanitized_branch_name: ${{ steps.sanitize.outputs.sanitized_branch_name }} steps: - - name: Sanitize branch name - id: sanitize - run: echo "::set-output name=sanitized_branch_name::$(echo ${{ github.ref_name }} | sed 's/\//-/g')" - - docs: - runs-on: ubuntu-latest - container: registry.jetbrains.team/p/writerside/builder/writerside-builder:${{env.DOCKER_VERSION}} - steps: - - name: Install basic dependencies - run: | - wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash - echo "::add-path::$HOME/.nvm" - export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - nvm install 18 - nvm use 18 - echo "::add-path::$(dirname $(which npm))" - nvm --version - - name: Check Node.js version - run: | - node -v - npm -v - - name: Checkout repository - uses: https://github.com/actions/checkout@v3 - - name: Build docs - run: | - set -e - export DISPLAY=:99 - Xvfb :99 & - /opt/builder/bin/idea.sh helpbuilderinspect -source-dir . -product ${{env.INSTANCE}} -output-dir artifacts/ || true - echo "Test existing of ${{ env.ARTIFACT }} artifact" - test -e artifacts/${{ env.ARTIFACT }} - - name: rename artifact - run: | - mv artifacts/${{ env.ARTIFACT }} artifacts/wiki.zip - - name: Upload documentation - uses: actions/upload-artifact@v3 - with: - name: wiki.zip - path: artifacts/wiki.zip - retention-days: 14 - - name: Upload algolia-indexes - uses: actions/upload-artifact@v3 - with: - name: algolia-indexes.zip - path: artifacts/${{ env.ALGOLIA_ARTIFACT }} - retention-days: 14 - - build-docs-container: - runs-on: ubuntu-latest - container: catthehacker/ubuntu:act-latest - needs: [ docs, preprocess ] - steps: - - name: Checkout repository - uses: https://github.com/actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - registry: ${{ github.server_url }} - username: ${{ github.actor }} - password: ${{ secrets.REGISTRY_TOKEN }} - - name: Retrieve docs artifact - uses: actions/download-artifact@v3 - with: - name: wiki.zip - path: ${{ github.workspace }} - - name: Unzip wiki.zip into .public - run: | - mkdir .public - unzip -jo -qq ./wiki.zip/wiki.zip -d .public - - name: Build and push - uses: docker/build-push-action@v5 - with: - context: . - file: Wiki.Dockerfile - push: true - tags: forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}-wiki - platforms: linux/amd64,linux/arm64 - - name: Build and push to latest - if: github.ref_name == 'master' - uses: docker/build-push-action@v5 - with: - context: . - file: Wiki.Dockerfile - push: true - tags: forge.rainote.dev/${{ github.repository }}:latest-wiki - platforms: linux/amd64, linux/arm64 - - deploy-wiki: - runs-on: ubuntu-latest - container: catthehacker/ubuntu:act-latest - needs: [ build-docs-container, docs, preprocess ] - steps: - - name: Deploy Image to CapRrover - run: | - docker run caprover/cli-caprover:2.2.3 caprover deploy --caproverUrl ${{ secrets.CAPROVER_SERVER }} --appToken ${{ secrets.WIKI_APP_TOKEN }} --imageName forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}-wiki -a ${{ secrets.WIKI_APP_NAME }} + - name: Sanitize branch name + id: sanitize + run: echo "::set-output name=sanitized_branch_name::$(echo ${{ github.ref_name }} | sed 's/\//-/g')" build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Setup dotnet - uses: https://github.com/actions/setup-dotnet@v3 - with: - dotnet-version: 8.0 - - name: Install dependencies - run: dotnet restore - - name: Build - run: | - dotnet build Server -c Release + - uses: actions/checkout@v3 + - name: Setup dotnet + uses: https://github.com/actions/setup-dotnet@v3 + with: + dotnet-version: 8.0 + - name: Install dependencies + run: dotnet restore + - name: Build + run: | + dotnet build Server -c Release # dotnet test Server.Tests -c Release sonarqube: @@ -139,143 +32,143 @@ jobs: runs-on: ubuntu-latest if: github.ref_name == 'master' steps: - - uses: actions/checkout@v3 - - name: Setup dotnet - uses: https://github.com/actions/setup-dotnet@v3 - with: - dotnet-version: 8.0 - - name: Install dependencies - run: | - dotnet restore - echo "::add-path::$HOME/.dotnet/tools" - - name: Setup Sonarqube Dependencies - run: | - apt-get update - apt-get install --yes openjdk-11-jre - dotnet tool install --global dotnet-sonarscanner - dotnet tool install --global dotnet-coverage - - name: Sonarqube Begin - run: | - dotnet sonarscanner begin /key:"${{ secrets.SONAR_PROJECT_KEY }}" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}" - - name: Sonarqube Scan - run: | - dotnet build Server -c Release + - uses: actions/checkout@v3 + - name: Setup dotnet + uses: https://github.com/actions/setup-dotnet@v3 + with: + dotnet-version: 8.0 + - name: Install dependencies + run: | + dotnet restore + echo "::add-path::$HOME/.dotnet/tools" + - name: Setup Sonarqube Dependencies + run: | + apt-get update + apt-get install --yes openjdk-11-jre + dotnet tool install --global dotnet-sonarscanner + dotnet tool install --global dotnet-coverage + - name: Sonarqube Begin + run: | + dotnet sonarscanner begin /key:"${{ secrets.SONAR_PROJECT_KEY }}" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}" + - name: Sonarqube Scan + run: | + dotnet build Server -c Release # dotnet test --collect "Code Coverage" --logger trx --results-directory "TestsResults" # dotnet-coverage collect 'dotnet test' -f xml -o 'coverage.xml' - - name: Sonarqube End - run: | - dotnet sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" + - name: Sonarqube End + run: | + dotnet sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" sbom-scan: needs: build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Setup dotnet - uses: https://github.com/actions/setup-dotnet@v3 - with: - dotnet-version: | - 7.0 - 8.0 - - name: Install dependencies - run: | - dotnet restore - echo "::add-path::$HOME/.dotnet/tools" - - name: Setup Dependency Track Dependencies - run: | - dotnet tool install --global CycloneDX - - name: Generate SBOM - run: | - dotnet CycloneDX Server/Server.csproj -o . -dgl - - name: Upload SBOM - uses: https://github.com/DependencyTrack/gh-upload-sbom@v2.0.1 - with: - apiKey: ${{ secrets.DEPENDENCY_TRACK_API_KEY }} - serverHostname: ${{ secrets.DEPENDENCY_TRACK_URL }} - projectName: ${{ secrets.DEPENDENCY_TRACK_PROJECT_NAME }} - autoCreate: true + - uses: actions/checkout@v3 + - name: Setup dotnet + uses: https://github.com/actions/setup-dotnet@v3 + with: + dotnet-version: | + 7.0 + 8.0 + - name: Install dependencies + run: | + dotnet restore + echo "::add-path::$HOME/.dotnet/tools" + - name: Setup Dependency Track Dependencies + run: | + dotnet tool install --global CycloneDX + - name: Generate SBOM + run: | + dotnet CycloneDX Server/Server.csproj -o . -dgl + - name: Upload SBOM + uses: https://github.com/DependencyTrack/gh-upload-sbom@v2.0.1 + with: + apiKey: ${{ secrets.DEPENDENCY_TRACK_API_KEY }} + serverHostname: ${{ secrets.DEPENDENCY_TRACK_URL }} + projectName: ${{ secrets.DEPENDENCY_TRACK_PROJECT_NAME }} + autoCreate: true # set projectversion to be the branch name - projectVersion: "${{ github.ref_name }}" - bomFilename: "${{ github.workspace }}/bom.xml" + projectVersion: ${{ github.ref_name }} + bomFilename: ${{ github.workspace }}/bom.xml container-build: runs-on: ubuntu-latest container: catthehacker/ubuntu:act-latest - needs: [ build, preprocess ] + needs: [build, preprocess] steps: - - uses: actions/checkout@v3 - - name: Setup dotnet - uses: https://github.com/actions/setup-dotnet@v3 - with: - dotnet-version: 8.0 + - uses: actions/checkout@v3 + - name: Setup dotnet + uses: https://github.com/actions/setup-dotnet@v3 + with: + dotnet-version: 8.0 # Add support for more platforms with QEMU (optional) # https://github.com/docker/setup-qemu-action - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - registry: ${{ github.server_url }} - username: ${{ github.actor }} - password: ${{ secrets.REGISTRY_TOKEN }} - - name: Build and push - uses: docker/build-push-action@v5 - with: - context: . - file: Server/Dockerfile - push: true - tags: forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }} - platforms: linux/amd64,linux/arm64 - - name: Build and push to latest - if: github.ref_name == 'master' - uses: docker/build-push-action@v5 - with: - context: . - file: Server/Dockerfile - push: true - tags: forge.rainote.dev/${{ github.repository }}:latest - platforms: linux/amd64, linux/arm64 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: ${{ github.server_url }} + username: ${{ github.actor }} + password: ${{ secrets.REGISTRY_TOKEN }} + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + file: Server/Dockerfile + push: true + tags: forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }} + platforms: linux/amd64,linux/arm64 + - name: Build and push to latest + if: github.ref_name == 'master' + uses: docker/build-push-action@v5 + with: + context: . + file: Server/Dockerfile + push: true + tags: forge.rainote.dev/${{ github.repository }}:latest + platforms: linux/amd64, linux/arm64 container-sbom-scan: - needs: [ container-build, preprocess ] + needs: [container-build, preprocess] runs-on: ubuntu-latest container: catthehacker/ubuntu:act-latest steps: - - uses: actions/checkout@v3 - - name: Setup dotnet - uses: https://github.com/actions/setup-dotnet@v3 - with: - dotnet-version: 8.0 - - name: Install dependencies - run: | - dotnet restore - echo "::add-path::$HOME/.dotnet/tools" - - name: Setup Dependency Track Dependencies - run: | - mkdir ~/.docker - curl -sSfL https://raw.githubusercontent.com/docker/sbom-cli-plugin/main/install.sh | sh -s -- - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - registry: ${{ github.server_url }} - username: ${{ github.actor }} - password: ${{ secrets.REGISTRY_TOKEN }} - - name: Generate SBOM - run: | - echo forge.rainote.dev/${{ github.repository }} - echo forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }} - docker pull forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }} - docker sbom -D forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }} --format cyclonedx-json --output container-bom.json - - name: Upload SBOM - uses: https://github.com/DependencyTrack/gh-upload-sbom@v2.0.1 - with: - apiKey: ${{ secrets.DEPENDENCY_TRACK_API_KEY }} - serverHostname: ${{ secrets.DEPENDENCY_TRACK_URL }} - projectName: "${{ secrets.DEPENDENCY_TRACK_PROJECT_NAME }}-container" - autoCreate: true + - uses: actions/checkout@v3 + - name: Setup dotnet + uses: https://github.com/actions/setup-dotnet@v3 + with: + dotnet-version: 8.0 + - name: Install dependencies + run: | + dotnet restore + echo "::add-path::$HOME/.dotnet/tools" + - name: Setup Dependency Track Dependencies + run: | + mkdir ~/.docker + curl -sSfL https://raw.githubusercontent.com/docker/sbom-cli-plugin/main/install.sh | sh -s -- + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: ${{ github.server_url }} + username: ${{ github.actor }} + password: ${{ secrets.REGISTRY_TOKEN }} + - name: Generate SBOM + run: | + echo forge.rainote.dev/${{ github.repository }} + echo forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }} + docker pull forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }} + docker sbom -D forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }} --format cyclonedx-json --output container-bom.json + - name: Upload SBOM + uses: https://github.com/DependencyTrack/gh-upload-sbom@v2.0.1 + with: + apiKey: ${{ secrets.DEPENDENCY_TRACK_API_KEY }} + serverHostname: ${{ secrets.DEPENDENCY_TRACK_URL }} + projectName: ${{ secrets.DEPENDENCY_TRACK_PROJECT_NAME }}-container + autoCreate: true # set projectversion to be the branch name - projectVersion: "${{ github.ref_name }}" - bomFilename: "${{ github.workspace }}/container-bom.json" + projectVersion: ${{ github.ref_name }} + bomFilename: ${{ github.workspace }}/container-bom.json From ea89688d8b4fed55e458046cf06017aada506609 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 14:38:38 +0100 Subject: [PATCH 46/60] chore: add hadolint to commit hooks --- .pre-commit-config.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5f35186..8f66be6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,3 +17,7 @@ repos: hooks: - id: pretty-format-yaml args: [--autofix, --indent, '2'] +- repo: https://github.com/hadolint/hadolint + rev: v2.12.0 + hooks: + - id: hadolint-docker From 6e406b4b00dad041a3f0b701cbcb19e6b6258253 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 14:42:17 +0100 Subject: [PATCH 47/60] ci: adjust when docs workflow runs --- .gitea/workflows/docs.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/docs.yaml b/.gitea/workflows/docs.yaml index 56ee0b3..770de43 100644 --- a/.gitea/workflows/docs.yaml +++ b/.gitea/workflows/docs.yaml @@ -1,6 +1,9 @@ name: Build documentation run-name: ${{ gitea.actor }} is building the Wiki documentation -on: [push] +on: + push: + paths: + - Wiki/** env: # Name of module and id separated by a slash From bdc3b149fec443e101fb579487cbcffe72ec6d25 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 14:58:38 +0100 Subject: [PATCH 48/60] docs: character creation packets documented --- Wiki/topics/Character-Creation-Packet.md | 20 +++++++ .../Character-Creation-Response-Packet.md | 58 +++++++++++++++++++ Wiki/wiki.tree | 2 + 3 files changed, 80 insertions(+) create mode 100644 Wiki/topics/Character-Creation-Packet.md create mode 100644 Wiki/topics/Character-Creation-Response-Packet.md diff --git a/Wiki/topics/Character-Creation-Packet.md b/Wiki/topics/Character-Creation-Packet.md new file mode 100644 index 0000000..8b9f11a --- /dev/null +++ b/Wiki/topics/Character-Creation-Packet.md @@ -0,0 +1,20 @@ +# Character Creation Packet + +## Metadata + +**Operation Code**: 15 + +### Structure + +| Identifier | Datatype | Size in bytes | +|------------|----------|---------------| +| Slot | byte | 1 | +| Unknown | byte | 1 | +| Id | ushort | 2 | +| Name | string | 20 | +| First Job | byte | 1 | +| Gender | byte | 1 | +| Hair | byte | 1 | +| Eyes | byte | 1 | +| Shirt | byte | 1 | +| Pants | byte | 1 | diff --git a/Wiki/topics/Character-Creation-Response-Packet.md b/Wiki/topics/Character-Creation-Response-Packet.md new file mode 100644 index 0000000..3a4f2c6 --- /dev/null +++ b/Wiki/topics/Character-Creation-Response-Packet.md @@ -0,0 +1,58 @@ +# Character Creation Response Packet + +## Metadata + +**Operation Code**: 13 + +### Structure + +Total size: 1 + 132 + +| Identifier | Datatype | Size in bytes | +|----------------|---------------|---------------| +| Is Duplicate | byte | 1 | +| Character data | CharacterData | 132 | + +### Subtypes + +#### CharacterData + +Total size: 132 bytes + +| Identifier | Datatype | Size in bytes | +|------------------------|------------------|---------------| +| Character Slot | int | 4 | +| Character Name | string | 20 | +| Jobs | Job Data | 4 | +| Gender | byte | 1 | +| Level | unsigned short | 2 | +| Exp? | byte | 1 | +| Stats | BaseStats | 12 | +| Health | int | 4 | +| Mana | int | 4 | +| Equipped Item Ids | unsigned short[] | 20 * 2 (40) | +| Equipped Cash Item Ids | unsigned short[] | 20 * 2 (40) | + +#### Job Data + +Total size: 4 bytes + +| Identifier | Datatype | Size in bytes | +|------------|----------|---------------| +| First Job | byte | 1 | +| Second Job | byte | 1 | +| Third Job | byte | 1 | +| Fourth Job | byte | 1 | + +#### BaseStats + +Total size: 12 bytes + +| Identifier | Datatype | Size in bytes | +|--------------|----------|---------------| +| Strength | short | 2 | +| Dexterity | short | 2 | +| Intelligence | short | 2 | +| Vitality | short | 2 | +| Luck | short | 2 | +| Wisdom | short | 2 | diff --git a/Wiki/wiki.tree b/Wiki/wiki.tree index 0e3a3fb..b0cc25e 100644 --- a/Wiki/wiki.tree +++ b/Wiki/wiki.tree @@ -7,6 +7,8 @@ + + From 04f8c5d52c4153d5339c13aad30af5a59f5bdcab Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 15:02:32 +0100 Subject: [PATCH 49/60] ci: ignore unnecessary changes --- .gitea/workflows/server.yaml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/server.yaml b/.gitea/workflows/server.yaml index f71cf3d..4405a1f 100644 --- a/.gitea/workflows/server.yaml +++ b/.gitea/workflows/server.yaml @@ -1,6 +1,11 @@ name: Build, Package and Push Images run-name: ${{ gitea.actor }} is building the Server application -on: [push] +on: + push: + paths-ignore: + - Wiki/** + - Benchmarks/** + - .run/** jobs: preprocess: @@ -25,7 +30,7 @@ jobs: - name: Build run: | dotnet build Server -c Release - # dotnet test Server.Tests -c Release + # dotnet test Server.Tests -c Release sonarqube: needs: build @@ -53,8 +58,8 @@ jobs: - name: Sonarqube Scan run: | dotnet build Server -c Release - # dotnet test --collect "Code Coverage" --logger trx --results-directory "TestsResults" - # dotnet-coverage collect 'dotnet test' -f xml -o 'coverage.xml' + # dotnet test --collect "Code Coverage" --logger trx --results-directory "TestsResults" + # dotnet-coverage collect 'dotnet test' -f xml -o 'coverage.xml' - name: Sonarqube End run: | dotnet sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" @@ -101,8 +106,8 @@ jobs: uses: https://github.com/actions/setup-dotnet@v3 with: dotnet-version: 8.0 - # Add support for more platforms with QEMU (optional) - # https://github.com/docker/setup-qemu-action + # Add support for more platforms with QEMU (optional) + # https://github.com/docker/setup-qemu-action - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx From 3db8a918e3a3e7efca537398b52a4a5a7d1af416 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 16:29:00 +0100 Subject: [PATCH 50/60] docs: add missing entries to Packet list --- Wiki/topics/Packets.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Wiki/topics/Packets.md b/Wiki/topics/Packets.md index 765659c..08a8ac9 100644 --- a/Wiki/topics/Packets.md +++ b/Wiki/topics/Packets.md @@ -7,6 +7,9 @@
  • Login Response
  • Channel Selection
  • Channel Selection Response
  • +
  • Character Selection Set Guild Name Packet
  • +
  • Character Creation Packet
  • +
  • Character Creation Response Packet
  • From 87955f3ba3dd721359d38462dfd25e3ab1a3db21 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 16:33:30 +0100 Subject: [PATCH 51/60] ci: smaller image for docs --- .gitea/workflows/docs.yaml | 1 + Wiki.Dockerfile | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/docs.yaml b/.gitea/workflows/docs.yaml index 770de43..db61bd1 100644 --- a/.gitea/workflows/docs.yaml +++ b/.gitea/workflows/docs.yaml @@ -4,6 +4,7 @@ on: push: paths: - Wiki/** + - Wiki.Dockerfile env: # Name of module and id separated by a slash diff --git a/Wiki.Dockerfile b/Wiki.Dockerfile index eb4f5a6..88b3e03 100644 --- a/Wiki.Dockerfile +++ b/Wiki.Dockerfile @@ -1,2 +1,2 @@ -FROM nginx +FROM nginx:1.25.3-alpine3.18-slim COPY .public /usr/share/nginx/html From 16673ef3c001f8133ebf5d5d0a34f11bb1839f11 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 17:55:36 +0100 Subject: [PATCH 52/60] feat: implement character deletion --- .../CharacterDeletionHandler.cs | 41 +++++++++++++++++++ Server/Program.cs | 1 - .../Packets/Incoming/CharacterDeletePacket.cs | 24 +++++++++++ Wonderking/Packets/OperationCode.cs | 1 + .../Outgoing/CharacterDeleteResponsePacket.cs | 19 +++++++++ .../Outgoing/CharacterDeletionResponse.cs | 7 ++++ 6 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 Server/PacketHandlers/CharacterDeletionHandler.cs create mode 100644 Wonderking/Packets/Incoming/CharacterDeletePacket.cs create mode 100644 Wonderking/Packets/Outgoing/CharacterDeleteResponsePacket.cs create mode 100644 Wonderking/Packets/Outgoing/CharacterDeletionResponse.cs diff --git a/Server/PacketHandlers/CharacterDeletionHandler.cs b/Server/PacketHandlers/CharacterDeletionHandler.cs new file mode 100644 index 0000000..8a43182 --- /dev/null +++ b/Server/PacketHandlers/CharacterDeletionHandler.cs @@ -0,0 +1,41 @@ +using Microsoft.EntityFrameworkCore; +using NetCoreServer; +using Server.DB; +using Wonderking.Packets.Incoming; +using Wonderking.Packets.Outgoing; + +namespace Server.PacketHandlers; + +public class CharacterDeletionHandler : IPacketHandler +{ + private readonly WonderkingContext _wonderkingContext; + + public CharacterDeletionHandler(WonderkingContext wonderkingContext) + { + _wonderkingContext = wonderkingContext; + } + + public async Task HandleAsync(CharacterDeletePacket packet, TcpSession session) + { + using var authSession = session as AuthSession; + var response = new CharacterDeleteResponsePacket { Response = CharacterDeletionResponse.MakeClientStuck }; + if (authSession == null) + { + return; + } + + var character = await _wonderkingContext.Characters.FirstOrDefaultAsync(x => x.Name == packet.Name) + .ConfigureAwait(false); + if (character == null) + { + authSession.Send(response); + return; + } + + _wonderkingContext.Characters.Remove(character); + await _wonderkingContext.SaveChangesAsync().ConfigureAwait(false); + response.Response = CharacterDeletionResponse.Ok; + + authSession.Send(response); + } +} diff --git a/Server/Program.cs b/Server/Program.cs index 3a3659d..cdd05d0 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -23,7 +23,6 @@ var loggerFactory = LoggerFactory.Create(loggingBuilder => loggingBuilder.AddFile("Logs/Server-{Date}.json.log", LogLevel.Trace, isJson: true); loggingBuilder.AddConsole(); }); -builder.Services.AddEntityFrameworkNpgsql(); builder.Services.AddDbContextPool(o => { using var configuration = builder.Configuration; diff --git a/Wonderking/Packets/Incoming/CharacterDeletePacket.cs b/Wonderking/Packets/Incoming/CharacterDeletePacket.cs new file mode 100644 index 0000000..545c524 --- /dev/null +++ b/Wonderking/Packets/Incoming/CharacterDeletePacket.cs @@ -0,0 +1,24 @@ +using System.Text; + +namespace Wonderking.Packets.Incoming; + +[PacketId(OperationCode.CharacterDeletion)] +public class CharacterDeletePacket : IPacket +{ + public byte Slot { get; set; } + public string Name { get; set; } + public uint Unknown { get; set; } + + public void Deserialize(byte[] data) + { + Span span = data; + Slot = span[0]; + Name = Encoding.ASCII.GetString(span.Slice(1, 20)).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0'); + Unknown = BitConverter.ToUInt32(span.Slice(21, 4)); + } + + public byte[] Serialize() + { + throw new NotSupportedException(); + } +} diff --git a/Wonderking/Packets/OperationCode.cs b/Wonderking/Packets/OperationCode.cs index 28967dd..d7beb64 100644 --- a/Wonderking/Packets/OperationCode.cs +++ b/Wonderking/Packets/OperationCode.cs @@ -11,6 +11,7 @@ public enum OperationCode : ushort CharacterCreation = 15, CharacterCreationResponse = 15, CharacterDeletion = 16, + CharacterDeletionResponse = 16, CharacterSelection = 17, CharacterSelectionSetGuildName = 19, } diff --git a/Wonderking/Packets/Outgoing/CharacterDeleteResponsePacket.cs b/Wonderking/Packets/Outgoing/CharacterDeleteResponsePacket.cs new file mode 100644 index 0000000..72392c3 --- /dev/null +++ b/Wonderking/Packets/Outgoing/CharacterDeleteResponsePacket.cs @@ -0,0 +1,19 @@ +namespace Wonderking.Packets.Outgoing; + +[PacketId(OperationCode.CharacterDeletionResponse)] +public class CharacterDeleteResponsePacket : IPacket +{ + public required CharacterDeletionResponse Response { get; set; } + + public void Deserialize(byte[] data) + { + throw new NotImplementedException(); + } + + public byte[] Serialize() + { + Span data = stackalloc byte[1]; + data[0] = (byte)Response; + return data.ToArray(); + } +} diff --git a/Wonderking/Packets/Outgoing/CharacterDeletionResponse.cs b/Wonderking/Packets/Outgoing/CharacterDeletionResponse.cs new file mode 100644 index 0000000..8676416 --- /dev/null +++ b/Wonderking/Packets/Outgoing/CharacterDeletionResponse.cs @@ -0,0 +1,7 @@ +namespace Wonderking.Packets.Outgoing; + +public enum CharacterDeletionResponse : byte +{ + Ok = 0, + MakeClientStuck = 1, +} From 71cf2a8dd3c0086d69954e9ed917db95faf7e3d5 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 17:56:13 +0100 Subject: [PATCH 53/60] chore: add useful scripts --- scripts/dexor_binary_file.ps1 | 14 ++++++++++++++ scripts/split_dat_file.ps1 | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 scripts/dexor_binary_file.ps1 create mode 100644 scripts/split_dat_file.ps1 diff --git a/scripts/dexor_binary_file.ps1 b/scripts/dexor_binary_file.ps1 new file mode 100644 index 0000000..5df9244 --- /dev/null +++ b/scripts/dexor_binary_file.ps1 @@ -0,0 +1,14 @@ +$SourceFilePath = $args[0] +$DestinationFilePath = $args[1] +$XorKey = [byte]$args[2] + +# Load the binary data from the file +$data = [System.IO.File]::ReadAllBytes($SourceFilePath) + +# Apply the XOR operation +for ($i = 0; $i -lt $data.Length; $i++) { + $data[$i] = $data[$i] -bxor $XorKey +} + +# Write the output to the destination file +[System.IO.File]::WriteAllBytes($DestinationFilePath, $data) diff --git a/scripts/split_dat_file.ps1 b/scripts/split_dat_file.ps1 new file mode 100644 index 0000000..39be178 --- /dev/null +++ b/scripts/split_dat_file.ps1 @@ -0,0 +1,36 @@ +$SourceFilePath = $args[0] +$ChunkSize = $args[1] +$Offset = $args[2] + +# Function to create chunk file +function Create-ChunkFile { + param ( + [byte[]]$ChunkData, + [int]$ChunkNumber + ) + + $chunkFileName = Join-Path $subDir ("{0}_{1:D5}.bin" -f [System.IO.Path]::GetFileNameWithoutExtension($SourceFilePath), $ChunkNumber) + [System.IO.File]::WriteAllBytes($chunkFileName, $ChunkData) +} + +# Create subdirectory named after the source file (without extension) for chunks +$subDir = Join-Path ([System.IO.Path]::GetDirectoryName($SourceFilePath)) ([System.IO.Path]::GetFileNameWithoutExtension($SourceFilePath)) +if (!(Test-Path -Path $subDir)) { + New-Item -ItemType Directory -Path $subDir +} + +# Read the binary data from the file +$data = [System.IO.File]::ReadAllBytes($SourceFilePath) +$dataLength = $data.Length +$chunkNumber = 0 + +# Adjust the start position based on the offset +$startPosition = [Math]::Min($Offset, $dataLength) + +# Process chunks +for ($i = $startPosition; $i -lt $dataLength; $i += $ChunkSize) { + $chunkEnd = [Math]::Min($i + $ChunkSize, $dataLength) + $chunkData = $data[$i..($chunkEnd - 1)] + Create-ChunkFile -ChunkData $chunkData -ChunkNumber $chunkNumber + $chunkNumber++ +} From a247f82a8ff74095194d122c9aa866bd46a1cc50 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 18:29:26 +0100 Subject: [PATCH 54/60] chore: add character stats mapping file --- config/character-stats.mapping.json | 150 ++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 config/character-stats.mapping.json diff --git a/config/character-stats.mapping.json b/config/character-stats.mapping.json new file mode 100644 index 0000000..82a42bc --- /dev/null +++ b/config/character-stats.mapping.json @@ -0,0 +1,150 @@ +{ + "default": { + "items": [ + { + "id": 841, + "quantity": 1 + } + ] + }, + "1": { + "items": [ + { + "id": 70, + "quantity": 1 + } + ], + "baseStats": { + "strength": 13, + "dexterity": 9, + "intelligence": 3, + "luck": 5, + "vitality": 17, + "wisdom": 5 + }, + "dynamicStats": { + "healthPerLevel": 20, + "manaPerLevel": 8, + "meleeDamagePerStrength": 0.4, + "rangedDamagePerDexterity": 0, + "hitRatingPerDexterity": 4.2, + "magicPowerPerIntelligence": 0.55, + "meleeDamagePerLuck": 0, + "rangedDamagePerLuck": 0, + "evasionPerLuck": 1.35, + "criticalPerLuck": 0.25, + "healthPerVitality": 5.75, + "physicalDefensePerVitality": 0.11, + "manaPerWisdom": 3.4, + "elementalDefensePerWisdom": 0.045, + "elementalPowerPerMagicPower": 0.4, + "elementalDefensePerMagicPower": 0.1 + } + }, + "2": { + "items": [ + { + "id": 150, + "quantity": 1 + } + ], + "baseStats": { + "strength": 6, + "dexterity": 6, + "intelligence": 14, + "luck": 3, + "vitality": 16, + "wisdom": 11 + }, + "dynamicStats": { + "healthPerLevel": 12, + "manaPerLevel": 16, + "meleeDamagePerStrength": 0.3, + "rangedDamagePerDexterity": 0, + "hitRatingPerDexterity": 6.2, + "magicPowerPerIntelligence": 0.85, + "meleeDamagePerLuck": 0, + "rangedDamagePerLuck": 0, + "evasionPerLuck": 1.35, + "criticalPerLuck": 0.25, + "healthPerVitality": 3.45, + "physicalDefensePerVitality": 0.04, + "manaPerWisdom": 3.6, + "elementalDefensePerWisdom": 0.29, + "elementalPowerPerMagicPower": 0.4, + "elementalDefensePerMagicPower": 0.1 + } + }, + "3": { + "items": [ + { + "id": 218, + "quantity": 1 + } + ], + "baseStats": { + "strength": 7, + "dexterity": 11, + "intelligence": 59, + "luck": 5, + "vitality": 15, + "wisdom": 6 + }, + "dynamicStats": { + "healthPerLevel": 13, + "manaPerLevel": 12, + "meleeDamagePerStrength": 0, + "rangedDamagePerDexterity": 0, + "hitRatingPerDexterity": 4.2, + "magicPowerPerIntelligence": 0.55, + "meleeDamagePerLuck": 0.3, + "rangedDamagePerLuck": 0.15, + "evasionPerLuck": 1.5, + "criticalPerLuck": 0.25, + "healthPerVitality": 4.55, + "physicalDefensePerVitality": 0.04, + "manaPerWisdom": 3.4, + "elementalDefensePerWisdom": 0.065, + "elementalPowerPerMagicPower": 0.4, + "elementalDefensePerMagicPower": 0.1 + } + }, + "4": { + "items": [ + { + "id": 299, + "quantity": 1 + }, + { + "id": 305, + "quantity": 1000 + } + ], + "baseStats": { + "strength": 6, + "dexterity": 13, + "intelligence": 8, + "luck": 5, + "vitality": 8, + "wisdom": 7 + }, + "dynamicStats": { + "healthPerLevel": 12, + "manaPerLevel": 15, + "meleeDamagePerStrength": 0, + "rangedDamagePerDexterity": 0.25, + "hitRatingPerDexterity": 3.0, + "magicPowerPerIntelligence": 0.55, + "meleeDamagePerLuck": 0, + "rangedDamagePerLuck": 0, + "evasionPerLuck": 1.35, + "criticalPerLuck": 0.25, + "healthPerVitality": 4.55, + "physicalDefensePerVitality": 0.1, + "manaPerWisdom": 3.5, + "elementalDefensePerWisdom": 0.055, + "elementalPowerPerMagicPower": 0.4, + "elementalDefensePerMagicPower": 0.1 + } + } +} From 58f4448438220986757eb9c499c65eec690915b7 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 18:48:29 +0100 Subject: [PATCH 55/60] feat: add config to docker --- .pre-commit-config.yaml | 1 + Server/Dockerfile | 7 +++--- Server/docker-compose.yml | 52 ++++++++++++++++++++++----------------- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8f66be6..e0303ff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,3 +21,4 @@ repos: rev: v2.12.0 hooks: - id: hadolint-docker + args: [--ignore, SC2086] diff --git a/Server/Dockerfile b/Server/Dockerfile index af03a37..54c4c82 100644 --- a/Server/Dockerfile +++ b/Server/Dockerfile @@ -7,17 +7,16 @@ ENV TZ=Etc/UTC ENV DOTNET_TieredPGO=1 ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 -RUN echo "Target: $TARGETARCH" -RUN echo "Build: $BUILDPLATFORM" +RUN echo "Target: $TARGETARCH" && echo "Build: $BUILDPLATFORM" WORKDIR /src COPY ["Wonderking/Wonderking.csproj", "Wonderking/"] COPY ["Server/Server.csproj", "Server/"] -RUN dotnet restore "Wonderking/Wonderking.csproj" -a $TARGETARCH -RUN dotnet restore "Server/Server.csproj" -a $TARGETARCH +RUN dotnet restore "Wonderking/Wonderking.csproj" -a $TARGETARCH && dotnet restore "Server/Server.csproj" -a $TARGETARCH COPY . . FROM build AS publish RUN dotnet publish "Server/Server.csproj" -c Release -a $TARGETARCH --no-restore -f net8.0 -o /app +COPY ../config /app/config FROM base AS final WORKDIR /app diff --git a/Server/docker-compose.yml b/Server/docker-compose.yml index 657f148..18f2a41 100644 --- a/Server/docker-compose.yml +++ b/Server/docker-compose.yml @@ -1,44 +1,52 @@ -services: +services: server: container_name: continuity-server image: continuity:latest restart: always depends_on: - - db + - db environment: - - ENVIRONMENT=Development - - Testing:CreateAccountOnLogin=true - - DB:Host=db - - DB:Port=5432 - - DB:Username=continuity - - DB:Password=continuity - - Game:Data:Path=/app/data/ + - ENVIRONMENT=Development + - Testing:CreateAccountOnLogin=true + - DB:Host=db + - DB:Port=5432 + - DB:Username=continuity + - DB:Password=continuity + - Game:Data:Path=/app/data/ networks: - - continuity + - continuity ports: - - "10001:10001" + - 10001:10001 volumes: - - type: bind - source: ../wk-data - target: /app/data - read_only: true + - type: bind + source: ../wk-data + target: /app/data + read_only: true + - type: bind + source: ../wk-logs + target: /app/logs + read_only: false + - type: bind + source: ../config + target: /app/config + read_only: true db: container_name: continuity-db image: postgres:16.1-alpine restart: always environment: - - POSTGRES_USER=continuity - - POSTGRES_DB=continuity - - POSTGRES_PASSWORD=continuity + - POSTGRES_USER=continuity + - POSTGRES_DB=continuity + - POSTGRES_PASSWORD=continuity networks: - - continuity + - continuity ports: - - "5432:5432" + - 5432:5432 volumes: - - db-data:/var/lib/postgresql/data + - db-data:/var/lib/postgresql/data healthcheck: - test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}" ] + test: [CMD-SHELL, 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}'] interval: 10s timeout: 3s retries: 3 From 55276354dba9655d3848cc873827ff75291059b5 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 18:49:43 +0100 Subject: [PATCH 56/60] chore: adjust log directory --- Server/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Server/Program.cs b/Server/Program.cs index cdd05d0..d9f97d9 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -19,8 +19,8 @@ builder.Configuration.AddJsonFile("settings.json", true, true) builder.Services.AddLogging(); var loggerFactory = LoggerFactory.Create(loggingBuilder => { - loggingBuilder.AddFile("Logs/Server-{Date}.log", LogLevel.Trace); - loggingBuilder.AddFile("Logs/Server-{Date}.json.log", LogLevel.Trace, isJson: true); + loggingBuilder.AddFile("logs/Server-{Date}.log", LogLevel.Trace); + loggingBuilder.AddFile("logs/Server-{Date}.json.log", LogLevel.Trace, isJson: true); loggingBuilder.AddConsole(); }); builder.Services.AddDbContextPool(o => From 1f4f3cff514c018b7c1f7aa02411a310c43fc41d Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 21:01:55 +0100 Subject: [PATCH 57/60] feat: implement character stat mapping to created char --- ...andler..cs => CharacterCreationHandler.cs} | 69 ++++++++++++++----- .../CharacterDeletionHandler.cs | 3 +- .../CharacterMappingItemEntry.cs | 10 +++ .../CharacterStatsMappingConfiguration.cs | 15 ++++ .../PacketHandlers/DefaultCharacterMapping.cs | 8 +++ Server/PacketHandlers/DynamicStats.cs | 47 +++++++++++++ Server/PacketHandlers/Item.cs | 11 +++ Server/PacketHandlers/JobSpecificMapping.cs | 14 ++++ Server/Program.cs | 9 +++ Server/Services/PacketDistributorService.cs | 20 +++--- Wonderking/Packets/Outgoing/Data/BaseStats.cs | 13 ++-- 11 files changed, 185 insertions(+), 34 deletions(-) rename Server/PacketHandlers/{CharacterCreationHandler..cs => CharacterCreationHandler.cs} (51%) create mode 100644 Server/PacketHandlers/CharacterMappingItemEntry.cs create mode 100644 Server/PacketHandlers/CharacterStatsMappingConfiguration.cs create mode 100644 Server/PacketHandlers/DefaultCharacterMapping.cs create mode 100644 Server/PacketHandlers/DynamicStats.cs create mode 100644 Server/PacketHandlers/Item.cs create mode 100644 Server/PacketHandlers/JobSpecificMapping.cs diff --git a/Server/PacketHandlers/CharacterCreationHandler..cs b/Server/PacketHandlers/CharacterCreationHandler.cs similarity index 51% rename from Server/PacketHandlers/CharacterCreationHandler..cs rename to Server/PacketHandlers/CharacterCreationHandler.cs index 7bd5485..c2ea731 100644 --- a/Server/PacketHandlers/CharacterCreationHandler..cs +++ b/Server/PacketHandlers/CharacterCreationHandler.cs @@ -14,11 +14,14 @@ public class CharacterCreationHandler : IPacketHandler { private readonly WonderkingContext _wonderkingContext; private readonly ItemObjectPoolService _itemObjectPoolService; + private readonly CharacterStatsMappingConfiguration _characterStatsMapping; - public CharacterCreationHandler(WonderkingContext wonderkingContext, ItemObjectPoolService itemObjectPoolService) + public CharacterCreationHandler(WonderkingContext wonderkingContext, ItemObjectPoolService itemObjectPoolService, + CharacterStatsMappingConfiguration characterStatsMappingConfiguration) { _wonderkingContext = wonderkingContext; _itemObjectPoolService = itemObjectPoolService; + _characterStatsMapping = characterStatsMappingConfiguration; } public async Task HandleAsync(CharacterCreationPacket packet, TcpSession session) @@ -26,14 +29,38 @@ public class CharacterCreationHandler : IPacketHandler var authSession = session as AuthSession; var account = _wonderkingContext.Accounts.FirstOrDefault(a => authSession != null && a.Id == authSession.AccountId); - var items = new List + var mappedDefaultItems = _characterStatsMapping.DefaultCharacterMapping.Items + .Select(i => _itemObjectPoolService.GetBaseInventoryItem(i.Id, i.Quantity)).ToArray(); + + var firstJobConfig = packet.FirstJob switch { - _itemObjectPoolService.GetBaseInventoryItem(25), - _itemObjectPoolService.GetBaseInventoryItem(764), - _itemObjectPoolService.GetBaseInventoryItem(766), - _itemObjectPoolService.GetBaseInventoryItem(763), - _itemObjectPoolService.GetBaseInventoryItem(767) + 1 => _characterStatsMapping.Swordsman, + 2 => _characterStatsMapping.Mage, + 3 => _characterStatsMapping.Thief, + 4 => _characterStatsMapping.Scout, + _ => _characterStatsMapping.Swordsman }; + + var mappedJobItems = firstJobConfig.Items + .Select(i => _itemObjectPoolService.GetBaseInventoryItem(i.Id, i.Quantity)).ToArray(); + InventoryItem[] items = + [ + .. mappedDefaultItems, + .. mappedJobItems, + _itemObjectPoolService.GetBaseInventoryItem((ushort)((packet.FirstJob - 1) * 6 + + ((byte)packet.Gender - 1) * 3 + + packet.Hair + 1)), + _itemObjectPoolService.GetBaseInventoryItem((ushort)((packet.FirstJob - 1) * 6 + + ((byte)packet.Gender - 1) * 3 + + packet.Eyes + 25)), + _itemObjectPoolService.GetBaseInventoryItem((ushort)(((byte)packet.Gender - 1) * 3 + + packet.Shirt + 49)), + _itemObjectPoolService.GetBaseInventoryItem((ushort)(((byte)packet.Gender - 1) * 3 + + packet.Pants + 58)), + ]; + + var calculateCurrentMana = CalculateCurrentMana(1, firstJobConfig); + var calculateCurrentHealth = CalculateCurrentHealth(1, firstJobConfig); account?.Characters.Add(new Character { Account = account, @@ -42,22 +69,14 @@ public class CharacterCreationHandler : IPacketHandler LastXCoordinate = 113, LastYCoordinate = 0, PvPLevel = PvPLevel.None, - Gender = Gender.None, + Gender = packet.Gender, Experience = 0, Level = 1, InventoryItems = items, - BaseStats = new BaseStats - { - Strength = 5, - Dexterity = 5, - Intelligence = 5, - Vitality = 5, - Luck = 5, - Wisdom = 5 - }, + BaseStats = firstJobConfig.BaseStats, JobData = new JobData { FirstJob = packet.FirstJob, SecondJob = 0, ThirdJob = 0, FourthJob = 0 }, - Health = 250, - Mana = 250, + Health = calculateCurrentHealth, + Mana = calculateCurrentMana }); await _wonderkingContext.SaveChangesAsync().ConfigureAwait(true); @@ -93,4 +112,16 @@ public class CharacterCreationHandler : IPacketHandler isDuplicate = false, }); } + + private static int CalculateCurrentHealth(ushort level, JobSpecificMapping firstJobConfig) + { + return (int)((level - 1) * firstJobConfig.DynamicStats.HealthPerLevel + + firstJobConfig.BaseStats.Vitality * firstJobConfig.DynamicStats.HealthPerVitality); + } + + private static int CalculateCurrentMana(ushort level, JobSpecificMapping firstJobConfig) + { + return (int)((level - 1) * firstJobConfig.DynamicStats.ManaPerLevel + + firstJobConfig.BaseStats.Wisdom * firstJobConfig.DynamicStats.ManaPerWisdom); + } } diff --git a/Server/PacketHandlers/CharacterDeletionHandler.cs b/Server/PacketHandlers/CharacterDeletionHandler.cs index 8a43182..f5de8d9 100644 --- a/Server/PacketHandlers/CharacterDeletionHandler.cs +++ b/Server/PacketHandlers/CharacterDeletionHandler.cs @@ -24,7 +24,8 @@ public class CharacterDeletionHandler : IPacketHandler return; } - var character = await _wonderkingContext.Characters.FirstOrDefaultAsync(x => x.Name == packet.Name) + var character = await _wonderkingContext.Characters.FirstOrDefaultAsync(x => x.Name == packet.Name && + x.Account.Id == authSession.AccountId) .ConfigureAwait(false); if (character == null) { diff --git a/Server/PacketHandlers/CharacterMappingItemEntry.cs b/Server/PacketHandlers/CharacterMappingItemEntry.cs new file mode 100644 index 0000000..055abd0 --- /dev/null +++ b/Server/PacketHandlers/CharacterMappingItemEntry.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Server.PacketHandlers; + +[StructLayout(LayoutKind.Auto)] +public struct CharacterMappingItemEntry +{ + public required ushort Id { get; set; } + public required ushort Quantity { get; set; } +} diff --git a/Server/PacketHandlers/CharacterStatsMappingConfiguration.cs b/Server/PacketHandlers/CharacterStatsMappingConfiguration.cs new file mode 100644 index 0000000..5e02715 --- /dev/null +++ b/Server/PacketHandlers/CharacterStatsMappingConfiguration.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Server.PacketHandlers; + +public class CharacterStatsMappingConfiguration +{ + [JsonPropertyName("default")] public DefaultCharacterMapping DefaultCharacterMapping { get; set; } + [JsonPropertyName("1")] public JobSpecificMapping Swordsman { get; set; } + + [JsonPropertyName("2")] public JobSpecificMapping Mage { get; set; } + + [JsonPropertyName("3")] public JobSpecificMapping Thief { get; set; } + + [JsonPropertyName("4")] public JobSpecificMapping Scout { get; set; } +} diff --git a/Server/PacketHandlers/DefaultCharacterMapping.cs b/Server/PacketHandlers/DefaultCharacterMapping.cs new file mode 100644 index 0000000..22c267f --- /dev/null +++ b/Server/PacketHandlers/DefaultCharacterMapping.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Server.PacketHandlers; + +public class DefaultCharacterMapping +{ + [JsonPropertyName("items")] public List Items { get; set; } +} diff --git a/Server/PacketHandlers/DynamicStats.cs b/Server/PacketHandlers/DynamicStats.cs new file mode 100644 index 0000000..09d62e4 --- /dev/null +++ b/Server/PacketHandlers/DynamicStats.cs @@ -0,0 +1,47 @@ +using System.Text.Json.Serialization; + +namespace Server.PacketHandlers; + +public class DynamicStats +{ + [JsonPropertyName("healthPerLevel")] public int HealthPerLevel { get; set; } + [JsonPropertyName("manaPerLevel")] public int ManaPerLevel { get; set; } + + [JsonPropertyName("meleeDamagePerStrength")] + public double MeleeDamagePerStrength { get; set; } + + [JsonPropertyName("rangedDamagePerDexterity")] + public double RangedDamagePerDexterity { get; set; } + + [JsonPropertyName("hitRatingPerDexterity")] + public double HitRatingPerDexterity { get; set; } + + [JsonPropertyName("magicPowerPerIntelligence")] + public double MagicPowerPerIntelligence { get; set; } + + [JsonPropertyName("meleeDamagePerLuck")] + public double MeleeDamagePerLuck { get; set; } + + [JsonPropertyName("rangedDamagePerLuck")] + public double RangedDamagePerLuck { get; set; } + + [JsonPropertyName("evasionPerLuck")] public double EvasionPerLuck { get; set; } + [JsonPropertyName("criticalPerLuck")] public double CriticalPerLuck { get; set; } + + [JsonPropertyName("healthPerVitality")] + public double HealthPerVitality { get; set; } + + [JsonPropertyName("physicalDefensePerVitality")] + public double PhysicalDefensePerVitality { get; set; } + + [JsonPropertyName("manaPerWisdom")] public double ManaPerWisdom { get; set; } + + [JsonPropertyName("elementalDefensePerWisdom")] + public double ElementalDefensePerWisdom { get; set; } + + [JsonPropertyName("elementalPowerPerMagicPower")] + public double ElementalPowerPerMagicPower { get; set; } + + [JsonPropertyName("elementalDefensePerMagicPower")] + public double ElementalDefensePerMagicPower { get; set; } +} diff --git a/Server/PacketHandlers/Item.cs b/Server/PacketHandlers/Item.cs new file mode 100644 index 0000000..259ef5c --- /dev/null +++ b/Server/PacketHandlers/Item.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace Server.PacketHandlers; + +public class Item +{ + [JsonPropertyName("id")] + public ushort Id { get; set; } + [JsonPropertyName("quantity")] + public ushort Quantity { get; set; } +} diff --git a/Server/PacketHandlers/JobSpecificMapping.cs b/Server/PacketHandlers/JobSpecificMapping.cs new file mode 100644 index 0000000..03afe7e --- /dev/null +++ b/Server/PacketHandlers/JobSpecificMapping.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; +using Wonderking.Packets.Outgoing.Data; + +namespace Server.PacketHandlers; + +public class JobSpecificMapping +{ + [JsonPropertyName("items")] + public ICollection Items { get; set; } + [JsonPropertyName("baseStats")] + public BaseStats BaseStats { get; set; } + [JsonPropertyName("dynamicStats")] + public DynamicStats DynamicStats { get; set; } +} diff --git a/Server/Program.cs b/Server/Program.cs index d9f97d9..5d2892b 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -1,5 +1,6 @@ using System.Net; using System.Reflection; +using System.Text.Json; using MassTransit; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -7,15 +8,21 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Server.DB; +using Server.PacketHandlers; using Server.Services; var builder = Host.CreateApplicationBuilder(); #if DEBUG builder.Environment.EnvironmentName = "Development"; #endif + builder.Configuration.AddJsonFile("settings.json", true, true) .AddJsonFile($"settings.{builder.Environment.EnvironmentName}.json", true) .AddEnvironmentVariables().Build(); +builder.Services.AddSingleton( + JsonSerializer.Deserialize( + File.ReadAllText("config/character-stats.mapping.json")) ?? throw new InvalidOperationException()); + builder.Services.AddLogging(); var loggerFactory = LoggerFactory.Create(loggingBuilder => { @@ -23,6 +30,7 @@ var loggerFactory = LoggerFactory.Create(loggingBuilder => loggingBuilder.AddFile("logs/Server-{Date}.json.log", LogLevel.Trace, isJson: true); loggingBuilder.AddConsole(); }); + builder.Services.AddDbContextPool(o => { using var configuration = builder.Configuration; @@ -30,6 +38,7 @@ builder.Services.AddDbContextPool(o => $"Host={configuration["DB:Host"]};Username={configuration["DB:Username"]};Password={configuration["DB:Password"]};Database={configuration["DB:Database"]};Port={configuration["DB:Port"]}") .EnableSensitiveDataLogging().UseLazyLoadingProxies().UseLoggerFactory(loggerFactory); }); + builder.Services.AddSingleton(loggerFactory); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/Server/Services/PacketDistributorService.cs b/Server/Services/PacketDistributorService.cs index 5bc2d43..a6e89c6 100644 --- a/Server/Services/PacketDistributorService.cs +++ b/Server/Services/PacketDistributorService.cs @@ -22,17 +22,22 @@ public class PacketDistributorService : IHostedService { private readonly ConcurrentQueue _concurrentQueue; - private readonly - ImmutableDictionary> _deserializationMap; + private ImmutableDictionary> _deserializationMap; private readonly ILogger _logger; - private readonly ConcurrentDictionary _packetHandlersInstantiation; + private readonly IServiceProvider _serviceProvider; + private ConcurrentDictionary _packetHandlersInstantiation; public PacketDistributorService(ILogger logger, IServiceProvider serviceProvider) { this._concurrentQueue = new ConcurrentQueue(); this._logger = logger; + _serviceProvider = serviceProvider; + } + + public Task StartAsync(CancellationToken cancellationToken) + { var tempDeserializationMap = new Dictionary>(); @@ -43,7 +48,7 @@ public class PacketDistributorService : IHostedService packetHandlers.ForEach(x => { var packetHandler = - ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider, + ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider, x.Value); this._packetHandlersInstantiation.TryAdd(x.Key, packetHandler); }); @@ -60,15 +65,14 @@ public class PacketDistributorService : IHostedService Return(packetVariable); }).Compile(); - logger.PacketCreationFunctionCreated(packetsType.Key); + _logger.PacketCreationFunctionCreated(packetsType.Key); tempDeserializationMap.Add(packetsType.Key, lambda); } this._deserializationMap = tempDeserializationMap.ToImmutableDictionary(); + return Task.CompletedTask; } - public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask; - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; private Dictionary GetPacketsWithId(Assembly executingAssembly) diff --git a/Wonderking/Packets/Outgoing/Data/BaseStats.cs b/Wonderking/Packets/Outgoing/Data/BaseStats.cs index 1dfba40..0ed902f 100644 --- a/Wonderking/Packets/Outgoing/Data/BaseStats.cs +++ b/Wonderking/Packets/Outgoing/Data/BaseStats.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Serialization; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; @@ -7,10 +8,10 @@ namespace Wonderking.Packets.Outgoing.Data; [Owned] public class BaseStats { - public required short Strength { get; set; } - public required short Dexterity { get; set; } - public required short Intelligence { get; set; } - public required short Vitality { get; set; } - public required short Luck { get; set; } - public required short Wisdom { get; set; } + [JsonPropertyName("strength")] public required short Strength { get; set; } + [JsonPropertyName("dexterity")] public required short Dexterity { get; set; } + [JsonPropertyName("intelligence")] public required short Intelligence { get; set; } + [JsonPropertyName("vitality")] public required short Vitality { get; set; } + [JsonPropertyName("luck")] public required short Luck { get; set; } + [JsonPropertyName("wisdom")] public required short Wisdom { get; set; } } From 4b7f0b6f005b1f0a427e14ad743ba38071b7b922 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 21:05:33 +0100 Subject: [PATCH 58/60] refactor: namespace adjustments --- Server/PacketHandlers/CharacterCreationHandler.cs | 1 + Server/Program.cs | 2 +- .../Game/Mapping}/CharacterStatsMappingConfiguration.cs | 2 +- .../Game/Mapping}/DefaultCharacterMapping.cs | 2 +- .../PacketHandlers => Wonderking/Game/Mapping}/DynamicStats.cs | 2 +- {Server/PacketHandlers => Wonderking/Game/Mapping}/Item.cs | 2 +- .../Game/Mapping}/JobSpecificMapping.cs | 2 +- 7 files changed, 7 insertions(+), 6 deletions(-) rename {Server/PacketHandlers => Wonderking/Game/Mapping}/CharacterStatsMappingConfiguration.cs (93%) rename {Server/PacketHandlers => Wonderking/Game/Mapping}/DefaultCharacterMapping.cs (81%) rename {Server/PacketHandlers => Wonderking/Game/Mapping}/DynamicStats.cs (97%) rename {Server/PacketHandlers => Wonderking/Game/Mapping}/Item.cs (85%) rename {Server/PacketHandlers => Wonderking/Game/Mapping}/JobSpecificMapping.cs (91%) diff --git a/Server/PacketHandlers/CharacterCreationHandler.cs b/Server/PacketHandlers/CharacterCreationHandler.cs index c2ea731..85f2a26 100644 --- a/Server/PacketHandlers/CharacterCreationHandler.cs +++ b/Server/PacketHandlers/CharacterCreationHandler.cs @@ -4,6 +4,7 @@ using Server.DB; using Server.DB.Documents; using Server.Services; using Wonderking.Game.Data.Character; +using Wonderking.Game.Mapping; using Wonderking.Packets.Incoming; using Wonderking.Packets.Outgoing; using Wonderking.Packets.Outgoing.Data; diff --git a/Server/Program.cs b/Server/Program.cs index 5d2892b..e203b3b 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -8,8 +8,8 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Server.DB; -using Server.PacketHandlers; using Server.Services; +using Wonderking.Game.Mapping; var builder = Host.CreateApplicationBuilder(); #if DEBUG diff --git a/Server/PacketHandlers/CharacterStatsMappingConfiguration.cs b/Wonderking/Game/Mapping/CharacterStatsMappingConfiguration.cs similarity index 93% rename from Server/PacketHandlers/CharacterStatsMappingConfiguration.cs rename to Wonderking/Game/Mapping/CharacterStatsMappingConfiguration.cs index 5e02715..5851db2 100644 --- a/Server/PacketHandlers/CharacterStatsMappingConfiguration.cs +++ b/Wonderking/Game/Mapping/CharacterStatsMappingConfiguration.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Server.PacketHandlers; +namespace Wonderking.Game.Mapping; public class CharacterStatsMappingConfiguration { diff --git a/Server/PacketHandlers/DefaultCharacterMapping.cs b/Wonderking/Game/Mapping/DefaultCharacterMapping.cs similarity index 81% rename from Server/PacketHandlers/DefaultCharacterMapping.cs rename to Wonderking/Game/Mapping/DefaultCharacterMapping.cs index 22c267f..38c1e8c 100644 --- a/Server/PacketHandlers/DefaultCharacterMapping.cs +++ b/Wonderking/Game/Mapping/DefaultCharacterMapping.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Server.PacketHandlers; +namespace Wonderking.Game.Mapping; public class DefaultCharacterMapping { diff --git a/Server/PacketHandlers/DynamicStats.cs b/Wonderking/Game/Mapping/DynamicStats.cs similarity index 97% rename from Server/PacketHandlers/DynamicStats.cs rename to Wonderking/Game/Mapping/DynamicStats.cs index 09d62e4..e68880e 100644 --- a/Server/PacketHandlers/DynamicStats.cs +++ b/Wonderking/Game/Mapping/DynamicStats.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Server.PacketHandlers; +namespace Wonderking.Game.Mapping; public class DynamicStats { diff --git a/Server/PacketHandlers/Item.cs b/Wonderking/Game/Mapping/Item.cs similarity index 85% rename from Server/PacketHandlers/Item.cs rename to Wonderking/Game/Mapping/Item.cs index 259ef5c..79e2eab 100644 --- a/Server/PacketHandlers/Item.cs +++ b/Wonderking/Game/Mapping/Item.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Server.PacketHandlers; +namespace Wonderking.Game.Mapping; public class Item { diff --git a/Server/PacketHandlers/JobSpecificMapping.cs b/Wonderking/Game/Mapping/JobSpecificMapping.cs similarity index 91% rename from Server/PacketHandlers/JobSpecificMapping.cs rename to Wonderking/Game/Mapping/JobSpecificMapping.cs index 03afe7e..2dabc00 100644 --- a/Server/PacketHandlers/JobSpecificMapping.cs +++ b/Wonderking/Game/Mapping/JobSpecificMapping.cs @@ -1,7 +1,7 @@ using System.Text.Json.Serialization; using Wonderking.Packets.Outgoing.Data; -namespace Server.PacketHandlers; +namespace Wonderking.Game.Mapping; public class JobSpecificMapping { From 0077d380f38ab053ed6242b52101f9ffa0635007 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 16 Nov 2023 23:51:30 +0100 Subject: [PATCH 59/60] chore: I don't know right now --- Server/PacketHandlers/CharacterCreationHandler.cs | 5 +++-- Server/PacketHandlers/CharacterDeletionHandler.cs | 6 +++--- Server/PacketHandlers/LoginHandler.cs | 2 +- Server/Services/WonderkingAuthServer.cs | 12 ++++++++++++ .../Outgoing/CharacterDeleteResponsePacket.cs | 4 ++-- .../Packets/Outgoing/CharacterDeletionResponse.cs | 7 ------- 6 files changed, 21 insertions(+), 15 deletions(-) delete mode 100644 Wonderking/Packets/Outgoing/CharacterDeletionResponse.cs diff --git a/Server/PacketHandlers/CharacterCreationHandler.cs b/Server/PacketHandlers/CharacterCreationHandler.cs index 85f2a26..7e9dcb4 100644 --- a/Server/PacketHandlers/CharacterCreationHandler.cs +++ b/Server/PacketHandlers/CharacterCreationHandler.cs @@ -62,7 +62,7 @@ public class CharacterCreationHandler : IPacketHandler var calculateCurrentMana = CalculateCurrentMana(1, firstJobConfig); var calculateCurrentHealth = CalculateCurrentHealth(1, firstJobConfig); - account?.Characters.Add(new Character + var toBeAddedCharacter = new Character { Account = account, MapId = 300, @@ -78,7 +78,8 @@ public class CharacterCreationHandler : IPacketHandler JobData = new JobData { FirstJob = packet.FirstJob, SecondJob = 0, ThirdJob = 0, FourthJob = 0 }, Health = calculateCurrentHealth, Mana = calculateCurrentMana - }); + }; + account?.Characters.Add(toBeAddedCharacter); await _wonderkingContext.SaveChangesAsync().ConfigureAwait(true); var amountOfCharacters = await _wonderkingContext.Characters.AsNoTrackingWithIdentityResolution() diff --git a/Server/PacketHandlers/CharacterDeletionHandler.cs b/Server/PacketHandlers/CharacterDeletionHandler.cs index f5de8d9..920131a 100644 --- a/Server/PacketHandlers/CharacterDeletionHandler.cs +++ b/Server/PacketHandlers/CharacterDeletionHandler.cs @@ -18,15 +18,16 @@ public class CharacterDeletionHandler : IPacketHandler public async Task HandleAsync(CharacterDeletePacket packet, TcpSession session) { using var authSession = session as AuthSession; - var response = new CharacterDeleteResponsePacket { Response = CharacterDeletionResponse.MakeClientStuck }; if (authSession == null) { + session.Disconnect(); return; } var character = await _wonderkingContext.Characters.FirstOrDefaultAsync(x => x.Name == packet.Name && x.Account.Id == authSession.AccountId) - .ConfigureAwait(false); + .ConfigureAwait(true); + var response = new CharacterDeleteResponsePacket { IsDeleted = 0 }; if (character == null) { authSession.Send(response); @@ -35,7 +36,6 @@ public class CharacterDeletionHandler : IPacketHandler _wonderkingContext.Characters.Remove(character); await _wonderkingContext.SaveChangesAsync().ConfigureAwait(false); - response.Response = CharacterDeletionResponse.Ok; authSession.Send(response); } diff --git a/Server/PacketHandlers/LoginHandler.cs b/Server/PacketHandlers/LoginHandler.cs index a79cf44..1d60f1b 100644 --- a/Server/PacketHandlers/LoginHandler.cs +++ b/Server/PacketHandlers/LoginHandler.cs @@ -108,7 +108,7 @@ public class LoginHandler : IPacketHandler var loginResponsePacket = new LoginResponsePacket { ResponseReason = loginResponseReason, - ChannelData = new[] { new ServerChannelData { ChannelId = 0, LoadPercentage = 75, ServerId = 0 } }, + ChannelData = new[] { new ServerChannelData { ChannelId = 0, LoadPercentage = 0, ServerId = 0 } }, UnknownFlag = 1, IsGameMaster = true }; diff --git a/Server/Services/WonderkingAuthServer.cs b/Server/Services/WonderkingAuthServer.cs index fd9d6e1..30ae4e8 100644 --- a/Server/Services/WonderkingAuthServer.cs +++ b/Server/Services/WonderkingAuthServer.cs @@ -58,5 +58,17 @@ public class WonderkingAuthServer : TcpServer, IHostedService base.OnStopped(); } + protected override void OnConnected(TcpSession session) + { + this._logger.LogInformation("Client connected {Session}", session.Id); + base.OnConnected(session); + } + + protected override void OnDisconnected(TcpSession session) + { + this._logger.LogInformation("Client disconnected {Session}", session.Id); + base.OnDisconnected(session); + } + protected override void OnError(SocketError error) => this._logger.LogError("An error has occured {Error}", error); } diff --git a/Wonderking/Packets/Outgoing/CharacterDeleteResponsePacket.cs b/Wonderking/Packets/Outgoing/CharacterDeleteResponsePacket.cs index 72392c3..d467c06 100644 --- a/Wonderking/Packets/Outgoing/CharacterDeleteResponsePacket.cs +++ b/Wonderking/Packets/Outgoing/CharacterDeleteResponsePacket.cs @@ -3,7 +3,7 @@ namespace Wonderking.Packets.Outgoing; [PacketId(OperationCode.CharacterDeletionResponse)] public class CharacterDeleteResponsePacket : IPacket { - public required CharacterDeletionResponse Response { get; set; } + public required byte IsDeleted { get; set; } public void Deserialize(byte[] data) { @@ -13,7 +13,7 @@ public class CharacterDeleteResponsePacket : IPacket public byte[] Serialize() { Span data = stackalloc byte[1]; - data[0] = (byte)Response; + data[0] = IsDeleted; return data.ToArray(); } } diff --git a/Wonderking/Packets/Outgoing/CharacterDeletionResponse.cs b/Wonderking/Packets/Outgoing/CharacterDeletionResponse.cs deleted file mode 100644 index 8676416..0000000 --- a/Wonderking/Packets/Outgoing/CharacterDeletionResponse.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Wonderking.Packets.Outgoing; - -public enum CharacterDeletionResponse : byte -{ - Ok = 0, - MakeClientStuck = 1, -} From 42c53584f7164cefa270b3de91acec7633042635 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Fri, 17 Nov 2023 08:25:29 +0100 Subject: [PATCH 60/60] refactor: slight adjustments --- Server/AuthSession.cs | 6 ++++++ Server/PacketHandlers/ChannelSelectionHandler.cs | 12 ++++++------ Wonderking/Packets/Outgoing/LoginResponsePacket.cs | 5 ++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Server/AuthSession.cs b/Server/AuthSession.cs index d871bb0..ae0f4c9 100644 --- a/Server/AuthSession.cs +++ b/Server/AuthSession.cs @@ -1,3 +1,4 @@ +using System.Net.Sockets; using Wonderking.Packets; namespace Server; @@ -105,4 +106,9 @@ public class AuthSession : TcpSession return buffer; } + + protected override void OnError(SocketError error) + { + _logger.LogWarning("An error has occured: {Error}", error); + } } diff --git a/Server/PacketHandlers/ChannelSelectionHandler.cs b/Server/PacketHandlers/ChannelSelectionHandler.cs index 8a66499..9e474ff 100644 --- a/Server/PacketHandlers/ChannelSelectionHandler.cs +++ b/Server/PacketHandlers/ChannelSelectionHandler.cs @@ -25,10 +25,6 @@ public class ChannelSelectionHandler : IPacketHandler this._wonderkingContext = wonderkingContext; } - public ChannelSelectionHandler() - { - } - public async Task HandleAsync(ChannelSelectionPacket packet, TcpSession session) { var authSession = (AuthSession)session; @@ -80,12 +76,16 @@ public class ChannelSelectionHandler : IPacketHandler { ChannelIsFullFlag = 0, Endpoint = "127.0.0.1", - Port = 12345, + Port = 2000, Characters = Array.Empty() }; } authSession.Send(responsePacket); - authSession.Send(guildNameResponsePacket); + if (guildNameResponsePacket.GuildNames.Length > 0 && + guildNameResponsePacket.GuildNames.Select(n => n != string.Empty).Any()) + { + authSession.Send(guildNameResponsePacket); + } } } diff --git a/Wonderking/Packets/Outgoing/LoginResponsePacket.cs b/Wonderking/Packets/Outgoing/LoginResponsePacket.cs index e3060bb..de08649 100644 --- a/Wonderking/Packets/Outgoing/LoginResponsePacket.cs +++ b/Wonderking/Packets/Outgoing/LoginResponsePacket.cs @@ -1,3 +1,4 @@ +using System.Buffers.Binary; using Wonderking.Packets.Outgoing.Data; namespace Wonderking.Packets.Outgoing; @@ -34,9 +35,7 @@ public class LoginResponsePacket : IPacket dataSpan[0] = (byte)this.ResponseReason; dataSpan[1] = this.UnknownFlag; dataSpan[2] = BitConverter.GetBytes(this.IsGameMaster)[0]; - var bytesOfChannelAmount = BitConverter.GetBytes((ushort)this.ChannelData.Length); - dataSpan[3] = bytesOfChannelAmount[0]; - dataSpan[4] = bytesOfChannelAmount[1]; + BinaryPrimitives.WriteUInt16LittleEndian(dataSpan.Slice(3, 2), (ushort)this.ChannelData.Length); for (var i = 0; i < this.ChannelData.Length; i++) {