chore: cleanup

This commit is contained in:
redglow
2026-07-29 11:05:27 +02:00
parent 2c0a3f47a4
commit 5c4519ed26
20 changed files with 110 additions and 70 deletions
+7 -2
View File
@@ -1,4 +1,5 @@
using Godot; using Godot;
using GodotHostTest.AetherBind;
using GodotHostTest.Game.Level; using GodotHostTest.Game.Level;
using GodotHostTest.Game.Score; using GodotHostTest.Game.Score;
using GodotHostTest.Game.Target; using GodotHostTest.Game.Target;
@@ -6,7 +7,6 @@ using GodotHostTest.Interfaces;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using OwofGames.GodotHost.Observability; using OwofGames.GodotHost.Observability;
using OwofGames.GodotLume; using OwofGames.GodotLume;
using RootServiceSource = GodotHostTest.AetherBind.RootServiceSource;
namespace GodotHostTest.Game.Game; namespace GodotHostTest.Game.Game;
@@ -17,9 +17,14 @@ public partial class GameServiceSource : RootServiceSource
protected override void Configure(Builder builder) protected override void Configure(Builder builder)
{ {
builder builder
// root scope services
.AddSingleton<ITargetEventBus, TargetEventBus>() .AddSingleton<ITargetEventBus, TargetEventBus>()
.AddSingleton<IScore, TotalScore>() .AddSingleton<IScore, TotalScore>()
.AddLevelServices(_levelsData); // current level / level data management
.AddInstance<ILevelDataProvider, LevelsData>(_levelsData)
.AddSingleton<ICurrentLevel, CurrentLevel>()
// level scope services
.AddLevelServices();
} }
protected override void ConfigureServices(IServiceCollection serviceCollection) protected override void ConfigureServices(IServiceCollection serviceCollection)
+6 -7
View File
@@ -13,7 +13,8 @@ namespace GodotHostTest.Game.Level;
public partial class LevelServiceSource : ServiceSource public partial class LevelServiceSource : ServiceSource
{ {
private Activity? _activityValue; private Activity? _levelActivity;
[Export] private TargetSpawner _targetSpawner = null!; [Export] private TargetSpawner _targetSpawner = null!;
protected override void OnScopeCreated(IProvider serviceProvider) protected override void OnScopeCreated(IProvider serviceProvider)
@@ -22,15 +23,13 @@ public partial class LevelServiceSource : ServiceSource
} }
[WithProvider] [WithProvider]
private void OnScopeCreated(IValueSetter<ITargetCollector> targetCollector, private void OnScopeCreated(IActivitySourceFactory activitySourceFactory,
IValueSetter<ITargetSpawner> targetSpawner, IActivitySourceFactory activitySourceFactory, IValueSetter<ITargetCollector> targetCollector, IValueSetter<ITargetSpawner> targetSpawner)
IValueSetter<Activity> activity)
{ {
// ReSharper disable once ExplicitCallerInfoArgument - the caller name is not significant in this case // ReSharper disable once ExplicitCallerInfoArgument - the caller name is not significant in this case
_activityValue = activitySourceFactory.ActivitySource.StartActivity("Level") ?? _levelActivity = activitySourceFactory.ActivitySource.StartActivity("Level") ??
throw new InvalidOperationException( throw new InvalidOperationException(
"No listeners for the activity - is OpenTelemetry running?"); "No listeners for the activity - is OpenTelemetry running?");
activity.Set(_activityValue);
targetCollector.Set(_targetSpawner); targetCollector.Set(_targetSpawner);
targetSpawner.Set(_targetSpawner); targetSpawner.Set(_targetSpawner);
} }
@@ -38,6 +37,6 @@ public partial class LevelServiceSource : ServiceSource
public override void _Ready() public override void _Ready()
{ {
// after 3 seconds, dispose the activity for simulation purposes // after 3 seconds, dispose the activity for simulation purposes
GetTree().CreateTimer(3).Timeout += _activityValue!.Dispose; GetTree().CreateTimer(3).Timeout += _levelActivity!.Dispose;
} }
} }
+3 -9
View File
@@ -1,4 +1,3 @@
using System.Diagnostics;
using GodotHostTest.AetherBind; using GodotHostTest.AetherBind;
using GodotHostTest.Interfaces; using GodotHostTest.Interfaces;
using OwofGames.GodotLume; using OwofGames.GodotLume;
@@ -7,17 +6,12 @@ namespace GodotHostTest.Game.Level;
public static class LevelServices public static class LevelServices
{ {
public static void AddLevelServices(this Builder builder, LevelsData levelsData) public static void AddLevelServices(this Builder builder)
{ {
builder builder
// current level / level data management // value providers to send level data from root to level scope
.AddSingleton<ICurrentLevel, CurrentLevel>()
.AddInstance<ILevelDataProvider, LevelsData>(levelsData)
// value provider for activity
.AddValueProvider<Activity, LevelScope>()
// value providers to send data into level scope
.AddValueProvider<ILevel, RootScope, LevelScope>() .AddValueProvider<ILevel, RootScope, LevelScope>()
// value provider for level scope setup // value providers for level scope setup
.AddValueProvider<ITargetCollector, LevelScope>() .AddValueProvider<ITargetCollector, LevelScope>()
.AddValueProvider<ITargetSpawner, LevelScope>(); .AddValueProvider<ITargetSpawner, LevelScope>();
} }
+12 -4
View File
@@ -9,16 +9,24 @@ namespace GodotHostTest.Game.Score;
public partial class Score : Control public partial class Score : Control
{ {
[Export] private AnimationPlayer _animationPlayer = null!; [Export] private AnimationPlayer _animationPlayer = null!;
[Inject] private ICurrentLevel _currentLevel = null!; [Inject] private ICurrentLevel _currentLevel = null!;
[Export] private Label _label = null!; [Export] private Label _label = null!;
[Inject] private IScore _score = null!; [Inject] private IScore _score = null!;
public override void _Ready() public override void _Ready()
{ {
_score.Score.Subscribe(UpdateScore).AddTo(this); // update the score label whenever the score changes
_score.Score.Chunk(2, 1).Where(values => values.Length == 2 && values[1] < values[0]) _score.Score
.Subscribe(OnScoreDecreasing).AddTo(this); .Subscribe(UpdateScore)
.AddTo(this);
// run an animation whenever the score decreases
_score.Score
.Chunk(2, 1)
.Where(values => values.Length == 2 && values[1] < values[0])
.Subscribe(OnScoreDecreasing)
.AddTo(this);
} }
private void OnScoreDecreasing<T>(T _) private void OnScoreDecreasing<T>(T _)
+8 -9
View File
@@ -6,24 +6,23 @@ namespace GodotHostTest.Game.Score;
public class TotalScore : IScore, IDisposable public class TotalScore : IScore, IDisposable
{ {
private readonly IDisposable _connectedObservableDisposable;
public TotalScore(ITargetEventBus targetEventBus) public TotalScore(ITargetEventBus targetEventBus)
{ {
var connectedObservable = targetEventBus.Hit.Select(_ => 20) Score =
// map every hit target to +20 points
targetEventBus.Hit.Select(_ => 20)
// map every missed target to -5 points
.Merge(targetEventBus.TimedOut.Select(_ => -5)) .Merge(targetEventBus.TimedOut.Select(_ => -5))
// sum them all (starting with 0)
.Scan(0, (x1, x2) => x1 + x2) .Scan(0, (x1, x2) => x1 + x2)
.Prepend(0) .ToReadOnlyReactiveProperty();
.Replay(1);
_connectedObservableDisposable = connectedObservable.Connect();
Score = connectedObservable.AsObservable();
} }
public void Dispose() public void Dispose()
{ {
_connectedObservableDisposable.Dispose(); Score.Dispose();
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public Observable<int> Score { get; } public ReadOnlyReactiveProperty<int> Score { get; }
} }
+13 -17
View File
@@ -8,14 +8,14 @@ namespace GodotHostTest.Game.Target;
public partial class Target : Node2D, ITarget public partial class Target : Node2D, ITarget
{ {
private readonly Subject<Unit> _hit = new(); private readonly Subject<Unit> _hit = new();
private readonly Subject<Unit> _timedOut = new(); private readonly Subject<Unit> _timedOut = new();
[Export] public required Area2D CollisionArea; [Export] public required Area2D CollisionArea;
[Export] public required Sprite2D Image; [Export] public required Sprite2D Image;
[Export] public required Timer TimeOutTimer; [Export] public required Timer TimeOutTimer;
[Export] private PackedScene _explosionPackedScene = null!; [Export] private PackedScene _explosionPackedScene = null!;
[Inject] private ILevel _level = null!;
[Inject] private ILevel _level = null!;
[Inject] private ITargetCollector _targetCollector = null!; [Inject] private ITargetCollector _targetCollector = null!;
public Observable<Unit> TimedOut => _timedOut.AsObservable(); public Observable<Unit> TimedOut => _timedOut.AsObservable();
@@ -30,26 +30,30 @@ public partial class Target : Node2D, ITarget
TimeOutTimer.Start(_level.SpawnDuration.TotalSeconds); TimeOutTimer.Start(_level.SpawnDuration.TotalSeconds);
} }
private void Disable()
{
Image.Visible = false;
// can't set monitoring/monitorable when inside collision code, like during OnHit, so let's call them deferred
CollisionArea.SetDeferred(Area2D.PropertyName.Monitoring, false);
CollisionArea.SetDeferred(Area2D.PropertyName.Monitorable, false);
TimeOutTimer.Stop();
}
public override void _Ready() public override void _Ready()
{ {
_targetCollector.NewTarget(this); _targetCollector.NewTarget(this);
} }
public override void _ExitTree() protected override void Dispose(bool disposing)
{ {
base.Dispose(disposing);
_timedOut.Dispose(); _timedOut.Dispose();
_hit.Dispose(); _hit.Dispose();
} }
private void OnAreaEntered(Area2D _) private void OnAreaEntered(Area2D _)
{
OnHit();
}
private void OnHit()
{ {
Disable(); Disable();
TimeOutTimer.Stop();
_hit.OnNext(Unit.Default); _hit.OnNext(Unit.Default);
var explosion = (GpuParticles2D)_explosionPackedScene.Instantiate(); var explosion = (GpuParticles2D)_explosionPackedScene.Instantiate();
@@ -58,14 +62,6 @@ public partial class Target : Node2D, ITarget
GetTree().CurrentScene.AddChild(explosion); GetTree().CurrentScene.AddChild(explosion);
} }
private void Disable()
{
Image.Visible = false;
// can't se monitoring/monitorable when inside the collision code, like during OnHit
CollisionArea.SetDeferred(Area2D.PropertyName.Monitoring, false);
CollisionArea.SetDeferred(Area2D.PropertyName.Monitorable, false);
}
private void OnTimeout() private void OnTimeout()
{ {
Disable(); Disable();
+13 -8
View File
@@ -10,31 +10,36 @@ namespace GodotHostTest.Game.Target;
public partial class TargetSpawner : Node2D, ITargetCollector, ITargetSpawner public partial class TargetSpawner : Node2D, ITargetCollector, ITargetSpawner
{ {
private readonly List<ITarget> _availableTargets = []; private readonly List<ITarget> _availableTargets = [];
private readonly Subject<ITarget> _targetSpawned = new(); private readonly Subject<ITarget> _targetSpawned = new();
[Export] public required Timer Timer; [Export] public required Timer Timer;
[Inject] private ILevel _level = null!; [Inject] private ILevel _level = null!;
[Inject] private ITargetEventBus _targetEventBus = null!; [Inject] private ITargetEventBus _targetEventBus = null!;
/// <inheritdoc />
public void NewTarget(ITarget target) public void NewTarget(ITarget target)
{ {
// record the target as available
_availableTargets.Add(target); _availableTargets.Add(target);
target.Hit.Merge(target.TimedOut).Select(_ => target).Subscribe(MakeTargetAvailable).AddTo(this);
// whenever the target gets disabled, mark it as available again
target.Hit.Merge(target.TimedOut)
.Select(_ => target)
.Subscribe(_availableTargets.Add)
.AddTo(this);
} }
public Observable<ITarget> TargetSpawned => _targetSpawned.AsObservable(); public Observable<ITarget> TargetSpawned => _targetSpawned.AsObservable();
public override void _Ready() public override void _Ready()
{ {
// inform the target event bus that there's a new target spawner
_targetEventBus.NewTargetSpawner(this); _targetEventBus.NewTargetSpawner(this);
Spawn();
Timer.Start(_level.TimeBetweenSpawns.TotalSeconds);
}
private void MakeTargetAvailable(ITarget target) // spawn every total seconds (and immediately spawn something at ready)
{ Timer.Start(_level.TimeBetweenSpawns.TotalSeconds);
_availableTargets.Add(target); Spawn();
} }
private void OnTimer() private void OnTimer()
-1
View File
@@ -11,7 +11,6 @@ public partial class Turret : Sprite2D
[Inject] private ILogger<Turret> _logger = null!; [Inject] private ILogger<Turret> _logger = null!;
[Export] private PackedScene _projectilePackedScene = null!; [Export] private PackedScene _projectilePackedScene = null!;
private double _rotationDirection; private double _rotationDirection;
[Export] private double _rotationSpeed = 6; [Export] private double _rotationSpeed = 6;
[Inject] private ISceneInstantiator _sceneInstantiator = null!; [Inject] private ISceneInstantiator _sceneInstantiator = null!;
+7
View File
@@ -16,4 +16,11 @@ public static class ObservableHelpers
}); });
return disposable; return disposable;
} }
public static ConnectableObservable<T> Connect<T>(this ConnectableObservable<T> connectableObservable,
out IDisposable disposable)
{
disposable = connectableObservable.Connect();
return connectableObservable;
}
} }
+10
View File
@@ -2,8 +2,18 @@ using R3;
namespace GodotHostTest.Interfaces; namespace GodotHostTest.Interfaces;
/// <summary>
/// The object that handles the current level.
/// </summary>
public interface ICurrentLevel public interface ICurrentLevel
{ {
/// <summary>
/// The current level.
/// </summary>
public ReadOnlyReactiveProperty<int> LevelNumber { get; } public ReadOnlyReactiveProperty<int> LevelNumber { get; }
/// <summary>
/// Move to the next level.
/// </summary>
public void NextLevel(); public void NextLevel();
} }
+3
View File
@@ -2,6 +2,9 @@ using System;
namespace GodotHostTest.Interfaces; namespace GodotHostTest.Interfaces;
/// <summary>
/// Description of a single level.
/// </summary>
public interface ILevel public interface ILevel
{ {
/// <summary> /// <summary>
+8
View File
@@ -1,6 +1,14 @@
namespace GodotHostTest.Interfaces; namespace GodotHostTest.Interfaces;
/// <summary>
/// An object that provides the information about a certain level.
/// </summary>
public interface ILevelDataProvider public interface ILevelDataProvider
{ {
/// <summary>
/// Return the level data for a specific level.
/// </summary>
/// <param name="levelNumber">Level number.</param>
/// <returns>The information about a certain level.</returns>
public ILevel GetLevelData(int levelNumber); public ILevel GetLevelData(int levelNumber);
} }
+1 -1
View File
@@ -10,5 +10,5 @@ public interface IScore
/// <summary> /// <summary>
/// The current score. /// The current score.
/// </summary> /// </summary>
public Observable<int> Score { get; } public ReadOnlyReactiveProperty<int> Score { get; }
} }
+3 -4
View File
@@ -3,18 +3,17 @@ using R3;
namespace GodotHostTest.Interfaces; namespace GodotHostTest.Interfaces;
/// <summary> /// <summary>
/// A target for our projectiles. /// A target for our projectiles. Targets can be reused multiple times.
/// </summary> /// </summary>
public interface ITarget public interface ITarget
{ {
/// <summary> /// <summary>
/// The target hasn't been hit in the allotted time and has timed out; once this observable emits an event, both /// An observable that emits a <see cref="Unit"/> value when the target hasn't been hit in the allotted time and has timed out. Every time this observable emits, the target becomes disabled.
/// it and <see cref="Hit"/> complete.
/// </summary> /// </summary>
Observable<Unit> TimedOut { get; } Observable<Unit> TimedOut { get; }
/// <summary> /// <summary>
/// The target has been hit; once this observable emits an event, both it and <see cref="TimedOut"/> complete. /// An observable that emits a <see cref="Unit"/> value when the target has been hit in the allotted time. Every time this observable emits, the target becomes disabled.
/// </summary> /// </summary>
Observable<Unit> Hit { get; } Observable<Unit> Hit { get; }
+1 -1
View File
@@ -1,7 +1,7 @@
namespace GodotHostTest.Interfaces; namespace GodotHostTest.Interfaces;
/// <summary> /// <summary>
/// An object which collects targets as soon as they are created. /// An object which receives targets as soon as they are created.
/// </summary> /// </summary>
public interface ITargetCollector public interface ITargetCollector
{ {
+5 -2
View File
@@ -2,6 +2,9 @@ using R3;
namespace GodotHostTest.Interfaces; namespace GodotHostTest.Interfaces;
/// <summary>
/// An event bus that summarizes information from all <see cref="ITargetSpawner" />'s targets.
/// </summary>
public interface ITargetEventBus public interface ITargetEventBus
{ {
/// <summary> /// <summary>
@@ -15,8 +18,8 @@ public interface ITargetEventBus
Observable<Unit> Hit { get; } Observable<Unit> Hit { get; }
/// <summary> /// <summary>
/// A new target spawner has been added. /// Callback method to invoke when a new target spawner has been added.
/// </summary> /// </summary>
/// <param name="targetSpawner"></param> /// <param name="targetSpawner">The new target spawner.</param>
void NewTargetSpawner(ITargetSpawner targetSpawner); void NewTargetSpawner(ITargetSpawner targetSpawner);
} }
+1 -1
View File
@@ -3,7 +3,7 @@ using R3;
namespace GodotHostTest.Interfaces; namespace GodotHostTest.Interfaces;
/// <summary> /// <summary>
/// An object that spawns targets. /// An object that spawns targets. A target is considered spawned whenever it's enabled, even if it was already present in the scene.
/// </summary> /// </summary>
public interface ITargetSpawner public interface ITargetSpawner
{ {
+4 -2
View File
@@ -1,10 +1,12 @@
using JetBrains.Annotations;
using OwofGames.GodotLume; using OwofGames.GodotLume;
namespace GodotHostTest.AetherBind; namespace GodotHostTest.AetherBind;
public static class BuilderExtensions public static class BuilderExtensions
{ {
public static Builder AddValueProvider<TValueType, TSourceScope, TDestinationScope>(this Builder builder) public static Builder AddValueProvider<TValueType, [MeansImplicitUse] TSourceScope,
[MeansImplicitUse] TDestinationScope>(this Builder builder)
where TValueType : class where TValueType : class
where TSourceScope : RootScope where TSourceScope : RootScope
where TDestinationScope : TSourceScope where TDestinationScope : TSourceScope
@@ -22,7 +24,7 @@ public static class BuilderExtensions
provider => provider.Get<IValueGetter<TValueType>>().Value); provider => provider.Get<IValueGetter<TValueType>>().Value);
} }
public static Builder AddValueProvider<TValueType, TScope>(this Builder builder) public static Builder AddValueProvider<TValueType, [MeansImplicitUse] TScope>(this Builder builder)
where TValueType : class where TValueType : class
where TScope : RootScope where TScope : RootScope
{ {
+1
View File
@@ -7,6 +7,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2026.2.0"/>
<PackageReference Include="Microsoft.Extensions.Diagnostics" Version="10.0.0"/> <PackageReference Include="Microsoft.Extensions.Diagnostics" Version="10.0.0"/>
<PackageReference Include="OpenTelemetry" Version="1.17.0"/> <PackageReference Include="OpenTelemetry" Version="1.17.0"/>
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.17.0"/> <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.17.0"/>
+2
View File
@@ -21,8 +21,10 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALoggerInformation_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FSourcesCache_003F58a2e8187d354ab2e42d1cca1ce63b1edb0381e3ac126b055eb2481382014_003FLoggerInformation_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALoggerInformation_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FSourcesCache_003F58a2e8187d354ab2e42d1cca1ce63b1edb0381e3ac126b055eb2481382014_003FLoggerInformation_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALogger_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FSourcesCache_003F342e7081b95d68bdbbd320f210c35f9376e2b2b914a05d1cfef054c46bee58_003FLogger_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALogger_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FSourcesCache_003F342e7081b95d68bdbbd320f210c35f9376e2b2b914a05d1cfef054c46bee58_003FLogger_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMethodBaseInvoker_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6cc9b871557b441baa469ce23b255f41dbb200_003F7b_003Fb6307b47_003FMethodBaseInvoker_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMethodBaseInvoker_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6cc9b871557b441baa469ce23b255f41dbb200_003F7b_003Fb6307b47_003FMethodBaseInvoker_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANode_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fcd2081581a57397285b1beba7fc14bdbc61c89e3ac09a962676d880d9dd3c_003FNode_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOtlpHttpExportClient_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FSourcesCache_003F4033dea463e391648d47bc0d947734cc9785594d32f2e63e7fdaa66dc6f69_003FOtlpHttpExportClient_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOtlpHttpExportClient_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FSourcesCache_003F4033dea463e391648d47bc0d947734cc9785594d32f2e63e7fdaa66dc6f69_003FOtlpHttpExportClient_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AProtobufOtlpLogSerializer_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fc7a11e51b08d59f654af762b7dc9ef7158afab9213be73c87d181effe335_003FProtobufOtlpLogSerializer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AProtobufOtlpLogSerializer_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fc7a11e51b08d59f654af762b7dc9ef7158afab9213be73c87d181effe335_003FProtobufOtlpLogSerializer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AReadOnlyReactiveProperty_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5c3b451e92d746ce859467e287bf9baf92400_003F9d_003F98b1b0ac_003FReadOnlyReactiveProperty_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARuntimeType_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6cc9b871557b441baa469ce23b255f41dbb200_003F5e_003F88e82907_003FRuntimeType_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARuntimeType_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6cc9b871557b441baa469ce23b255f41dbb200_003F5e_003F88e82907_003FRuntimeType_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASuppressInstrumentationScope_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FSourcesCache_003Ffdb78768d9162cf6ca6280f1fa20f07dfc0fdd0d0352108d2d26c0cde1e0_003FSuppressInstrumentationScope_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASuppressInstrumentationScope_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E2_003Fresharper_002Dhost_003FSourcesCache_003Ffdb78768d9162cf6ca6280f1fa20f07dfc0fdd0d0352108d2d26c0cde1e0_003FSuppressInstrumentationScope_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelpers_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6cc9b871557b441baa469ce23b255f41dbb200_003F8e_003Fc3f51c26_003FThrowHelpers_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelpers_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6cc9b871557b441baa469ce23b255f41dbb200_003F8e_003Fc3f51c26_003FThrowHelpers_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>