diff --git a/SCHALE.Common/Database/ModelExtensions/AccountExtensions.cs b/SCHALE.Common/Database/ModelExtensions/AccountExtensions.cs index 0775d2e..1272911 100644 --- a/SCHALE.Common/Database/ModelExtensions/AccountExtensions.cs +++ b/SCHALE.Common/Database/ModelExtensions/AccountExtensions.cs @@ -51,5 +51,19 @@ return [.. items]; } + + public static List AddGears(this AccountDB account, SCHALEContext context, params GearDB[] gears) + { + foreach (var gear in gears) + { + gear.AccountServerId = account.ServerId; + context.Gears.Add(gear); + + var targetCharacter = account.Characters.FirstOrDefault(x => x.ServerId == gear.BoundCharacterServerId); + targetCharacter.EquipmentServerIds.Add(gear.ServerId); + } + + return [.. gears]; + } } } diff --git a/SCHALE.Common/Database/SCHALEContext.cs b/SCHALE.Common/Database/SCHALEContext.cs index 7fc2516..5106e21 100644 --- a/SCHALE.Common/Database/SCHALEContext.cs +++ b/SCHALE.Common/Database/SCHALEContext.cs @@ -16,6 +16,7 @@ namespace SCHALE.Common.Database public DbSet Characters { get; set; } public DbSet Equipment { get; set; } public DbSet Weapons { get; set; } + public DbSet Gears { get; set; } public DbSet Echelons { get; set; } public DbSet AccountTutorials { get; set; } @@ -62,10 +63,16 @@ namespace SCHALE.Common.Database .WithOne(x => x.Account) .HasForeignKey(x => x.AccountServerId) .IsRequired(); + modelBuilder.Entity() + .HasMany(x => x.Gears) + .WithOne(x => x.Account) + .HasForeignKey(x => x.AccountServerId) + .IsRequired(); modelBuilder.Entity().Property(x => x.ServerId).ValueGeneratedOnAdd(); modelBuilder.Entity().Property(x => x.ServerId).ValueGeneratedOnAdd(); modelBuilder.Entity().Property(x => x.ServerId).ValueGeneratedOnAdd(); + modelBuilder.Entity().Property(x => x.ServerId).ValueGeneratedOnAdd(); modelBuilder.Entity().Property(x => x.ServerId).ValueGeneratedOnAdd(); diff --git a/SCHALE.Common/Database/dbs.cs b/SCHALE.Common/Database/dbs.cs index 49d9575..5b1c9fb 100644 --- a/SCHALE.Common/Database/dbs.cs +++ b/SCHALE.Common/Database/dbs.cs @@ -209,6 +209,10 @@ namespace SCHALE.Common.Database [JsonIgnore] public virtual ICollection Weapons { get; } + [JsonIgnore] + public virtual ICollection Gears { get; } + + [JsonIgnore] public long RaidSeasonId { get; set; } // idk where to store this @@ -1094,7 +1098,7 @@ namespace SCHALE.Common.Database } - public class EliminateRaidLobbyInfoDB + public class EliminateRaidLobbyInfoDB : RaidLobbyInfoDB { public List OpenedBossGroups { get; set; } public Dictionary BestRankingPointPerBossGroup { get; set; } @@ -1456,19 +1460,44 @@ namespace SCHALE.Common.Database public class GearDB : ParcelBase { + [NotMapped] public override ParcelType Type { get => ParcelType.CharacterGear; } + [NotMapped] [JsonIgnore] public override IEnumerable ParcelInfos { get; } + [JsonIgnore] + public virtual AccountDB Account { get; set; } + + [JsonIgnore] + public long AccountServerId { get; set; } + + [Key] public long ServerId { get; set; } + + public long UniqueId { get; set; } public int Level { get; set; } public long Exp { get; set; } public int Tier { get; set; } public long SlotIndex { get; set; } public long BoundCharacterServerId { get; set; } - public EquipmentDB ToEquipmentDB { get; set; } + + [NotMapped] + public EquipmentDB ToEquipmentDB { get { + return new() + { + IsNew = true, + ServerId = ServerId, + BoundCharacterServerId = BoundCharacterServerId, + Tier = Tier, + Level = Level, + StackCount = 1, + Exp = Exp + }; + } + } } @@ -1895,6 +1924,7 @@ namespace SCHALE.Common.Database public DateTime NextSeasonEndDate { get; set; } public DateTime NextSettlementEndDate { get; set; } public ClanAssistUseInfo ClanAssistUseInfo { get; set; } + public Dictionary RemainFailCompensation { get; set; } } diff --git a/SCHALE.Common/Migrations/20240518014039_Gears.Designer.cs b/SCHALE.Common/Migrations/20240518014039_Gears.Designer.cs new file mode 100644 index 0000000..1fd7446 --- /dev/null +++ b/SCHALE.Common/Migrations/20240518014039_Gears.Designer.cs @@ -0,0 +1,521 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SCHALE.Common.Database; + +#nullable disable + +namespace SCHALE.Common.Migrations +{ + [DbContext(typeof(SCHALEContext))] + [Migration("20240518014039_Gears")] + partial class Gears + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.2") + .HasAnnotation("Proxies:ChangeTracking", false) + .HasAnnotation("Proxies:CheckEquality", false) + .HasAnnotation("Proxies:LazyLoading", true) + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("SCHALE.Common.Database.AccountDB", b => + { + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ServerId")); + + b.Property("BirthDay") + .HasColumnType("datetime2"); + + b.Property("CallName") + .HasColumnType("nvarchar(max)"); + + b.Property("CallNameUpdateTime") + .HasColumnType("datetime2"); + + b.Property("Comment") + .HasColumnType("nvarchar(max)"); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("DevId") + .HasColumnType("nvarchar(max)"); + + b.Property("Exp") + .HasColumnType("bigint"); + + b.Property("LastConnectTime") + .HasColumnType("datetime2"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("LinkRewardDate") + .HasColumnType("datetime2"); + + b.Property("LobbyMode") + .HasColumnType("int"); + + b.Property("MemoryLobbyUniqueId") + .HasColumnType("bigint"); + + b.Property("Nickname") + .HasColumnType("nvarchar(max)"); + + b.Property("PublisherAccountId") + .HasColumnType("bigint"); + + b.Property("RaidSeasonId") + .HasColumnType("bigint"); + + b.Property("RepresentCharacterServerId") + .HasColumnType("int"); + + b.Property("RetentionDays") + .HasColumnType("int"); + + b.Property("State") + .HasColumnType("int"); + + b.Property("UnReadMailCount") + .HasColumnType("int"); + + b.Property("VIPLevel") + .HasColumnType("int"); + + b.HasKey("ServerId"); + + b.ToTable("Accounts"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.CharacterDB", b => + { + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ServerId")); + + b.Property("AccountServerId") + .HasColumnType("bigint"); + + b.Property("EquipmentServerIds") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EquipmentSlotAndDBIds") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ExSkillLevel") + .HasColumnType("int"); + + b.Property("Exp") + .HasColumnType("bigint"); + + b.Property("ExtraPassiveSkillLevel") + .HasColumnType("int"); + + b.Property("FavorExp") + .HasColumnType("bigint"); + + b.Property("FavorRank") + .HasColumnType("int"); + + b.Property("IsFavorite") + .HasColumnType("bit"); + + b.Property("IsLocked") + .HasColumnType("bit"); + + b.Property("LeaderSkillLevel") + .HasColumnType("int"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("PassiveSkillLevel") + .HasColumnType("int"); + + b.Property("PotentialStats") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PublicSkillLevel") + .HasColumnType("int"); + + b.Property("StarGrade") + .HasColumnType("int"); + + b.Property("UniqueId") + .HasColumnType("bigint"); + + b.HasKey("ServerId"); + + b.HasIndex("AccountServerId"); + + b.ToTable("Characters"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.EchelonDB", b => + { + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ServerId")); + + b.Property("AccountServerId") + .HasColumnType("bigint"); + + b.Property("EchelonNumber") + .HasColumnType("bigint"); + + b.Property("EchelonType") + .HasColumnType("int"); + + b.Property("ExtensionType") + .HasColumnType("int"); + + b.Property("LeaderServerId") + .HasColumnType("bigint"); + + b.Property("MainSlotServerIds") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SkillCardMulliganCharacterIds") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SupportSlotServerIds") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TSSInteractionServerId") + .HasColumnType("bigint"); + + b.Property("UsingFlag") + .HasColumnType("int"); + + b.HasKey("ServerId"); + + b.HasIndex("AccountServerId"); + + b.ToTable("Echelons"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.EquipmentDB", b => + { + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ServerId")); + + b.Property("AccountServerId") + .HasColumnType("bigint"); + + b.Property("BoundCharacterServerId") + .HasColumnType("bigint"); + + b.Property("Exp") + .HasColumnType("bigint"); + + b.Property("IsLocked") + .HasColumnType("bit"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("StackCount") + .HasColumnType("bigint"); + + b.Property("Tier") + .HasColumnType("int"); + + b.Property("UniqueId") + .HasColumnType("bigint"); + + b.HasKey("ServerId"); + + b.HasIndex("AccountServerId"); + + b.ToTable("Equipment"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.GearDB", b => + { + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ServerId")); + + b.Property("AccountServerId") + .HasColumnType("bigint"); + + b.Property("BoundCharacterServerId") + .HasColumnType("bigint"); + + b.Property("Exp") + .HasColumnType("bigint"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("SlotIndex") + .HasColumnType("bigint"); + + b.Property("Tier") + .HasColumnType("int"); + + b.Property("UniqueId") + .HasColumnType("bigint"); + + b.HasKey("ServerId"); + + b.HasIndex("AccountServerId"); + + b.ToTable("Gears"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.ItemDB", b => + { + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ServerId")); + + b.Property("AccountServerId") + .HasColumnType("bigint"); + + b.Property("IsLocked") + .HasColumnType("bit"); + + b.Property("StackCount") + .HasColumnType("bigint"); + + b.Property("UniqueId") + .HasColumnType("bigint"); + + b.HasKey("ServerId"); + + b.HasIndex("AccountServerId"); + + b.ToTable("Items"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.MissionProgressDB", b => + { + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ServerId")); + + b.Property("AccountServerId") + .HasColumnType("bigint"); + + b.Property("Complete") + .HasColumnType("bit"); + + b.Property("MissionUniqueId") + .HasColumnType("bigint"); + + b.Property("ProgressParameters") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.HasKey("ServerId"); + + b.HasIndex("AccountServerId"); + + b.ToTable("MissionProgresses"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.Models.AccountTutorial", b => + { + b.Property("AccountServerId") + .HasColumnType("bigint"); + + b.Property("TutorialIds") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("AccountServerId"); + + b.ToTable("AccountTutorials"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.Models.GuestAccount", b => + { + b.Property("Uid") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Uid")); + + b.Property("DeviceId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Token") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Uid"); + + b.ToTable("GuestAccounts"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.WeaponDB", b => + { + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ServerId")); + + b.Property("AccountServerId") + .HasColumnType("bigint"); + + b.Property("BoundCharacterServerId") + .HasColumnType("bigint"); + + b.Property("Exp") + .HasColumnType("bigint"); + + b.Property("IsLocked") + .HasColumnType("bit"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("StarGrade") + .HasColumnType("int"); + + b.Property("UniqueId") + .HasColumnType("bigint"); + + b.HasKey("ServerId"); + + b.HasIndex("AccountServerId"); + + b.ToTable("Weapons"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.CharacterDB", b => + { + b.HasOne("SCHALE.Common.Database.AccountDB", "Account") + .WithMany("Characters") + .HasForeignKey("AccountServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.EchelonDB", b => + { + b.HasOne("SCHALE.Common.Database.AccountDB", "Account") + .WithMany("Echelons") + .HasForeignKey("AccountServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.EquipmentDB", b => + { + b.HasOne("SCHALE.Common.Database.AccountDB", "Account") + .WithMany("Equipment") + .HasForeignKey("AccountServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.GearDB", b => + { + b.HasOne("SCHALE.Common.Database.AccountDB", "Account") + .WithMany("Gears") + .HasForeignKey("AccountServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.ItemDB", b => + { + b.HasOne("SCHALE.Common.Database.AccountDB", "Account") + .WithMany("Items") + .HasForeignKey("AccountServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.MissionProgressDB", b => + { + b.HasOne("SCHALE.Common.Database.AccountDB", "Account") + .WithMany("MissionProgresses") + .HasForeignKey("AccountServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.WeaponDB", b => + { + b.HasOne("SCHALE.Common.Database.AccountDB", "Account") + .WithMany("Weapons") + .HasForeignKey("AccountServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("SCHALE.Common.Database.AccountDB", b => + { + b.Navigation("Characters"); + + b.Navigation("Echelons"); + + b.Navigation("Equipment"); + + b.Navigation("Gears"); + + b.Navigation("Items"); + + b.Navigation("MissionProgresses"); + + b.Navigation("Weapons"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SCHALE.Common/Migrations/20240518014039_Gears.cs b/SCHALE.Common/Migrations/20240518014039_Gears.cs new file mode 100644 index 0000000..264b4d4 --- /dev/null +++ b/SCHALE.Common/Migrations/20240518014039_Gears.cs @@ -0,0 +1,51 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SCHALE.Common.Migrations +{ + /// + public partial class Gears : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Gears", + columns: table => new + { + ServerId = table.Column(type: "bigint", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + AccountServerId = table.Column(type: "bigint", nullable: false), + UniqueId = table.Column(type: "bigint", nullable: false), + Level = table.Column(type: "int", nullable: false), + Exp = table.Column(type: "bigint", nullable: false), + Tier = table.Column(type: "int", nullable: false), + SlotIndex = table.Column(type: "bigint", nullable: false), + BoundCharacterServerId = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Gears", x => x.ServerId); + table.ForeignKey( + name: "FK_Gears_Accounts_AccountServerId", + column: x => x.AccountServerId, + principalTable: "Accounts", + principalColumn: "ServerId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Gears_AccountServerId", + table: "Gears", + column: "AccountServerId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Gears"); + } + } +} diff --git a/SCHALE.Common/Migrations/SCHALEContextModelSnapshot.cs b/SCHALE.Common/Migrations/SCHALEContextModelSnapshot.cs index 26923f0..cf8f6d7 100644 --- a/SCHALE.Common/Migrations/SCHALEContextModelSnapshot.cs +++ b/SCHALE.Common/Migrations/SCHALEContextModelSnapshot.cs @@ -254,6 +254,42 @@ namespace SCHALE.Common.Migrations b.ToTable("Equipment"); }); + modelBuilder.Entity("SCHALE.Common.Database.GearDB", b => + { + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ServerId")); + + b.Property("AccountServerId") + .HasColumnType("bigint"); + + b.Property("BoundCharacterServerId") + .HasColumnType("bigint"); + + b.Property("Exp") + .HasColumnType("bigint"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("SlotIndex") + .HasColumnType("bigint"); + + b.Property("Tier") + .HasColumnType("int"); + + b.Property("UniqueId") + .HasColumnType("bigint"); + + b.HasKey("ServerId"); + + b.HasIndex("AccountServerId"); + + b.ToTable("Gears"); + }); + modelBuilder.Entity("SCHALE.Common.Database.ItemDB", b => { b.Property("ServerId") @@ -416,6 +452,17 @@ namespace SCHALE.Common.Migrations b.Navigation("Account"); }); + modelBuilder.Entity("SCHALE.Common.Database.GearDB", b => + { + b.HasOne("SCHALE.Common.Database.AccountDB", "Account") + .WithMany("Gears") + .HasForeignKey("AccountServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + modelBuilder.Entity("SCHALE.Common.Database.ItemDB", b => { b.HasOne("SCHALE.Common.Database.AccountDB", "Account") @@ -457,6 +504,8 @@ namespace SCHALE.Common.Migrations b.Navigation("Equipment"); + b.Navigation("Gears"); + b.Navigation("Items"); b.Navigation("MissionProgresses"); diff --git a/SCHALE.GameServer/Commands/InventoryCommand.cs b/SCHALE.GameServer/Commands/InventoryCommand.cs index 04de0c8..e0592a5 100644 --- a/SCHALE.GameServer/Commands/InventoryCommand.cs +++ b/SCHALE.GameServer/Commands/InventoryCommand.cs @@ -6,12 +6,12 @@ using SCHALE.GameServer.Services.Irc; namespace SCHALE.GameServer.Commands { - [CommandHandler("inventory", "Command to manage inventory (chars, weapons, equipment, items)", "/inventory ")] + [CommandHandler("inventory", "Command to manage inventory (chars, weapons, equipment, items)", "/inventory ")] internal class InventoryCommand : Command { public InventoryCommand(IrcConnection connection, string[] args, bool validate = true) : base(connection, args, validate) { } - [Argument(0, @"^addall$|^clearall$", "The operation selected (addall, clearall)", ArgumentFlags.IgnoreCase)] + [Argument(0, @"^addall$|^removeall$", "The operation selected (addall, removeall)", ArgumentFlags.IgnoreCase)] public string Op { get; set; } = string.Empty; public override void Execute() @@ -25,13 +25,19 @@ namespace SCHALE.GameServer.Commands InventoryUtils.AddAllWeapons(connection); InventoryUtils.AddAllEquipment(connection); InventoryUtils.AddAllItems(connection); + InventoryUtils.AddAllGears(connection); + + connection.SendChatMessage("Added Everything!"); break; - case "clearall": + case "removeall": InventoryUtils.RemoveAllCharacters(connection); context.Weapons.RemoveRange(context.Weapons.Where(x => x.AccountServerId == connection.AccountServerId)); context.Equipment.RemoveRange(context.Equipment.Where(x => x.AccountServerId == connection.AccountServerId)); context.Items.RemoveRange(context.Items.Where(x => x.AccountServerId == connection.AccountServerId)); + context.Gears.RemoveRange(context.Gears.Where(x => x.AccountServerId == connection.AccountServerId)); + + connection.SendChatMessage("Removed Everything!"); break; } diff --git a/SCHALE.GameServer/Commands/SetAccountCommand.cs b/SCHALE.GameServer/Commands/SetAccountCommand.cs index 82e4fc2..86c5a65 100644 --- a/SCHALE.GameServer/Commands/SetAccountCommand.cs +++ b/SCHALE.GameServer/Commands/SetAccountCommand.cs @@ -2,15 +2,16 @@ using SCHALE.GameServer.Services.Irc; using System.ComponentModel; using System.Reflection; +using SCHALE.GameServer.Utils; namespace SCHALE.GameServer.Commands { - [CommandHandler("setaccount", "Command to change player's account data", "/setaccount <|Level|Nickname|RaidSeasonId|Property|...> ")] + [CommandHandler("setaccount", "Command to change player's account data (case-sensitive)", "/setaccount <|Level|Nickname|RaidSeasonId|Property|...> ")] internal class SetAccountCommand : Command { public SetAccountCommand(IrcConnection connection, string[] args, bool validate = true) : base(connection, args, validate) { } - [Argument(0, @"^[a-zA-Z]+$", "The Account Property you want to change.", ArgumentFlags.IgnoreCase)] + [Argument(0, @"^[a-zA-Z]+$", "The Account Property you want to change. (case-sensitive)", ArgumentFlags.IgnoreCase)] public string Property { get; set; } = string.Empty; [Argument(1, @"", "The value you want to change it to, must match the property type.", ArgumentFlags.IgnoreCase)] @@ -18,7 +19,7 @@ namespace SCHALE.GameServer.Commands public override void Execute() { - PropertyInfo? targetProperty = typeof(AccountDB).GetProperty(Property); + PropertyInfo? targetProperty = typeof(AccountDB).GetProperty(Property) ?? typeof(AccountDB).GetProperty(Property.Capitalize()); if (targetProperty != null) { @@ -31,17 +32,20 @@ namespace SCHALE.GameServer.Commands object targetValue = converter.ConvertFromString(Value); targetProperty.SetValue(connection.Account, targetValue); + connection.Context.SaveChanges(); + + connection.SendChatMessage($"Set Player with UID {connection.AccountServerId}'s {Property} to {Value}"); } catch (Exception) { throw new ArgumentException("Invalid Value"); } } - } else + } + + else { throw new ArgumentException("Invalid Player Property!"); } - - } } } diff --git a/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Account.cs b/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Account.cs index 352edb2..c0a2da2 100644 --- a/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Account.cs +++ b/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Account.cs @@ -320,6 +320,11 @@ namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers EquipmentDBs = [.. account.Equipment] }, + CharacterGearListResponse = new CharacterGearListResponse() + { + GearDBs = [.. account.Gears] + }, + ClanLoginResponse = new ClanLoginResponse() { AccountClanMemberDB = new() @@ -328,6 +333,16 @@ namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers } }, + EliminateRaidLoginResponse = new EliminateRaidLoginResponse() + { + SeasonType = RaidSeasonType.Open, + SweepPointByRaidUniqueId = new() + { + { 2041104, int.MaxValue }, + { 2041204, int.MaxValue } + } + }, + FriendCode = "SCHALE", }; } @@ -445,13 +460,6 @@ namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers { return new MiniGameMissionListResponse(); } - - // these will probably be commands - private void SetRaidSeason(AccountDB account, long seasonId) - { - account.RaidSeasonId = seasonId; - context.SaveChanges(); - } } } diff --git a/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/CharacterGear.cs b/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/CharacterGear.cs new file mode 100644 index 0000000..4ceef8e --- /dev/null +++ b/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/CharacterGear.cs @@ -0,0 +1,66 @@ +using SCHALE.Common.Database; +using SCHALE.Common.Database.ModelExtensions; +using SCHALE.Common.FlatData; +using SCHALE.Common.NetworkProtocol; +using SCHALE.GameServer.Services; + +namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers +{ + public class CharacterGear : ProtocolHandlerBase + { + private readonly ISessionKeyService sessionKeyService; + private readonly SCHALEContext context; + private readonly ExcelTableService excelTableService; + + public CharacterGear(IProtocolHandlerFactory protocolHandlerFactory, ISessionKeyService _sessionKeyService, SCHALEContext _context, ExcelTableService _excelTableService) : base(protocolHandlerFactory) + { + sessionKeyService = _sessionKeyService; + context = _context; + excelTableService = _excelTableService; + } + + [ProtocolHandler(Protocol.CharacterGear_Unlock)] + public ResponsePacket UnlockHandler(CharacterGearUnlockRequest req) + { + var account = sessionKeyService.GetAccount(req.SessionKey); + + var gearExcelTable = excelTableService.GetTable().UnPack().DataList; + var targetCharacter = account.Characters.FirstOrDefault(x => x.ServerId == req.CharacterServerId); + + var gearId = gearExcelTable.FirstOrDefault(x => x.CharacterId == targetCharacter.UniqueId).Id; + + var newGear = new GearDB() + { + UniqueId = gearId, + Level = 1, + SlotIndex = req.SlotIndex, + BoundCharacterServerId = req.CharacterServerId, + Tier = 1, + Exp = 0, + }; + + account.AddGears(context, [newGear]); + context.SaveChanges(); + + return new CharacterGearUnlockResponse() + { + GearDB = newGear, + CharacterDB = targetCharacter, + }; + } + + [ProtocolHandler(Protocol.CharacterGear_TierUp)] + public ResponsePacket TierUpHandler(CharacterGearTierUpRequest req) + { // doesnt work + var targetGear = context.Gears.FirstOrDefault(x => x.ServerId == req.GearServerId); + + targetGear.Tier++; + context.SaveChanges(); + + return new CharacterGearTierUpResponse() + { + GearDB = targetGear, + }; + } + } +} diff --git a/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Clan.cs b/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Clan.cs index 784220b..8a9052f 100644 --- a/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Clan.cs +++ b/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Clan.cs @@ -1,6 +1,7 @@ using SCHALE.Common.Database; using SCHALE.Common.NetworkProtocol; using SCHALE.GameServer.Services; +using SCHALE.GameServer.Utils; namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers { @@ -26,8 +27,8 @@ namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers { IrcConfig = new() { - HostAddress = REPLACE WITH YOUR IP, - Port = 6667, + HostAddress = Config.Instance.Address, + Port = Config.Instance.Port, Password = "" }, AccountClanDB = new() diff --git a/SCHALE.GameServer/GameServer.cs b/SCHALE.GameServer/GameServer.cs index cf62032..258a73c 100644 --- a/SCHALE.GameServer/GameServer.cs +++ b/SCHALE.GameServer/GameServer.cs @@ -8,6 +8,8 @@ using SCHALE.GameServer.Services; using Microsoft.EntityFrameworkCore; using SCHALE.GameServer.Services.Irc; using SCHALE.GameServer.Commands; +using SCHALE.GameServer.Utils; +using System.Net.NetworkInformation; namespace SCHALE.GameServer { @@ -47,6 +49,15 @@ namespace SCHALE.GameServer // Load Commands CommandFactory.LoadCommands(); + // Load Config + Config.Load(); + + if (Config.Instance.Address == "127.0.0.1") + { + Config.Instance.Address = NetworkInterface.GetAllNetworkInterfaces().Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback && i.OperationalStatus == OperationalStatus.Up).First().GetIPProperties().UnicastAddresses.Where(a => a.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).First().Address.ToString(); + Config.Save(); + } + var builder = WebApplication.CreateBuilder(args); builder.Services.Configure(op => op.AllowSynchronousIO = true); diff --git a/SCHALE.GameServer/Services/Irc/IrcConnection.cs b/SCHALE.GameServer/Services/Irc/IrcConnection.cs index b64768e..85fa509 100644 --- a/SCHALE.GameServer/Services/Irc/IrcConnection.cs +++ b/SCHALE.GameServer/Services/Irc/IrcConnection.cs @@ -22,12 +22,12 @@ namespace SCHALE.GameServer.Services.Irc public void SendChatMessage(string text) { - SendChatMessage(text, "Shiroko", 10010, 0, IrcMessageType.Chat); + SendChatMessage(text, "Arona", 19009113, 0, IrcMessageType.Chat); } public void SendEmote(long stickerId) { - SendChatMessage("", "Shiroko", 10010, stickerId, IrcMessageType.Sticker); + SendChatMessage("", "Arona", 19009113, stickerId, IrcMessageType.Sticker); } public void SendChatMessage(string text, string nickname, long pfpCharacterId, long stickerId, IrcMessageType messageType) diff --git a/SCHALE.GameServer/Services/Irc/IrcServer.cs b/SCHALE.GameServer/Services/Irc/IrcServer.cs index 1a88ec4..0611a7d 100644 --- a/SCHALE.GameServer/Services/Irc/IrcServer.cs +++ b/SCHALE.GameServer/Services/Irc/IrcServer.cs @@ -163,6 +163,7 @@ namespace SCHALE.GameServer.Services.Irc { Command? cmd = CommandFactory.CreateCommand(cmdStr, connection, cmdStrings[1..]); + if (cmd is null) { connection.SendChatMessage($"Invalid command {cmdStr}, try /help"); @@ -174,7 +175,10 @@ namespace SCHALE.GameServer.Services.Irc } catch (Exception ex) { + var cmdAtr = (CommandHandlerAttribute?)Attribute.GetCustomAttribute(CommandFactory.commands[cmdStr], typeof(CommandHandlerAttribute)); + connection.SendChatMessage($"Command {cmdStr} failed to execute!, " + ex.Message); + connection.SendChatMessage($"Usage: {cmdAtr.Usage}"); } } } diff --git a/SCHALE.GameServer/Utils/Config.cs b/SCHALE.GameServer/Utils/Config.cs new file mode 100644 index 0000000..0f7fa2b --- /dev/null +++ b/SCHALE.GameServer/Utils/Config.cs @@ -0,0 +1,32 @@ +using Serilog; +using System.Text.Json; + +namespace SCHALE.GameServer.Utils +{ + public class Config : Singleton + { + public static string ConfigPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"); + + public string Address { get; set; } = "127.0.0.1"; + public int Port { get; set; } = 6667; + + public static void Load() + { + if (!File.Exists(ConfigPath)) + Save(); + + string json = File.ReadAllText(ConfigPath); + + Instance = JsonSerializer.Deserialize(json); + + Log.Debug($"Config loaded"); + } + + public static void Save() + { + File.WriteAllText(ConfigPath, JsonSerializer.Serialize(Instance)); + + Log.Debug($"Config saved"); + } + } +} diff --git a/SCHALE.GameServer/Utils/InventoryUtils.cs b/SCHALE.GameServer/Utils/InventoryUtils.cs index dfb3416..bdd7311 100644 --- a/SCHALE.GameServer/Utils/InventoryUtils.cs +++ b/SCHALE.GameServer/Utils/InventoryUtils.cs @@ -1,6 +1,7 @@ using SCHALE.Common.Database; using SCHALE.Common.Database.ModelExtensions; using SCHALE.Common.FlatData; +using SCHALE.Common.Migrations; using SCHALE.GameServer.Services; using SCHALE.GameServer.Services.Irc; using System; @@ -23,6 +24,8 @@ namespace SCHALE.Common.Utils account.AddCharacters(context, [.. allCharacters]); context.SaveChanges(); + + connection.SendChatMessage("Added all characters!"); } public static void AddAllEquipment(IrcConnection connection) @@ -40,6 +43,8 @@ namespace SCHALE.Common.Utils connection.Account.AddEquipment(connection.Context, [.. allEquipment]); connection.Context.SaveChanges(); + + connection.SendChatMessage("Added all equipment!"); } public static void AddAllItems(IrcConnection connection) @@ -57,6 +62,8 @@ namespace SCHALE.Common.Utils connection.Account.AddItems(connection.Context, [.. allItems]); connection.Context.SaveChanges(); + + connection.SendChatMessage("Added all items!"); } public static void AddAllWeapons(IrcConnection connection) @@ -79,8 +86,34 @@ namespace SCHALE.Common.Utils account.AddWeapons(context, [.. allWeapons]); context.SaveChanges(); + + connection.SendChatMessage("Added all weapons!"); } + public static void AddAllGears(IrcConnection connection) + { + var account = connection.Account; + var context = connection.Context; + + var gearExcel = connection.ExcelTableService.GetTable().UnPack().DataList; + + var allGears = gearExcel.Where(x => x.Tier == 2 && context.Characters.Any(y => y.UniqueId == x.CharacterId)).Select(x => new GearDB() + { + UniqueId = x.Id, + Level = 1, + SlotIndex = 4, + BoundCharacterServerId = context.Characters.FirstOrDefault(z => z.UniqueId == x.CharacterId).ServerId, + Tier = 2, + Exp = 0, + }); + + account.AddGears(context, [.. allGears]); + context.SaveChanges(); + + connection.SendChatMessage("Added all gears!"); + } + + public static void RemoveAllCharacters(IrcConnection connection) // removing default characters breaks game { var characterDB = connection.Context.Characters; @@ -89,6 +122,8 @@ namespace SCHALE.Common.Utils var removed = characterDB.Where(x => x.AccountServerId == connection.AccountServerId && !defaultCharacters.Contains(x.UniqueId)); characterDB.RemoveRange(removed); + + connection.SendChatMessage("Removed all characters!"); } public static CharacterDB CreateMaxCharacterFromId(long characterId) diff --git a/SCHALE.GameServer/Utils/Singleton.cs b/SCHALE.GameServer/Utils/Singleton.cs new file mode 100644 index 0000000..b7ceef8 --- /dev/null +++ b/SCHALE.GameServer/Utils/Singleton.cs @@ -0,0 +1,18 @@ +namespace SCHALE.GameServer.Utils +{ + public abstract class Singleton where T : new() + { + static T instance; + public static T Instance + { + get + { + if (instance == null) + instance = new T(); + + return instance; + } + set => instance = value; + } + } +} diff --git a/SCHALE.GameServer/Utils/StringExtension.cs b/SCHALE.GameServer/Utils/StringExtension.cs new file mode 100644 index 0000000..fddf84d --- /dev/null +++ b/SCHALE.GameServer/Utils/StringExtension.cs @@ -0,0 +1,10 @@ +namespace SCHALE.GameServer.Utils +{ + public static class StringExtension + { + public static string Capitalize(this string str) + { + return char.ToUpperInvariant(str[0]) + str.Substring(1); + } + } +}