Compare commits

...

4 Commits

Author SHA1 Message Date
morbalint 540c665a14 [be] optional open telemetry console logging
staging logs are full :(
2024-01-24 16:42:25 +01:00
morbalint 8f32daf337 [fe] [2e] add basic szintlepes 2024-01-24 16:38:16 +01:00
morbalint 978618d1b1 [be] open telemetry: only log to console when no other exporter exists 2024-01-23 13:31:22 +01:00
morbalint 65132fc7c6 add open telemetry and refactor setup 2024-01-23 13:10:16 +01:00
18 changed files with 813 additions and 112 deletions
+10
View File
@@ -0,0 +1,10 @@
namespace Kemkas.Web.Config;
public class ExternalAuthOptions
{
public string? ClientId { get; set; }
public string? ClientSecret { get; set; }
public bool IsValid() => ClientId != null && ClientSecret != null;
}
@@ -0,0 +1,84 @@
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
namespace Kemkas.Web.Config;
public static class WebApplicationBuilderExtensions
{
public static void AddAuth(this WebApplicationBuilder builder)
{
var externalAuthConfigSection = builder.Configuration.GetSection("Authentication");
var authenticationBuilder = builder.Services.AddAuthentication();
var googleAuthOptions = new ExternalAuthOptions();
externalAuthConfigSection.GetSection("Google").Bind(googleAuthOptions);
if (googleAuthOptions.IsValid())
{
authenticationBuilder.AddGoogle(options =>
{
options.ClientId = googleAuthOptions.ClientId!;
options.ClientSecret = googleAuthOptions.ClientSecret!;
});
}
var facebookAuthOptions = new ExternalAuthOptions();
externalAuthConfigSection.GetSection("Facebook").Bind(facebookAuthOptions);
if (facebookAuthOptions.IsValid())
{
authenticationBuilder.AddFacebook(options =>
{
options.ClientId = facebookAuthOptions.ClientId!;
options.ClientSecret = facebookAuthOptions.ClientSecret!;
});
}
var discordAuthOptions = new ExternalAuthOptions();
externalAuthConfigSection.GetSection("Discord").Bind(discordAuthOptions);
if (discordAuthOptions.IsValid())
{
authenticationBuilder.AddDiscord(options =>
{
options.ClientId = discordAuthOptions.ClientId!;
options.ClientSecret = discordAuthOptions.ClientSecret!;
});
}
}
public static void AddOpenTelemetry(this WebApplicationBuilder builder)
{
var otlResourceBuilder = ResourceBuilder.CreateDefault().AddService("kemkas-api");
var openTelemetryEndpoint = builder.Configuration.GetSection("monitoring")["url"];
var consoleEnabled = builder.Configuration.GetSection("monitoring")["console"] == "true";
builder.Services.AddOpenTelemetry().WithTracing(tracerProviderBuilder =>
{
tracerProviderBuilder
.SetResourceBuilder(otlResourceBuilder)
.AddAspNetCoreInstrumentation();
if (openTelemetryEndpoint != null)
{
tracerProviderBuilder.AddOtlpExporter(opt => opt.Endpoint = new Uri(openTelemetryEndpoint));
}
else if (consoleEnabled)
{
tracerProviderBuilder.AddConsoleExporter();
}
})
.WithMetrics(meterProviderBuilder =>
{
meterProviderBuilder
.SetResourceBuilder(otlResourceBuilder)
.AddAspNetCoreInstrumentation();
if (openTelemetryEndpoint != null)
{
meterProviderBuilder.AddOtlpExporter(opt => opt.Endpoint = new Uri(openTelemetryEndpoint));
}
else if (consoleEnabled)
{
meterProviderBuilder.AddConsoleExporter();
}
});
}
}
+5
View File
@@ -18,6 +18,7 @@
<ItemGroup>
<PackageReference Include="AspNet.Security.OAuth.Discord" Version="8.0.0" />
<PackageReference Include="Grpc.Core" Version="2.46.6" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Facebook" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="8.0.0" />
@@ -36,6 +37,10 @@
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.7.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.7.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.7.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.7.0" />
</ItemGroup>
<ItemGroup>
+10 -19
View File
@@ -11,6 +11,9 @@ using Kemkas.Web.Services.Identity;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Identity.UI.Services;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
var builder = WebApplication.CreateBuilder(args);
@@ -34,25 +37,7 @@ builder.Services.AddSingleton<IEmailSender, MailgunEmailSender>();
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddAuthentication()
.AddGoogle(options =>
{
var section = builder.Configuration.GetSection("Authentication:Google");
options.ClientId = section["ClientId"];
options.ClientSecret = section["ClientSecret"];
})
.AddFacebook(options =>
{
var section = builder.Configuration.GetSection("Authentication:Facebook");
options.ClientId = section["ClientId"];
options.ClientSecret = section["ClientSecret"];
})
.AddDiscord(options =>
{
var section = builder.Configuration.GetSection("Authentication:Discord");
options.ClientId = section["ClientId"];
options.ClientSecret = section["ClientSecret"];
});
builder.AddAuth();
builder.Services.AddControllersWithViews().AddJsonOptions(options =>
{
@@ -62,6 +47,10 @@ builder.Services.AddControllersWithViews().AddJsonOptions(options =>
});
builder.Services.AddRazorPages();
builder.AddOpenTelemetry();
builder.Services.AddHealthChecks();
builder.Services.AddCharacterServices();
var app = builder.Build();
@@ -93,6 +82,8 @@ else
app.UseHsts(); // TODO: check if this should be handled by proxy
}
app.UseHealthChecks("/health");
await using (var scope = app.Services.CreateAsyncScope())
{
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
@@ -0,0 +1,88 @@
import React from "react";
import {Karakter2E} from "../domain-models/karakter2E";
import {SignedNumberToText} from "../../shared/components/Helpers";
import {Mentok, MentoTipus, MentoTipusLabel, MentokTotal} from "../domain-models/mentok";
import {BaseHP, HP} from "../domain-models/masodlagos_ertekek";
import {TamadasBonusz} from "../domain-models/tamadas_bonusz";
function MentoNovekedes(props : {tipus: MentoTipus, ujMentok: Mentok, regiMentok: Mentok}) {
const {tipus, ujMentok, regiMentok} = props
if (ujMentok[tipus] === regiMentok[tipus]) {
return null
}
return <div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label'>{MentoTipusLabel(tipus)} mentő növekedés{' '}</label>
<span className='col col-form-label'>{SignedNumberToText(ujMentok[tipus] - regiMentok[tipus])}</span>
</div>;
}
function UjMento(props : {tipus: MentoTipus, ujMentok: Mentok, regiMentok: Mentok}) {
const {tipus, ujMentok, regiMentok} = props
if (ujMentok[tipus] === regiMentok[tipus]) {
return null
}
return <div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label'>{MentoTipusLabel(tipus)} mentő új értéke{' '}</label>
<span className='col col-form-label'>{SignedNumberToText(ujMentok[tipus])}</span>
</div>;
}
function BasicNewLevel(props: {
karakter: Pick<Karakter2E, 'osztaly' | 'tulajdonsagok' | 'szintlepesek'>
szint: number
changeRolledHP: (hp: number) => void,
}) {
const {karakter, szint, changeRolledHP} = props
const regiMentok = MentokTotal({...karakter, szint: szint - 1})
const ujMentok = MentokTotal({...karakter, szint: szint})
const regiTB = TamadasBonusz(karakter.osztaly, szint-1)
const ujTB = TamadasBonusz(karakter.osztaly, szint)
if (regiTB.length < ujTB.length) {
regiTB.push(0)
}
const TBdiff = ujTB.map((x, i) => x - regiTB[i])
const isTBincreased = TBdiff.length > 0 && TBdiff.some(x => x > 0)
return <>
<div className='row'>
<div className='col-lg-6'>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label'>Plusz dobott HP (d{BaseHP(karakter.osztaly)}){' '}</label>
<div className='col-sm-2 col'>
<input
className='form-control'
maxLength={2}
type='number'
value={karakter.szintlepesek[szint-1].HProll || 1}
min={1}
max={12}
onChange={e => changeRolledHP(Number(e.target.value))} />
</div>
</div>
{ isTBincreased && <div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label'>Támadás bónusz növekedés{' '}</label>
<span className='col col-form-label'>{TBdiff.map(SignedNumberToText).join(" / ")}</span>
</div>}
<MentoNovekedes tipus={'kitartas'} ujMentok={ujMentok} regiMentok={regiMentok} />
<MentoNovekedes tipus={'reflex'} ujMentok={ujMentok} regiMentok={regiMentok} />
<MentoNovekedes tipus={'akaratero'} ujMentok={ujMentok} regiMentok={regiMentok} />
</div>
<div className='col-lg-6'>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label'>Új HP{' '}</label>
<span className='col col-form-label'>{HP({...karakter, szintlepesek: karakter.szintlepesek.slice(0, szint-1)})}</span>
</div>
{isTBincreased && <div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label'>Új Támadás bónusz{' '}</label>
<span className='col col-form-label'>{ujTB.map(SignedNumberToText).join(" / ")}</span>
</div>}
<UjMento tipus={'kitartas'} ujMentok={ujMentok} regiMentok={regiMentok} />
<UjMento tipus={'reflex'} ujMentok={ujMentok} regiMentok={regiMentok} />
<UjMento tipus={'akaratero'} ujMentok={ujMentok} regiMentok={regiMentok} />
</div>
</div>
</>
}
export default BasicNewLevel
@@ -0,0 +1,86 @@
import React from "react";
import {Fegyver} from "../domain-models/felszereles";
export function FegyverLabel(fegyver: Fegyver): string {
const flags = [] as string[]
if (fegyver.Harcos) {
flags.push("H")
}
if (fegyver.Masfelkezes) {
flags.push("1½K")
}
if (fegyver.Ketkezes) {
if (fegyver.Type === "kozelharci") {
flags.push("2K")
} else {
if (fegyver.KetkezesBonusz) {
flags.push("2KB")
} else {
flags.push("2KN")
}
}
}
if (fegyver.HarciManover) {
flags.push("+2 HM")
}
if (fegyver.Alakzat){
flags.push("AL")
}
if (fegyver.Hosszu) {
flags.push("HO")
}
if (fegyver.Kabito) {
flags.push("KO")
}
if (fegyver.LovasRoham) {
flags.push("LR")
}
if (fegyver.PancelToro) {
flags.push("PT")
}
if (fegyver.PajzsZuzo) {
flags.push("PZ")
}
if (fegyver.Rohamtoro) {
flags.push("RT")
}
// Replaced by new group "dobhato"
// if (fegyver.EroBonusz) {
// flags.push("ER")
// }
if (fegyver.Megjegyzes != null && fegyver.Megjegyzes.length > 0) {
flags.push(fegyver.Megjegyzes)
}
const serializedFlags = flags.reduce((p,c) => `${p}${c} `, '');
const sizeLabel = fegyver.Size < 1 && fegyver.Size > 0 ? ` [1/${Math.round(1 / fegyver.Size)}]` : (fegyver.Size > 1 ? ` [${fegyver.Size}]` : '');
return `${fegyver.Name}${sizeLabel} ${serializedFlags.length > 0 ? "| " + serializedFlags : ''}| ${
fegyver.DamageMultiplier > 1 ? `${fegyver.DamageMultiplier}*` : ''}${fegyver.NumberOfDamageDice}d${fegyver.DamageDice}${fegyver.DamageBonus > 0 ? ` +${fegyver.DamageBonus}` : ''} ${
fegyver.CritRangeStart < 20 ? `${fegyver.CritRangeStart}-20` : ''}x${fegyver.CritMultiplier}${
fegyver.Price > 0 ? ` | ár: ${fegyver.Price} at ` : ''}${
fegyver.Range > 10 ? ` | táv: ${fegyver.Range}` : ''}`
}
function FegyverSelector(props: {fegyverek: Fegyver[], selectedId: string, onChange: (id: string) => void}) {
const { fegyverek, selectedId, onChange } = props
return <>
<select className='col form-select' value={selectedId}
onChange={e => onChange(e.target.value)}>
<optgroup label="Közelharci">
{fegyverek.filter(f => f.Type === 'kozelharci').map(f => <option
key={f.Id} value={f.Id}>{FegyverLabel(f)}</option>)}
</optgroup>
<optgroup label="Közelharci, dobható">
{fegyverek.filter(f => f.Type === 'dobhato').map(f => <option
key={f.Id} value={f.Id}>{FegyverLabel(f)}</option>)}
</optgroup>
<optgroup label="Távolsági">
{fegyverek.filter(f => f.Type === 'lofegyver').map(f => <option
key={f.Id} value={f.Id}>{FegyverLabel(f)}</option>)}
</optgroup>
</select>
</>
}
export default FegyverSelector
@@ -5,64 +5,7 @@ import {AllowedPajzsTypes, AllowedPancelTypes} from '../domain-models/allowed-pa
import fegyverek from '../domain-models/fegyver.json'
import {arraySetN} from "../../util";
import {AllowedFegyver} from "../domain-models/allowed-fegyver";
export function FegyverLabel(fegyver: Fegyver): string {
const flags = [] as string[]
if (fegyver.Harcos) {
flags.push("H")
}
if (fegyver.Masfelkezes) {
flags.push("1½K")
}
if (fegyver.Ketkezes) {
if (fegyver.Type === "kozelharci") {
flags.push("2K")
} else {
if (fegyver.KetkezesBonusz) {
flags.push("2KB")
} else {
flags.push("2KN")
}
}
}
if (fegyver.HarciManover) {
flags.push("+2 HM")
}
if (fegyver.Alakzat){
flags.push("AL")
}
if (fegyver.Hosszu) {
flags.push("HO")
}
if (fegyver.Kabito) {
flags.push("KO")
}
if (fegyver.LovasRoham) {
flags.push("LR")
}
if (fegyver.PancelToro) {
flags.push("PT")
}
if (fegyver.PajzsZuzo) {
flags.push("PZ")
}
if (fegyver.Rohamtoro) {
flags.push("RT")
}
// if (fegyver.EroBonusz) {
// flags.push("ER")
// }
if (fegyver.Megjegyzes != null && fegyver.Megjegyzes.length > 0) {
flags.push(fegyver.Megjegyzes)
}
const serializedFlags = flags.reduce((p,c) => `${p}${c} `, '');
const sizeLabel = fegyver.Size < 1 && fegyver.Size > 0 ? ` [1/${Math.round(1 / fegyver.Size)}]` : (fegyver.Size > 1 ? ` [${fegyver.Size}]` : '');
return `${fegyver.Name}${sizeLabel} ${serializedFlags.length > 0 ? "| " + serializedFlags : ''}| ${
fegyver.DamageMultiplier > 1 ? `${fegyver.DamageMultiplier}*` : ''}${fegyver.NumberOfDamageDice}d${fegyver.DamageDice}${fegyver.DamageBonus > 0 ? ` +${fegyver.DamageBonus}` : ''} ${
fegyver.CritRangeStart < 20 ? `${fegyver.CritRangeStart}-20` : ''}x${fegyver.CritMultiplier}${
fegyver.Price > 0 ? ` | ár: ${fegyver.Price} at ` : ''}${
fegyver.Range > 10 ? ` | táv: ${fegyver.Range}` : ''}`
}
import FegyverSelector from "./FegyverSelector";
function Felszereles(props: {felszereles: KarakterFelszereles, changeFelszereles: (felszereles: KarakterFelszereles) => void, osztaly: Osztaly2E}) {
const { felszereles, changeFelszereles, osztaly } = props;
@@ -116,21 +59,7 @@ function Felszereles(props: {felszereles: KarakterFelszereles, changeFelszereles
{valasztottFegyverek.map(({idx, fegyver}) => (
<div key={`fegyver-${idx}`} className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Fegyver</label>
<select className='col form-select' value={fegyver.Id}
onChange={e => changeFegyver(idx, e.target.value)}>
<optgroup label="Közelharci">
{allowedFegyverek.filter(f => f.Type === 'kozelharci').map(f => <option
key={f.Id} value={f.Id}>{FegyverLabel(f)}</option>)}
</optgroup>
<optgroup label="Közelharci, dobható">
{allowedFegyverek.filter(f => f.Type === 'dobhato').map(f => <option
key={f.Id} value={f.Id}>{FegyverLabel(f)}</option>)}
</optgroup>
<optgroup label="Távolsági">
{allowedFegyverek.filter(f => f.Type === 'lofegyver').map(f => <option
key={f.Id} value={f.Id}>{FegyverLabel(f)}</option>)}
</optgroup>
</select>
<FegyverSelector fegyverek={allowedFegyverek} selectedId={fegyver.Id} onChange={(fegyverId) => changeFegyver(idx, fegyverId)} />
<button className='col-md-2 col-sm-3 btn btn-outline-dark btn-lg ms-2' type='button'
onClick={() => changeFelszereles({
...felszereles,
@@ -0,0 +1,32 @@
import React from "react";
import {GetFegyver} from "../domain-models/felszereles";
import FegyverSelector from "./FegyverSelector";
import {AllowedFegyver} from "../domain-models/allowed-fegyver";
import {Osztaly2E} from "../domain-models/osztaly2E";
function HarcosFegyverSpecializacio(props: {
specialization: string,
existingSpecializations: (string | undefined)[],
changeSpecialization: (specialization: string) => void
szint: number
}) {
const {
specialization,
existingSpecializations,
changeSpecialization,
szint
} = props
const fegyver = GetFegyver(specialization)
const availableFegyverek = AllowedFegyver(Osztaly2E.Harcos)
const pickableFegyverek = availableFegyverek.filter(f => f.Id === specialization || existingSpecializations.filter(x => x === f.Id).length < (szint < 9 ? 1 : 2))
return <>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label' >Fegyver specializáció</label>
<FegyverSelector fegyverek={pickableFegyverek} selectedId={specialization} onChange={changeSpecialization} />
{fegyver == null && <span className='form-field-error'>Ismeretlen fegyver: {specialization}</span>}
</div>
</>
}
export default HarcosFegyverSpecializacio
@@ -0,0 +1,28 @@
import React from "react";
import {Osztaly2E} from "../domain-models/osztaly2E";
import {AllowedFegyver} from "../domain-models/allowed-fegyver";
import FegyverSelector from "./FegyverSelector";
function KalozKritikus(props: {
kritFegyverId: string,
existingKrits: (string | undefined)[],
changeKrit: (krit: string) => void,
szint: number
}) {
const {
kritFegyverId,
existingKrits,
changeKrit,
szint
} = props
const fegyverek = AllowedFegyver(Osztaly2E.Tengeresz).filter(f => f.Id === kritFegyverId || existingKrits.filter(x => x === f.Id).length < (szint < 9 ? 1 : 2))
return <>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Kritikus</label>
<FegyverSelector fegyverek={fegyverek} selectedId={kritFegyverId} onChange={changeKrit} />
</div>
</>
}
export default KalozKritikus
@@ -0,0 +1,126 @@
import React from "react";
import {Karakter2E} from "../domain-models/karakter2E";
import BasicNewLevel from "./BasicNewLevel";
import TulajdonsagNoveles from "./TulajdonsagNoveles";
import {Tulajdonsag2E, TulajdonsagokTotal} from "../domain-models/tulajdonsag2E";
import {arraySetN} from "../../util";
import {Osztaly2E} from "../domain-models/osztaly2E";
import KalozKritikus from "./KalozKritikus";
import HarcosFegyverSpecializacio from "./HarcosFegyverSpec";
function LevelUp(props: {
szint: number,
karakter: Karakter2E,
changeTulajdonsagNoveles: (t: Tulajdonsag2E) => void,
changeRolledHP: (rolledHP: number) => void,
changeHarcosSpec: (spec: string) => void,
changeKalozKritikus: (krit: string) => void,
}) {
const {
szint,
karakter,
changeTulajdonsagNoveles,
changeRolledHP,
changeHarcosSpec,
changeKalozKritikus,
} = props
const specialization = karakter.szintlepesek[szint-1].harcosFegyver
const krit = karakter.szintlepesek[szint-1].kalozKritikus
return <div>
<div className='row mt-3'>
<h5 className='col align-self-center'>{szint}. Szint</h5>
</div>
{szint % 4 === 0
&& <TulajdonsagNoveles
tulajdonsagok={karakter.tulajdonsagok}
tulajdonsagNoveles={karakter.szintlepesek[szint-1].tulajdonsagNoveles!}
changeTulajdonsagNoveles={changeTulajdonsagNoveles}
/>}
<BasicNewLevel
szint={szint}
karakter={karakter}
changeRolledHP={changeRolledHP}
/>
{karakter.osztaly === Osztaly2E.Harcos && szint % 2 === 1 &&
<HarcosFegyverSpecializacio
specialization={specialization!}
existingSpecializations={karakter.szintlepesek.map(x => x.harcosFegyver)}
changeSpecialization={changeHarcosSpec}
szint={szint}
/>
}
{karakter.osztaly === Osztaly2E.Tengeresz && szint % 3 === 0 && !!krit &&
<KalozKritikus
kritFegyverId={krit}
existingKrits={karakter.szintlepesek.map(x => x.kalozKritikus)}
changeKrit={changeKalozKritikus}
szint={szint}
/>
}
</div>
}
export function LevelUps(props: {karakter: Karakter2E, changeKarakter: (inputs: Karakter2E) => void}){
const {karakter, changeKarakter} = props;
const tulajdonsagokAtSzint = (szint: number) => TulajdonsagokTotal({...karakter, szintlepesek: karakter.szintlepesek.slice(0, szint - 1)})
const changeRolledHPAtSzint = (szint: number) => (newHProll: number) => {
changeKarakter({...karakter, szintlepesek: arraySetN(karakter.szintlepesek, szint-2, {...karakter.szintlepesek[szint-2], HProll: newHProll})})
console.log(karakter)
}
const changeTulajdonsagNovelesAtSzint = (szint: number) => {
return (t: Tulajdonsag2E) => {
changeKarakter({...karakter,
szintlepesek: arraySetN(karakter.szintlepesek, szint-1, {
...karakter.szintlepesek[szint-1],
tulajdonsagNoveles: t
})
})
};
}
const changeHarcosSpecializacioAtSzint = (szint: number) => {
return (spec: string) => {
changeKarakter({
...karakter,
szintlepesek: arraySetN(karakter.szintlepesek, szint-1, {
...karakter.szintlepesek[szint-1],
harcosFegyver: spec
})
})
};
}
const changeKalozKritikusAtSzint = (szint: number) => {
return (krit: string) => {
changeKarakter({
...karakter,
szintlepesek: arraySetN(karakter.szintlepesek, szint-1, {
...karakter.szintlepesek[szint-1],
kalozKritikus: krit
})
})
};
}
const levels = karakter.szintlepesek.map((_, i) => i+1);
return <>
{levels.slice(1).map(szint =>
<LevelUp
key={`level-up-${szint}`}
szint={szint}
karakter={{...karakter, tulajdonsagok: tulajdonsagokAtSzint(szint)}}
changeTulajdonsagNoveles={changeTulajdonsagNovelesAtSzint(szint)}
changeRolledHP={changeRolledHPAtSzint(szint)}
changeHarcosSpec={changeHarcosSpecializacioAtSzint(szint)}
changeKalozKritikus={changeKalozKritikusAtSzint(szint)}
/>
)}
</>
}
export default LevelUps
@@ -0,0 +1,37 @@
import React from "react";
import {
KarakterTulajdonsagok,
Modifier,
Tulajdonsag2E,
TulajdonsagIDs,
TulajdonsagLabel
} from "../domain-models/tulajdonsag2E";
import {SignedNumberToText} from "../../shared/components/Helpers";
function TulajdonsagNoveles(props: {tulajdonsagok: KarakterTulajdonsagok, tulajdonsagNoveles: Tulajdonsag2E, changeTulajdonsagNoveles: (tulajdonsag: Tulajdonsag2E) => void}) {
const {tulajdonsagok, tulajdonsagNoveles, changeTulajdonsagNoveles} = props
return <>
<div className='row'>
<div className='col-lg-6'>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label'>Tulajdonság növelés{' '}</label>
<div className='col'>
<select className='form-select' value={tulajdonsagNoveles} onChange={(e) => changeTulajdonsagNoveles(e.target.value as Tulajdonsag2E)}>
{TulajdonsagIDs.filter(t => t === tulajdonsagNoveles || tulajdonsagok[t] < 18).map(t => (
<option value={t} key={t}>{TulajdonsagLabel(t)}</option>
))}
</select>
</div>
</div>
</div>
<div className='col-lg-6'>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label'>Új {TulajdonsagLabel(tulajdonsagNoveles)}{' '}</label>
<span className='col col-form-label'>{tulajdonsagok[tulajdonsagNoveles]} ({SignedNumberToText(Modifier(tulajdonsagok[tulajdonsagNoveles]))})</span>
</div>
</div>
</div>
</>
}
export default TulajdonsagNoveles
@@ -4,6 +4,10 @@ import {Osztaly2E} from "./osztaly2E";
import {KepzettsegId} from "./kepzettsegek2E";
import {JellemID} from "./jellem";
import {DefaultFelszereles, KarakterFelszereles} from "./felszereles";
import {Szintlepes} from "./szintlepes";
import {AllowedFegyver} from "./allowed-fegyver";
import {BaseHP} from "./masodlagos_ertekek";
import {dAny} from "../../shared/domain-models/kockak";
export interface Karakter2E {
name: string,
@@ -14,13 +18,11 @@ export interface Karakter2E {
tulajdonsagok: KarakterTulajdonsagok
faj: Faj2E
osztaly: Osztaly2E
harcosSpecializaciok: string[],
kalozKritikus: string[],
kepzettsegek: KepzettsegId[]
tolvajKepzettsegek?: KepzettsegId[]
szint: number,
hpRolls: number[]
felszereles: KarakterFelszereles,
szintlepesek: Szintlepes[]
}
export const DefaultKarakter: Karakter2E = {
@@ -30,11 +32,44 @@ export const DefaultKarakter: Karakter2E = {
tulajdonsagok: TulajdonsagDefaults,
faj: Faj2E.Ember,
osztaly: Osztaly2E.Tengeresz,
harcosSpecializaciok: [],
kalozKritikus: [],
kepzettsegek: [],
tolvajKepzettsegek: [],
szint: 1,
hpRolls: [],
felszereles: DefaultFelszereles,
szintlepesek: [{
osztaly: Osztaly2E.Tengeresz,
HProll: 10,
}]
}
function setOsztalyForSzintLepes(szintlepes: Szintlepes, osztaly: Osztaly2E, szint: number): Szintlepes {
const base : Szintlepes = {
...szintlepes,
osztaly,
harcosFegyver: undefined,
kalozKritikus: undefined
}
if (osztaly === Osztaly2E.Harcos && szint % 2 === 1) {
base.harcosFegyver = AllowedFegyver(Osztaly2E.Harcos)[szint].Id
}
if (osztaly === Osztaly2E.Tengeresz && szint % 3 === 0) {
base.kalozKritikus = AllowedFegyver(Osztaly2E.Tengeresz)[szint].Id
}
const HD = BaseHP(osztaly)
if (szint === 1) {
base.HProll = HD
} else {
base.HProll = dAny(HD)
console.log(`Level ${szint} HP roll on d${HD} is: ${base.HProll} due to class change`)
}
return base
}
export function ChangeLvl1Osztaly(karakter: Karakter2E, osztaly: Osztaly2E): Karakter2E {
return {
...karakter,
osztaly,
szintlepesek: karakter.szintlepesek.map((l, idx) => setOsztalyForSzintLepes(l, osztaly, idx +1))
}
}
@@ -228,7 +228,7 @@ export function GetNumberOfKepzettsegek(t_int: number, faj: Faj2E, max: number =
return numberOfKepzettseg;
}
export function SetDefaultTolvajKepzettsegek(karakter: Pick<Karakter2E, 'osztaly' | 'tolvajKepzettsegek'>, changeTolvajKepzettsegek: (tolvajKepzettsegek?: KepzettsegId[]) => void) {
export function SetDefaultTolvajKepzettsegek(karakter: Pick<Karakter2E, 'osztaly' | 'szintlepesek' | 'tolvajKepzettsegek'>, changeTolvajKepzettsegek: (tolvajKepzettsegek?: KepzettsegId[]) => void) {
if (karakter.osztaly !== Osztaly2E.Tolvaj && karakter.tolvajKepzettsegek != null) {
changeTolvajKepzettsegek(undefined)
}
@@ -14,7 +14,7 @@ export interface MasodlagosErtekekView {
Mentok: Mentok
}
export type KarakterpickForMasodlagosErtekek = Pick<Karakter2E, 'osztaly' | 'hpRolls' | 'tulajdonsagok' | 'szint' | 'faj' | 'felszereles'>
export type KarakterpickForMasodlagosErtekek = Pick<Karakter2E, 'osztaly' | 'tulajdonsagok' | 'szint' | 'szintlepesek' | 'faj' | 'felszereles'>
export function BaseHP(osztaly: Osztaly2E) {
let base = 4;
@@ -45,11 +45,11 @@ export function BaseHP(osztaly: Osztaly2E) {
return base
}
export function HP(karakter: Pick<Karakter2E, 'osztaly' | 'tulajdonsagok' | 'hpRolls'>): number {
export function HP(karakter: Pick<Karakter2E, 'osztaly' | 'tulajdonsagok' | 'szintlepesek'>): number {
const egeszsegModifier = Modifier(karakter.tulajdonsagok.t_egs)
return BaseHP(karakter.osztaly) + egeszsegModifier + karakter.hpRolls
return karakter.szintlepesek
// https://lfg.hu/forum/topic/15079-kard-es-magia/page/219/#comment-2218333
.map(hp => Math.max(1, hp + egeszsegModifier))
.map(szint => Math.max(1, szint.HProll + egeszsegModifier))
.reduce((sum, val) => sum + val, 0)
}
@@ -0,0 +1,60 @@
import {Tulajdonsag2E, TulajdonsagIDs, TulajdonsagokTotal} from "./tulajdonsag2E";
import {KepzettsegId} from "./kepzettsegek2E";
import {Osztaly2E} from "./osztaly2E";
import {Karakter2E} from "./karakter2E";
import {BaseHP} from "./masodlagos_ertekek";
import {dAny} from "../../shared/domain-models/kockak";
import {AllowedFegyver} from "./allowed-fegyver";
export interface Szintlepes {
osztaly: Osztaly2E;
HProll: number;
tolvajExtraKepzettseg?: KepzettsegId; // only at level 5 and 9
harcosFegyver?: string; // every 3rd level
kalozKritikus?: string; // every odd level
tulajdonsagNoveles?: Tulajdonsag2E;
}
export function PickableSpecializationFilter(existingSpecilizations: (string | undefined)[], szint: number) {
return (fegyver: {Id: string}) => existingSpecilizations.filter(x => x === fegyver.Id).length < (szint < 9 ? 1 : 2)
}
export function LevelUp(karakter: Karakter2E, changeKarakter: (input: Karakter2E) => void) {
const tulajdonsagokTotal = TulajdonsagokTotal(karakter)
const szint = karakter.szint + 1
const dice = BaseHP(karakter.osztaly)
const roll = dAny(dice)
console.log(`Level ${szint} HP roll on d${dice} is: ${roll}`)
const szintlepes: Szintlepes = {
osztaly: karakter.osztaly,
HProll: roll
}
if (szint % 4 === 0) {
for (const tulajdonsag of TulajdonsagIDs) {
if (tulajdonsagokTotal[tulajdonsag] < 18) {
szintlepes.tulajdonsagNoveles = tulajdonsag;
break;
}
}
}
if(karakter.osztaly === Osztaly2E.Harcos && szint % 2 === 1) {
const harcosSpecializaciok = karakter.szintlepesek.map(x => x.harcosFegyver)
szintlepes.harcosFegyver = AllowedFegyver(Osztaly2E.Harcos).filter(PickableSpecializationFilter(harcosSpecializaciok, karakter.szint))[0].Id
}
if(karakter.osztaly === Osztaly2E.Tengeresz && szint % 3 === 0) {
let kalozKritikus = karakter.szintlepesek.map(x => x.kalozKritikus)
szintlepes.kalozKritikus = AllowedFegyver(Osztaly2E.Tengeresz).filter(PickableSpecializationFilter(kalozKritikus, szint))[0].Id
}
changeKarakter({...karakter, szint, szintlepesek: [...karakter.szintlepesek, szintlepes]})
}
export function LevelDown(karakter: Karakter2E, changeKarakter: (input: Karakter2E) => void) {
if (karakter.szintlepesek.length > 1) {
changeKarakter({...karakter, szint: karakter.szint - 1, szintlepesek: karakter.szintlepesek.slice(0, -1)})
}
else {
console.error(`Can't level down character: ${karakter}`)
}
}
@@ -1,4 +1,6 @@
import {d6} from "../../shared/domain-models/kockak";
import {Karakter2E} from "./karakter2E";
import {TulajdonsagokFajjal} from "./faj2E";
export enum Tulajdonsag2E {
Ero = 't_ero',
@@ -74,3 +76,15 @@ export function TulajdonsagModosito(tulajdonsagok: KarakterTulajdonsagok): Karak
}
return response
}
export function TulajdonsagokTotal(karakter: Pick<Karakter2E, 'tulajdonsagok' | 'faj' | 'szintlepesek'>): KarakterTulajdonsagok {
const level1 = TulajdonsagokFajjal(karakter.tulajdonsagok, karakter.faj)
return {
t_ero: level1.t_ero + karakter.szintlepesek.filter(s => s.tulajdonsagNoveles === 't_ero').length,
t_ugy: level1.t_ugy + karakter.szintlepesek.filter(s => s.tulajdonsagNoveles === 't_ugy').length,
t_egs: level1.t_egs + karakter.szintlepesek.filter(s => s.tulajdonsagNoveles === 't_egs').length,
t_int: level1.t_int + karakter.szintlepesek.filter(s => s.tulajdonsagNoveles === 't_int').length,
t_bol: level1.t_bol + karakter.szintlepesek.filter(s => s.tulajdonsagNoveles === 't_bol').length,
t_kar: level1.t_kar + karakter.szintlepesek.filter(s => s.tulajdonsagNoveles === 't_kar').length,
}
}
@@ -1,7 +1,7 @@
import React, {useState} from 'react'
import {useParams} from "react-router-dom";
import {Toast, ToastContainer} from "react-bootstrap";
import {DefaultKarakter} from "../domain-models/karakter2E";
import {ChangeLvl1Osztaly, DefaultKarakter} from "../domain-models/karakter2E";
import {KarakterTulajdonsagok} from "../domain-models/tulajdonsag2E";
import FajSelector2E from "../components/FajSelector2E";
import Tulajdonsagok2E from "../components/Tulajdonsagok2E";
@@ -20,6 +20,10 @@ import MasodlagosErtekek from "../components/MasodlagosErtekek";
import {CalculateMasodlagosErtekek} from "../domain-models/masodlagos_ertekek";
import Felszereles from "../components/Felszereles";
import {KarakterFelszereles} from "../domain-models/felszereles";
import LevelUps from "../components/LevelUps";
import {LevelDown, LevelUp} from "../domain-models/szintlepes";
import HarcosFegyverSpecializacio from "../components/HarcosFegyverSpec";
import {arraySetN} from "../../util";
function CreateCharacter2E(props: {}) {
@@ -100,12 +104,15 @@ function CreateCharacter2E(props: {}) {
<OsztalySelector2E
currentFaj={karakter.faj}
currentOsztaly={karakter.osztaly}
changeOsztaly={osztaly => setKarakter({...karakter, osztaly})}
changeOsztaly={o => setKarakter(ChangeLvl1Osztaly(karakter, o))}
/>
{karakter.osztaly === Osztaly2E.Harcos &&
<div className='mb-6'>
TODO: Harcos fegyver specializaciok!!
</div>}
{karakter.szintlepesek[0].osztaly === Osztaly2E.Harcos &&
<HarcosFegyverSpecializacio
specialization={karakter.szintlepesek[0].harcosFegyver || 'kard_hosszu'}
szint={1}
changeSpecialization={(spec) => setKarakter({...karakter, szintlepesek: arraySetN(karakter.szintlepesek, 0, {...karakter.szintlepesek[0], harcosFegyver: spec})})}
existingSpecializations={karakter.szintlepesek.map(x => x.harcosFegyver)}
/>}
<KarakterKepzettsegek
getKepzettsegListaN={GetKepzettsegListaN(karakter)}
numberOfKepzettsegek={GetNumberOfKepzettsegek(tulajdonsagokFajjal.t_int, karakter.faj)}
@@ -120,10 +127,21 @@ function CreateCharacter2E(props: {}) {
...karakter,
tulajdonsagok: tulajdonsagokFajjal,
szint: 1,
hpRolls: []
szintlepesek: karakter.szintlepesek.slice(0,1)
})} />
<hr />
<Felszereles osztaly={karakter.osztaly} felszereles={karakter.felszereles}
<LevelUps karakter={karakter} changeKarakter={setKarakter}/>
{karakter.szint > 1 && <hr/>}
<div className='d-grid gap-2 m-5'>
{karakter.szint < 12 &&
<button className='btn btn-dark btn-lg' type='button' onClick={() => LevelUp(karakter, setKarakter)}>Szintlépés!
</button>}
{karakter.szint > 1 &&
<button className='btn btn-dark btn-lg' type='button' onClick={() => LevelDown(karakter, setKarakter)}>Visszalépés!
</button>}
</div>
<hr/>
<Felszereles osztaly={karakter.szintlepesek[0].osztaly} felszereles={karakter.felszereles}
changeFelszereles={changeFelszereles}/>
<hr/>
+158
View File
@@ -8,6 +8,16 @@
"resolved": "8.0.0",
"contentHash": "+MtOHPctq1QT1c03h+7g4oBWtZ/buQYUJg4AR0DG+qjmD2cQRMGWXC1J6/ITeCc6xa5BcXzJmt1L/8pVXIB4kw=="
},
"Grpc.Core": {
"type": "Direct",
"requested": "[2.46.6, )",
"resolved": "2.46.6",
"contentHash": "ZoRg3KmOJ2urTF4+u3H0b1Yv10xzz2Y/flFWS2tnRmj8dbKLeiJaSRqu4LOBD3ova90evqLkVZ85kUkC4JT4lw==",
"dependencies": {
"Grpc.Core.Api": "2.46.6",
"System.Memory": "4.5.3"
}
},
"Microsoft.AspNetCore.Authentication.Facebook": {
"type": "Direct",
"requested": "[8.0.0, )",
@@ -124,6 +134,78 @@
"Npgsql.EntityFrameworkCore.PostgreSQL": "1.1.0"
}
},
"OpenTelemetry.Exporter.Console": {
"type": "Direct",
"requested": "[1.7.0, )",
"resolved": "1.7.0",
"contentHash": "9mcwpArzG7+5cLnG6marWdVD1TyoVR03WAjShvxADqgsEeuzLMClTlgXi41bTCAgxjYyiHV5l60fJwXTekiJdQ==",
"dependencies": {
"OpenTelemetry": "1.7.0",
"System.Text.Encodings.Web": "4.7.2",
"System.Text.Json": "4.7.2"
}
},
"OpenTelemetry.Exporter.OpenTelemetryProtocol": {
"type": "Direct",
"requested": "[1.7.0, )",
"resolved": "1.7.0",
"contentHash": "ULGTxg4HdnJD+CIuiD15Uov4xVVqwpbNGFd075J3MT3lLM9XcwU4YRz7CCNmqIlehYaiZrGhUurXrI2LgZZL1Q==",
"dependencies": {
"Google.Protobuf": "[3.22.5, 4.0.0)",
"Grpc.Net.Client": "[2.52.0, 3.0.0)",
"OpenTelemetry": "1.7.0"
}
},
"OpenTelemetry.Extensions.Hosting": {
"type": "Direct",
"requested": "[1.7.0, )",
"resolved": "1.7.0",
"contentHash": "MbB7CWWqb7xHK0jTF9Gtvw/eLWdaKqzkE1XAwLe05xyskHuwJWAbZFax4nGLA71YkMWQNO5iPIBlirvYXOLMlg==",
"dependencies": {
"Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0",
"Microsoft.Extensions.Hosting.Abstractions": "8.0.0",
"OpenTelemetry": "1.7.0"
}
},
"OpenTelemetry.Instrumentation.AspNetCore": {
"type": "Direct",
"requested": "[1.7.0, )",
"resolved": "1.7.0",
"contentHash": "K/mjCmuR9zgrL6fMC6sbnX/y6BOJJFI3hp42O+tz8nT/w6sxbfjrLcU+fVwfct3lsnohSKdPfrq6fSrxAF5ezw==",
"dependencies": {
"OpenTelemetry.Api.ProviderBuilderExtensions": "1.7.0"
}
},
"Google.Protobuf": {
"type": "Transitive",
"resolved": "3.22.5",
"contentHash": "tTMtDZPbLxJew8pk7NBdqhLqC4OipfkZdwPuCEUNr2AoDo1siUGcxFqJK0wDewTL8ge5Cjrb16CToMPxBUHMGA=="
},
"Grpc.Core.Api": {
"type": "Transitive",
"resolved": "2.52.0",
"contentHash": "SQiPyBczG4vKPmI6Fd+O58GcxxDSFr6nfRAJuBDUNj+PgdokhjWJvZE/La1c09AkL2FVm/jrDloG89nkzmVF7A==",
"dependencies": {
"System.Memory": "4.5.3"
}
},
"Grpc.Net.Client": {
"type": "Transitive",
"resolved": "2.52.0",
"contentHash": "hWVH9g/Nnjz40ni//2S8UIOyEmhueQREoZIkD0zKHEPqLxXcNlbp4eebXIOicZtkwDSx0TFz9NpkbecEDn6rBw==",
"dependencies": {
"Grpc.Net.Common": "2.52.0",
"Microsoft.Extensions.Logging.Abstractions": "3.0.3"
}
},
"Grpc.Net.Common": {
"type": "Transitive",
"resolved": "2.52.0",
"contentHash": "di9qzpdx525IxumZdYmu6sG2y/gXJyYeZ1ruFUzB9BJ1nj4kU1/dTAioNCMt1VLRvNVDqh8S8B1oBdKhHJ4xRg==",
"dependencies": {
"Grpc.Core.Api": "2.52.0"
}
},
"Humanizer.Core": {
"type": "Transitive",
"resolved": "2.14.1",
@@ -265,6 +347,15 @@
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
@@ -273,6 +364,14 @@
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "mBMoXLsr5s1y2zOHWmKsE9veDcx8h1x/c3rz4baEdQKTeDcmQAPNbB54Pi/lhFO3K431eEq6PFbMgLaa6PHFfA==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Transitive",
"resolved": "8.0.0",
@@ -371,6 +470,21 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
}
},
"Microsoft.Extensions.Logging.Configuration": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "ixXXV0G/12g6MXK65TLngYN9V5hQQRuV+fZi882WIoVJT7h5JvoYoxTEwCgdqwLjSneqh1O+66gM8sMr9z/rsQ==",
"dependencies": {
"Microsoft.Extensions.Configuration": "8.0.0",
"Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
"Microsoft.Extensions.Configuration.Binder": "8.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Logging": "8.0.0",
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
"Microsoft.Extensions.Options": "8.0.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0"
}
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "8.0.0",
@@ -380,6 +494,18 @@
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Options.ConfigurationExtensions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
"Microsoft.Extensions.Configuration.Binder": "8.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Options": "8.0.0",
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "8.0.0",
@@ -472,6 +598,33 @@
"Microsoft.Extensions.Logging.Abstractions": "8.0.0"
}
},
"OpenTelemetry": {
"type": "Transitive",
"resolved": "1.7.0",
"contentHash": "HNmOJg++4FtEJGCIn1dCJcDkHRLNuLKU047owrGXUOfz/C/c5oZHOPKrowKVOy2jOOh/F9+HDshzeOk5NnQEZg==",
"dependencies": {
"Microsoft.Extensions.Logging": "8.0.0",
"Microsoft.Extensions.Logging.Configuration": "8.0.0",
"OpenTelemetry.Api.ProviderBuilderExtensions": "1.7.0"
}
},
"OpenTelemetry.Api": {
"type": "Transitive",
"resolved": "1.7.0",
"contentHash": "w5uDHl1Nm7R5igWu1QjewpdPzJSavua8NTI3+iThOdRNHD3cabox8JeIJ8LpBSXGHARSNFMJZ/Xr5EGRinMTMg==",
"dependencies": {
"System.Diagnostics.DiagnosticSource": "8.0.0"
}
},
"OpenTelemetry.Api.ProviderBuilderExtensions": {
"type": "Transitive",
"resolved": "1.7.0",
"contentHash": "fHLmJcRJk1dAwddjBgvqi/Grmr04hPo1DoFwTa6hxtSvAFOXPU56Xe9Sh2VXqz6Gstzp6TCju8z0Sob1t7BzSg==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"OpenTelemetry.Api": "1.7.0"
}
},
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
"type": "Transitive",
"resolved": "4.3.0",
@@ -887,6 +1040,11 @@
"System.Threading": "4.3.0"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.3",
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
},
"System.Net.Http": {
"type": "Transitive",
"resolved": "4.3.0",