more work

This commit is contained in:
Alex
2025-01-10 13:49:28 +00:00
parent fa22bc8019
commit 966562bc08
38 changed files with 880 additions and 149 deletions
+17 -3
View File
@@ -1,12 +1,26 @@
using Core.Models.Eft.Common;
using Core.Annotations;
using Core.Controllers;
using Core.Models.Eft.Common;
using Core.Models.Eft.Launcher;
using Core.Servers;
using Core.Utils;
namespace Core.Callbacks;
[Injectable]
public class LauncherCallbacks
{
public LauncherCallbacks()
protected HttpResponseUtil _httpResponseUtil;
protected LauncherController _launcherController;
protected SaveServer _saveServer;
protected Watermark _watermark;
public LauncherCallbacks(
HttpResponseUtil httpResponse,
LauncherController launcherController,
SaveServer saveServer,
Watermark watermark)
{
}
public string Connect()
@@ -73,4 +87,4 @@ public class LauncherCallbacks
{
throw new NotImplementedException();
}
}
}
+3 -1
View File
@@ -1,9 +1,11 @@
using Core.Annotations;
using Core.Models.Eft.Launcher;
using Core.Models.Eft.Profile;
using Core.Models.Spt.Mod;
namespace Core.Controllers;
[Injectable]
public class LauncherController
{
/// <summary>
@@ -144,4 +146,4 @@ public class LauncherController
{
throw new NotImplementedException();
}
}
}
+29 -11
View File
@@ -1,7 +1,9 @@
using System.Text.Json;
using Core.Models.Eft.Common;
using Core.Models.Eft.ItemEvent;
using Core.Models.Eft.Profile;
using Core.Models.Utils;
using Core.Utils;
namespace Core.DI;
@@ -39,22 +41,27 @@ public abstract class Router
.Where((r) => !r.dynamic)
.Any((r) => r.route == url);
}
public abstract Type? GetBodyDeserializationType();
}
public abstract class StaticRouter : Router
{
private List<RouteAction<IRequestData>> actions;
private List<RouteAction> actions;
private JsonUtil _jsonUtil;
public StaticRouter(List<RouteAction<IRequestData>> routes) : base()
public StaticRouter(JsonUtil jsonUtil, List<RouteAction> routes) : base()
{
actions = routes;
_jsonUtil = jsonUtil;
}
public object HandleStatic(string url, IRequestData? info, string sessionID, string output)
public object HandleStatic(string url, string? body, string sessionID, string output)
{
return actions.Single(route => route.url == url).action(url, info, sessionID, output);
var action = actions.Single(route => route.url == url);
var type = action.bodyType;
IRequestData? info = null;
if (type != null && !string.IsNullOrEmpty(body))
info = (IRequestData?) _jsonUtil.Deserialize(body, type);
return action.action(url, info, sessionID, output);
}
protected override List<HandledRoute> GetHandledRoutes()
@@ -65,16 +72,23 @@ public abstract class StaticRouter : Router
public abstract class DynamicRouter : Router
{
private List<RouteAction<IRequestData>> actions;
private List<RouteAction> actions;
private JsonUtil _jsonUtil;
public DynamicRouter(List<RouteAction<IRequestData>> routes) : base()
public DynamicRouter(JsonUtil jsonUtil, List<RouteAction> routes) : base()
{
actions = routes;
_jsonUtil = jsonUtil;
}
public object HandleDynamic(string url, IRequestData? info, string sessionID, string output)
public object HandleDynamic(string url, string? body, string sessionID, string output)
{
return actions.First(r => url.Contains(r.url)).action(url, info, sessionID, output);
var action = actions.First(r => url.Contains(r.url));
var type = action.bodyType;
IRequestData? info = null;
if (type != null && !string.IsNullOrEmpty(body))
info = (IRequestData?) _jsonUtil.Deserialize(body, type);
return action.action(url, info, sessionID, output);
}
protected override List<HandledRoute> GetHandledRoutes()
@@ -103,5 +117,9 @@ public abstract class SaveLoadRouter : Router
public record HandledRoute(string route, bool dynamic);
public record RouteAction<T>(string url, Func<string, T?, string?, string?, object> action) where T : IRequestData;
public record RouteAction(
string url,
Func<string, IRequestData?, string?, string?, object> action,
Type? bodyType = null
);
//public action: (url: string, info: any, sessionID: string, output: string) => Promise<any>,
+3 -1
View File
@@ -4,9 +4,11 @@ namespace Core.Models.Common;
public class MinMax
{
[JsonPropertyName("type")]
public string? Type { get; set; }
[JsonPropertyName("max")]
public double? Max { get; set; }
[JsonPropertyName("min")]
public double? Min { get; set; }
}
}
+4 -1
View File
@@ -18,6 +18,9 @@ public class Item
[JsonPropertyName("location")]
public object? Location { get; set; } // TODO: Can be IItemLocation or number
[JsonPropertyName("desc")]
public string? Desc { get; set; }
[JsonPropertyName("upd")]
public Upd? Update { get; set; }
@@ -254,4 +257,4 @@ public class UpdCultistAmulet
{
[JsonPropertyName("NumberOfUsages")]
public double? NumberOfUsages { get; set; }
}
}
+3 -2
View File
@@ -1,12 +1,13 @@
using System.Text.Json.Serialization;
using Core.Models.Utils;
namespace Core.Models.Eft.Launcher;
public class LoginRequestData
public class LoginRequestData : IRequestData
{
[JsonPropertyName("username")]
public string? Username { get; set; }
[JsonPropertyName("password")]
public string? Password { get; set; }
}
}
+3 -2
View File
@@ -58,7 +58,8 @@ public class AirdropChancePercent
/// </summary>
public class AirdropLoot
{
[JsonPropertyName("Icon")]
[JsonPropertyName("icon")]
[JsonConverter(typeof(JsonStringEnumConverter))]
public AirdropTypeEnum Icon { get; set; }
/// <summary>
@@ -126,4 +127,4 @@ public class AirdropLoot
[JsonPropertyName("forcedLoot")]
public Dictionary<string, MinMax>? ForcedLoot { get; set; }
}
}
+32 -2
View File
@@ -138,6 +138,15 @@ public class PresetBatch
[JsonPropertyName("bossKnight")]
public int BossKnight { get; set; }
[JsonPropertyName("bossZryachiy")]
public int BossZryachiy { get; set; }
[JsonPropertyName("bossKolontay")]
public int BossKolontay { get; set; }
[JsonPropertyName("bossPartisan")]
public int BossPartisan { get; set; }
[JsonPropertyName("bossTest")]
public int BossTest { get; set; }
@@ -180,6 +189,21 @@ public class PresetBatch
[JsonPropertyName("followerBoar")]
public int FollowerBoar { get; set; }
[JsonPropertyName("followerBoarClose1")]
public int FollowerBoarClose1 { get; set; }
[JsonPropertyName("followerBoarClose2")]
public int FollowerBoarClose2 { get; set; }
[JsonPropertyName("followerZryachiy")]
public int FollowerZryachiy { get; set; }
[JsonPropertyName("followerKolontayAssault")]
public int FollowerKolontayAssault { get; set; }
[JsonPropertyName("followerKolontaySecurity")]
public int FollowerKolontaySecurity { get; set; }
[JsonPropertyName("marksman")]
public int Marksman { get; set; }
@@ -221,6 +245,12 @@ public class PresetBatch
[JsonPropertyName("pmcBEAR")]
public int PmcBEAR { get; set; }
[JsonPropertyName("shooterBTR")]
public int ShooterBTR { get; set; }
[JsonExtensionData]
public IDictionary<string, object> AdditionalData { get; set; }
}
public class WalletLootSettings
@@ -288,7 +318,7 @@ public class EquipmentFilters
/// <summary>
/// Chance NODS are down/active during the day
/// </summary>
[JsonPropertyName("NvgIsActiveChanceDayPercent")]
[JsonPropertyName("nvgIsActiveChanceDayPercent")]
public float? NvgIsActiveChanceDayPercent { get; set; }
/// <summary>
@@ -522,4 +552,4 @@ public class RandomisedResourceValues
/// </summary>
[JsonPropertyName("chanceMaxResourcePercent")]
public float ChanceMaxResourcePercent { get; set; }
}
}
+17 -14
View File
@@ -11,43 +11,43 @@ public class BotDurability
public PmcDurability Pmc { get; set; }
[JsonPropertyName("boss")]
public BotDurability Boss { get; set; }
public PmcDurability Boss { get; set; }
[JsonPropertyName("follower")]
public BotDurability Follower { get; set; }
public PmcDurability Follower { get; set; }
[JsonPropertyName("assault")]
public BotDurability Assault { get; set; }
public PmcDurability Assault { get; set; }
[JsonPropertyName("cursedassault")]
public BotDurability CursedAssault { get; set; }
public PmcDurability CursedAssault { get; set; }
[JsonPropertyName("marksman")]
public BotDurability Marksman { get; set; }
public PmcDurability Marksman { get; set; }
[JsonPropertyName("pmcbot")]
public BotDurability PmcBot { get; set; }
public PmcDurability PmcBot { get; set; }
[JsonPropertyName("arenafighterevent")]
public BotDurability ArenaFighterEvent { get; set; }
public PmcDurability ArenaFighterEvent { get; set; }
[JsonPropertyName("arenafighter")]
public BotDurability ArenaFighter { get; set; }
public PmcDurability ArenaFighter { get; set; }
[JsonPropertyName("crazyassaultevent")]
public BotDurability CrazyAssaultEvent { get; set; }
public PmcDurability CrazyAssaultEvent { get; set; }
[JsonPropertyName("exusec")]
public BotDurability Exusec { get; set; }
public PmcDurability Exusec { get; set; }
[JsonPropertyName("gifter")]
public BotDurability Gifter { get; set; }
public PmcDurability Gifter { get; set; }
[JsonPropertyName("sectantpriest")]
public BotDurability SectantPriest { get; set; }
public PmcDurability SectantPriest { get; set; }
[JsonPropertyName("sectantwarrior")]
public BotDurability SectantWarrior { get; set; }
public PmcDurability SectantWarrior { get; set; }
}
/** Durability values to be used when a more specific bot type can't be found */
@@ -82,6 +82,9 @@ public class PmcDurabilityArmor
[JsonPropertyName("minDelta")]
public double MinDelta { get; set; }
[JsonPropertyName("minLimitPercent")]
public double MinLimitPercent { get; set; }
}
public class ArmorDurability
@@ -112,4 +115,4 @@ public class WeaponDurability
[JsonPropertyName("minLimitPercent")]
public double MinLimitPercent { get; set; }
}
}
+5 -1
View File
@@ -73,4 +73,8 @@ public class Gift
[JsonPropertyName("maxToSendPlayer")]
public int? MaxToSendPlayer { get; set; }
}
[JsonPropertyName("maxToSendToPlayer")]
public int? MaxToSendToPlayer { get; set; }
}
+4 -1
View File
@@ -33,6 +33,9 @@ public class InventoryConfig : BaseConfig
public class RewardDetails
{
[JsonPropertyName("_type")]
public string? Type { get; set; }
[JsonPropertyName("rewardCount")]
public int RewardCount { get; set; }
@@ -69,4 +72,4 @@ public class SealedAirdropContainerSettings
[JsonPropertyName("allowBossItems")]
public bool AllowBossItems { get; set; }
}
}
+4 -1
View File
@@ -254,6 +254,9 @@ public class LootMultiplier
[JsonPropertyName("sandbox")]
public double Sandbox { get; set; }
[JsonPropertyName("sandbox_high")]
public double SandboxHigh { get; set; }
}
public class ContainerRandomisationSettings
@@ -316,4 +319,4 @@ public class ScavRaidTimeLocationSettings
/** Should bot waves be removed / spawn times be adjusted */
[JsonPropertyName("adjustWaves")]
public bool AdjustWaves { get; set; }
}
}
+7 -1
View File
@@ -60,4 +60,10 @@ public class LostEquipment
[JsonPropertyName("Scabbard")]
public bool Scabbard { get; set; }
}
[JsonPropertyName("Compass")]
public bool Compass { get; set; }
[JsonPropertyName("SecuredContainer")]
public bool SecuredContainer { get; set; }
}
+4 -1
View File
@@ -9,4 +9,7 @@ public class MatchConfig : BaseConfig
[JsonPropertyName("enabled")]
public bool Enabled { get; set; }
}
[JsonPropertyName("randomiseMapContainers")]
public Dictionary<string, bool> RandomiseMapContainers { get; set; }
}
+4 -1
View File
@@ -26,6 +26,9 @@ public class KarmaLevel
[JsonPropertyName("equipmentBlacklist")]
public Dictionary<string, string[]> EquipmentBlacklist { get; set; }
[JsonPropertyName("labsAccessCardChancePercent")]
public double? LabsAccessCardChancePercent { get; set; }
[JsonPropertyName("lootItemsToAddChancePercent")]
public Dictionary<string, double> LootItemsToAddChancePercent { get; set; }
}
@@ -58,4 +61,4 @@ public class ItemLimits
[JsonPropertyName("grenades")]
public GenerationData Grenades { get; set; }
}
}
+10 -1
View File
@@ -53,6 +53,9 @@ public class PmcConfig : BaseConfig
[JsonPropertyName("looseWeaponInBackpackLootMinMax")]
public MinMax LooseWeaponInBackpackLootMinMax { get; set; }
[JsonPropertyName("_isUsec")]
public string? IsUsecDescription { get; set; }
/** Percentage chance PMC will be USEC */
[JsonPropertyName("isUsec")]
public double IsUsec { get; set; }
@@ -65,6 +68,9 @@ public class PmcConfig : BaseConfig
[JsonPropertyName("bearType")]
public string BearType { get; set; }
[JsonPropertyName("_pmcType")]
public string? PmcTypeDescription { get; set; }
/** What 'brain' does a PMC use, keyed by map and side (USEC/BEAR) key: map location, value: type for usec/bear */
[JsonPropertyName("pmcType")]
public Dictionary<string, Dictionary<string, Dictionary<string, double>>> PmcType { get; set; }
@@ -106,6 +112,9 @@ public class PmcConfig : BaseConfig
/** Should secure container loot from usec.json/bear.json be added to pmc bots secure */
[JsonPropertyName("addSecureContainerLootFromBotConfig")]
public bool AddSecureContainerLootFromBotConfig { get; set; }
[JsonPropertyName("addPrefixToSameNamePMCAsPlayerChance")]
public int? AddPrefixToSameNamePMCAsPlayerChance { get; set; }
}
public class HostilitySettings
@@ -159,4 +168,4 @@ public class IMinMaxLootValue : MinMax
{
[JsonPropertyName("value")]
public double Value { get; set; }
}
}
+14 -4
View File
@@ -56,14 +56,17 @@ public class PlayerTypeQuestIds
public class QuestTypeIds
{
[JsonPropertyName("Elimination")]
[JsonPropertyName("elimination")]
public string Elimination { get; set; }
[JsonPropertyName("Completion")]
[JsonPropertyName("completion")]
public string Completion { get; set; }
[JsonPropertyName("Exploration")]
[JsonPropertyName("exploration")]
public string Exploration { get; set; }
[JsonPropertyName("pickup")]
public string Pickup { get; set; }
}
public class EventQuestData
@@ -173,6 +176,8 @@ public class RewardScaling
public class TraderWhitelist
{
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("traderId")]
public string TraderId { get; set; }
@@ -253,6 +258,11 @@ public class Pickup : BaseQuestConfig
{
[JsonPropertyName("ItemTypeToFetchWithMaxCount")]
public List<PickupTypeWithMaxCount> ItemTypeToFetchWithMaxCount { get; set; }
public List<string> ItemTypesToFetch { get; set; }
[JsonPropertyName("maxItemFetchCount")]
public int? MaxItemFetchCount { get; set; }
}
public class PickupTypeWithMaxCount
@@ -370,4 +380,4 @@ public class ProbabilityObject
[JsonPropertyName("data")]
public object Data { get; set; }
}
}
+14 -4
View File
@@ -116,7 +116,7 @@ public class Dynamic
[JsonPropertyName("condition")]
/** Settings to control the durability range of item items listed on flea */
public Condition Condition { get; set; }
public Dictionary<string, Condition> Condition { get; set; }
[JsonPropertyName("stackablePercent")]
/** Size stackable items should be listed for in percent of max stack size */
@@ -138,6 +138,9 @@ public class Dynamic
/** A multipler to apply to individual tpls price just prior to item quality adjustment */
public Dictionary<string, double> ItemPriceMultiplier { get; set; }
[JsonPropertyName("_currencies")]
public string? CurrenciesDescription { get; set; }
[JsonPropertyName("currencies")]
/** Percentages to sell offers in each currency */
public Dictionary<string, double> Currencies { get; set; }
@@ -265,6 +268,9 @@ public class Condition
[JsonPropertyName("max")]
public MinMax Max { get; set; }
[JsonPropertyName("_name")]
public string Name { get; set; }
}
public class RagfairBlacklist
@@ -352,6 +358,9 @@ public class UnreasonableModPrices
/// </summary>
[JsonPropertyName("newPriceHandbookMultiplier")]
public int NewPriceHandbookMultiplier { get; set; }
[JsonPropertyName("itemType")]
public string ItemType { get; set; }
}
public class ArmorSettings
@@ -386,8 +395,9 @@ public class TieredFlea
[JsonPropertyName("unlocksType")]
public Dictionary<string, int> UnlocksType { get; set; }
public bool AmmoTiersEnabled { get; set; }
[JsonPropertyName("ammoTplUnlocks")]
public Dictionary<string, int> AmmoTplUnlocks { get; set; }
}
[JsonPropertyName("ammoTiersEnabled")]
public bool AmmoTiersEnabled { get; set; }
}
+9 -3
View File
@@ -82,6 +82,12 @@ public class RepairKit
[JsonPropertyName("weapon")]
public BonusSettings Weapon { get; set; }
[JsonPropertyName("vest")]
public BonusSettings Vest { get; set; }
[JsonPropertyName("headwear")]
public BonusSettings Headwear { get; set; }
}
public class BonusSettings
@@ -92,10 +98,10 @@ public class BonusSettings
[JsonPropertyName("bonusTypeWeight")]
public Dictionary<string, double> BonusTypeWeight { get; set; }
[JsonPropertyName("common")]
[JsonPropertyName("Common")]
public Dictionary<string, BonusValues> Common { get; set; }
[JsonPropertyName("rare")]
[JsonPropertyName("Rare")]
public Dictionary<string, BonusValues> Rare { get; set; }
}
@@ -107,4 +113,4 @@ public class BonusValues
/** What dura is buff active between (min max of current max) */
[JsonPropertyName("activeDurabilityPercentMinMax")]
public MinMax ActiveDurabilityPercentMinMax { get; set; }
}
}
+10 -1
View File
@@ -1,6 +1,7 @@
using System.Text.Json.Serialization;
using Core.Models.Eft.Common;
using Core.Models.Enums;
using Core.Utils.Json.Converters;
namespace Core.Models.Spt.Config;
@@ -20,6 +21,7 @@ public class SeasonalEventConfig : BaseConfig
[JsonPropertyName("eventLoot")]
public Dictionary<string, Dictionary<string, Dictionary<string, Dictionary<string, int>>>> EventLoot { get; set; }
[JsonPropertyName("events")]
public List<SeasonalEvent> Events { get; set; }
[JsonPropertyName("eventBotMapping")]
@@ -59,19 +61,26 @@ public class SeasonalEvent
public SeasonalEventType Type { get; set; }
[JsonPropertyName("startDay")]
[JsonConverter(typeof(StringToNumberFactoryConverter))]
public int StartDay { get; set; }
[JsonPropertyName("startMonth")]
[JsonConverter(typeof(StringToNumberFactoryConverter))]
public int StartMonth { get; set; }
[JsonPropertyName("endDay")]
[JsonConverter(typeof(StringToNumberFactoryConverter))]
public int EndDay { get; set; }
[JsonPropertyName("endMonth")]
[JsonConverter(typeof(StringToNumberFactoryConverter))]
public int EndMonth { get; set; }
[JsonPropertyName("settings")]
public Dictionary<string, object> Settings { get; set; } // TODO: Type was Record<string, ISeasonalEventSettings | IZombieSettings>
[JsonPropertyName("setting")]
public Dictionary<string, object> SettingsDoNOTUse { set => Settings = value; }
}
public class SeasonalEventSettings
@@ -102,4 +111,4 @@ public class GifterSetting
[JsonPropertyName("spawnChance")]
public int SpawnChance { get; set; }
}
}
+7 -1
View File
@@ -34,6 +34,9 @@ public class TraderConfig : BaseConfig
public class UpdateTime
{
[JsonPropertyName("_name")]
public string Name { get; set; }
[JsonPropertyName("traderId")]
public string TraderId { get; set; }
@@ -139,6 +142,9 @@ public class CoopExtractReward : LootRequest
{
[JsonPropertyName("sendGift")]
public bool SendGift { get; set; }
[JsonPropertyName("useRewarditemBlacklist")]
public bool UseRewarditemBlacklist { get; set; }
[JsonPropertyName("messageLocaleIds")]
public List<string> MessageLocaleIds { get; set; }
@@ -171,4 +177,4 @@ public class ModdedTraders
/** Trader Ids to enable the clothing service for */
[JsonPropertyName("clothingService")]
public List<string> ClothingService { get; set; }
}
}
+4 -1
View File
@@ -93,6 +93,9 @@ public class ProfileChangeEvent
[JsonPropertyName("entity")]
public string? Entity { get; set; }
[JsonPropertyName("data")]
public string? Data { get; set; }
}
public enum ProfileChangeEventType
@@ -105,4 +108,4 @@ public enum ProfileChangeEventType
UnlockTrader,
AssortmentUnlockRule,
HideoutAreaLevel
}
}
+3 -6
View File
@@ -1,12 +1,14 @@
using Core.Annotations;
using Core.DI;
using Core.Utils;
namespace Core.Routers.Dynamic;
[Injectable(InjectableTypeOverride = typeof(DynamicRouter))]
public class HttpDynamicRouter : DynamicRouter
{
public HttpDynamicRouter(ImageRouter imageRouter) : base(
public HttpDynamicRouter(ImageRouter imageRouter, JsonUtil jsonUtil) : base(
jsonUtil,
[
new(".jpg", (_, _, _, _) => imageRouter.GetImage()),
new(".png", (_, _, _, _) => imageRouter.GetImage()),
@@ -15,9 +17,4 @@ public class HttpDynamicRouter : DynamicRouter
)
{
}
public override Type? GetBodyDeserializationType()
{
return null;
}
}
+2 -5
View File
@@ -73,13 +73,10 @@ public class HttpRouter
foreach (var route in routers)
{
if (route.CanHandle(url, dynamic)) {
var type = route.GetBodyDeserializationType();
if (type != null && !string.IsNullOrEmpty(body))
deserializedObject = JsonSerializer.Deserialize(body, type);
if (dynamic) {
wrapper.Output = (route as DynamicRouter).HandleDynamic(url, deserializedObject, sessionID, wrapper.Output) as string;
wrapper.Output = (route as DynamicRouter).HandleDynamic(url, body, sessionID, wrapper.Output) as string;
} else {
wrapper.Output = (route as StaticRouter).HandleStatic(url, deserializedObject, sessionID, wrapper.Output) as string;
wrapper.Output = (route as StaticRouter).HandleStatic(url, body, sessionID, wrapper.Output) as string;
}
matched = true;
}
+58 -49
View File
@@ -2,59 +2,68 @@ using Core.Annotations;
using Core.Callbacks;
using Core.DI;
using Core.Models.Eft.Common;
using Core.Models.Eft.Launcher;
using Core.Utils;
namespace Core.Routers.Static;
[Injectable(InjectableTypeOverride = typeof(StaticRouter))]
public class LauncherStaticRouter : StaticRouter {
public LauncherStaticRouter(LauncherCallbacks launcherCallbacks) : base([
new RouteAction<EmptyRequestData?>(
"/launcher/ping",
(url, _, sessionID, _) => launcherCallbacks.Ping(url, null, sessionID)),
new RouteAction<EmptyRequestData?>(
"/launcher/server/connect",
(_, _, _, _) => launcherCallbacks.Connect()),
new RouteAction(
"/launcher/profile/login",
(url, info, sessionID, _) => launcherCallbacks.Login(url, info, sessionID)),
new RouteAction(
"/launcher/profile/register",
(url, info, sessionID, _) => launcherCallbacks.Register(url, info, sessionID)),
new RouteAction(
"/launcher/profile/get",
(url, info, sessionID, _) => launcherCallbacks.Get(url, info, sessionID)),
new RouteAction(
"/launcher/profile/change/username",
(url, info, sessionID, _) => launcherCallbacks.ChangeUsername(url, info, sessionID)),
new RouteAction(
"/launcher/profile/change/password",
(url, info, sessionID, _) => launcherCallbacks.ChangePassword(url, info, sessionID)),
new RouteAction(
"/launcher/profile/change/wipe",
(url, info, sessionID, _) => launcherCallbacks.Wipe(url, info, sessionID)),
new RouteAction(
"/launcher/profile/remove",
(url, info, sessionID, _) => launcherCallbacks.RemoveProfile(url, info, sessionID)),
new RouteAction(
"/launcher/profile/compatibleTarkovVersion",
(_, _, _, _) => launcherCallbacks.GetCompatibleTarkovVersion()),
new RouteAction(
"/launcher/server/version",
(_, _, _, _) => launcherCallbacks.GetServerVersion()),
new RouteAction(
"/launcher/server/loadedServerMods",
(_, _, _, _) => launcherCallbacks.GetLoadedServerMods()),
new RouteAction(
"/launcher/server/serverModsUsedByProfile",
(url, info, sessionID, _) => launcherCallbacks.GetServerModsProfileUsed(url, info, sessionID)),
])
public class LauncherStaticRouter : StaticRouter
{
public LauncherStaticRouter(LauncherCallbacks launcherCallbacks, JsonUtil jsonUtil) : base(
jsonUtil,
[
new RouteAction(
"/launcher/ping",
(url, _, sessionID, _) => launcherCallbacks.Ping(url, null, sessionID)),
new RouteAction(
"/launcher/server/connect",
(_, _, _, _) => launcherCallbacks.Connect()),
new RouteAction(
"/launcher/profile/login",
(url, info, sessionID, _) => launcherCallbacks.Login(url, info as LoginRequestData, sessionID),
typeof(LoginRequestData)),
new RouteAction(
"/launcher/profile/register",
(url, info, sessionID, _) => launcherCallbacks.Register(url, info as RegisterData, sessionID),
typeof(RegisterData)),
new RouteAction(
"/launcher/profile/get",
(url, info, sessionID, _) => launcherCallbacks.Get(url, info as LoginRequestData, sessionID),
typeof(LoginRequestData)),
new RouteAction(
"/launcher/profile/change/username",
(url, info, sessionID, _) =>
launcherCallbacks.ChangeUsername(url, info as ChangeRequestData, sessionID),
typeof(ChangeRequestData)),
new RouteAction(
"/launcher/profile/change/password",
(url, info, sessionID, _) =>
launcherCallbacks.ChangePassword(url, info as ChangeRequestData, sessionID),
typeof(ChangeRequestData)),
new RouteAction(
"/launcher/profile/change/wipe",
(url, info, sessionID, _) => launcherCallbacks.Wipe(url, info as RegisterData, sessionID),
typeof(RegisterData)),
new RouteAction(
"/launcher/profile/remove",
(url, info, sessionID, _) => launcherCallbacks.RemoveProfile(url, info as RemoveProfileData, sessionID),
typeof(RemoveProfileData)),
new RouteAction(
"/launcher/profile/compatibleTarkovVersion",
(_, _, _, _) => launcherCallbacks.GetCompatibleTarkovVersion()),
new RouteAction(
"/launcher/server/version",
(_, _, _, _) => launcherCallbacks.GetServerVersion()),
new RouteAction(
"/launcher/server/loadedServerMods",
(_, _, _, _) => launcherCallbacks.GetLoadedServerMods()),
new RouteAction(
"/launcher/server/serverModsUsedByProfile",
(url, info, sessionID, _) =>
launcherCallbacks.GetServerModsProfileUsed(url, info as EmptyRequestData, sessionID),
typeof(EmptyRequestData))
])
{
}
public override Type? GetBodyDeserializationType()
{
throw new NotImplementedException();
}
}
}
+8 -6
View File
@@ -4,6 +4,7 @@ using System.Text.Json.Serialization;
using Core.Annotations;
using Core.Models.Enums;
using Core.Models.Spt.Config;
using Core.Utils;
using ILogger = Core.Models.Utils.ILogger;
namespace Core.Servers;
@@ -11,16 +12,18 @@ namespace Core.Servers;
[Injectable(InjectionType.Singleton)]
public class ConfigServer
{
private ILogger _logger;
protected ILogger _logger;
protected JsonUtil _jsonUtil;
protected Dictionary<string, object> configs = new();
protected readonly string[] acceptableFileExtensions = [".json", ".jsonc"];
public ConfigServer(
ILogger logger
// TODO: We need JsonUtil here => JsonUtil jsonUtil,
ILogger logger,
JsonUtil jsonUtil
)
{
_logger = logger;
_jsonUtil = jsonUtil;
Initialize();
}
@@ -50,8 +53,7 @@ public class ConfigServer
{
var fileContent = File.ReadAllText(file);
var type = GetConfigTypeByFilename(file);
var deserializedContent =
JsonSerializer.Deserialize(fileContent, type, new JsonSerializerOptions() { Converters = { new JsonStringEnumConverter() } });
var deserializedContent = _jsonUtil.Deserialize(fileContent, type);
if (deserializedContent == null)
{
@@ -78,4 +80,4 @@ public class ConfigServer
.First(en => en.GetValue().Contains(Path.GetFileNameWithoutExtension(filename)));
return type.GetConfigType();
}
}
}
+6 -4
View File
@@ -19,12 +19,13 @@ public class SptHttpListener : IHttpListener
protected readonly ILogger _logger;
protected readonly HttpResponseUtil _httpResponseUtil;
protected readonly LocalisationService _localisationService;
protected readonly JsonUtil _jsonUtil;
public SptHttpListener(
HttpRouter httpRouter, // TODO: delay required
IEnumerable<ISerializer> serializers,
ILogger logger,
// TODO: requestsLogger: ILogger,
// TODO: JsonUtil jsonUtil,
JsonUtil jsonUtil,
HttpResponseUtil httpHttpResponseUtil,
LocalisationService localisationService
)
@@ -34,6 +35,7 @@ public class SptHttpListener : IHttpListener
_logger = logger;
_httpResponseUtil = httpHttpResponseUtil;
_localisationService = localisationService;
_jsonUtil = jsonUtil;
}
private static readonly ImmutableHashSet<string> SupportedMethods = ["GET", "PUT", "POST"];
@@ -98,8 +100,8 @@ public class SptHttpListener : IHttpListener
)
{
if (body == null)
body = "{}";
var bodyInfo = JsonSerializer.Serialize(body);
body = new object();
var bodyInfo = _jsonUtil.Serialize(body);
if (IsDebugRequest(req)) {
// Send only raw response without transformation
@@ -160,7 +162,7 @@ public class SptHttpListener : IHttpListener
/* route doesn't exist or response is not properly set up */
if (string.IsNullOrEmpty(output)) {
_logger.Error(_localisationService.GetText("unhandled_response", req.Path));
_logger.Info(JsonSerializer.Serialize(deserializedObject));
_logger.Info(_jsonUtil.Serialize(deserializedObject));
output = _httpResponseUtil.GetBody<object?>(null, 404, $"UNHANDLED RESPONSE: {req.Path}");
}
return output;
+261
View File
@@ -0,0 +1,261 @@
using System.Diagnostics;
using System.Runtime.InteropServices.JavaScript;
using Core.Annotations;
using Core.DI;
using Core.Models.Eft.Common;
using Core.Models.Eft.Profile;
using Core.Models.Enums;
using Core.Models.Spt.Config;
using Core.Services;
using Core.Utils;
using ILogger = Core.Models.Utils.ILogger;
namespace Core.Servers;
[Injectable(InjectionType.Singleton)]
public class SaveServer
{
protected string profileFilepath = "user/profiles/";
protected Dictionary<string, SptProfile> profiles = new();
// onLoad = require("../bindings/SaveLoad");
protected readonly Dictionary<string, Func<SptProfile, SptProfile>> onBeforeSaveCallbacks = new();
protected Dictionary<string, string> saveMd5 = new();
protected readonly FileUtil _vfs;
protected readonly IEnumerable<SaveLoadRouter> _saveLoadRouters;
protected readonly JsonUtil _jsonUtil;
protected readonly HashUtil _hashUtil;
protected readonly LocalisationService _localisationService;
protected readonly ILogger _logger;
protected readonly ConfigServer _configServer;
public SaveServer(
FileUtil vfs,
IEnumerable<SaveLoadRouter> saveLoadRouters,
JsonUtil jsonUtil,
HashUtil hashUtil,
LocalisationService localisationService,
ILogger logger,
ConfigServer configServer
)
{
_vfs = vfs;
_saveLoadRouters = saveLoadRouters;
_jsonUtil = jsonUtil;
_hashUtil = hashUtil;
_localisationService = localisationService;
_logger = logger;
_configServer = configServer;
}
/**
* Add callback to occur prior to saving profile changes
* @param id Id for save callback
* @param callback Callback to execute prior to running SaveServer.saveProfile()
*/
public void AddBeforeSaveCallback(string id, Func<SptProfile, SptProfile> callback)
{
onBeforeSaveCallbacks[id] = callback;
}
/**
* Remove a callback from being executed prior to saving profile in SaveServer.saveProfile()
* @param id Id of callback to remove
*/
public void RemoveBeforeSaveCallback(string id)
{
if (onBeforeSaveCallbacks.ContainsKey(id))
onBeforeSaveCallbacks.Remove(id);
}
/**
* Load all profiles in /user/profiles folder into memory (this.profiles)
*/
public void Load()
{
// get files to load
if (!_vfs.DirectoryExists(profileFilepath))
{
_vfs.CreateDirectory(profileFilepath);
}
var files = _vfs.GetFiles(profileFilepath).Where((item) => _vfs.GetFileExtension(item) == "json");
// load profiles
var stopwatch = Stopwatch.StartNew();
foreach (var file in files) {
LoadProfile(_vfs.StripExtension(file));
}
stopwatch.Stop();
_logger.Debug($"{files.Count()} Profiles took: {stopwatch.ElapsedMilliseconds}ms to load.");
}
/**
* Save changes for each profile from memory into user/profiles json
*/
public void Save()
{
// Save every profile
var totalTime = 0L;
foreach ( var sessionID in profiles) {
totalTime += SaveProfile(sessionID.Key);
}
_logger.Debug($"Saved {profiles.Count} profiles, took: {totalTime}ms", false);
}
/**
* Get a player profile from memory
* @param sessionId Session id
* @returns ISptProfile
*/
public SptProfile GetProfile(string sessionId)
{
if (!string.IsNullOrEmpty(sessionId))
{
throw new Exception("session id provided was empty, did you restart the server while the game was running?");
}
if (profiles == null || profiles.Count == 0)
{
throw new Exception($"no profiles found in saveServer with id: ${sessionId}");
}
if (!profiles.TryGetValue(sessionId, out var sptProfile))
throw new Exception($"no profile found for sessionId: {sessionId}");
return sptProfile;
}
public bool ProfileExists(string id)
{
return profiles.ContainsKey(id);
}
/**
* Get all profiles from memory
* @returns Dictionary of ISptProfile
*/
public Dictionary<string, SptProfile> GetProfiles() => profiles;
/**
* Delete a profile by id
* @param sessionID Id of profile to remove
* @returns true when deleted, false when profile not found
*/
public bool DeleteProfileById(string sessionID)
{
if (profiles.ContainsKey(sessionID))
{
profiles.Remove(sessionID);
return true;
}
return false;
}
/**
* Create a new profile in memory with empty pmc/scav objects
* @param profileInfo Basic profile data
*/
public void CreateProfile(Info profileInfo)
{
if (profiles.ContainsKey(profileInfo.ProfileId))
{
throw new Exception($"profile already exists for sessionId: {profileInfo.ProfileId}");
}
profiles.Add(profileInfo.ProfileId,
new SptProfile()
{
ProfileInfo = profileInfo,
CharacterData = new Characters() { PmcData = new PmcData(), ScavData = new PmcData() }
});
}
/**
* Add full profile in memory by key (info.id)
* @param profileDetails Profile to save
*/
public void AddProfile(SptProfile profileDetails)
{
profiles.Add(profileDetails.ProfileInfo.ProfileId, profileDetails);
}
/**
* Look up profile json in user/profiles by id and store in memory
* Execute saveLoadRouters callbacks after being loaded into memory
* @param sessionID Id of profile to store in memory
*/
public void LoadProfile(string sessionID)
{
var filename = $"{sessionID}.json";
var filePath = $"{profileFilepath}{filename}";
if (_vfs.FileExists(filePath))
{
// File found, store in profiles[]
profiles[sessionID] = _jsonUtil.Deserialize<SptProfile>(_vfs.ReadFile(filePath));
}
// Run callbacks
foreach (var callback in _saveLoadRouters) {
profiles[sessionID] = callback.HandleLoad(GetProfile(sessionID));
}
}
/**
* Save changes from in-memory profile to user/profiles json
* Execute onBeforeSaveCallbacks callbacks prior to being saved to json
* @param sessionID profile id (user/profiles/id.json)
* @returns time taken to save in MS
*/
public long SaveProfile(string sessionID)
{
var filePath = $"{profileFilepath}{sessionID}.json";
// Run pre-save callbacks before we save into json
foreach ( var callback in onBeforeSaveCallbacks)
{
var previous = profiles[sessionID];
try
{
profiles[sessionID] = onBeforeSaveCallbacks[callback.Key](profiles[sessionID]);
}
catch (Exception e)
{
_logger.Error(_localisationService.GetText("profile_save_callback_error", new { callback, error = e }));
profiles[sessionID] = previous;
}
}
var start = Stopwatch.StartNew();
var jsonProfile = _jsonUtil.Serialize(profiles[sessionID], !_configServer.GetConfig<CoreConfig>(ConfigTypes.CORE).Features.CompressProfile);
var fmd5 = _hashUtil.GenerateMd5ForData(jsonProfile);
if (!saveMd5.TryGetValue(sessionID, out var currentMd5) || currentMd5 != fmd5) {
saveMd5[sessionID] = fmd5;
// save profile to disk
_vfs.WriteFile(filePath, jsonProfile);
}
start.Stop();
return start.ElapsedMilliseconds;
}
/**
* Remove a physical profile json from user/profiles
* @param sessionID Profile id to remove
* @returns true if file no longer exists
*/
public bool RemoveProfile(string sessionID)
{
var file = $"{profileFilepath}{sessionID}.json";
if (profiles.ContainsKey(sessionID))
{
profiles.Remove(sessionID);
_vfs.DeleteFile(file);
}
return !_vfs.FileExists(file);
}
}
+6 -4
View File
@@ -1,4 +1,5 @@
using System.Text.Json;
using Core.Utils;
namespace Core.Services;
@@ -8,17 +9,18 @@ public class I18nService
private Dictionary<string, string> _fallbacks;
private string _defaultLocale;
private string _directory;
private JsonUtil _jsonUtil;
private string _setLocale;
private Dictionary<string, Dictionary<string, string>> _loadedLocales = new();
public I18nService(List<string> locales, Dictionary<string, string> fallbacks, string defaultLocale, string directory)
public I18nService(JsonUtil jsonUtil, List<string> locales, Dictionary<string, string> fallbacks, string defaultLocale, string directory)
{
_locales = locales;
_fallbacks = fallbacks;
_defaultLocale = defaultLocale;
_directory = directory;
_jsonUtil = jsonUtil;
Initialize();
}
@@ -30,7 +32,7 @@ public class I18nService
throw new Exception($"Localisation files in directory {_directory} not found.");
foreach (var file in files)
_loadedLocales.Add(Path.GetFileNameWithoutExtension(file),
JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(file)) ?? new Dictionary<string, string>());
_jsonUtil.Deserialize<Dictionary<string, string>>(File.ReadAllText(file)) ?? new Dictionary<string, string>());
if (!_loadedLocales.ContainsKey(_defaultLocale))
throw new Exception($"The default locale '{_defaultLocale}' does not exist on the loaded locales.");
@@ -76,4 +78,4 @@ public class I18nService
// TODO: Deal with arguments
return GetLocalised(key);
}
}
}
+4 -2
View File
@@ -18,7 +18,8 @@ public class LocalisationService
ILogger logger,
RandomUtil randomUtil,
DatabaseServer databaseServer,
LocaleService localeService
LocaleService localeService,
JsonUtil jsonUtil
)
{
_logger = logger;
@@ -26,6 +27,7 @@ public class LocalisationService
_databaseServer = databaseServer;
_localeService = localeService;
_i18nService = new I18nService(
jsonUtil,
localeService.GetServerSupportedLocales(),
localeService.GetLocaleFallbacks(),
"en",
@@ -48,4 +50,4 @@ public class LocalisationService
{
throw new NotImplementedException();
}
}
}
+38
View File
@@ -29,4 +29,42 @@ public class FileUtil
{
return keepPath ? path.Split('.').First() : Path.GetFileNameWithoutExtension(path);
}
public bool DirectoryExists(string path)
{
return Directory.Exists(path);
}
public void CreateDirectory(string path)
{
Directory.CreateDirectory(path);
}
public bool FileExists(string path)
{
return File.Exists(path);
}
public string ReadFile(string path)
{
return File.ReadAllText(path);
}
public void WriteFile(string filePath, string jsonProfile)
{
if (!FileExists(filePath))
CreateFile(filePath);
File.WriteAllText(filePath, jsonProfile);
}
private void CreateFile(string filePath)
{
File.Create(filePath);
}
public void DeleteFile(string filePath)
{
File.Delete(filePath);
}
}
+5 -3
View File
@@ -13,13 +13,15 @@ namespace Core.Utils;
public class HttpResponseUtil
{
protected readonly LocalisationService _localisationService;
protected readonly JsonUtil _jsonUtil;
public HttpResponseUtil(
// JsonUtil jsonUtil,
JsonUtil jsonUtil,
LocalisationService localisationService
)
{
_localisationService = localisationService;
_jsonUtil = jsonUtil;
}
private readonly ImmutableList<Regex> _cleanupRegexList =
@@ -49,7 +51,7 @@ public class HttpResponseUtil
*/
public string NoBody<T>(T data)
{
return ClearString(JsonSerializer.Serialize(data));
return ClearString(_jsonUtil.Serialize(data));
}
/**
@@ -68,7 +70,7 @@ public class HttpResponseUtil
public string GetUnclearedBody<T>(T? data, int err = 0, string? errmsg = null)
{
return JsonSerializer.Serialize(new GetBodyResponseData<T> { Err = err, ErrMsg = errmsg, Data = data });
return _jsonUtil.Serialize(new GetBodyResponseData<T> { Err = err, ErrMsg = errmsg, Data = data });
}
public string EmptyResponse()
+6 -9
View File
@@ -10,17 +10,14 @@ namespace Core.Utils;
public class ImporterUtil
{
private readonly FileUtil _fileUtil;
private readonly JsonUtil _jsonUtil;
private readonly HashSet<string> filesToIgnore = ["bearsuits.json", "usecsuits.json", "archivedquests.json"];
private readonly JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions
{
UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow, Converters = { new ListOrTConverterFactory(), new DictionaryOrListConverter() }
};
public ImporterUtil(FileUtil fileUtil)
public ImporterUtil(FileUtil fileUtil, JsonUtil jsonUtil)
{
_fileUtil = fileUtil;
_jsonUtil = jsonUtil;
}
/**
@@ -64,7 +61,7 @@ public class ImporterUtil
);
try
{
var fileDeserialized = JsonSerializer.Deserialize(fileData, propertyType, jsonSerializerOptions);
var fileDeserialized = _jsonUtil.Deserialize(fileData, propertyType);
if (onObjectDeserialized != null)
onObjectDeserialized(file, fileDeserialized);
@@ -159,7 +156,7 @@ public class ImporterUtil
if (matchedProperty == null)
throw new Exception($"Unable to find property '{Path.GetFileNameWithoutExtension(file)}' for type '{loadedType.Name}'");
var propertyType = matchedProperty.PropertyType;
var fileDeserialized = JsonSerializer.Deserialize(fileData, propertyType);
var fileDeserialized = _jsonUtil.Deserialize(fileData, propertyType);
onObjectDeserialized(file, fileDeserialized);
matchedProperty.GetSetMethod().Invoke(result, [fileDeserialized]);
@@ -198,7 +195,7 @@ public class ImporterUtil
promises.Add(File.ReadAllTextAsync(fileNode).ContinueWith(fd =>
{
onReadCallback(fileNode, fd.Result);
return JsonSerializer.Deserialize(fd.Result, typeof(object));
return _jsonUtil.Deserialize(fd.Result, typeof(object));
}));
/*
this.vfs
@@ -0,0 +1,40 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Core.Utils.Json.Converters;
public class EftEnumConverter<T> : JsonConverter<T>
{
private static readonly JsonSerializerOptions _options = new() {Converters = { new JsonStringEnumConverter() }};
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String || reader.TokenType == JsonTokenType.PropertyName)
{
var str = reader.GetString();
return (T)Enum.Parse(typeof(T), str, true);
}
if (reader.TokenType == JsonTokenType.Number)
{
var str = reader.GetInt32().ToString();
return (T)Enum.Parse(typeof(T), str, true);
}
return default;
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, _options);
}
public override T ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return Read(ref reader, typeToConvert, options);
}
public override void WriteAsPropertyName(Utf8JsonWriter writer, [DisallowNull] T value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, _options);
}
}
@@ -1,3 +1,4 @@
using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;
@@ -39,7 +40,7 @@ public class StringToNumberFactoryConverter : JsonConverterFactory
}
catch (Exception ex)
{
Console.WriteLine($"Tried to convert {value} into {type.Name} but failed to parse it, null value will be used instead.");
Debug.WriteLine($"Tried to convert {value} into {type.Name} but failed to parse it, null value will be used instead.");
}
return default;
+42 -1
View File
@@ -1,8 +1,49 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Core.Annotations;
using Core.Models.Enums;
using Core.Models.Spt.Dialog;
using Core.Utils.Json.Converters;
namespace Core.Utils;
[Injectable(InjectionType.Singleton)]
public class JsonUtil
{
}
private readonly JsonSerializerOptions jsonSerializerOptions = new()
{
UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow,
Converters =
{
new ListOrTConverterFactory(),
new DictionaryOrListConverter(),
new EftEnumConverter<SptAirdropTypeEnum>(),
new EftEnumConverter<GiftSenderType>(),
new EftEnumConverter<SeasonalEventType>(),
new EftEnumConverter<ProfileChangeEventType>()
}
};
public T? Deserialize<T>(string? json)
{
return string.IsNullOrEmpty(json) ? default : JsonSerializer.Deserialize<T>(json, jsonSerializerOptions);
}
public object? Deserialize(string? json, Type type)
{
return string.IsNullOrEmpty(json) ? null : JsonSerializer.Deserialize(json, type, jsonSerializerOptions);
}
public string? Serialize<T>(T? obj, bool indented = false)
{
jsonSerializerOptions.WriteIndented = indented;
return obj == null ? null : JsonSerializer.Serialize(obj, jsonSerializerOptions);
}
public string? Serialize(object? obj, Type type, bool indented = false)
{
jsonSerializerOptions.WriteIndented = indented;
return obj == null ? null : JsonSerializer.Serialize(obj, type, jsonSerializerOptions);
}
}
+188
View File
@@ -0,0 +1,188 @@
using Core.Annotations;
using Core.Models.Enums;
using Core.Models.Logging;
using Core.Models.Spt.Config;
using Core.Servers;
using Core.Services;
namespace Core.Utils;
[Injectable]
public class WatermarkLocale {
protected List<string> description;
protected List<string> warning;
protected List<string> modding;
public WatermarkLocale(LocalisationService localisationService) {
description = [
localisationService.GetText("watermark-discord_url"),
"",
localisationService.GetText("watermark-free_of_charge"),
localisationService.GetText("watermark-paid_scammed"),
localisationService.GetText("watermark-commercial_use_prohibited"),
];
warning = [
"",
localisationService.GetText("watermark-testing_build"),
localisationService.GetText("watermark-no_support"),
"",
$"{localisationService.GetText("watermark-report_issues_to")}:",
localisationService.GetText("watermark-issue_tracker_url"),
"",
localisationService.GetText("watermark-use_at_own_risk"),
];
modding = [
"",
localisationService.GetText("watermark-modding_disabled"),
"",
localisationService.GetText("watermark-not_an_issue"),
localisationService.GetText("watermark-do_not_report"),
];
}
public List<string> GetDescription() => description;
public List<string> GetWarning() => warning;
public List<string> GetModding() => modding;
}
[Injectable]
public class Watermark {
protected CoreConfig sptConfig;
protected List<string> text = [];
protected string versionLabel = "";
protected Models.Utils.ILogger _logger;
protected ConfigServer _configServer;
protected LocalisationService _localisationService;
protected WatermarkLocale _watermarkLocale;
public Watermark(
Models.Utils.ILogger logger,
ConfigServer configServer,
LocalisationService localisationService,
WatermarkLocale watermarkLocale
) {
_logger = logger;
_configServer = configServer;
_localisationService = localisationService;
_watermarkLocale = watermarkLocale;
sptConfig = _configServer.GetConfig<CoreConfig>(ConfigTypes.CORE);
}
public void Initialize()
{
var description = _watermarkLocale.GetDescription();
var warning = _watermarkLocale.GetWarning();
var modding = _watermarkLocale.GetModding();
var versionTag = GetVersionTag();
versionLabel = $"{sptConfig.ProjectName} {versionTag}";
text = [versionLabel];
text = [..text, ..description];
/*
if (ProgramStatics.DEBUG) {
text = text.concat([...warning]);
}
if (!ProgramStatics.MODS) {
text = text.concat([...modding]);
}
*/
if (sptConfig.CustomWatermarkLocaleKeys?.Count > 0) {
foreach (var key in sptConfig.CustomWatermarkLocaleKeys) {
text.AddRange(["", _localisationService.GetText(key)]);
}
}
SetTitle();
ResetCursor();
Draw();
}
/**
* Get a version string (x.x.x) or (x.x.x-BLEEDINGEDGE) OR (X.X.X (18xxx))
* @param withEftVersion Include the eft version this spt version was made for
* @returns string
*/
public string GetVersionTag(bool withEftVersion = false) {
var sptVersion = /*ProgramStatics.SPT_VERSION ||*/ sptConfig.SptVersion;
var versionTag = /*ProgramStatics.DEBUG&*/ $"{sptVersion} - {_localisationService.GetText("bleeding_edge_build")}";
if (withEftVersion) {
var tarkovVersion = sptConfig.CompatibleTarkovVersion.Split(".").First();
return $"{versionTag} ({tarkovVersion})";
}
return versionTag;
}
/**
* Handle singleplayer/settings/version
* Get text shown in game on screen, can't be translated as it breaks bsgs client when certian characters are used
* @returns string
*/
public string GetInGameVersionLabel()
{
var sptVersion = /*ProgramStatics.SPT_VERSION ||*/ sptConfig.SptVersion;
var versionTag = /*ProgramStatics.DEBUG ? */
$"{sptVersion} - BLEEDINGEDGE { /*ProgramStatics.COMMIT?.slice(0, 6) ?? */""}";
//: `${sptVersion} - ${ProgramStatics.COMMIT?.slice(0, 6) ?? ""}`;
return $"{sptConfig.ProjectName} {versionTag}";
}
/** Set window title */
protected void SetTitle()
{
Console.Title = versionLabel;
}
/** Reset console cursor to top */
protected void ResetCursor()
{
/*
if (!ProgramStatics.COMPILED) {
process.stdout.write("\u001B[2J\u001B[0;0f");
}
*/
}
/** Draw the watermark */
protected void Draw()
{
var result = new List<string>();
// Calculate size, add 10% for spacing to the right
var longestLength = text.Aggregate((a, b) => a.Length > b.Length ? a : b).Length * 1.1;
// Create line of - to add top/bottom of watermark
var line = "";
for (var i = 0; i < longestLength; ++i) {
line += "─";
}
// Opening line
result.Add($"┌─{line}─┐");
// Add content of watermark to screen
foreach (var watermarkText in text) {
var spacingSize = longestLength - watermarkText.Length;
var textWithRightPadding = watermarkText;
for (var i = 0; i < spacingSize; ++i) {
textWithRightPadding += " ";
}
result.Add($"│ {textWithRightPadding} │");
}
// Closing line
result.Add($"└─{line}─┘");
// Log watermark to screen
foreach (var text in result) {
_logger.LogWithColor(text, LogTextColor.Yellow);
}
}
}
+4 -1
View File
@@ -19,6 +19,9 @@ public static class Program
{
var serviceProvider = builder.Services.BuildServiceProvider();
var watermark = serviceProvider.GetService<Watermark>();
watermark.Initialize();
// TODO: var preSptModLoader = serviceProvider.GetService<PreSptModLoader>();
var app = serviceProvider.GetService<App>();
var appContext = serviceProvider.GetService<ApplicationContext>();
appContext.AddValue(ContextVariableType.APP_BUILDER, builder);
@@ -78,4 +81,4 @@ public static class Program
RegisterComponents(builderServices, typeof(App).Assembly.GetTypes()
.Where(type => Attribute.IsDefined(type, typeof(Injectable))));
}
}
}