runnable app!
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
namespace Core.Annotations;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class Injectable(InjectionType injectionType = InjectionType.Scoped, Type? type = null) : Attribute
|
||||
public class Injectable(InjectionType injectionType = InjectionType.Scoped, Type? type = null, int typePriority = Int32.MaxValue) : Attribute
|
||||
{
|
||||
public InjectionType InjectionType { get; set; } = injectionType;
|
||||
public Type? InjectableTypeOverride { get; set; } = type;
|
||||
public int TypePriority { get; set; } = typePriority;
|
||||
}
|
||||
|
||||
public enum InjectionType
|
||||
|
||||
@@ -1,26 +1,34 @@
|
||||
using Core.DI;
|
||||
using Core.Annotations;
|
||||
using Core.Context;
|
||||
using Core.DI;
|
||||
using Core.Servers;
|
||||
|
||||
namespace Core.Callbacks;
|
||||
|
||||
[Injectable(InjectionType.Singleton, typePriority: 1)]
|
||||
public class HttpCallbacks : OnLoad
|
||||
{
|
||||
public HttpCallbacks()
|
||||
private readonly HttpServer _httpServer;
|
||||
private readonly ApplicationContext _applicationContext;
|
||||
public HttpCallbacks(HttpServer httpServer, ApplicationContext applicationContext)
|
||||
{
|
||||
|
||||
_httpServer = httpServer;
|
||||
_applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public async Task OnLoad()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
_httpServer.Load((WebApplicationBuilder) _applicationContext.GetLatestValue(ContextVariableType.APP_BUILDER).Value);
|
||||
_applicationContext.ClearValues(ContextVariableType.APP_BUILDER);
|
||||
}
|
||||
|
||||
public string GetRoute()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return "spt-http";
|
||||
}
|
||||
|
||||
public string GetImage()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -13,4 +13,5 @@ public enum ContextVariableType
|
||||
RAID_ADJUSTMENTS = 4,
|
||||
/** Data returned from client request object from endLocalRaid() */
|
||||
TRANSIT_INFO = 5,
|
||||
APP_BUILDER = 6
|
||||
}
|
||||
@@ -10,10 +10,12 @@ public class DialogueController
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="chatBot"></param>
|
||||
/*
|
||||
public void RegisterChatBot(DialogueChatBot chatBot) // TODO: this is in with the helper types
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Handle onUpdate spt event
|
||||
|
||||
@@ -70,10 +70,12 @@ public class GameController
|
||||
/// </summary>
|
||||
/// <param name="sessionId"></param>
|
||||
/// <returns></returns>
|
||||
/*
|
||||
public CurrentGroupResponse GetCurrentGroup(string sessionId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/checkVersion
|
||||
|
||||
@@ -125,7 +125,7 @@ public class TradeController
|
||||
private void MailMoneyToPlayer(
|
||||
string sessionId,
|
||||
int roublesToSend,
|
||||
Traders trader) // TODO: This is a static class now and cannot be passed as a param.
|
||||
string trader)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
+408
-408
File diff suppressed because it is too large
Load Diff
@@ -9,9 +9,13 @@ public class Item
|
||||
public string Id { get; set; }
|
||||
[JsonPropertyName("_tpl")]
|
||||
public string Template { get; set; }
|
||||
[JsonPropertyName("parentId")]
|
||||
public string? ParentId { get; set; }
|
||||
[JsonPropertyName("slotId")]
|
||||
public string? SlotId { get; set; }
|
||||
[JsonPropertyName("location")]
|
||||
public object? Location { get; set; } // TODO: Can be IItemLocation or number
|
||||
[JsonPropertyName("upd")]
|
||||
public Upd? Update { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,32 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Core.Models.Eft.Match;
|
||||
|
||||
namespace Core.Models.Eft.Ws;
|
||||
|
||||
public class WsGroupMatchInviteAccept : WsNotificationEvent, GroupCharacter // TODO: trying to inherit multiTypes
|
||||
public class WsGroupMatchInviteAccept : WsNotificationEvent // TODO: trying to inherit multiTypes
|
||||
{
|
||||
// Copy pasted properties from GroupCharacter to resolve multitype
|
||||
[JsonPropertyName("_id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonPropertyName("aid")]
|
||||
public int Aid { get; set; }
|
||||
|
||||
[JsonPropertyName("Info")]
|
||||
public CharacterInfo Info { get; set; }
|
||||
|
||||
[JsonPropertyName("PlayerVisualRepresentation")]
|
||||
public PlayerVisualRepresentation? VisualRepresentation { get; set; }
|
||||
|
||||
[JsonPropertyName("isLeader")]
|
||||
public bool IsLeader { get; set; }
|
||||
|
||||
[JsonPropertyName("isReady")]
|
||||
public bool? IsReady { get; set; }
|
||||
|
||||
[JsonPropertyName("region")]
|
||||
public string? Region { get; set; }
|
||||
|
||||
[JsonPropertyName("lookingGroup")]
|
||||
public bool? LookingGroup { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Core.Models.Enums;
|
||||
|
||||
public enum ModSpawn
|
||||
{
|
||||
/** Chosen mod should be the tpl from the default weapon template */
|
||||
DEFAULT_MOD = 0,
|
||||
/** Normal behaviour */
|
||||
SPAWN = 1,
|
||||
/** Item should not be chosen */
|
||||
SKIP = 2,
|
||||
}
|
||||
@@ -19,7 +19,7 @@ public class GenerateEquipmentProperties
|
||||
public Dictionary<string, int> RootEquipmentPool { get; set; }
|
||||
|
||||
[JsonPropertyName("modPool")]
|
||||
public Mods ModPool { get; set; }
|
||||
public GlobalMods ModPool { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of mod items and their chance to spawn for this bot type
|
||||
@@ -34,7 +34,7 @@ public class GenerateEquipmentProperties
|
||||
public BotData BotData { get; set; }
|
||||
|
||||
[JsonPropertyName("inventory")]
|
||||
public PmcInventory Inventory { get; set; }
|
||||
public BotBaseInventory Inventory { get; set; }
|
||||
|
||||
[JsonPropertyName("botEquipmentConfig")]
|
||||
public EquipmentFilters BotEquipmentConfig { get; set; }
|
||||
|
||||
@@ -11,7 +11,7 @@ public class GenerateWeaponRequest
|
||||
|
||||
/** Pool of compatible mods to attach to weapon */
|
||||
[JsonPropertyName("modPool")]
|
||||
public Mods ModPool { get; set; }
|
||||
public GlobalMods ModPool { get; set; }
|
||||
|
||||
/** ParentId of weapon */
|
||||
[JsonPropertyName("weaponId")]
|
||||
@@ -71,4 +71,31 @@ public class WeaponStats
|
||||
|
||||
[JsonPropertyName("hasRearIronSight")]
|
||||
public bool? HasRearIronSight { get; set; }
|
||||
}
|
||||
|
||||
public class BotModLimits
|
||||
{
|
||||
[JsonPropertyName("scope")]
|
||||
public ItemCount Scope { get; set; }
|
||||
|
||||
[JsonPropertyName("scopeMax")]
|
||||
public int ScopeMax { get; set; }
|
||||
|
||||
[JsonPropertyName("scopeBaseTypes")]
|
||||
public string[] ScopeBaseTypes { get; set; }
|
||||
|
||||
[JsonPropertyName("flashlightLaser")]
|
||||
public ItemCount FlashlightLaser { get; set; }
|
||||
|
||||
[JsonPropertyName("flashlightLaserMax")]
|
||||
public int FlashlightLaserMax { get; set; }
|
||||
|
||||
[JsonPropertyName("flashlightLaserBaseTypes")]
|
||||
public string[] FlashlightLaserBaseTypes { get; set; }
|
||||
}
|
||||
|
||||
public class ItemCount
|
||||
{
|
||||
[JsonPropertyName("count")]
|
||||
public int Count { get; set; }
|
||||
}
|
||||
@@ -15,7 +15,7 @@ public class GenerateWeaponResult
|
||||
public string ChosenUbglAmmoTemplate { get; set; }
|
||||
|
||||
[JsonPropertyName("weaponMods")]
|
||||
public Mods WeaponMods { get; set; }
|
||||
public GlobalMods WeaponMods { get; set; }
|
||||
|
||||
[JsonPropertyName("weaponTemplate")]
|
||||
public TemplateItem WeaponTemplate { get; set; }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Core.Models.Eft.Common.Tables;
|
||||
using Core.Models.Enums;
|
||||
using Core.Models.Spt.Config;
|
||||
|
||||
namespace Core.Models.Spt.Bots;
|
||||
|
||||
@@ -23,7 +23,7 @@ public class HttpConfig : BaseConfig
|
||||
public string BackendIp { get; set; }
|
||||
|
||||
[JsonPropertyName("backendPort")]
|
||||
public string BackendPort { get; set; }
|
||||
public int BackendPort { get; set; }
|
||||
|
||||
[JsonPropertyName("webSocketPingDelayMs")]
|
||||
public int WebSocketPingDelayMs { get; set; }
|
||||
|
||||
@@ -205,55 +205,55 @@ public class BotTypeLimit : MinMax
|
||||
public class LootMultiplier
|
||||
{
|
||||
[JsonPropertyName("bigmap")]
|
||||
public int BigMap { get; set; }
|
||||
public double BigMap { get; set; }
|
||||
|
||||
[JsonPropertyName("develop")]
|
||||
public int Develop { get; set; }
|
||||
public double Develop { get; set; }
|
||||
|
||||
[JsonPropertyName("factory4_day")]
|
||||
public int Factory4Day { get; set; }
|
||||
public double Factory4Day { get; set; }
|
||||
|
||||
[JsonPropertyName("factory4_night")]
|
||||
public int Factory4Night { get; set; }
|
||||
public double Factory4Night { get; set; }
|
||||
|
||||
[JsonPropertyName("interchange")]
|
||||
public int Interchange { get; set; }
|
||||
public double Interchange { get; set; }
|
||||
|
||||
[JsonPropertyName("laboratory")]
|
||||
public int Laboratory { get; set; }
|
||||
public double Laboratory { get; set; }
|
||||
|
||||
[JsonPropertyName("rezervbase")]
|
||||
public int RezervBase { get; set; }
|
||||
public double RezervBase { get; set; }
|
||||
|
||||
[JsonPropertyName("shoreline")]
|
||||
public int Shoreline { get; set; }
|
||||
public double Shoreline { get; set; }
|
||||
|
||||
[JsonPropertyName("woods")]
|
||||
public int Woods { get; set; }
|
||||
public double Woods { get; set; }
|
||||
|
||||
[JsonPropertyName("hideout")]
|
||||
public int Hideout { get; set; }
|
||||
public double Hideout { get; set; }
|
||||
|
||||
[JsonPropertyName("lighthouse")]
|
||||
public int Lighthouse { get; set; }
|
||||
public double Lighthouse { get; set; }
|
||||
|
||||
[JsonPropertyName("privatearea")]
|
||||
public int PrivateArea { get; set; }
|
||||
public double PrivateArea { get; set; }
|
||||
|
||||
[JsonPropertyName("suburbs")]
|
||||
public int Suburbs { get; set; }
|
||||
public double Suburbs { get; set; }
|
||||
|
||||
[JsonPropertyName("tarkovstreets")]
|
||||
public int TarkovStreets { get; set; }
|
||||
public double TarkovStreets { get; set; }
|
||||
|
||||
[JsonPropertyName("terminal")]
|
||||
public int Terminal { get; set; }
|
||||
public double Terminal { get; set; }
|
||||
|
||||
[JsonPropertyName("town")]
|
||||
public int Town { get; set; }
|
||||
public double Town { get; set; }
|
||||
|
||||
[JsonPropertyName("sandbox")]
|
||||
public int Sandbox { get; set; }
|
||||
public double Sandbox { get; set; }
|
||||
}
|
||||
|
||||
public class ContainerRandomisationSettings
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Core.Models.Common;
|
||||
using Core.Models.Enums;
|
||||
using Core.Utils.Json.Converters;
|
||||
|
||||
namespace Core.Models.Spt.Config;
|
||||
|
||||
@@ -77,7 +78,8 @@ public class EventQuestData
|
||||
public long StartTimestamp { get; set; }
|
||||
|
||||
[JsonPropertyName("endTimestamp")]
|
||||
public long EndTimestamp { get; set; }
|
||||
[JsonConverter(typeof(NullableObjectToLongConverter))]
|
||||
public long? EndTimestamp { get; set; }
|
||||
|
||||
[JsonPropertyName("yearly")]
|
||||
public bool Yearly { get; set; }
|
||||
@@ -157,13 +159,13 @@ public class RewardScaling
|
||||
public List<int> Items { get; set; }
|
||||
|
||||
[JsonPropertyName("reputation")]
|
||||
public List<int> Reputation { get; set; }
|
||||
public List<double> Reputation { get; set; }
|
||||
|
||||
[JsonPropertyName("rewardSpread")]
|
||||
public int RewardSpread { get; set; }
|
||||
public double RewardSpread { get; set; }
|
||||
|
||||
[JsonPropertyName("skillRewardChance")]
|
||||
public List<int> SkillRewardChance { get; set; }
|
||||
public List<double> SkillRewardChance { get; set; }
|
||||
|
||||
[JsonPropertyName("skillPointReward")]
|
||||
public List<int> SkillPointReward { get; set; }
|
||||
@@ -217,7 +219,7 @@ public class Exploration : BaseQuestConfig
|
||||
public class SpecificExits
|
||||
{
|
||||
[JsonPropertyName("probability")]
|
||||
public int Probability { get; set; }
|
||||
public double Probability { get; set; }
|
||||
|
||||
[JsonPropertyName("passageRequirementWhitelist")]
|
||||
public List<string> PassageRequirementWhitelist { get; set; }
|
||||
|
||||
@@ -59,7 +59,7 @@ public class Chance
|
||||
|
||||
/** Value to multiply the sell chance by */
|
||||
[JsonPropertyName("sellMultiplier")]
|
||||
public int SellMultiplier { get; set; }
|
||||
public double SellMultiplier { get; set; }
|
||||
|
||||
/** Max possible sell chance % for a player listed offer */
|
||||
[JsonPropertyName("maxSellChancePercent")]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Core.Models.Common;
|
||||
using Core.Models.Enums;
|
||||
using Core.Utils.Json.Converters;
|
||||
|
||||
namespace Core.Models.Spt.Config;
|
||||
|
||||
@@ -31,15 +32,19 @@ public class SeasonDateTimes
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("startDay")]
|
||||
[JsonConverter(typeof(NotNullObjectToIntConverter))]
|
||||
public int StartDay { get; set; }
|
||||
|
||||
[JsonPropertyName("startMonth")]
|
||||
[JsonConverter(typeof(NotNullObjectToIntConverter))]
|
||||
public int StartMonth { get; set; }
|
||||
|
||||
[JsonPropertyName("endDay")]
|
||||
[JsonConverter(typeof(NotNullObjectToIntConverter))]
|
||||
public int EndDay { get; set; }
|
||||
|
||||
[JsonPropertyName("endMonth")]
|
||||
[JsonConverter(typeof(NotNullObjectToIntConverter))]
|
||||
public int EndMonth { get; set; }
|
||||
}
|
||||
|
||||
@@ -60,7 +65,7 @@ public class WeatherValues
|
||||
public class SeasonalValues
|
||||
{
|
||||
[JsonPropertyName("clouds")]
|
||||
public WeatherSettings<string> Clouds { get; set; }
|
||||
public WeatherSettings<double> Clouds { get; set; }
|
||||
|
||||
[JsonPropertyName("windSpeed")]
|
||||
public WeatherSettings<double> WindSpeed { get; set; }
|
||||
@@ -78,7 +83,7 @@ public class SeasonalValues
|
||||
public MinMax RainIntensity { get; set; }
|
||||
|
||||
[JsonPropertyName("fog")]
|
||||
public WeatherSettings<string> Fog { get; set; }
|
||||
public WeatherSettings<double> Fog { get; set; }
|
||||
|
||||
[JsonPropertyName("temp")]
|
||||
public TempDayNight Temp { get; set; }
|
||||
|
||||
@@ -89,7 +89,7 @@ public class ProfileChangeEvent
|
||||
public ProfileChangeEventType Type { get; set; }
|
||||
|
||||
[JsonPropertyName("value")]
|
||||
public int Value { get; set; }
|
||||
public double Value { get; set; }
|
||||
|
||||
[JsonPropertyName("entity")]
|
||||
public string? Entity { get; set; }
|
||||
|
||||
@@ -5,14 +5,14 @@ namespace Core.Models.Spt.Server;
|
||||
|
||||
public class DatabaseTables
|
||||
{
|
||||
public Bots.Bots? bots { get; }
|
||||
public Hideout.Hideout? hideout { get; }
|
||||
public LocaleBase? locales { get; }
|
||||
public Locations? locations { get; }
|
||||
public Match? match { get; }
|
||||
public Templates.Templates? templates { get; }
|
||||
public Dictionary<string, Trader>? traders { get; }
|
||||
public Globals? globals { get; }
|
||||
public ServerBase? server { get; }
|
||||
public SettingsBase? settings { get; }
|
||||
public Bots.Bots? bots { get; set; }
|
||||
public Hideout.Hideout? hideout { get; set; }
|
||||
public LocaleBase? locales { get; set; }
|
||||
public Locations? locations { get; set; }
|
||||
public Match? match { get; set; }
|
||||
public Templates.Templates? templates { get; set; }
|
||||
public Dictionary<string, Trader>? traders { get; set; }
|
||||
public Globals? globals { get; set; }
|
||||
public ServerBase? server { get; set; }
|
||||
public SettingsBase? settings { get; set; }
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Runtime.InteropServices.JavaScript;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Core.Annotations;
|
||||
using Core.Models.Enums;
|
||||
using Core.Models.Spt.Config;
|
||||
@@ -11,8 +12,8 @@ namespace Core.Servers;
|
||||
public class ConfigServer
|
||||
{
|
||||
private ILogger _logger;
|
||||
protected Dictionary<string, object> configs;
|
||||
protected readonly string[] acceptableFileExtensions = ["json", "jsonc"];
|
||||
protected Dictionary<string, object> configs = new();
|
||||
protected readonly string[] acceptableFileExtensions = [".json", ".jsonc"];
|
||||
|
||||
public ConfigServer(
|
||||
ILogger logger
|
||||
@@ -53,7 +54,7 @@ public class ConfigServer
|
||||
{
|
||||
var fileContent = File.ReadAllText(file);
|
||||
var type = GetConfigTypeByFilename(file);
|
||||
var deserializedContent = JsonSerializer.Deserialize(fileContent, type);
|
||||
var deserializedContent = JsonSerializer.Deserialize(fileContent, type, options: new JsonSerializerOptions() {Converters = { new JsonStringEnumConverter() }});
|
||||
|
||||
if (deserializedContent == null)
|
||||
{
|
||||
@@ -61,7 +62,7 @@ public class ConfigServer
|
||||
throw new Exception($"Server will not run until the: {file} config error mentioned above is fixed");
|
||||
}
|
||||
|
||||
this.configs[$"spt-{Path.GetFileNameWithoutExtension(file)}"] = deserializedContent;
|
||||
configs[$"spt-{Path.GetFileNameWithoutExtension(file)}"] = deserializedContent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Core.Models.Spt.Server;
|
||||
using Core.Annotations;
|
||||
using Core.Models.Spt.Server;
|
||||
|
||||
namespace Core.Servers;
|
||||
|
||||
[Injectable(InjectionType.Singleton)]
|
||||
public class DatabaseServer
|
||||
{
|
||||
protected DatabaseTables tableData = new();
|
||||
|
||||
@@ -36,7 +36,7 @@ public class LocalisationService
|
||||
|
||||
public string GetText(string key, object? args = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return _i18nService.GetLocalised(key, args);
|
||||
}
|
||||
|
||||
public ICollection<string> GetKeys()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Core.Annotations;
|
||||
using Core.DI;
|
||||
using Core.Models.Enums;
|
||||
using Core.Models.Spt.Config;
|
||||
using Core.Servers;
|
||||
@@ -46,4 +47,83 @@ public class App
|
||||
|
||||
_coreConfig = configServer.GetConfig<CoreConfig>(ConfigTypes.CORE);
|
||||
}
|
||||
|
||||
public async Task Load()
|
||||
{
|
||||
// execute onLoad callbacks
|
||||
_logger.Info(_localisationService.GetText("executing_startup_callbacks"));
|
||||
|
||||
/*
|
||||
_logger.Debug($"OS: {os.arch()} | {os.version()} | {process.platform}");
|
||||
_logger.Debug($"CPU: {os.cpus()[0]?.model} cores: {os.cpus().length}");
|
||||
_logger.Debug($"RAM: {(os.totalmem() / 1024 / 1024 / 1024).toFixed(2)}GB");
|
||||
_logger.Debug($"PATH: {this.encodingUtil.toBase64(process.argv[0])}");
|
||||
_logger.Debug($"PATH: {this.encodingUtil.toBase64(process.execPath)}");
|
||||
_logger.Debug($"Server: {ProgramStatics.SPT_VERSION || this.coreConfig.sptVersion}");
|
||||
|
||||
const nodeVersion = process.version.replace(/^v/, "");
|
||||
if (ProgramStatics.EXPECTED_NODE && nodeVersion !== ProgramStatics.EXPECTED_NODE) {
|
||||
this.logger.error(
|
||||
"Node version mismatch. Required: ${ProgramStatics.EXPECTED_NODE} | Current: ${nodeVersion}",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
_logger.Debug("Node: ${nodeVersion}");
|
||||
|
||||
if (ProgramStatics.BUILD_TIME) {
|
||||
_logger.Debug("Date: ${ProgramStatics.BUILD_TIME}");
|
||||
}
|
||||
|
||||
if (ProgramStatics.COMMIT) {
|
||||
_logger.Debug("Commit: ${ProgramStatics.COMMIT}");
|
||||
}
|
||||
*/
|
||||
foreach (var onLoad in _onLoad)
|
||||
{
|
||||
await onLoad.OnLoad();
|
||||
}
|
||||
|
||||
var timer = new Timer(_ =>
|
||||
{
|
||||
update(_onUpdate);
|
||||
}, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(5000));
|
||||
}
|
||||
|
||||
protected async Task update(IEnumerable<OnUpdate> onUpdateComponents)
|
||||
{
|
||||
// If the server has failed to start, skip any update calls
|
||||
if (!_httpServer.IsStarted() || !_databaseService.IsDatabaseValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var updateable in onUpdateComponents)
|
||||
{
|
||||
var success = false;
|
||||
if (!_onUpdateLastRun.TryGetValue(updateable.GetRoute(), out var lastRunTimeTimestamp))
|
||||
lastRunTimeTimestamp = 0;
|
||||
var secondsSinceLastRun = _timeUtil.GetTimeStamp() - lastRunTimeTimestamp;
|
||||
|
||||
try {
|
||||
success = await updateable.OnUpdate(secondsSinceLastRun);
|
||||
} catch (Exception err) {
|
||||
LogUpdateException(err, updateable);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
_onUpdateLastRun[updateable.GetRoute()] = _timeUtil.GetTimeStamp();
|
||||
} else {
|
||||
/* temporary for debug */
|
||||
var warnTime = 20 * 60;
|
||||
|
||||
if (secondsSinceLastRun % warnTime == 0) {
|
||||
_logger.Debug(_localisationService.GetText("route_onupdate_no_response", updateable.GetRoute()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void LogUpdateException(Exception err, OnUpdate updateable) {
|
||||
_logger.Error(_localisationService.GetText("scheduled_event_failed_to_run", updateable.GetRoute()));
|
||||
_logger.Error(err.ToString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
using Core.Annotations;
|
||||
using Core.DI;
|
||||
using Core.Models.Enums;
|
||||
using Core.Models.Spt.Config;
|
||||
using Core.Models.Spt.Server;
|
||||
using Core.Servers;
|
||||
using Core.Services;
|
||||
using ILogger = Core.Models.Utils.ILogger;
|
||||
|
||||
namespace Core.Utils;
|
||||
|
||||
[Injectable(InjectionType.Singleton, typePriority: 0)]
|
||||
public class DatabaseImporter : OnLoad
|
||||
{
|
||||
private object hashedFile;
|
||||
private ValidationResult valid = ValidationResult.UNDEFINED;
|
||||
private string filepath;
|
||||
protected HttpConfig httpConfig;
|
||||
|
||||
protected readonly ILogger _logger;
|
||||
protected readonly LocalisationService _localisationService;
|
||||
protected readonly DatabaseServer _databaseServer;
|
||||
//protected readonly ImageRouter _imageRouter;
|
||||
protected readonly EncodingUtil _encodingUtil;
|
||||
protected readonly HashUtil _hashUtil;
|
||||
protected readonly ImporterUtil _importerUtil;
|
||||
protected readonly ConfigServer _configServer;
|
||||
|
||||
public DatabaseImporter(
|
||||
ILogger logger,
|
||||
// TODO: are we gonna use this? @inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||
LocalisationService localisationService,
|
||||
DatabaseServer databaseServer,
|
||||
//ImageRouter imageRouter,
|
||||
EncodingUtil encodingUtil,
|
||||
HashUtil hashUtil,
|
||||
ImporterUtil importerUtil,
|
||||
ConfigServer configServer
|
||||
) {
|
||||
_logger = logger;
|
||||
_localisationService = localisationService;
|
||||
_databaseServer = databaseServer;
|
||||
_encodingUtil = encodingUtil;
|
||||
_hashUtil = hashUtil;
|
||||
_importerUtil = importerUtil;
|
||||
_configServer = configServer;
|
||||
httpConfig = _configServer.GetConfig<HttpConfig>(ConfigTypes.HTTP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path to spt data
|
||||
* @returns path to data
|
||||
*/
|
||||
public string GetSptDataPath() {
|
||||
return "./Assets/";
|
||||
}
|
||||
|
||||
public async Task OnLoad()
|
||||
{
|
||||
filepath = GetSptDataPath();
|
||||
|
||||
/*
|
||||
if (ProgramStatics.COMPILED) {
|
||||
try {
|
||||
// Reading the dynamic SHA1 file
|
||||
const file = "checks.dat";
|
||||
const fileWithPath = `${this.filepath}${file}`;
|
||||
if (this.vfs.exists(fileWithPath)) {
|
||||
this.hashedFile = this.jsonUtil.deserialize(
|
||||
this.encodingUtil.fromBase64(this.vfs.readFile(fileWithPath)),
|
||||
file,
|
||||
);
|
||||
} else {
|
||||
this.valid = ValidationResult.NOT_FOUND;
|
||||
this.logger.debug(this.localisationService.getText("validation_not_found"));
|
||||
}
|
||||
} catch (e) {
|
||||
this.valid = ValidationResult.FAILED;
|
||||
this.logger.warning(this.localisationService.getText("validation_error_decode"));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
await HydrateDatabase(filepath);
|
||||
|
||||
var imageFilePath = $"${filepath}images/";
|
||||
/*
|
||||
var directories = this.vfs.getDirs(imageFilePath);
|
||||
this.loadImages(imageFilePath, directories, [
|
||||
"/files/achievement/",
|
||||
"/files/CONTENT/banners/",
|
||||
"/files/handbook/",
|
||||
"/files/Hideout/",
|
||||
"/files/launcher/",
|
||||
"/files/prestige/",
|
||||
"/files/quest/icon/",
|
||||
"/files/trader/avatar/",
|
||||
]);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all json files in database folder and map into a json object
|
||||
* @param filepath path to database folder
|
||||
*/
|
||||
protected async Task HydrateDatabase(string filepath)
|
||||
{
|
||||
_logger.Info(_localisationService.GetText("importing_database"));
|
||||
|
||||
var dataToImport = await _importerUtil.LoadRecursiveAsync(
|
||||
$"{filepath}database/",
|
||||
typeof(DatabaseTables),
|
||||
OnReadValidate
|
||||
);
|
||||
|
||||
var validation = valid == ValidationResult.FAILED || this.valid == ValidationResult.NOT_FOUND ? "." : "";
|
||||
_logger.Info($"{_localisationService.GetText("importing_database_finish")}{validation}");
|
||||
_databaseServer.SetTables((DatabaseTables) dataToImport);
|
||||
}
|
||||
|
||||
protected void OnReadValidate(string fileWithPath, string data)
|
||||
{
|
||||
// Validate files
|
||||
//if (ProgramStatics.COMPILED && hashedFile && !ValidateFile(fileWithPath, data)) {
|
||||
// this.valid = ValidationResult.FAILED;
|
||||
//}
|
||||
}
|
||||
|
||||
public string GetRoute()
|
||||
{
|
||||
return "spt-database";
|
||||
}
|
||||
|
||||
protected bool ValidateFile(string filePathAndName, object fileData)
|
||||
{
|
||||
/*
|
||||
try {
|
||||
const finalPath = filePathAndName.replace(this.filepath, "").replace(".json", "");
|
||||
let tempObject: any;
|
||||
for (const prop of finalPath.split("/")) {
|
||||
if (!tempObject) {
|
||||
tempObject = this.hashedFile[prop];
|
||||
} else {
|
||||
tempObject = tempObject[prop];
|
||||
}
|
||||
}
|
||||
|
||||
if (tempObject !== this.hashUtil.generateSha1ForData(fileData)) {
|
||||
this.logger.debug(this.localisationService.getText("validation_error_file", filePathAndName));
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.warning(this.localisationService.getText("validation_error_exception", filePathAndName));
|
||||
this.logger.warning(e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and map files with image router inside a designated path
|
||||
* @param filepath Path to find files in
|
||||
*/
|
||||
public void LoadImages(string filepath, List<string> directories, List<string> routes)
|
||||
{
|
||||
/*
|
||||
for (const directoryIndex in directories) {
|
||||
// Get all files in directory
|
||||
const filesInDirectory = this.vfs.getFiles(`${filepath}${directories[directoryIndex]}`);
|
||||
for (const file of filesInDirectory) {
|
||||
// Register each file in image router
|
||||
const filename = this.vfs.stripExtension(file);
|
||||
const routeKey = `${routes[directoryIndex]}${filename}`;
|
||||
let imagePath = `${filepath}${directories[directoryIndex]}/${file}`;
|
||||
|
||||
const pathOverride = this.getImagePathOverride(imagePath);
|
||||
if (pathOverride) {
|
||||
this.logger.debug(`overrode route: ${routeKey} endpoint: ${imagePath} with ${pathOverride}`);
|
||||
imagePath = pathOverride;
|
||||
}
|
||||
|
||||
this.imageRouter.addRoute(routeKey, imagePath);
|
||||
}
|
||||
}
|
||||
|
||||
// Map icon file separately
|
||||
this.imageRouter.addRoute("/favicon.ico", `${filepath}icon.ico`);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a path override in the http json config file
|
||||
* @param imagePath Key
|
||||
* @returns override for key
|
||||
*/
|
||||
protected string GetImagePathOverride(string imagePath)
|
||||
{
|
||||
return httpConfig.ServerImagePathOverride[imagePath];
|
||||
}
|
||||
}
|
||||
|
||||
enum ValidationResult {
|
||||
SUCCESS = 0,
|
||||
FAILED = 1,
|
||||
NOT_FOUND = 2,
|
||||
UNDEFINED = 3,
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Core.Annotations;
|
||||
|
||||
namespace Core.Utils;
|
||||
|
||||
[Injectable]
|
||||
public class FileUtil
|
||||
{
|
||||
public List<string> GetFiles(string path, bool recursive = false)
|
||||
{
|
||||
var files = new List<string>(Directory.GetFiles(path));
|
||||
|
||||
if (recursive)
|
||||
files.AddRange(Directory.GetDirectories(path).SelectMany(d => GetFiles(d, recursive)));
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
public string[] GetDirectories(string path)
|
||||
{
|
||||
return Directory.GetDirectories(path);
|
||||
}
|
||||
|
||||
public string GetFileExtension(string path)
|
||||
{
|
||||
return Path.GetExtension(path).Replace(".", "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Core.Annotations;
|
||||
|
||||
namespace Core.Utils;
|
||||
|
||||
[Injectable(InjectionType.Singleton)]
|
||||
public class ImporterUtil
|
||||
{
|
||||
private readonly FileUtil _fileUtil;
|
||||
|
||||
public ImporterUtil(FileUtil fileUtil)
|
||||
{
|
||||
_fileUtil = fileUtil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load files into js objects recursively (asynchronous)
|
||||
* @param filepath Path to folder with files
|
||||
* @returns Promise<T> return T type associated with this class
|
||||
*/
|
||||
public async Task<object> LoadRecursiveAsync(
|
||||
string filepath,
|
||||
Type loadedType,
|
||||
Action<string, string>? onReadCallback = null,
|
||||
Action<string, object>? onObjectDeserialized = null
|
||||
) {
|
||||
var result = Activator.CreateInstance(loadedType);
|
||||
|
||||
// get all filepaths
|
||||
var files = _fileUtil.GetFiles(filepath);
|
||||
var directories = _fileUtil.GetDirectories(filepath);
|
||||
|
||||
// add file content to result
|
||||
foreach (var file in files)
|
||||
{
|
||||
if (_fileUtil.GetFileExtension(file) != "json") continue;
|
||||
// const filename = this.vfs.stripExtension(file);
|
||||
// const filePathAndName = `${filepath}${file}`;
|
||||
var fileData = await File.ReadAllTextAsync(file);
|
||||
if (onReadCallback != null)
|
||||
onReadCallback(file, fileData);
|
||||
|
||||
var matchedProperty = loadedType.GetProperties().FirstOrDefault(prop => prop.Name.ToLower() == Path.GetFileNameWithoutExtension(file).ToLower());
|
||||
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, new JsonSerializerOptions { UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow });
|
||||
if (onObjectDeserialized != null)
|
||||
onObjectDeserialized(file, fileDeserialized);
|
||||
|
||||
matchedProperty.SetValue(result, fileDeserialized);
|
||||
}
|
||||
|
||||
// deep tree search
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
var matchedProperty = loadedType.GetProperties().FirstOrDefault(prop => prop.Name.ToLower() == directory.ToLower());
|
||||
if (matchedProperty == null)
|
||||
throw new Exception($"Unable to find property '{directory}' for type '{loadedType.Name}'");
|
||||
matchedProperty.GetSetMethod().Invoke(result, [await LoadRecursiveAsync($"{filepath}{directory}/", matchedProperty.PropertyType)]);
|
||||
}
|
||||
|
||||
// return the result of all async fetch
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load files into js objects recursively (synchronous)
|
||||
* @param filepath Path to folder with files
|
||||
* @returns
|
||||
*/
|
||||
public object LoadRecursive(
|
||||
string filepath,
|
||||
Type loadedType,
|
||||
Action<string, string>? onReadCallback = null,
|
||||
Action<string, object>? onObjectDeserialized = null
|
||||
)
|
||||
{
|
||||
var result = Activator.CreateInstance(loadedType);
|
||||
|
||||
// get all filepaths
|
||||
var files = Directory.GetFiles(filepath);
|
||||
var directories = Directory.GetDirectories(filepath);
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
if (Path.GetExtension(file) == "json")
|
||||
{
|
||||
// const filename = this.vfs.stripExtension(file);
|
||||
// const filePathAndName = `${filepath}${file}`;
|
||||
var fileData = File.ReadAllText(file);
|
||||
onReadCallback(file, fileData);
|
||||
var matchedProperty = loadedType.GetProperties().FirstOrDefault(prop => prop.Name.ToLower() == Path.GetFileNameWithoutExtension(file).ToLower());
|
||||
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);
|
||||
onObjectDeserialized(file, fileDeserialized);
|
||||
|
||||
matchedProperty.GetSetMethod().Invoke(result, [fileDeserialized]);
|
||||
}
|
||||
}
|
||||
|
||||
// deep tree search
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
var matchedProperty = loadedType.GetProperties().FirstOrDefault(prop => prop.Name.ToLower() == directory.ToLower());
|
||||
if (matchedProperty == null)
|
||||
throw new Exception($"Unable to find property '{directory}' for type '{loadedType.Name}'");
|
||||
matchedProperty.GetSetMethod().Invoke(result, [LoadRecursive($"{filepath}{directory}/", matchedProperty.PropertyType)]);
|
||||
}
|
||||
|
||||
// return the result of all async fetch
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<object> LoadAsync(
|
||||
string filepath,
|
||||
Type loadedType,
|
||||
string strippablePath = "",
|
||||
Action<string, string>? onReadCallback = null,
|
||||
Action<string, object>? onObjectDeserialized = null
|
||||
)
|
||||
{
|
||||
var result = Activator.CreateInstance(loadedType);
|
||||
var promises = new List<Task<object>>();
|
||||
var filesToProcess = new Queue<string>(_fileUtil.GetFiles(filepath, true));
|
||||
|
||||
while (filesToProcess.Count != 0) {
|
||||
var fileNode = filesToProcess.Dequeue();
|
||||
if (fileNode == null || _fileUtil.GetFileExtension(fileNode) != "json") {
|
||||
continue;
|
||||
}
|
||||
|
||||
promises.Add(File.ReadAllTextAsync(fileNode).ContinueWith(fd =>
|
||||
{
|
||||
onReadCallback(fileNode, fd.Result);
|
||||
return JsonSerializer.Deserialize(fd.Result, typeof(object));
|
||||
}));
|
||||
/*
|
||||
this.vfs
|
||||
.readFileAsync(filePathAndName)
|
||||
.then(async (fileData) => {
|
||||
onReadCallback(filePathAndName, fileData);
|
||||
return this.jsonUtil.deserializeWithCacheCheckAsync<any>(fileData, filePathAndName);
|
||||
})
|
||||
.then(async (fileDeserialized) => {
|
||||
onObjectDeserialized(filePathAndName, fileDeserialized);
|
||||
const strippedFilePath = this.vfs.stripExtension(filePathAndName).replace(filepath, "");
|
||||
this.placeObject(fileDeserialized, strippedFilePath, result, strippablePath);
|
||||
})
|
||||
.then(() => progressWriter.increment()),
|
||||
);*/
|
||||
}
|
||||
|
||||
//await JSType.Promise<>.all(promises).catch((e) => console.error(e));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
protected placeObject<T>(fileDeserialized: any, strippedFilePath: string, result: T, strippablePath: string): void {
|
||||
const strippedFinalPath = strippedFilePath.replace(strippablePath, "");
|
||||
let temp = result;
|
||||
const propertiesToVisit = strippedFinalPath.split("/");
|
||||
for (let i = 0; i < propertiesToVisit.length; i++) {
|
||||
const property = propertiesToVisit[i];
|
||||
|
||||
if (i === propertiesToVisit.length - 1) {
|
||||
temp[property] = fileDeserialized;
|
||||
} else {
|
||||
if (!temp[property]) {
|
||||
temp[property] = {};
|
||||
}
|
||||
temp = temp[property];
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Core.Utils.Json.Converters;
|
||||
|
||||
public class NotNullObjectToIntConverter : JsonConverter<int>
|
||||
{
|
||||
public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
int result;
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonTokenType.String:
|
||||
var value = reader.GetString();
|
||||
if (string.IsNullOrWhiteSpace(value) || !int.TryParse(value, out result))
|
||||
return 0;
|
||||
break;
|
||||
case JsonTokenType.Number:
|
||||
result = reader.GetInt32();
|
||||
break;
|
||||
case JsonTokenType.Null:
|
||||
return 0;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
|
||||
{
|
||||
if (value == null)
|
||||
writer.WriteStringValue("");
|
||||
else
|
||||
writer.WriteStringValue($"{value}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Core.Utils.Json.Converters;
|
||||
|
||||
public class NullableObjectToLongConverter : JsonConverter<long?>
|
||||
{
|
||||
public override long? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
long result;
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonTokenType.String:
|
||||
var value = reader.GetString();
|
||||
if (string.IsNullOrWhiteSpace(value) || !long.TryParse(value, out result))
|
||||
return null;
|
||||
break;
|
||||
case JsonTokenType.Number:
|
||||
result = reader.GetInt64();
|
||||
break;
|
||||
case JsonTokenType.Null:
|
||||
return null;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, long? value, JsonSerializerOptions options)
|
||||
{
|
||||
if (value == null)
|
||||
writer.WriteStringValue("");
|
||||
else if (value is long longValue)
|
||||
writer.WriteNumberValue(longValue);
|
||||
else
|
||||
throw new Exception("Cannot convert the object valur to a long.");
|
||||
}
|
||||
}
|
||||
@@ -1156,7 +1156,7 @@
|
||||
"5": 3,
|
||||
"10": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"pocketLoot": {
|
||||
"weights": {
|
||||
@@ -1165,7 +1165,7 @@
|
||||
"2": 2,
|
||||
"3": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"vestLoot": {
|
||||
"weights": {
|
||||
@@ -1175,7 +1175,7 @@
|
||||
"3": 1,
|
||||
"4": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
@@ -1184,14 +1184,14 @@
|
||||
"2": 4,
|
||||
"3": 3
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"0": 10,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"equipment": {
|
||||
|
||||
@@ -43,38 +43,38 @@
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"drugs": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"0": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"looseLoot": {
|
||||
"weights": {
|
||||
"0": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"grenades": {
|
||||
"weights": {
|
||||
"0": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"labsAccessCardChancePercent": 0,
|
||||
@@ -123,20 +123,20 @@
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"drugs": {
|
||||
"weights": {
|
||||
"0": 1,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"0": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"looseLoot": {
|
||||
"weights": {
|
||||
@@ -146,21 +146,21 @@
|
||||
"3": 1,
|
||||
"4": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
"1": 2,
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"grenades": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"lootItemsToAddChancePercent": {}
|
||||
@@ -208,20 +208,20 @@
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"drugs": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"0": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"looseLoot": {
|
||||
"weights": {
|
||||
@@ -231,21 +231,21 @@
|
||||
"3": 1,
|
||||
"4": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
"1": 2,
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"grenades": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"lootItemsToAddChancePercent": {}
|
||||
@@ -293,20 +293,20 @@
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"drugs": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"0": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"looseLoot": {
|
||||
"weights": {
|
||||
@@ -316,21 +316,21 @@
|
||||
"3": 1,
|
||||
"4": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
"1": 2,
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"grenades": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"lootItemsToAddChancePercent": {}
|
||||
@@ -378,20 +378,20 @@
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"drugs": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"0": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"looseLoot": {
|
||||
"weights": {
|
||||
@@ -401,21 +401,21 @@
|
||||
"3": 1,
|
||||
"4": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
"1": 2,
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"grenades": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"lootItemsToAddChancePercent": {}
|
||||
@@ -463,20 +463,20 @@
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"drugs": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"0": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"looseLoot": {
|
||||
"weights": {
|
||||
@@ -486,21 +486,21 @@
|
||||
"3": 1,
|
||||
"4": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
"1": 2,
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"grenades": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"lootItemsToAddChancePercent": {}
|
||||
@@ -548,20 +548,20 @@
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"drugs": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"0": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"looseLoot": {
|
||||
"weights": {
|
||||
@@ -571,21 +571,21 @@
|
||||
"3": 1,
|
||||
"4": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
"1": 2,
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"grenades": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"lootItemsToAddChancePercent": {}
|
||||
@@ -633,20 +633,20 @@
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"drugs": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"0": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"looseLoot": {
|
||||
"weights": {
|
||||
@@ -656,21 +656,21 @@
|
||||
"3": 1,
|
||||
"4": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
"1": 1,
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"grenades": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"lootItemsToAddChancePercent": {}
|
||||
@@ -718,7 +718,7 @@
|
||||
"0": 2,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"drugs": {
|
||||
"weights": {
|
||||
@@ -726,13 +726,13 @@
|
||||
"1": 1,
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"0": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"looseLoot": {
|
||||
"weights": {
|
||||
@@ -741,7 +741,7 @@
|
||||
"3": 1,
|
||||
"4": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
@@ -749,7 +749,7 @@
|
||||
"2": 2,
|
||||
"3": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"grenades": {
|
||||
"weights": {
|
||||
@@ -757,7 +757,7 @@
|
||||
"1": 1,
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"lootItemsToAddChancePercent": {
|
||||
@@ -808,42 +808,42 @@
|
||||
"1": 1,
|
||||
"2": 2
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"drugs": {
|
||||
"weights": {
|
||||
"1": 1,
|
||||
"2": 2
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"0": 1,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"looseLoot": {
|
||||
"weights": {
|
||||
"2": 2,
|
||||
"3": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
"2": 2,
|
||||
"3": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"grenades": {
|
||||
"weights": {
|
||||
"1": 1,
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"lootItemsToAddChancePercent": {
|
||||
@@ -895,21 +895,21 @@
|
||||
"1": 1,
|
||||
"2": 2
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"drugs": {
|
||||
"weights": {
|
||||
"1": 1,
|
||||
"2": 2
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"1": 5,
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"looseLoot": {
|
||||
"weights": {
|
||||
@@ -918,7 +918,7 @@
|
||||
"4": 1,
|
||||
"5": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
@@ -926,14 +926,14 @@
|
||||
"3": 2,
|
||||
"4": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"grenades": {
|
||||
"weights": {
|
||||
"1": 1,
|
||||
"2": 2
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"lootItemsToAddChancePercent": {
|
||||
@@ -984,21 +984,21 @@
|
||||
"weights": {
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"drugs": {
|
||||
"weights": {
|
||||
"2": 1,
|
||||
"3": 2
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"1": 1,
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"looseLoot": {
|
||||
"weights": {
|
||||
@@ -1007,7 +1007,7 @@
|
||||
"5": 1,
|
||||
"6": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
@@ -1015,14 +1015,14 @@
|
||||
"3": 2,
|
||||
"4": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"grenades": {
|
||||
"weights": {
|
||||
"2": 1,
|
||||
"3": 2
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"lootItemsToAddChancePercent": {
|
||||
@@ -1074,21 +1074,21 @@
|
||||
"2": 5,
|
||||
"3": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"drugs": {
|
||||
"weights": {
|
||||
"2": 1,
|
||||
"3": 2
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"1": 1,
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"looseLoot": {
|
||||
"weights": {
|
||||
@@ -1097,21 +1097,21 @@
|
||||
"5": 2,
|
||||
"6": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
"3": 2,
|
||||
"4": 2
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"grenades": {
|
||||
"weights": {
|
||||
"2": 2,
|
||||
"3": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"lootItemsToAddChancePercent": {
|
||||
@@ -1180,21 +1180,21 @@
|
||||
"2": 1,
|
||||
"3": 2
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"drugs": {
|
||||
"weights": {
|
||||
"3": 1,
|
||||
"4": 2
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"2": 1,
|
||||
"3": 2
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"looseLoot": {
|
||||
"weights": {
|
||||
@@ -1202,21 +1202,21 @@
|
||||
"6": 2,
|
||||
"7": 1
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"magazines": {
|
||||
"weights": {
|
||||
"3": 1,
|
||||
"4": 4
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
},
|
||||
"grenades": {
|
||||
"weights": {
|
||||
"3": 1,
|
||||
"4": 2
|
||||
},
|
||||
"whitelist": []
|
||||
"whitelist": {}
|
||||
}
|
||||
},
|
||||
"lootItemsToAddChancePercent": {
|
||||
|
||||
+44
-24
@@ -1,5 +1,7 @@
|
||||
using Core.Annotations;
|
||||
using Core.Context;
|
||||
using Core.Servers;
|
||||
using Core.Utils;
|
||||
|
||||
namespace Server;
|
||||
|
||||
@@ -8,22 +10,32 @@ public static class Program
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
|
||||
RegisterSptComponents(builder.Services);
|
||||
|
||||
|
||||
// TODO: deal with modding overriding services here!
|
||||
|
||||
var httpServer = builder.Services.BuildServiceProvider().GetService<HttpServer>();
|
||||
httpServer.Load(builder);
|
||||
try
|
||||
{
|
||||
|
||||
var serviceProvider = builder.Services.BuildServiceProvider();
|
||||
var app = serviceProvider.GetService<App>();
|
||||
var appContext = serviceProvider.GetService<ApplicationContext>();
|
||||
appContext.AddValue(ContextVariableType.APP_BUILDER, builder);
|
||||
app.Load().Wait();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegisterComponents(IServiceCollection builderServices, IEnumerable<Type> types)
|
||||
{
|
||||
// We get all injectable services to register them on our services
|
||||
foreach (var injectableType in types)
|
||||
var groupedTypes = types.Select(t =>
|
||||
{
|
||||
var attribute = (Injectable) Attribute.GetCustomAttribute(injectableType, typeof(Injectable))!;
|
||||
var registerableType = injectableType;
|
||||
var attribute = (Injectable)Attribute.GetCustomAttribute(t, typeof(Injectable))!;
|
||||
var registerableType = t;
|
||||
// if we have a type override this takes priority
|
||||
if (attribute.InjectableTypeOverride != null)
|
||||
{
|
||||
@@ -34,28 +46,36 @@ public static class Program
|
||||
{
|
||||
registerableType = registerableType.GetInterfaces()[0];
|
||||
}
|
||||
|
||||
switch (attribute.InjectionType)
|
||||
|
||||
return (registerableInterface: registerableType, typeToRegister: t, injectableAttribute: attribute);
|
||||
}).GroupBy(t => t.registerableInterface.FullName);
|
||||
// We get all injectable services to register them on our services
|
||||
foreach (var groupedInjectables in groupedTypes)
|
||||
{
|
||||
foreach (var valueTuple in groupedInjectables.OrderBy(t => t.injectableAttribute.TypePriority))
|
||||
{
|
||||
case InjectionType.Singleton:
|
||||
builderServices.AddSingleton(registerableType, injectableType);
|
||||
break;
|
||||
case InjectionType.Transient:
|
||||
builderServices.AddTransient(registerableType, injectableType);
|
||||
break;
|
||||
case InjectionType.Scoped:
|
||||
builderServices.AddScoped(registerableType, injectableType);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
switch (valueTuple.injectableAttribute.InjectionType)
|
||||
{
|
||||
case InjectionType.Singleton:
|
||||
builderServices.AddSingleton(valueTuple.registerableInterface, valueTuple.typeToRegister);
|
||||
break;
|
||||
case InjectionType.Transient:
|
||||
builderServices.AddTransient(valueTuple.registerableInterface, valueTuple.typeToRegister);
|
||||
break;
|
||||
case InjectionType.Scoped:
|
||||
builderServices.AddScoped(valueTuple.registerableInterface, valueTuple.typeToRegister);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void RegisterSptComponents(IServiceCollection builderServices)
|
||||
{
|
||||
// We get all the services from this assembly first, since mods will override them later
|
||||
RegisterComponents(builderServices, typeof(Program).Assembly.GetTypes()
|
||||
RegisterComponents(builderServices, typeof(App).Assembly.GetTypes()
|
||||
.Where(type => Attribute.IsDefined(type, typeof(Injectable))));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<EnableDefaultContentItems>false</EnableDefaultContentItems>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
Reference in New Issue
Block a user