diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index 6d88186b23..44382aee7e 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -23,6 +23,7 @@ using Content.Client.Viewport; using Content.Client.Voting; using Content.Client.White.JoinQueue; using Content.Client.White.Jukebox; +using Content.Client.White.Reputation; using Content.Client.White.Sponsors; using Content.Shared.Ame; using Content.Client.White.Stalin; @@ -82,6 +83,7 @@ namespace Content.Client.Entry [Dependency] private readonly StalinManager _stalinManager = default!; [Dependency] private readonly ClientJukeboxSongsSyncManager _jukeboxSyncManager = default!; [Dependency] private readonly TTSManager _ttsManager = default!; + [Dependency] private readonly ReputationManager _reputationManager = default!; //WD-EDIT public override void Init() @@ -189,6 +191,7 @@ namespace Content.Client.Entry _queueManager.Initialize(); _jukeboxSyncManager.Initialize(); _ttsManager.Initialize(); + _reputationManager.Initialize(); //WD-EDIT _baseClient.RunLevelChanged += (_, args) => diff --git a/Content.Client/IoC/ClientContentIoC.cs b/Content.Client/IoC/ClientContentIoC.cs index 687e856481..a01cd183b3 100644 --- a/Content.Client/IoC/ClientContentIoC.cs +++ b/Content.Client/IoC/ClientContentIoC.cs @@ -19,6 +19,7 @@ using Content.Client.Guidebook; using Content.Client.Replay; using Content.Client.White.JoinQueue; using Content.Client.White.Jukebox; +using Content.Client.White.Reputation; using Content.Client.White.Sponsors; using Content.Client.White.Stalin; using Content.Client.White.Trail.Line.Manager; @@ -59,6 +60,7 @@ namespace Content.Client.IoC IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); //WD-EDIT } } diff --git a/Content.Client/RoundEnd/RoundEndSummaryWindow.cs b/Content.Client/RoundEnd/RoundEndSummaryWindow.cs index bb75d75d75..792c0792b8 100644 --- a/Content.Client/RoundEnd/RoundEndSummaryWindow.cs +++ b/Content.Client/RoundEnd/RoundEndSummaryWindow.cs @@ -20,7 +20,7 @@ namespace Content.Client.RoundEnd { _entityManager = entityManager; - MinSize = SetSize = new Vector2(520, 580); + MinSize = SetSize = new Vector2(800, 580); Title = Loc.GetString("round-end-summary-window-title"); @@ -155,7 +155,8 @@ namespace Content.Client.RoundEnd ("playerOOCName", playerInfo.PlayerOOCName), ("icNameColor", icNameColor), ("playerICName", playerInfo.PlayerICName), - ("playerRole", Loc.GetString(playerInfo.Role)))); + ("playerRole", Loc.GetString(playerInfo.Role)), + ("reputation", playerInfo.Reputation))); } } hBox.AddChild(playerInfoText); diff --git a/Content.Client/White/Reputation/ReputationManager.cs b/Content.Client/White/Reputation/ReputationManager.cs new file mode 100644 index 0000000000..7a39791ac4 --- /dev/null +++ b/Content.Client/White/Reputation/ReputationManager.cs @@ -0,0 +1,23 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.White.Reputation; +using Robust.Shared.Network; + +namespace Content.Client.White.Reputation; + +public sealed class ReputationManager +{ + [Dependency] private readonly IClientNetManager _netMgr = default!; + + private ReputationInfo? _info; + + public void Initialize() + { + _netMgr.RegisterNetMessage(msg => _info = msg.Info); + } + + public bool TryGetInfo([NotNullWhen(true)] out float? value) + { + value = _info?.Value; + return _info != null; + } +} diff --git a/Content.Server.Database/Migrations/Postgres/20231006121631_PlayerReputation.Designer.cs b/Content.Server.Database/Migrations/Postgres/20231006121631_PlayerReputation.Designer.cs new file mode 100644 index 0000000000..75c78a307c --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20231006121631_PlayerReputation.Designer.cs @@ -0,0 +1,1424 @@ +// +using System; +using System.Net; +using System.Text.Json; +using Content.Server.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Content.Server.Database.Migrations.Postgres +{ + [DbContext(typeof(PostgresServerDbContext))] + [Migration("20231006121631_PlayerReputation")] + partial class PlayerReputation + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("AdminRankId") + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + b.Property("AdminServer") + .HasColumnType("text") + .HasColumnName("admin_server"); + + b.Property("Title") + .HasColumnType("text") + .HasColumnName("title"); + + b.HasKey("UserId") + .HasName("PK_admin"); + + b.HasIndex("AdminRankId") + .HasDatabaseName("IX_admin_admin_rank_id"); + + b.ToTable("admin", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_flag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminId") + .HasColumnType("uuid") + .HasColumnName("admin_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flag"); + + b.Property("Negative") + .HasColumnType("boolean") + .HasColumnName("negative"); + + b.HasKey("Id") + .HasName("PK_admin_flag"); + + b.HasIndex("AdminId") + .HasDatabaseName("IX_admin_flag_admin_id"); + + b.HasIndex("Flag", "AdminId") + .IsUnique(); + + b.ToTable("admin_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Date") + .HasColumnType("timestamp with time zone") + .HasColumnName("date"); + + b.Property("Impact") + .HasColumnType("smallint") + .HasColumnName("impact"); + + b.Property("Json") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("json"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text") + .HasColumnName("message"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.HasKey("Id", "RoundId") + .HasName("PK_admin_log"); + + b.HasIndex("Date"); + + b.HasIndex("Message") + .HasAnnotation("Npgsql:TsVectorConfig", "english"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Message"), "GIN"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_log_round_id"); + + b.HasIndex("Type") + .HasDatabaseName("IX_admin_log_type"); + + b.ToTable("admin_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogEntity", b => + { + b.Property("Uid") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("uid"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Uid")); + + b.Property("AdminLogId") + .HasColumnType("integer") + .HasColumnName("admin_log_id"); + + b.Property("AdminLogRoundId") + .HasColumnType("integer") + .HasColumnName("admin_log_round_id"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Uid") + .HasName("PK_admin_log_entity"); + + b.HasIndex("AdminLogId", "AdminLogRoundId") + .HasDatabaseName("IX_admin_log_entity_admin_log_id_admin_log_round_id"); + + b.ToTable("admin_log_entity", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("LogId") + .HasColumnType("integer") + .HasColumnName("log_id"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.HasKey("PlayerUserId", "LogId", "RoundId") + .HasName("PK_admin_log_player"); + + b.HasIndex("LogId", "RoundId"); + + b.ToTable("admin_log_player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_notes_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("ShownToPlayer") + .HasColumnType("boolean") + .HasColumnName("shown_to_player"); + + b.HasKey("Id") + .HasName("PK_admin_notes"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_notes_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_notes_round_id"); + + b.ToTable("admin_notes", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_admin_rank"); + + b.ToTable("admin_rank", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_rank_flag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminRankId") + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flag"); + + b.HasKey("Id") + .HasName("PK_admin_rank_flag"); + + b.HasIndex("AdminRankId"); + + b.HasIndex("Flag", "AdminRankId") + .IsUnique(); + + b.ToTable("admin_rank_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("antag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AntagName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("antag_name"); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_antag"); + + b.HasIndex("ProfileId", "AntagName") + .IsUnique(); + + b.ToTable("antag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AssignedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("assigned_user_id_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_assigned_user_id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("assigned_user_id", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("connection_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .IsRequired() + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("Denied") + .HasColumnType("smallint") + .HasColumnName("denied"); + + b.Property("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b.Property("Time") + .HasColumnType("timestamp with time zone") + .HasColumnName("time"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_connection_log"); + + b.HasIndex("UserId"); + + b.ToTable("connection_log", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("job_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("JobName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("job_name"); + + b.Property("Priority") + .HasColumnType("integer") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_job"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "JobName") + .IsUnique(); + + b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority") + .IsUnique() + .HasFilter("priority = 3"); + + b.ToTable("job", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.PlayTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("play_time_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("PlayerId") + .HasColumnType("uuid") + .HasColumnName("player_id"); + + b.Property("TimeSpent") + .HasColumnType("interval") + .HasColumnName("time_spent"); + + b.Property("Tracker") + .IsRequired() + .HasColumnType("text") + .HasColumnName("tracker"); + + b.HasKey("Id") + .HasName("PK_play_time"); + + b.HasIndex("PlayerId", "Tracker") + .IsUnique(); + + b.ToTable("play_time", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("player_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FirstSeenTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("first_seen_time"); + + b.Property("LastReadRules") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_read_rules"); + + b.Property("LastSeenAddress") + .IsRequired() + .HasColumnType("inet") + .HasColumnName("last_seen_address"); + + b.Property("LastSeenHWId") + .HasColumnType("bytea") + .HasColumnName("last_seen_hwid"); + + b.Property("LastSeenTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_seen_time"); + + b.Property("LastSeenUserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("last_seen_user_name"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player"); + + b.HasAlternateKey("UserId") + .HasName("ak_player_user_id"); + + b.HasIndex("LastSeenUserName"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("player", null, t => + { + t.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.PlayerReputation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("player_reputations_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Reputation") + .HasColumnType("real") + .HasColumnName("reputation"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player_reputations"); + + b.ToTable("player_reputations", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("preference_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminOOCColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("admin_ooc_color"); + + b.Property("SelectedCharacterSlot") + .HasColumnType("integer") + .HasColumnName("selected_character_slot"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_preference"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("preference", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Age") + .HasColumnType("integer") + .HasColumnName("age"); + + b.Property("Backpack") + .IsRequired() + .HasColumnType("text") + .HasColumnName("backpack"); + + b.Property("BodyType") + .IsRequired() + .HasColumnType("text") + .HasColumnName("body_type"); + + b.Property("BorgName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("borg_name"); + + b.Property("CharacterName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("char_name"); + + b.Property("Clothing") + .IsRequired() + .HasColumnType("text") + .HasColumnName("clothing"); + + b.Property("ClownName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("clown_name"); + + b.Property("EyeColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eye_color"); + + b.Property("FacialHairColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("facial_hair_color"); + + b.Property("FacialHairName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("facial_hair_name"); + + b.Property("FlavorText") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flavor_text"); + + b.Property("Gender") + .IsRequired() + .HasColumnType("text") + .HasColumnName("gender"); + + b.Property("HairColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("hair_color"); + + b.Property("HairName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("hair_name"); + + b.Property("Markings") + .HasColumnType("jsonb") + .HasColumnName("markings"); + + b.Property("MimeName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("mime_name"); + + b.Property("PreferenceId") + .HasColumnType("integer") + .HasColumnName("preference_id"); + + b.Property("PreferenceUnavailable") + .HasColumnType("integer") + .HasColumnName("pref_unavailable"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("text") + .HasColumnName("sex"); + + b.Property("SkinColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("skin_color"); + + b.Property("Slot") + .HasColumnType("integer") + .HasColumnName("slot"); + + b.Property("Species") + .IsRequired() + .HasColumnType("text") + .HasColumnName("species"); + + b.Property("Voice") + .IsRequired() + .HasColumnType("text") + .HasColumnName("voice"); + + b.HasKey("Id") + .HasName("PK_profile"); + + b.HasIndex("PreferenceId") + .HasDatabaseName("IX_profile_preference_id"); + + b.HasIndex("Slot", "PreferenceId") + .IsUnique(); + + b.ToTable("profile", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("round_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ServerId") + .HasColumnType("integer") + .HasColumnName("server_id"); + + b.HasKey("Id") + .HasName("PK_round"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_round_server_id"); + + b.ToTable("round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_server"); + + b.ToTable("server", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_ban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property?>("Address") + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("AutoDelete") + .HasColumnType("boolean") + .HasColumnName("auto_delete"); + + b.Property("BanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("uuid") + .HasColumnName("banning_admin"); + + b.Property("ExemptFlags") + .HasColumnType("integer") + .HasColumnName("exempt_flags"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("ServerName") + .HasColumnType("text") + .HasColumnName("server_name"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_server_ban"); + + b.HasIndex("Address"); + + b.HasIndex("UserId"); + + b.ToTable("server_ban", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("Flags") + .HasColumnType("integer") + .HasColumnName("flags"); + + b.HasKey("UserId") + .HasName("PK_server_ban_exemption"); + + b.ToTable("server_ban_exemption", null, t => + { + t.HasCheckConstraint("FlagsNotZero", "flags != 0"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_ban_hit_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("ConnectionId") + .HasColumnType("integer") + .HasColumnName("connection_id"); + + b.HasKey("Id") + .HasName("PK_server_ban_hit"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_server_ban_hit_ban_id"); + + b.HasIndex("ConnectionId") + .HasDatabaseName("IX_server_ban_hit_connection_id"); + + b.ToTable("server_ban_hit", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_role_ban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property?>("Address") + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("BanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("uuid") + .HasColumnName("banning_admin"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("role_id"); + + b.Property("ServerName") + .HasColumnType("text") + .HasColumnName("server_name"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_server_role_ban"); + + b.HasIndex("Address"); + + b.HasIndex("UserId"); + + b.ToTable("server_role_ban", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("role_unban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("uuid") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_role_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_role_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("unban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("uuid") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("trait_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.Property("TraitName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("trait_name"); + + b.HasKey("Id") + .HasName("PK_trait"); + + b.HasIndex("ProfileId", "TraitName") + .IsUnique(); + + b.ToTable("trait", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("uploaded_resource_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Data") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("data"); + + b.Property("Date") + .HasColumnType("timestamp with time zone") + .HasColumnName("date"); + + b.Property("Path") + .IsRequired() + .HasColumnType("text") + .HasColumnName("path"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_uploaded_resource_log"); + + b.ToTable("uploaded_resource_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Whitelist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_whitelist"); + + b.ToTable("whitelist", (string)null); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.Property("PlayersId") + .HasColumnType("integer") + .HasColumnName("players_id"); + + b.Property("RoundsId") + .HasColumnType("integer") + .HasColumnName("rounds_id"); + + b.HasKey("PlayersId", "RoundsId") + .HasName("PK_player_round"); + + b.HasIndex("RoundsId") + .HasDatabaseName("IX_player_round_rounds_id"); + + b.ToTable("player_round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.HasOne("Content.Server.Database.AdminRank", "AdminRank") + .WithMany("Admins") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_admin_rank_admin_rank_id"); + + b.Navigation("AdminRank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.HasOne("Content.Server.Database.Admin", "Admin") + .WithMany("Flags") + .HasForeignKey("AdminId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_flag_admin_admin_id"); + + b.Navigation("Admin"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("AdminLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_round_round_id"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogEntity", b => + { + b.HasOne("Content.Server.Database.AdminLog", null) + .WithMany("Entities") + .HasForeignKey("AdminLogId", "AdminLogRoundId") + .HasConstraintName("FK_admin_log_entity_admin_log_admin_log_id_admin_log_round_id"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminLogs") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_player_player_user_id"); + + b.HasOne("Content.Server.Database.AdminLog", "Log") + .WithMany("Players") + .HasForeignKey("LogId", "RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_admin_log_log_id_round_id"); + + b.Navigation("Log"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminNotesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_notes_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminNotesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .HasConstraintName("FK_admin_notes_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminNotesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_notes_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminNotesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_notes_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_notes_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.HasOne("Content.Server.Database.AdminRank", "Rank") + .WithMany("Flags") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id"); + + b.Navigation("Rank"); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Antags") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_antag_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Jobs") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_job_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.HasOne("Content.Server.Database.Preference", "Preference") + .WithMany("Profiles") + .HasForeignKey("PreferenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_preference_preference_id"); + + b.Navigation("Preference"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("Rounds") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_round_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithMany("BanHits") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_server_ban_ban_id"); + + b.HasOne("Content.Server.Database.ConnectionLog", "Connection") + .WithMany("BanHits") + .HasForeignKey("ConnectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_connection_log_connection_id"); + + b.Navigation("Ban"); + + b.Navigation("Connection"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.HasOne("Content.Server.Database.ServerRoleBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerRoleUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_role_unban_server_role_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_unban_server_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Traits") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_trait_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.HasOne("Content.Server.Database.Player", null) + .WithMany() + .HasForeignKey("PlayersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_player_players_id"); + + b.HasOne("Content.Server.Database.Round", null) + .WithMany() + .HasForeignKey("RoundsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_round_rounds_id"); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Navigation("Entities"); + + b.Navigation("Players"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Navigation("Admins"); + + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Navigation("BanHits"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("AdminNotesCreated"); + + b.Navigation("AdminNotesDeleted"); + + b.Navigation("AdminNotesLastEdited"); + + b.Navigation("AdminNotesReceived"); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Navigation("Antags"); + + b.Navigation("Jobs"); + + b.Navigation("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Navigation("Rounds"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Navigation("BanHits"); + + b.Navigation("Unban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Navigation("Unban"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/20231006121631_PlayerReputation.cs b/Content.Server.Database/Migrations/Postgres/20231006121631_PlayerReputation.cs new file mode 100644 index 0000000000..4b86a96ea4 --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20231006121631_PlayerReputation.cs @@ -0,0 +1,37 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Content.Server.Database.Migrations.Postgres +{ + /// + public partial class PlayerReputation : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "player_reputations", + columns: table => new + { + player_reputations_id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + user_id = table.Column(type: "uuid", nullable: false), + reputation = table.Column(type: "real", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_player_reputations", x => x.player_reputations_id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "player_reputations"); + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs index 797486e78a..f06f19f3b8 100644 --- a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs +++ b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs @@ -689,6 +689,29 @@ namespace Content.Server.Database.Migrations.Postgres }); }); + modelBuilder.Entity("Content.Server.Database.PlayerReputation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("player_reputations_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Reputation") + .HasColumnType("real") + .HasColumnName("reputation"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player_reputations"); + + b.ToTable("player_reputations", (string)null); + }); + modelBuilder.Entity("Content.Server.Database.Preference", b => { b.Property("Id") diff --git a/Content.Server.Database/Migrations/Sqlite/20231006121608_PlayerReputation.Designer.cs b/Content.Server.Database/Migrations/Sqlite/20231006121608_PlayerReputation.Designer.cs new file mode 100644 index 0000000000..90a1effac4 --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20231006121608_PlayerReputation.Designer.cs @@ -0,0 +1,1354 @@ +// +using System; +using Content.Server.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Content.Server.Database.Migrations.Sqlite +{ + [DbContext(typeof(SqliteServerDbContext))] + [Migration("20231006121608_PlayerReputation")] + partial class PlayerReputation + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.4"); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("AdminRankId") + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("AdminServer") + .HasColumnType("TEXT") + .HasColumnName("admin_server"); + + b.Property("Title") + .HasColumnType("TEXT") + .HasColumnName("title"); + + b.HasKey("UserId") + .HasName("PK_admin"); + + b.HasIndex("AdminRankId") + .HasDatabaseName("IX_admin_admin_rank_id"); + + b.ToTable("admin", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_flag_id"); + + b.Property("AdminId") + .HasColumnType("TEXT") + .HasColumnName("admin_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flag"); + + b.Property("Negative") + .HasColumnType("INTEGER") + .HasColumnName("negative"); + + b.HasKey("Id") + .HasName("PK_admin_flag"); + + b.HasIndex("AdminId") + .HasDatabaseName("IX_admin_flag_admin_id"); + + b.HasIndex("Flag", "AdminId") + .IsUnique(); + + b.ToTable("admin_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_log_id"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("Impact") + .HasColumnType("INTEGER") + .HasColumnName("impact"); + + b.Property("Json") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("json"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("Type") + .HasColumnType("INTEGER") + .HasColumnName("type"); + + b.HasKey("Id", "RoundId") + .HasName("PK_admin_log"); + + b.HasIndex("Date"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_log_round_id"); + + b.HasIndex("Type") + .HasDatabaseName("IX_admin_log_type"); + + b.ToTable("admin_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogEntity", b => + { + b.Property("Uid") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("uid"); + + b.Property("AdminLogId") + .HasColumnType("INTEGER") + .HasColumnName("admin_log_id"); + + b.Property("AdminLogRoundId") + .HasColumnType("INTEGER") + .HasColumnName("admin_log_round_id"); + + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Uid") + .HasName("PK_admin_log_entity"); + + b.HasIndex("AdminLogId", "AdminLogRoundId") + .HasDatabaseName("IX_admin_log_entity_admin_log_id_admin_log_round_id"); + + b.ToTable("admin_log_entity", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("LogId") + .HasColumnType("INTEGER") + .HasColumnName("log_id"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.HasKey("PlayerUserId", "LogId", "RoundId") + .HasName("PK_admin_log_player"); + + b.HasIndex("LogId", "RoundId"); + + b.ToTable("admin_log_player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_notes_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("ShownToPlayer") + .HasColumnType("INTEGER") + .HasColumnName("shown_to_player"); + + b.HasKey("Id") + .HasName("PK_admin_notes"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_notes_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_notes_round_id"); + + b.ToTable("admin_notes", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_admin_rank"); + + b.ToTable("admin_rank", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_flag_id"); + + b.Property("AdminRankId") + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flag"); + + b.HasKey("Id") + .HasName("PK_admin_rank_flag"); + + b.HasIndex("AdminRankId"); + + b.HasIndex("Flag", "AdminRankId") + .IsUnique(); + + b.ToTable("admin_rank_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("antag_id"); + + b.Property("AntagName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("antag_name"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_antag"); + + b.HasIndex("ProfileId", "AntagName") + .IsUnique(); + + b.ToTable("antag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AssignedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("assigned_user_id_id"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_assigned_user_id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("assigned_user_id", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("connection_log_id"); + + b.Property("Address") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("Denied") + .HasColumnType("INTEGER") + .HasColumnName("denied"); + + b.Property("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b.Property("Time") + .HasColumnType("TEXT") + .HasColumnName("time"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_connection_log"); + + b.HasIndex("UserId"); + + b.ToTable("connection_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("job_id"); + + b.Property("JobName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("job_name"); + + b.Property("Priority") + .HasColumnType("INTEGER") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_job"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "JobName") + .IsUnique(); + + b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority") + .IsUnique() + .HasFilter("priority = 3"); + + b.ToTable("job", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.PlayTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("play_time_id"); + + b.Property("PlayerId") + .HasColumnType("TEXT") + .HasColumnName("player_id"); + + b.Property("TimeSpent") + .HasColumnType("TEXT") + .HasColumnName("time_spent"); + + b.Property("Tracker") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("tracker"); + + b.HasKey("Id") + .HasName("PK_play_time"); + + b.HasIndex("PlayerId", "Tracker") + .IsUnique(); + + b.ToTable("play_time", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("player_id"); + + b.Property("FirstSeenTime") + .HasColumnType("TEXT") + .HasColumnName("first_seen_time"); + + b.Property("LastReadRules") + .HasColumnType("TEXT") + .HasColumnName("last_read_rules"); + + b.Property("LastSeenAddress") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_seen_address"); + + b.Property("LastSeenHWId") + .HasColumnType("BLOB") + .HasColumnName("last_seen_hwid"); + + b.Property("LastSeenTime") + .HasColumnType("TEXT") + .HasColumnName("last_seen_time"); + + b.Property("LastSeenUserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_seen_user_name"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player"); + + b.HasAlternateKey("UserId") + .HasName("ak_player_user_id"); + + b.HasIndex("LastSeenUserName"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.PlayerReputation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("player_reputations_id"); + + b.Property("Reputation") + .HasColumnType("REAL") + .HasColumnName("reputation"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player_reputations"); + + b.ToTable("player_reputations", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("preference_id"); + + b.Property("AdminOOCColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("admin_ooc_color"); + + b.Property("SelectedCharacterSlot") + .HasColumnType("INTEGER") + .HasColumnName("selected_character_slot"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_preference"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("preference", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("Age") + .HasColumnType("INTEGER") + .HasColumnName("age"); + + b.Property("Backpack") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("backpack"); + + b.Property("BodyType") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("body_type"); + + b.Property("BorgName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("borg_name"); + + b.Property("CharacterName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("char_name"); + + b.Property("Clothing") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("clothing"); + + b.Property("ClownName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("clown_name"); + + b.Property("EyeColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("eye_color"); + + b.Property("FacialHairColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("facial_hair_color"); + + b.Property("FacialHairName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("facial_hair_name"); + + b.Property("FlavorText") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flavor_text"); + + b.Property("Gender") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("gender"); + + b.Property("HairColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("hair_color"); + + b.Property("HairName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("hair_name"); + + b.Property("Markings") + .HasColumnType("jsonb") + .HasColumnName("markings"); + + b.Property("MimeName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("mime_name"); + + b.Property("PreferenceId") + .HasColumnType("INTEGER") + .HasColumnName("preference_id"); + + b.Property("PreferenceUnavailable") + .HasColumnType("INTEGER") + .HasColumnName("pref_unavailable"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("sex"); + + b.Property("SkinColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("skin_color"); + + b.Property("Slot") + .HasColumnType("INTEGER") + .HasColumnName("slot"); + + b.Property("Species") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("species"); + + b.Property("Voice") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("voice"); + + b.HasKey("Id") + .HasName("PK_profile"); + + b.HasIndex("PreferenceId") + .HasDatabaseName("IX_profile_preference_id"); + + b.HasIndex("Slot", "PreferenceId") + .IsUnique(); + + b.ToTable("profile", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("ServerId") + .HasColumnType("INTEGER") + .HasColumnName("server_id"); + + b.HasKey("Id") + .HasName("PK_round"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_round_server_id"); + + b.ToTable("round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_server"); + + b.ToTable("server", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_ban_id"); + + b.Property("Address") + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("AutoDelete") + .HasColumnType("INTEGER") + .HasColumnName("auto_delete"); + + b.Property("BanTime") + .HasColumnType("TEXT") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("banning_admin"); + + b.Property("ExemptFlags") + .HasColumnType("INTEGER") + .HasColumnName("exempt_flags"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("ServerName") + .HasColumnType("TEXT") + .HasColumnName("server_name"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_server_ban"); + + b.HasIndex("Address"); + + b.HasIndex("UserId"); + + b.ToTable("server_ban", null, t => + { + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("Flags") + .HasColumnType("INTEGER") + .HasColumnName("flags"); + + b.HasKey("UserId") + .HasName("PK_server_ban_exemption"); + + b.ToTable("server_ban_exemption", null, t => + { + t.HasCheckConstraint("FlagsNotZero", "flags != 0"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_ban_hit_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("ConnectionId") + .HasColumnType("INTEGER") + .HasColumnName("connection_id"); + + b.HasKey("Id") + .HasName("PK_server_ban_hit"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_server_ban_hit_ban_id"); + + b.HasIndex("ConnectionId") + .HasDatabaseName("IX_server_ban_hit_connection_id"); + + b.ToTable("server_ban_hit", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_role_ban_id"); + + b.Property("Address") + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("BanTime") + .HasColumnType("TEXT") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("banning_admin"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("role_id"); + + b.Property("ServerName") + .HasColumnType("TEXT") + .HasColumnName("server_name"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_server_role_ban"); + + b.HasIndex("Address"); + + b.HasIndex("UserId"); + + b.ToTable("server_role_ban", null, t => + { + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("role_unban_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("TEXT") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_role_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_role_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("unban_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("TEXT") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("trait_id"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("TraitName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("trait_name"); + + b.HasKey("Id") + .HasName("PK_trait"); + + b.HasIndex("ProfileId", "TraitName") + .IsUnique(); + + b.ToTable("trait", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("uploaded_resource_log_id"); + + b.Property("Data") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("data"); + + b.Property("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("path"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_uploaded_resource_log"); + + b.ToTable("uploaded_resource_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Whitelist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_whitelist"); + + b.ToTable("whitelist", (string)null); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.Property("PlayersId") + .HasColumnType("INTEGER") + .HasColumnName("players_id"); + + b.Property("RoundsId") + .HasColumnType("INTEGER") + .HasColumnName("rounds_id"); + + b.HasKey("PlayersId", "RoundsId") + .HasName("PK_player_round"); + + b.HasIndex("RoundsId") + .HasDatabaseName("IX_player_round_rounds_id"); + + b.ToTable("player_round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.HasOne("Content.Server.Database.AdminRank", "AdminRank") + .WithMany("Admins") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_admin_rank_admin_rank_id"); + + b.Navigation("AdminRank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.HasOne("Content.Server.Database.Admin", "Admin") + .WithMany("Flags") + .HasForeignKey("AdminId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_flag_admin_admin_id"); + + b.Navigation("Admin"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("AdminLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_round_round_id"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogEntity", b => + { + b.HasOne("Content.Server.Database.AdminLog", null) + .WithMany("Entities") + .HasForeignKey("AdminLogId", "AdminLogRoundId") + .HasConstraintName("FK_admin_log_entity_admin_log_admin_log_id_admin_log_round_id"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminLogs") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_player_player_user_id"); + + b.HasOne("Content.Server.Database.AdminLog", "Log") + .WithMany("Players") + .HasForeignKey("LogId", "RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_admin_log_log_id_round_id"); + + b.Navigation("Log"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminNotesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_notes_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminNotesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .HasConstraintName("FK_admin_notes_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminNotesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_notes_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminNotesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_notes_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_notes_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.HasOne("Content.Server.Database.AdminRank", "Rank") + .WithMany("Flags") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id"); + + b.Navigation("Rank"); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Antags") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_antag_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Jobs") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_job_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.HasOne("Content.Server.Database.Preference", "Preference") + .WithMany("Profiles") + .HasForeignKey("PreferenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_preference_preference_id"); + + b.Navigation("Preference"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("Rounds") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_round_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithMany("BanHits") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_server_ban_ban_id"); + + b.HasOne("Content.Server.Database.ConnectionLog", "Connection") + .WithMany("BanHits") + .HasForeignKey("ConnectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_connection_log_connection_id"); + + b.Navigation("Ban"); + + b.Navigation("Connection"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.HasOne("Content.Server.Database.ServerRoleBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerRoleUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_role_unban_server_role_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_unban_server_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Traits") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_trait_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.HasOne("Content.Server.Database.Player", null) + .WithMany() + .HasForeignKey("PlayersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_player_players_id"); + + b.HasOne("Content.Server.Database.Round", null) + .WithMany() + .HasForeignKey("RoundsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_round_rounds_id"); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Navigation("Entities"); + + b.Navigation("Players"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Navigation("Admins"); + + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Navigation("BanHits"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("AdminNotesCreated"); + + b.Navigation("AdminNotesDeleted"); + + b.Navigation("AdminNotesLastEdited"); + + b.Navigation("AdminNotesReceived"); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Navigation("Antags"); + + b.Navigation("Jobs"); + + b.Navigation("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Navigation("Rounds"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Navigation("BanHits"); + + b.Navigation("Unban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Navigation("Unban"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/20231006121608_PlayerReputation.cs b/Content.Server.Database/Migrations/Sqlite/20231006121608_PlayerReputation.cs new file mode 100644 index 0000000000..6383bdd7b8 --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20231006121608_PlayerReputation.cs @@ -0,0 +1,36 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Content.Server.Database.Migrations.Sqlite +{ + /// + public partial class PlayerReputation : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "player_reputations", + columns: table => new + { + player_reputations_id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + user_id = table.Column(type: "TEXT", nullable: false), + reputation = table.Column(type: "REAL", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_player_reputations", x => x.player_reputations_id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "player_reputations"); + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs index 688f9a3ace..ab7e807dc0 100644 --- a/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs +++ b/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs @@ -647,6 +647,27 @@ namespace Content.Server.Database.Migrations.Sqlite b.ToTable("player", (string)null); }); + modelBuilder.Entity("Content.Server.Database.PlayerReputation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("player_reputations_id"); + + b.Property("Reputation") + .HasColumnType("REAL") + .HasColumnName("reputation"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player_reputations"); + + b.ToTable("player_reputations", (string)null); + }); + modelBuilder.Entity("Content.Server.Database.Preference", b => { b.Property("Id") diff --git a/Content.Server.Database/Model.cs b/Content.Server.Database/Model.cs index afbee3e0e1..3fa21b2466 100644 --- a/Content.Server.Database/Model.cs +++ b/Content.Server.Database/Model.cs @@ -39,6 +39,7 @@ namespace Content.Server.Database public DbSet AdminNotes { get; set; } = null!; public DbSet AdminWatchlists { get; set; } = null!; public DbSet AdminMessages { get; set; } = null!; + public DbSet PlayerReputations { get; set; } = default!; // WD edit protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -301,6 +302,15 @@ namespace Content.Server.Database public abstract int CountAdminLogs(); } + // WD start + public class PlayerReputation + { + public int Id { get; set; } + public Guid UserId { get; set; } + public float Reputation { get; set; } + } + // WD end + public class Preference { // NOTE: on postgres there SHOULD be an FK ensuring that the selected character slot always exists. diff --git a/Content.Server/Administration/Commands/BanCommand.cs b/Content.Server/Administration/Commands/BanCommand.cs index 53fcd105ba..1837b0005d 100644 --- a/Content.Server/Administration/Commands/BanCommand.cs +++ b/Content.Server/Administration/Commands/BanCommand.cs @@ -20,6 +20,7 @@ public sealed class BanCommand : LocalizedCommands [Dependency] private readonly IBanManager _bans = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly UtkaTCPWrapper _utkaSockets = default!; // WD + [Dependency] private readonly IEntityManager _entMan = default!; // WD public override string Command => "ban"; @@ -128,6 +129,7 @@ public sealed class BanCommand : LocalizedCommands BanId = banId }; _utkaSockets.SendMessageToAll(utkaBanned); + _entMan.EventBus.RaiseEvent(EventSource.Local, utkaBanned); //WD end } diff --git a/Content.Server/Administration/Managers/RoleBanManager.cs b/Content.Server/Administration/Managers/RoleBanManager.cs index c4bb811608..190991f31d 100644 --- a/Content.Server/Administration/Managers/RoleBanManager.cs +++ b/Content.Server/Administration/Managers/RoleBanManager.cs @@ -31,6 +31,7 @@ public sealed class RoleBanManager [Dependency] private readonly UtkaTCPWrapper _utkaSockets = default!; // WD [Dependency] private readonly IEntitySystemManager _systems = default!; // WD [Dependency] private readonly IBanManager _banManager = default!; // WD + [Dependency] private readonly IEntityManager _entMan = default!; // WD private const string JobPrefix = "Job:"; @@ -425,6 +426,7 @@ public sealed class RoleBanManager }; _utkaSockets.SendMessageToAll(utkaBanned); + _entMan.EventBus.RaiseEvent(EventSource.Local, utkaBanned); } private async Task UtkaGetBanId(string reason, string role, NetUserId targetUid) diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index b58bddc4dc..2ad3cc1f8b 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -27,6 +27,7 @@ using Robust.Server.Containers; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Content.Server.Shuttles.Components; +using Content.Server.White.Reputation; using Content.Shared.Players; namespace Content.Server.Antag; @@ -47,6 +48,7 @@ public sealed class AntagSelectionSystem : GameRuleSystem [Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!; [Dependency] private readonly RoleSystem _roles = default!; // WD [Dependency] private readonly SharedPlayerSystem _sharedPlayerSystem = default!; // WD + [Dependency] private readonly ReputationManager _reputationManager = default!; // WD /// /// Attempts to start the game rule by checking if there are enough players in lobby and readied. @@ -217,7 +219,12 @@ public sealed class AntagSelectionSystem : GameRuleSystem for (var i = 0; i < antagCount; i++) { - results.Add(_random.PickAndTake(prefList)); + //results.Add(_random.PickAndTake(prefList)); + // WD EDIT START + var pref = _reputationManager.PickPlayerBasedOnReputation(prefList); + prefList.Remove(pref); + results.Add(pref); + // WD EDIT END Log.Info("Selected a preferred antag."); } return results; diff --git a/Content.Server/Chat/Managers/ChatManager.cs b/Content.Server/Chat/Managers/ChatManager.cs index 1ee36d8bc3..cc93bfd6d3 100644 --- a/Content.Server/Chat/Managers/ChatManager.cs +++ b/Content.Server/Chat/Managers/ChatManager.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.InteropServices; +using System.Threading.Tasks; using Content.Server.Administration.Logs; using Content.Server.Administration.Managers; using Content.Server.Administration.Systems; @@ -8,6 +9,7 @@ using Content.Server.MoMMI; using Content.Server.Players; using Content.Server.Preferences.Managers; using Content.Server.UtkaIntegration; +using Content.Server.White.Reputation; using Content.Server.White.Sponsors; using Content.Shared.Administration; using Content.Shared.CCVar; @@ -16,6 +18,7 @@ using Content.Shared.Database; using Content.Shared.Mind; using Content.Shared.White; using Robust.Server.Player; +using Robust.Shared.Asynchronous; using Robust.Shared.Configuration; using Robust.Shared.Network; using Robust.Shared.Player; @@ -54,6 +57,8 @@ namespace Content.Server.Chat.Managers [Dependency] private readonly SponsorsManager _sponsorsManager = default!; [Dependency] private readonly UtkaTCPWrapper _utkaSocketWrapper = default!; [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly ReputationManager _repManager = default!; + [Dependency] private readonly ITaskManager _taskManager = default!; /// WD-EDIT /// @@ -296,8 +301,22 @@ namespace Content.Server.Chat.Managers if (!TrySendNewMessage(player, message)) // WD return; + // WD start + //_repManager.GetCachedPlayerReputation(player.UserId, out var value); + var task = Task.Run(async () => await _repManager.GetPlayerReputation(player.UserId)); + _taskManager.BlockWaitOnTask(task); + var value = task.GetAwaiter().GetResult(); + + var reputation = ""; + if (value != null) + { + var color = value >= 0 ? "green" : "red"; + reputation = $"[color={color}]({value})[/color]"; + } + // WD end + Color? colorOverride = null; - var wrappedMessage = Loc.GetString("chat-manager-send-ooc-wrap-message", ("playerName",player.Name), ("message", FormattedMessage.EscapeText(message))); + var wrappedMessage = Loc.GetString("chat-manager-send-ooc-wrap-message", ("playerName",player.Name), ("message", FormattedMessage.EscapeText(message)), ("rep", reputation)); if (_adminManager.HasAdminFlag(player, AdminFlags.Admin)) { var prefs = _preferencesManager.GetPreferences(player.UserId); @@ -306,13 +325,13 @@ namespace Content.Server.Chat.Managers if (player.Channel.UserData.PatronTier is { } patron && PatronOocColors.TryGetValue(patron, out var patronColor)) { - wrappedMessage = Loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", patronColor),("playerName", player.Name), ("message", FormattedMessage.EscapeText(message))); + wrappedMessage = Loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", patronColor),("playerName", player.Name), ("message", FormattedMessage.EscapeText(message)), ("rep", reputation)); } //WD-EDIT if (_sponsorsManager.TryGetInfo(player.UserId, out var sponsorData) && sponsorData.OOCColor != null) { - wrappedMessage = Loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", sponsorData.OOCColor),("playerName", player.Name), ("message", FormattedMessage.EscapeText(message))); + wrappedMessage = Loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", sponsorData.OOCColor),("playerName", player.Name), ("message", FormattedMessage.EscapeText(message)), ("rep", reputation)); } //WD-EDIT diff --git a/Content.Server/Database/ServerDbBase.cs b/Content.Server/Database/ServerDbBase.cs index ef19928999..0b55b90ec1 100644 --- a/Content.Server/Database/ServerDbBase.cs +++ b/Content.Server/Database/ServerDbBase.cs @@ -1401,6 +1401,69 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id} #endregion + #region Player Reputation (WD edit) + + public async Task SetPlayerReputation(Guid player, float value) + { + await using var db = await GetDb(); + + var reputation = await db.DbContext.PlayerReputations + .SingleOrDefaultAsync(p => p.UserId == player); + + if (reputation == null) + { + reputation = new PlayerReputation() + { + UserId = player, + Reputation = value + }; + db.DbContext.PlayerReputations.Add(reputation); + } + else + { + reputation.Reputation = value; + } + + await db.DbContext.SaveChangesAsync(); + } + + public async Task ModifyPlayerReputation(Guid player, float value) + { + await using var db = await GetDb(); + + var reputation = await db.DbContext.PlayerReputations + .SingleOrDefaultAsync(p => p.UserId == player); + + if (reputation == null) + { + reputation = new PlayerReputation() + { + UserId = player, + Reputation = 0f + value + }; + db.DbContext.PlayerReputations.Add(reputation); + } + else + { + reputation.Reputation += value; + } + + await db.DbContext.SaveChangesAsync(); + } + + public async Task GetPlayerReputation(Guid player) + { + await using var db = await GetDb(); + + var reputation = await db.DbContext.PlayerReputations + .SingleOrDefaultAsync(p => p.UserId == player); + + return reputation?.Reputation ?? 0f; + } + + #endregion + + protected abstract Task GetDb([CallerMemberName] string? name = null); protected void LogDbOp(string? name) diff --git a/Content.Server/Database/ServerDbManager.cs b/Content.Server/Database/ServerDbManager.cs index 1c1aa4e07f..cc876e4e83 100644 --- a/Content.Server/Database/ServerDbManager.cs +++ b/Content.Server/Database/ServerDbManager.cs @@ -281,6 +281,31 @@ namespace Content.Server.Database Task MarkMessageAsSeen(int id); #endregion + + #region Player Reputation (WD edit) + + /// + /// Set player's reputation to the certain value. + /// + /// Guid of the player to set the value. + /// Value to set. + Task SetPlayerReputation(Guid player, float value); + + /// + /// Modify player's reputation by adding value (currentValue + value). + /// + /// Guid of the player to modify the value. + /// Value to add. + Task ModifyPlayerReputation(Guid player, float value); + + /// + /// Gets value of player reputation. + /// + /// Guid of the player to get the value. + /// Value of player's reputation. + Task GetPlayerReputation(Guid player); + + #endregion } public sealed class ServerDbManager : IServerDbManager @@ -502,6 +527,28 @@ namespace Content.Server.Database #endregion + #region Player Reputation (WD edit) + + public Task SetPlayerReputation(Guid player, float value) + { + DbWriteOpsMetric.Inc(); + return RunDbCommand(() => _db.SetPlayerReputation(player, value)); + } + + public Task ModifyPlayerReputation(Guid player, float value) + { + DbWriteOpsMetric.Inc(); + return RunDbCommand(() => _db.ModifyPlayerReputation(player, value)); + } + + public Task GetPlayerReputation(Guid player) + { + DbWriteOpsMetric.Inc(); + return RunDbCommand(() => _db.GetPlayerReputation(player)); + } + + #endregion + public Task UpdatePlayerRecordAsync( NetUserId userId, string userName, diff --git a/Content.Server/Entry/EntryPoint.cs b/Content.Server/Entry/EntryPoint.cs index 54d9b4f893..b0d61e6888 100644 --- a/Content.Server/Entry/EntryPoint.cs +++ b/Content.Server/Entry/EntryPoint.cs @@ -33,6 +33,7 @@ using Content.Server.UtkaIntegration; using Content.Server.White; using Content.Server.White.JoinQueue; using Content.Server.White.Jukebox; +using Content.Server.White.Reputation; using Content.Server.White.Sponsors; using Content.Server.White.Stalin; using Content.Server.White.TTS; diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index 5f862a9557..c4a5f17805 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -22,6 +22,7 @@ using Robust.Shared.Random; using Robust.Shared.Utility; using Content.Server.UtkaIntegration; using System.Threading.Tasks; +using Content.Server.White.Reputation; using Content.Server.White.Stalin; using Content.Shared.Database; using Content.Shared.White; @@ -37,6 +38,7 @@ namespace Content.Server.GameTicking //WD-EDIT [Dependency] private readonly UtkaTCPWrapper _utkaSocketWrapper = default!; [Dependency] private readonly StalinManager _stalinManager = default!; + [Dependency] private readonly ReputationSystem _repSys = default!; //WD-EDIT private static readonly Counter RoundNumberMetric = Metrics.CreateCounter( @@ -394,6 +396,17 @@ namespace Content.Server.GameTicking var roles = _roles.MindGetAllRoles(mindId); + // WD start + var reputation = ""; + if (mind.Session != null && + _repSys.TryModifyReputationOnRoundEnd(mind.Session.Name, out var value, out var delta)) + { + var color = value >= 0 ? "green" : "red"; + var change = delta >= 0 ? $"+{delta}" : $"{delta}"; + reputation = $"[color={color}]{value} ({change})"; + } + // WD end + var playerEndRoundInfo = new RoundEndMessageEvent.RoundEndPlayerInfo() { // Note that contentPlayerData?.Name sticks around after the player is disconnected. @@ -407,7 +420,8 @@ namespace Content.Server.GameTicking : roles.FirstOrDefault().Name ?? Loc.GetString("game-ticker-unknown-role"), Antag = antag, Observer = observer, - Connected = connected + Connected = connected, + Reputation = reputation }; listOfPlayerInfo.Add(playerEndRoundInfo); } diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 5bce7f20e4..2b35d0c210 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -29,6 +29,9 @@ using Content.Server.Station.Systems; using Content.Server.Store.Components; using Content.Server.Store.Systems; using Content.Shared.CCVar; +using Content.Server.Traitor; +using Content.Server.White.Administration; +using Content.Server.White.Reputation; using Content.Shared.Dataset; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Prototypes; @@ -86,6 +89,9 @@ public sealed class NukeopsRuleSystem : GameRuleSystem [Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly WarDeclaratorSystem _warDeclarator = default!; + //WD EDIT + [Dependency] private readonly ReputationManager _reputationManager = default!; + //WD EDIT [ValidatePrototypeId] @@ -686,7 +692,9 @@ public sealed class NukeopsRuleSystem : GameRuleSystem } else { - nukeOp = _random.PickAndTake(cmdrPrefList); + //nukeOp = _random.PickAndTake(cmdrPrefList); + nukeOp = _reputationManager.PickPlayerBasedOnReputation(cmdrPrefList); // WD edit + cmdrPrefList.Remove(nukeOp); // WD edit everyone.Remove(nukeOp); prefList.Remove(nukeOp); medPrefList.Remove(nukeOp); @@ -716,7 +724,9 @@ public sealed class NukeopsRuleSystem : GameRuleSystem } else { - nukeOp = _random.PickAndTake(medPrefList); + //nukeOp = _random.PickAndTake(medPrefList); + nukeOp = _reputationManager.PickPlayerBasedOnReputation(medPrefList); // WD edit + medPrefList.Remove(nukeOp); // WD edit everyone.Remove(nukeOp); prefList.Remove(nukeOp); Logger.InfoS("preset", "Insufficient preferred nukeop commanders, picking an agent"); @@ -725,7 +735,9 @@ public sealed class NukeopsRuleSystem : GameRuleSystem } else { - nukeOp = _random.PickAndTake(prefList); + //nukeOp = _random.PickAndTake(prefList); + nukeOp = _reputationManager.PickPlayerBasedOnReputation(prefList); // WD edit + prefList.Remove(nukeOp); // WD edit everyone.Remove(nukeOp); Logger.InfoS("preset", "Selected a preferred nukeop commander."); } diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index 34da3ed1d3..c74009a599 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -15,18 +15,15 @@ using Content.Shared.Mind; using Content.Shared.Mobs.Systems; using Content.Shared.Objectives.Components; using Content.Shared.PDA; -using Content.Shared.Preferences; using Content.Shared.Roles; using Content.Shared.Roles.Jobs; -using Robust.Server.Player; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; -using Content.Server.Objectives; +using Content.Server.White.Reputation; using Content.Shared.White.Mood; namespace Content.Server.GameTicking.Rules; @@ -49,6 +46,8 @@ public sealed class TraitorRuleSystem : GameRuleSystem [Dependency] private readonly ObjectivesSystem _objectives = default!; [Dependency] private readonly RoleSystem _roles = default!; // WD + private ISawmill _sawmill = default!; + private int PlayersPerTraitor => _cfg.GetCVar(CCVars.TraitorPlayersPerTraitor); private int MaxTraitors => _cfg.GetCVar(CCVars.TraitorMaxTraitors); diff --git a/Content.Server/IoC/ServerContentIoC.cs b/Content.Server/IoC/ServerContentIoC.cs index 821048bd22..67d67e61d5 100644 --- a/Content.Server/IoC/ServerContentIoC.cs +++ b/Content.Server/IoC/ServerContentIoC.cs @@ -23,6 +23,7 @@ using Content.Server.UtkaIntegration; using Content.Server.White; using Content.Server.White.JoinQueue; using Content.Server.White.Jukebox; +using Content.Server.White.Reputation; using Content.Server.White.Sponsors; using Content.Server.White.Stalin; using Content.Server.White.TTS; @@ -75,6 +76,7 @@ namespace Content.Server.IoC IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); // WD-EDIT } } diff --git a/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs b/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs index 78d1b1e212..3f5698ff5e 100644 --- a/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs +++ b/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs @@ -2,6 +2,7 @@ using System.Linq; using Content.Server.Administration.Managers; using Content.Server.Players.PlayTimeTracking; using Content.Server.Station.Components; +using Content.Server.White.Reputation; using Content.Shared.Preferences; using Content.Shared.Roles; using Robust.Shared.Network; @@ -17,6 +18,7 @@ public sealed partial class StationJobsSystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IBanManager _banManager = default!; [Dependency] private readonly PlayTimeTrackingSystem _playTime = default!; + [Dependency] private readonly ReputationManager _reputationManager = default!; // WD edit private Dictionary> _jobsByWeight = default!; private List _orderedWeights = default!; @@ -245,7 +247,8 @@ public sealed partial class StationJobsSystem continue; // Picking players it finds that have the job set. - var player = _random.Pick(jobPlayerOptions[job]); + //var player = _random.Pick(jobPlayerOptions[job]); + var player = GetPlayerToAssign(jobPlayerOptions[job]); // WD edit AssignPlayer(player, job, station); stationShares[station]--; @@ -374,4 +377,33 @@ public sealed partial class StationJobsSystem return outputDict; } + + // WD start + private NetUserId GetPlayerToAssign(HashSet players) + { + var list = new List(); + + foreach (var player in players) + { + if (!_reputationManager.GetCachedPlayerReputation(player, out var value)) + continue; + + if (value == null) + continue; + + var weight = _reputationManager.GetPlayerWeight(value.Value); + + for (var i = 0; i < weight; i++) + { + list.Add(player); + } + } + + if (list.Count == 0) + return _random.Pick(players); + + var number = _random.Next(list.Count - 1); + return list[number]; + } + // WD end } diff --git a/Content.Server/UtkaIntegration/Commands/UtkaBanCommand.cs b/Content.Server/UtkaIntegration/Commands/UtkaBanCommand.cs index c67b46ee6a..85478a0af4 100644 --- a/Content.Server/UtkaIntegration/Commands/UtkaBanCommand.cs +++ b/Content.Server/UtkaIntegration/Commands/UtkaBanCommand.cs @@ -16,6 +16,8 @@ public sealed class UtkaBanCommand : IUtkaCommand { [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private UtkaTCPWrapper _utkaSocketWrapper = default!; + [Dependency] private readonly IServerDbManager _db = default!; + [Dependency] private readonly IEntityManager _entMan = default!; private const ILocalizationManager LocalizationManager = default!; @@ -27,7 +29,6 @@ public sealed class UtkaBanCommand : IUtkaCommand var plyMgr = IoCManager.Resolve(); var locator = IoCManager.Resolve(); - var dbMan = IoCManager.Resolve(); IoCManager.InjectDependencies(this); var locatedPlayer = await locator.LookupIdByNameOrIdAsync(message.ACkey!); @@ -87,7 +88,7 @@ public sealed class UtkaBanCommand : IUtkaCommand IoCManager.Resolve().TryGetEntitySystem(out var ticker); int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId; - var playtime = (await dbMan.GetPlayTimes(targetUid)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall)?.TimeSpent ?? TimeSpan.Zero; + var playtime = (await _db.GetPlayTimes(targetUid)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall)?.TimeSpent ?? TimeSpan.Zero; var banDef = new ServerBanDef( null, @@ -106,7 +107,7 @@ public sealed class UtkaBanCommand : IUtkaCommand UtkaSendResponse(true); - await dbMan.AddServerBanAsync(banDef); + await _db.AddServerBanAsync(banDef); if (plyMgr.TryGetSessionById(targetUid, out var targetPlayer)) { @@ -114,7 +115,7 @@ public sealed class UtkaBanCommand : IUtkaCommand targetPlayer.ConnectedClient.Disconnect(msg); } - var banlist = await dbMan.GetServerBansAsync(null, targetUid, null); + var banlist = await _db.GetServerBansAsync(null, targetUid, null); var banId = banlist[^1].Id; var utkaBanned = new UtkaBannedEvent() @@ -129,6 +130,7 @@ public sealed class UtkaBanCommand : IUtkaCommand BanId = banId }; _utkaSocketWrapper.SendMessageToAll(utkaBanned); + _entMan.EventBus.RaiseEvent(EventSource.Local, utkaBanned); } private void UtkaSendResponse(bool banned) diff --git a/Content.Server/White/Reputation/Commands/GetPlayerReputationCommand.cs b/Content.Server/White/Reputation/Commands/GetPlayerReputationCommand.cs new file mode 100644 index 0000000000..171cfc6378 --- /dev/null +++ b/Content.Server/White/Reputation/Commands/GetPlayerReputationCommand.cs @@ -0,0 +1,43 @@ +using Content.Server.Administration; +using Content.Shared.Administration; +using Robust.Server.Player; +using Robust.Shared.Console; + +namespace Content.Server.White.Reputation.Commands; + +[AdminCommand(AdminFlags.Admin)] +public sealed class GetPlayerReputationCommand : IConsoleCommand +{ + public string Command => "getreput"; + public string Description => "Get player's reputation value."; + public string Help => "Usage: getreput {ckey}"; + public async void Execute(IConsoleShell shell, string argStr, string[] args) + { + var playerManager = IoCManager.Resolve(); + var repManager = IoCManager.Resolve(); + + if (args.Length < 1) + { + shell.WriteLine($"Not enough arguments.\n{Help}"); + return; + } + + if (!playerManager.TryGetPlayerDataByUsername(args[0], out var playerData)) + { + shell.WriteLine($"Couldn't find player: {args[0]}."); + return; + } + + var uid = playerData.UserId; + + var value = await repManager.GetPlayerReputation(uid); + + if (value == null) + { + shell.WriteLine("Couldn't get player's reputation."); + return; + } + + shell.WriteLine($"Reputation of {args[0]}: {value}"); + } +} diff --git a/Content.Server/White/Reputation/Commands/ModifyReputationCommand.cs b/Content.Server/White/Reputation/Commands/ModifyReputationCommand.cs new file mode 100644 index 0000000000..50dc2eb681 --- /dev/null +++ b/Content.Server/White/Reputation/Commands/ModifyReputationCommand.cs @@ -0,0 +1,43 @@ +using Content.Server.Administration; +using Content.Shared.Administration; +using Robust.Server.Player; +using Robust.Shared.Console; + +namespace Content.Server.White.Reputation.Commands; + +[AdminCommand(AdminFlags.Host)] +public sealed class ModifyReputationCommand : IConsoleCommand +{ + public string Command => "modifyreput"; + public string Description => "Add the value to user's reputation."; + public string Help => "Usage: modifyreput {ckey} {valueToAdd}"; + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + var playerManager = IoCManager.Resolve(); + var repManager = IoCManager.Resolve(); + + if (args.Length < 2) + { + shell.WriteLine($"Not enough arguments.\n{Help}"); + return; + } + + if (!playerManager.TryGetPlayerDataByUsername(args[0], out var playerData)) + { + shell.WriteLine($"Couldn't find player: {args[0]}."); + return; + } + + if (!float.TryParse(args[1], out var value)) + { + shell.WriteLine($"Invalid value: {args[1]}."); + return; + } + + var uid = playerData.UserId; + + repManager.ModifyPlayerReputation(uid, value); + + shell.WriteLine($"Added {args[1]} to the reputation of {args[0]}."); + } +} diff --git a/Content.Server/White/Reputation/Commands/SetReputationCommand.cs b/Content.Server/White/Reputation/Commands/SetReputationCommand.cs new file mode 100644 index 0000000000..a47c378c5a --- /dev/null +++ b/Content.Server/White/Reputation/Commands/SetReputationCommand.cs @@ -0,0 +1,43 @@ +using Content.Server.Administration; +using Content.Shared.Administration; +using Robust.Server.Player; +using Robust.Shared.Console; + +namespace Content.Server.White.Reputation.Commands; + +[AdminCommand(AdminFlags.Host)] +public sealed class SetReputationCommand : IConsoleCommand +{ + public string Command => "setreput"; + public string Description => "Sets the reputation to the certain value."; + public string Help => "Usage: setrep {ckey} {value}"; + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + var playerManager = IoCManager.Resolve(); + var repManager = IoCManager.Resolve(); + + if (args.Length < 2) + { + shell.WriteLine($"Not enough arguments.\n{Help}"); + return; + } + + if (!playerManager.TryGetPlayerDataByUsername(args[0], out var playerData)) + { + shell.WriteLine($"Couldn't find player: {args[0]}."); + return; + } + + if (!float.TryParse(args[1], out var value)) + { + shell.WriteLine($"Invalid value: {args[1]}."); + return; + } + + var uid = playerData.UserId; + + repManager.SetPlayerReputation(uid, value); + + shell.WriteLine($"Set reputation of {args[0]} to {args[1]}."); + } +} diff --git a/Content.Server/White/Reputation/Commands/ShowReputationCommand.cs b/Content.Server/White/Reputation/Commands/ShowReputationCommand.cs new file mode 100644 index 0000000000..5212b93f07 --- /dev/null +++ b/Content.Server/White/Reputation/Commands/ShowReputationCommand.cs @@ -0,0 +1,30 @@ +using Content.Shared.Administration; +using Robust.Shared.Console; + +namespace Content.Server.White.Reputation.Commands; + +[AnyCommand] +public sealed class ShowReputationCommand : IConsoleCommand +{ + [Dependency] private readonly ReputationManager _repManager = default!; + + public string Command => "showreput"; + public string Description => "Узнать свою репутацию."; + public string Help => "Использование: showreput"; + public async void Execute(IConsoleShell shell, string argStr, string[] args) + { + IoCManager.InjectDependencies(this); + + if (shell.Player == null) + return; + + var value = await _repManager.GetPlayerReputation(shell.Player.UserId); + if (value == null) + { + shell.WriteLine("Не удалось получить данные о репутации. Обратитесь к кодерам или попробуйте ещё раз."); + return; + } + + shell.WriteLine($"Ваша репутация: {value}"); + } +} diff --git a/Content.Server/White/Reputation/ReputationManager.cs b/Content.Server/White/Reputation/ReputationManager.cs new file mode 100644 index 0000000000..7bb947d612 --- /dev/null +++ b/Content.Server/White/Reputation/ReputationManager.cs @@ -0,0 +1,201 @@ +using System.Linq; +using System.Threading.Tasks; +using Content.Server.Database; +using Content.Shared.GameTicking; +using Content.Shared.White.Reputation; +using Robust.Server.Player; +using Robust.Shared.Network; +using Robust.Shared.Player; +using Robust.Shared.Random; + +namespace Content.Server.White.Reputation; + +public sealed class ReputationManager : EntitySystem +{ + [Dependency] private readonly IServerDbManager _db = default!; + [Dependency] private readonly IServerNetManager _netMgr = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + private readonly Dictionary _cacheReputation = new(); + + public override void Initialize() + { + base.Initialize(); + + _netMgr.RegisterNetMessage(); + + _netMgr.Connecting += OnConnecting; + _netMgr.Connected += OnConnected; + + SubscribeLocalEvent(OnRoundRestartCleanup); + SubscribeLocalEvent(UpdateCachedReputation); + } + + #region Cache + + private void OnConnected(object? sender, NetChannelArgs e) + { + _cacheReputation.TryGetValue(e.Channel.UserId, out var info); + var msg = new ReputationNetMsg() { Info = info }; + _netMgr.ServerSendMessage(msg, e.Channel); + } + + private async Task OnConnecting(NetConnectingArgs e) + { + var uid = e.UserId; + var value = await GetPlayerReputation(uid); + + if (value == null) + return; + + var info = new ReputationInfo() { Value = value.Value }; + _cacheReputation[e.UserId] = info; + } + + private async void UpdateCachedReputation(UpdateCachedReputationEvent ev) + { + var player = ev.Player; + if (!_cacheReputation.TryGetValue(player, out _)) + return; + + var value = await GetPlayerReputation(player); + + if (value == null) + return; + + var info = new ReputationInfo() { Value = value.Value }; + _cacheReputation[player] = info; + } + + private void OnRoundRestartCleanup(RoundRestartCleanupEvent ev) + { + var connectedPlayers = _netMgr.Channels.Select(channel => channel.UserId).ToList(); + var newDictionary = _cacheReputation + .Where(player => connectedPlayers.Contains(player.Key)) + .ToDictionary(player => player.Key, player => player.Value); + + _cacheReputation.Clear(); + + foreach (var kvp in newDictionary) + { + _cacheReputation.Add(kvp.Key, kvp.Value); + } + } + + #endregion + + + #region PublicApi + + public async void SetPlayerReputation(NetUserId player, float value) + { + var guid = player.UserId; + await SetPlayerReputationTask(guid, value); + RaiseLocalEvent(new UpdateCachedReputationEvent(player)); + } + + public async void ModifyPlayerReputation(NetUserId player, float value) + { + var guid = player.UserId; + await ModifyPlayerReputationTask(guid, value); + RaiseLocalEvent(new UpdateCachedReputationEvent(player)); + } + + public async Task GetPlayerReputation(NetUserId player) + { + var guid = player.UserId; + return await GetPlayerReputationTask(guid); + } + + public bool GetCachedPlayerReputation(NetUserId player, out float? value) + { + var success = _cacheReputation.TryGetValue(player, out var info); + value = info?.Value; + return success; + } + + public int GetPlayerWeight(float reputation) + { + return reputation switch + { + > 1000 => 9, + > 700 => 8, + > 500 => 7, + > 300 => 6, + > 100 => 5, + > 50 => 4, + > 15 => 3, + < 0 => 1, + _ => 2 + }; + } + + public ICommonSession PickPlayerBasedOnReputation(List prefList) + { + var list = new List(); + + foreach (var session in prefList) + { + if (!GetCachedPlayerReputation(session.UserId, out var value)) + continue; + + if (value == null) + continue; + + var weight = GetPlayerWeight(value.Value); + + for (var i = 0; i < weight; i++) + { + list.Add(session); + } + } + + if (list.Count == 0) + return _random.Pick(prefList); + + var number = _random.Next(list.Count - 1); + return list[number]; + } + + #endregion + + #region Private + + private async Task SetPlayerReputationTask(Guid player, float value) + { + try + { + await _db.SetPlayerReputation(player, value); + } + catch (Exception) + { + // Nope + } + } + + private async Task ModifyPlayerReputationTask(Guid player, float value) + { + try + { + await _db.ModifyPlayerReputation(player, value); + } + catch (Exception) + { + // Nope + } + } + + private async Task GetPlayerReputationTask(Guid player) + { + try + { + return await _db.GetPlayerReputation(player); + } + catch (Exception) + { + return null; + } + } + + #endregion +} diff --git a/Content.Server/White/Reputation/ReputationSystem.cs b/Content.Server/White/Reputation/ReputationSystem.cs new file mode 100644 index 0000000000..3926336a3f --- /dev/null +++ b/Content.Server/White/Reputation/ReputationSystem.cs @@ -0,0 +1,167 @@ +using System.Linq; +using System.Threading.Tasks; +using Content.Server.Administration; +using Content.Server.GameTicking; +using Content.Server.Mind.Components; +using Content.Server.UtkaIntegration; +using Content.Server.White.AspectsSystem.Base; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Robust.Server.Player; +using Robust.Shared.Asynchronous; +using Robust.Shared.Network; +using Robust.Shared.Utility; + +namespace Content.Server.White.Reputation; + +public sealed class ReputationSystem : EntitySystem +{ + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly ReputationManager _repManager = default!; + [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly ITaskManager _taskManager = default!; + [Dependency] private readonly IPlayerLocator _locator = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(ModifyReputationOnPlayerBanned); + } + + /// + /// Tries to modify reputation on round end and then returns it's new value and delta value if successful. + /// + /// Player to get new values for. + /// Modified player's reputation value. + /// + /// Success in modifying player's reputation. + public bool TryModifyReputationOnRoundEnd(string name, out float? newValue, out float? deltaValue) + { + newValue = null; + deltaValue = null; + + if (!_playerManager.TryGetSessionByUsername(name, out var session) || session.AttachedEntity == null) + return false; + + if (!TryCalculatePlayerReputation(session.AttachedEntity.Value, out var delta)) + return false; + + var uid = session.UserId; + _repManager.GetCachedPlayerReputation(uid, out var value); + + if (value == null) + return false; + + var longRound = _gameTicker.RoundDuration().Minutes >= 25; + if (delta != 0 && longRound) + { + _repManager.ModifyPlayerReputation(uid, delta); + } + + deltaValue = longRound ? delta : 0f; + newValue = value + deltaValue; + + return true; + } + + private bool TryCalculatePlayerReputation(EntityUid entity, out float deltaValue) + { + deltaValue = 0f; + var aspect = false; + + if (!TryComp(entity, out var state) || state.CurrentState is MobState.Dead or MobState.Invalid) + return true; + + var ruleEnt = _gameTicker.GetActiveGameRules() + .Where(HasComp) + .FirstOrNull(); + + if (ruleEnt != null) + { + if (TryComp(ruleEnt, out var comp)) + { + deltaValue += comp.Weight switch + { + 3 => 2f, + 2 => 3f, + 1 => 4f, + _ => 0f + }; + aspect = true; + } + } + + if (!aspect) + deltaValue += 1f; + + if (TryComp(entity, out var mind) + && mind.Mind != null + && mind.Mind.AllRoles.Any(role => role.Antagonist)) + { + var condCompleted = 0; + var totalCond = 0; + foreach (var obj in mind.Mind.AllObjectives) + { + foreach (var condition in obj.Conditions) + { + totalCond++; + + if (condition.Progress > 0.99f) + condCompleted++; + } + } + + if (aspect) + { + if (condCompleted == totalCond) + deltaValue += 1f + condCompleted; + else + deltaValue += 1f + condCompleted * 0.5f; + } + else + { + if (condCompleted == totalCond) + deltaValue += 2f + condCompleted * 0.5f; + else + deltaValue += condCompleted * 0.5f; + } + } + + return true; + } + + private async void ModifyReputationOnPlayerBanned(UtkaBannedEvent ev) + { + NetUserId uid; + float value; + + if (ev.Bantype == "server") + { + value = ev.Duration switch + { + > 10080 => -10f, + > 4320 => -7f, + > 1440 => -5f, + 0 => -25f, + _ => -3f + }; + } + else + value = -2f; + + if (_playerManager.TryGetPlayerDataByUsername(ev.Ckey!, out var data)) + uid = data.UserId; + else + { + var located = await _locator.LookupIdByNameAsync(ev.Ckey!); + + if (located == null) + return; + + uid = located.UserId; + } + + _repManager.ModifyPlayerReputation(uid, value); + } +} diff --git a/Content.Server/White/Reputation/UpdateCachedReputationEvent.cs b/Content.Server/White/Reputation/UpdateCachedReputationEvent.cs new file mode 100644 index 0000000000..427820636a --- /dev/null +++ b/Content.Server/White/Reputation/UpdateCachedReputationEvent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.Network; + +namespace Content.Server.White.Reputation; + +[Serializable] +public sealed class UpdateCachedReputationEvent : EntityEventArgs +{ + public NetUserId Player; + + public UpdateCachedReputationEvent(NetUserId player) + { + Player = player; + } +} diff --git a/Content.Shared/GameTicking/SharedGameTicker.cs b/Content.Shared/GameTicking/SharedGameTicker.cs index d8ce4585a7..5efe99d753 100644 --- a/Content.Shared/GameTicking/SharedGameTicker.cs +++ b/Content.Shared/GameTicking/SharedGameTicker.cs @@ -148,6 +148,7 @@ namespace Content.Shared.GameTicking public bool Antag; public bool Observer; public bool Connected; + public string Reputation; // WD edit } public string GamemodeTitle { get; } diff --git a/Content.Shared/White/Reputation/ReputationNetMsg.cs b/Content.Shared/White/Reputation/ReputationNetMsg.cs new file mode 100644 index 0000000000..1c0ce993e9 --- /dev/null +++ b/Content.Shared/White/Reputation/ReputationNetMsg.cs @@ -0,0 +1,49 @@ +using System.IO; +using System.Text.Json.Serialization; +using Lidgren.Network; +using Robust.Shared.Network; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared.White.Reputation; + +public sealed class ReputationNetMsg : NetMessage +{ + public override MsgGroups MsgGroup => MsgGroups.Command; + + public ReputationInfo? Info; + + public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) + { + var success = buffer.ReadBoolean(); + buffer.ReadPadBits(); + + if (!success) + return; + + var length = buffer.ReadVariableInt32(); + using var stream = buffer.ReadAlignedMemory(length); + serializer.DeserializeDirect(stream, out Info); + } + + public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) + { + buffer.Write(Info != null); + buffer.WritePadBits(); + + if (Info == null) + return; + + var stream = new MemoryStream(); + serializer.SerializeDirect(stream, Info); + buffer.WriteVariableInt32((int) stream.Length); + buffer.Write(stream.AsSpan()); + } +} + +[Serializable, NetSerializable] +public sealed class ReputationInfo +{ + [JsonPropertyName("value")] + public float Value { get; set; } +} diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl index 9eeb39a0f0..1ab765f590 100644 --- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl +++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl @@ -34,8 +34,8 @@ chat-manager-entity-me-wrap-message = [italic]{ PROPER($entity) -> } chat-manager-entity-looc-wrap-message = LOOC: [bold]{$entityName}:[/bold] {$message} -chat-manager-send-ooc-wrap-message = OOC: [bold]{$playerName}:[/bold] {$message} -chat-manager-send-ooc-patron-wrap-message = OOC: [bold][color={$patronColor}]{$playerName}[/color]:[/bold] {$message} +chat-manager-send-ooc-wrap-message = OOC: [bold]{$playerName}{$rep}:[/bold] {$message} +chat-manager-send-ooc-patron-wrap-message = OOC: [bold][color={$patronColor}]{$playerName}[/color]{$rep}:[/bold] {$message} chat-manager-send-dead-chat-wrap-message = {$deadChannelName}: [bold][BubbleHeader]{$playerName}[/BubbleHeader]:[/bold] [BubbleContent]{$message}[/BubbleContent] chat-manager-send-admin-dead-chat-wrap-message = {$adminChannelName}: [bold]([BubbleHeader]{$userName}[/BubbleHeader]):[/bold] [BubbleContent]{$message}[/BubbleContent] diff --git a/Resources/Locale/en-US/round-end/round-end-summary-window.ftl b/Resources/Locale/en-US/round-end/round-end-summary-window.ftl index 58d26319b3..4a7bfce9e9 100644 --- a/Resources/Locale/en-US/round-end/round-end-summary-window.ftl +++ b/Resources/Locale/en-US/round-end/round-end-summary-window.ftl @@ -5,4 +5,4 @@ round-end-summary-window-round-id-label = Round [color=white]#{$roundId}[/color] round-end-summary-window-gamemode-name-label = The game mode was [color=white]{$gamemode}[/color]. round-end-summary-window-duration-label = It lasted for [color=yellow]{$hours} hours, {$minutes} minutes, and {$seconds} seconds. round-end-summary-window-player-info-if-observer-text = [color=gray]{$playerOOCName}[/color] was [color=lightblue]{$playerICName}[/color], an observer. -round-end-summary-window-player-info-if-not-observer-text = [color=gray]{$playerOOCName}[/color] was [color={$icNameColor}]{$playerICName}[/color] playing role of [color=orange]{$playerRole}[/color]. +round-end-summary-window-player-info-if-not-observer-text = [color=gray]{$playerOOCName}[/color] was [color={$icNameColor}]{$playerICName}[/color] playing role of [color=orange]{$playerRole}[/color]. { $reputation } diff --git a/Resources/Locale/ru-RU/chat/managers/chat-manager.ftl b/Resources/Locale/ru-RU/chat/managers/chat-manager.ftl index 63469fca73..d48c131d46 100644 --- a/Resources/Locale/ru-RU/chat/managers/chat-manager.ftl +++ b/Resources/Locale/ru-RU/chat/managers/chat-manager.ftl @@ -31,8 +31,8 @@ chat-manager-entity-whisper-unknown-wrap-message = [font size=11][italic][Bubble chat-manager-entity-me-wrap-message = { $entityName } { $message } chat-manager-entity-looc-wrap-message = LOOC: [bold]{$entityName}:[/bold] {$message} -chat-manager-send-ooc-wrap-message = OOC: [bold]{$playerName}:[/bold] {$message} -chat-manager-send-ooc-patron-wrap-message = OOC: [bold][color={$patronColor}]{$playerName}[/color]:[/bold] {$message} +chat-manager-send-ooc-wrap-message = OOC: [bold]{$playerName}{$rep}:[/bold] {$message} +chat-manager-send-ooc-patron-wrap-message = OOC: [bold][color={$patronColor}]{$playerName}[/color]{$rep}:[/bold] {$message} chat-manager-send-dead-chat-wrap-message = {$deadChannelName}: [bold][BubbleHeader]{$playerName}[/BubbleHeader]:[/bold] [BubbleContent]{$message}[/BubbleContent] chat-manager-send-admin-dead-chat-wrap-message = {$adminChannelName}: [bold]([BubbleHeader]{$userName}[/BubbleHeader]):[/bold] [BubbleContent]{$message}[/BubbleContent] diff --git a/Resources/Locale/ru-RU/round-end/round-end-summary-window.ftl b/Resources/Locale/ru-RU/round-end/round-end-summary-window.ftl index 553a3107a9..a8847ae50b 100644 --- a/Resources/Locale/ru-RU/round-end/round-end-summary-window.ftl +++ b/Resources/Locale/ru-RU/round-end/round-end-summary-window.ftl @@ -5,4 +5,4 @@ round-end-summary-window-round-id-label = Раунд [color=white]#{ $roundId }[ round-end-summary-window-gamemode-name-label = Игровой режим был [color=white]{ $gamemode }[/color]. round-end-summary-window-duration-label = Он длился [color=yellow]{ $hours } ч., { $minutes } мин., и { $seconds } сек. round-end-summary-window-player-info-if-observer-text = [color=gray]{ $playerOOCName }[/color] был [color=lightblue]{ $playerICName }[/color], наблюдатель. -round-end-summary-window-player-info-if-not-observer-text = [color=gray]{ $playerOOCName }[/color] был [color={ $icNameColor }]{ $playerICName }[/color], в роли [color=orange]{ $playerRole }[/color]. +round-end-summary-window-player-info-if-not-observer-text = [color=gray]{ $playerOOCName }[/color] был [color={ $icNameColor }]{ $playerICName }[/color], в роли [color=orange]{ $playerRole }[/color]. { $reputation }