up
This commit is contained in:
commit
2c6320512f
3516 changed files with 194596 additions and 0 deletions
65
.editorconfig
Normal file
65
.editorconfig
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = crlf
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
insert_final_newline = false
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
# Microsoft .NET properties
|
||||||
|
csharp_new_line_before_members_in_object_initializers = false
|
||||||
|
csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
|
||||||
|
csharp_prefer_braces = true:hint
|
||||||
|
csharp_space_after_cast = false
|
||||||
|
csharp_style_var_elsewhere = false:error
|
||||||
|
csharp_style_var_for_built_in_types = false:error
|
||||||
|
csharp_style_var_when_type_is_apparent = true:error
|
||||||
|
dotnet_style_predefined_type_for_locals_parameters_members = true:hint
|
||||||
|
dotnet_style_predefined_type_for_member_access = true:hint
|
||||||
|
dotnet_style_qualification_for_event = false:hint
|
||||||
|
dotnet_style_qualification_for_field = false:hint
|
||||||
|
dotnet_style_qualification_for_method = false:hint
|
||||||
|
dotnet_style_qualification_for_property = false:hint
|
||||||
|
dotnet_style_require_accessibility_modifiers = for_non_interface_members:hint
|
||||||
|
|
||||||
|
# ReSharper properties
|
||||||
|
resharper_align_linq_query = true
|
||||||
|
resharper_align_multiline_binary_expressions_chain = false
|
||||||
|
resharper_align_multline_type_parameter_constrains = true
|
||||||
|
resharper_align_multline_type_parameter_list = true
|
||||||
|
resharper_apply_on_completion = true
|
||||||
|
resharper_autodetect_indent_settings = true
|
||||||
|
resharper_braces_redundant = true
|
||||||
|
resharper_constructor_or_destructor_body = expression_body
|
||||||
|
resharper_csharp_max_line_length = 200
|
||||||
|
resharper_csharp_stick_comment = false
|
||||||
|
resharper_indent_type_constraints = false
|
||||||
|
resharper_local_function_body = expression_body
|
||||||
|
resharper_method_or_operator_body = expression_body
|
||||||
|
resharper_parentheses_non_obvious_operations = equality
|
||||||
|
resharper_place_accessorholder_attribute_on_same_line = False
|
||||||
|
resharper_space_within_single_line_array_initializer_braces = true
|
||||||
|
resharper_use_indent_from_vs = false
|
||||||
|
|
||||||
|
# ReSharper inspection severities
|
||||||
|
resharper_arrange_constructor_or_destructor_body_highlighting = hint
|
||||||
|
resharper_arrange_local_function_body_highlighting = hint
|
||||||
|
resharper_arrange_method_or_operator_body_highlighting = hint
|
||||||
|
resharper_arrange_missing_parentheses_highlighting = hint
|
||||||
|
resharper_redundant_base_qualifier_highlighting = warning
|
||||||
|
resharper_web_config_module_not_resolved_highlighting = warning
|
||||||
|
resharper_web_config_type_not_resolved_highlighting = warning
|
||||||
|
resharper_web_config_wrong_module_highlighting = warning
|
||||||
|
|
||||||
|
[{*.yml,*.yaml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[{.eslintrc,.babelrc,.stylelintrc,jest.config,bowerrc,*.jsb3,*.jsb2,*.json}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.{appxmanifest,asax,ascx,aspx,build,cs,cshtml,dtd,fs,fsi,fsscript,fsx,master,ml,mli,nuspec,razor,resw,resx,skin,vb,xaml,xamlx,xoml,xsd}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
||||||
63
.gitattributes
vendored
Normal file
63
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
###############################################################################
|
||||||
|
# Set default behavior to automatically normalize line endings.
|
||||||
|
###############################################################################
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set default behavior for command prompt diff.
|
||||||
|
#
|
||||||
|
# This is need for earlier builds of msysgit that does not have it on by
|
||||||
|
# default for csharp files.
|
||||||
|
# Note: This is only used by command line
|
||||||
|
###############################################################################
|
||||||
|
#*.cs diff=csharp
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set the merge driver for project and solution files
|
||||||
|
#
|
||||||
|
# Merging from the command prompt will add diff markers to the files if there
|
||||||
|
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||||
|
# the diff markers are never inserted). Diff markers may cause the following
|
||||||
|
# file extensions to fail to load in VS. An alternative would be to treat
|
||||||
|
# these files as binary and thus will always conflict and require user
|
||||||
|
# intervention with every merge. To do so, just uncomment the entries below
|
||||||
|
###############################################################################
|
||||||
|
#*.sln merge=binary
|
||||||
|
#*.csproj merge=binary
|
||||||
|
#*.vbproj merge=binary
|
||||||
|
#*.vcxproj merge=binary
|
||||||
|
#*.vcproj merge=binary
|
||||||
|
#*.dbproj merge=binary
|
||||||
|
#*.fsproj merge=binary
|
||||||
|
#*.lsproj merge=binary
|
||||||
|
#*.wixproj merge=binary
|
||||||
|
#*.modelproj merge=binary
|
||||||
|
#*.sqlproj merge=binary
|
||||||
|
#*.wwaproj merge=binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# behavior for image files
|
||||||
|
#
|
||||||
|
# image files are treated as binary by default.
|
||||||
|
###############################################################################
|
||||||
|
#*.jpg binary
|
||||||
|
#*.png binary
|
||||||
|
#*.gif binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# diff behavior for common document formats
|
||||||
|
#
|
||||||
|
# Convert binary document formats to text before diffing them. This feature
|
||||||
|
# is only available from the command line. Turn it on by uncommenting the
|
||||||
|
# entries below.
|
||||||
|
###############################################################################
|
||||||
|
#*.doc diff=astextplain
|
||||||
|
#*.DOC diff=astextplain
|
||||||
|
#*.docx diff=astextplain
|
||||||
|
#*.DOCX diff=astextplain
|
||||||
|
#*.dot diff=astextplain
|
||||||
|
#*.DOT diff=astextplain
|
||||||
|
#*.pdf diff=astextplain
|
||||||
|
#*.PDF diff=astextplain
|
||||||
|
#*.rtf diff=astextplain
|
||||||
|
#*.RTF diff=astextplain
|
||||||
660
.gitignore
vendored
Normal file
660
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,660 @@
|
||||||
|
|
||||||
|
### ASPNETCore ###
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
bld/
|
||||||
|
dist/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
output/
|
||||||
|
|
||||||
|
# Visual Studio 2015 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# DNX
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/packages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/packages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/packages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignoreable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
node_modules/
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# CodeRush
|
||||||
|
.cr/
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/
|
||||||
|
|
||||||
|
### Csharp ###
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_h.h
|
||||||
|
*.iobj
|
||||||
|
*.ipdb
|
||||||
|
*_wpftmp.csproj
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- Backup*.rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
### Go ###
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
### Go Patch ###
|
||||||
|
/vendor/
|
||||||
|
/Godeps/
|
||||||
|
|
||||||
|
### VisualStudioCode ###
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
### VisualStudioCode Patch ###
|
||||||
|
# Ignore all local history of files
|
||||||
|
.history
|
||||||
|
|
||||||
|
### VisualStudio ###
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
|
||||||
|
# Others
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
configs/parse_command.yaml
|
||||||
|
|
||||||
|
# Config files
|
||||||
|
srcs/GameChannel/config/
|
||||||
867
WingsEmu.sln
Normal file
867
WingsEmu.sln
Normal file
|
|
@ -0,0 +1,867 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.31919.166
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Executables", "Executables", "{0D88B727-CA5D-48DB-835B-E90C8E282DA7}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsEmu.Communication.gRPC", "srcs\WingsEmu.Communication.gRPC\WingsEmu.Communication.gRPC.csproj", "{15948D13-B424-4DF3-B341-36CCB97D4D9F}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WingsAPI", "WingsAPI", "{404B2AB4-CAFA-4AF7-9593-F231A25A973C}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsAPI.Packets", "srcs\WingsAPI.Packets\WingsAPI.Packets.csproj", "{596A8193-7A5D-4055-BB8C-D2F27E1C6B94}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsAPI.Data", "srcs\WingsAPI.Data\WingsAPI.Data.csproj", "{0F1F75EF-8755-499B-8A28-F6653EB29FED}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsAPI.Game", "srcs\WingsAPI.Game\WingsAPI.Game.csproj", "{295450C6-C37F-4AA7-A59A-1BFFF352D0EA}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Toolkit", "srcs\Toolkit\Toolkit.csproj", "{05451402-CA5D-4474-A8BE-8E534AD17748}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{823AD0CF-E764-4113-A166-6A615A4B928F}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{F872A038-D325-4718-B0A0-4CBCEA3714ED}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsAPI.Game.Extensions", "srcs\WingsAPI.Game.Extensions\WingsAPI.Game.Extensions.csproj", "{D425AE9F-F25D-4B64-9983-FE76778524A7}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugin.DB.EF", "srcs\_plugins\Plugin.DB.EF\Plugin.DB.EF.csproj", "{8ED45EF3-0271-4867-9330-80D300029D8C}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsAPI.Commands", "srcs\WingsAPI.Commands\WingsAPI.Commands.csproj", "{B0D05BBF-F7FA-46EC-9224-182B2B98C4A3}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsEmu.Plugins.Essentials", "srcs\_plugins\WingsEmu.Plugins.Essentials\WingsEmu.Plugins.Essentials.csproj", "{3F789B98-5A34-41CF-B4F0-70B03C0C5ED2}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Master", "srcs\Master\Master.csproj", "{19A46232-72C5-4BB7-AE6B-A9062BE259A0}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GameChannel", "srcs\GameChannel\GameChannel.csproj", "{42DE9E8C-747E-46A4-9AC5-BB7ED1425BF0}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LoginServer", "srcs\LoginServer\LoginServer.csproj", "{D4506804-B9A1-4E1A-B97C-0F815814FE62}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsEmu.Plugins.BasicImplementations", "srcs\_plugins\WingsEmu.Plugins.BasicImplementation\WingsEmu.Plugins.BasicImplementations.csproj", "{D6B19207-B3E3-4B53-9964-19DEFB2089EF}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsEmu.Plugins.PacketHandling", "srcs\_plugins\WingsEmu.Plugins.PacketHandling\WingsEmu.Plugins.PacketHandling.csproj", "{F839354B-16C3-4EEF-8872-768B6D832BB2}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsEmu.Plugins.DistributedGameEvents", "srcs\_plugins\WingsEmu.Plugins.DistributedGameEvents\WingsEmu.Plugins.DistributedGameEvents.csproj", "{5707003E-0835-4E7A-BAD8-E55F4AE0D583}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsAPI.Communication", "srcs\WingsAPI.Communication\WingsAPI.Communication.csproj", "{CA5FFE88-A5FF-42D3-9E88-220A594AC27F}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsEmu.Plugins.GameEvents", "srcs\_plugins\WingsEmu.Plugins.GameEvents\WingsEmu.Plugins.GameEvents.csproj", "{A57EA67A-133B-4217-BE17-9E25622A95DD}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Scheduler", "srcs\Scheduler\Scheduler.csproj", "{105F39CC-E6C1-4365-B200-75D451E5747F}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscordNotifier", "srcs\DiscordNotifier\DiscordNotifier.csproj", "{B84EBC46-1CF7-47C9-B38E-F4919A11D6D0}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "srcs", "srcs", "{322AAFB0-04E1-42B9-B9F1-B836E12E1FCD}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FamilyServer", "srcs\FamilyServer\FamilyServer.csproj", "{5F2C76AE-81B2-4A24-80D2-086EFC41627B}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DatabaseServer", "srcs\DatabaseServer\DatabaseServer.csproj", "{18F61C6F-997C-459F-A3CE-63AEE311315E}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogsServer", "srcs\LogsServer\LogsServer.csproj", "{7B5A36CE-10AE-4915-84C2-61C205F4C004}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailServer", "srcs\MailServer\MailServer.csproj", "{58B6177B-2C56-4B37-88AB-8C454767D427}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BazaarServer", "srcs\BazaarServer\BazaarServer.csproj", "{61DB7D8D-7903-47EB-85FA-1E21001C5EAE}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsAPI.Scripting.LUA", "srcs\WingsAPI.Scripting.LUA\WingsAPI.Scripting.LUA.csproj", "{D7F1A231-791A-4B34-A9B2-21282EADA82C}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RelationServer", "srcs\RelationServer\RelationServer.csproj", "{42CDD6F5-A858-4A32-8982-E5A63689CD58}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugin.PlayerLogs", "srcs\_plugins\Plugin.PlayerLogs\Plugin.PlayerLogs.csproj", "{779A5516-5209-491C-878A-63086DB3F566}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugin.Raids", "srcs\_plugins\Plugin.Raids\Plugin.Raids.csproj", "{8293EEC9-C747-46C3-B3D2-30C552620269}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugin.Act4", "srcs\_plugins\Plugin.Act4\Plugin.Act4.csproj", "{DA4852B6-2FBD-4E5C-972C-049626152E4E}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsAPI.Scripting", "srcs\WingsAPI.Scripting\WingsAPI.Scripting.csproj", "{CCBE134E-2DAB-4CE7-B9F2-4F315CDEF1FA}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugin.TimeSpaces", "srcs\_plugins\Plugin.TimeSpaces\Plugin.TimeSpaces.csproj", "{E4E9646F-7B85-4446-9B6D-846D32BAC3F5}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugin.CoreImpl", "srcs\_plugins\Plugin.CoreImpl\Plugin.CoreImpl.csproj", "{3038B4F6-2CB4-4124-86EB-FE8D7FB3A0D7}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugin.FamilyImpl", "srcs\_plugins\Plugin.FamilyImpl\Plugin.FamilyImpl.csproj", "{E27BC1CD-D6DF-4EEE-B340-5424BE442AC5}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugin.QuestImpl", "srcs\_plugins\Plugin.QuestImpl\Plugin.QuestImpl.csproj", "{793F6EA6-6842-4E8F-908D-C7423734E385}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsAPI.Plugins", "srcs\WingsAPI.Plugins\WingsAPI.Plugins.csproj", "{8D22A73C-581D-4A85-917F-C56DFB386C6F}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsEmu.Health", "srcs\WingsEmu.Health\WingsEmu.Health.csproj", "{DA7F7106-23D0-4DC2-8B71-43A15A6BBDE8}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WingsAPI.Packets.Handling", "srcs\WingsAPI.Packets.Handling\WingsAPI.Packets.Handling.csproj", "{9892ED34-E662-48A2-95AB-F1655412081C}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugin.MongoLogs", "srcs\_plugins\Plugin.MongoLogs\Plugin.MongoLogs.csproj", "{55094771-469E-4E1A-8E7B-C7B915E023D0}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugin.ResourceLoader", "srcs\_plugins\Plugin.ResourceLoader\Plugin.ResourceLoader.csproj", "{50A948F4-EF18-4591-8CA9-564909B630A1}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TranslationsServer", "srcs\TranslationsServer\TranslationsServer.csproj", "{A3BDBA44-A720-48FA-8914-736B79830CE9}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugin.RainbowBattle", "srcs\Plugin.RainbowBattle\Plugin.RainbowBattle.csproj", "{192ECC49-3C79-48CD-A49B-296CC47546F5}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PheonixLib", "PheonixLib", "{368BD8A7-67D2-4F24-B655-C3B82BBDA840}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhoenixLib.Auth.JWT", "srcs\PhoenixLib.Auth.JWT\PhoenixLib.Auth.JWT.csproj", "{FAC76C2D-D586-4231-BF60-7766969BBD60}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhoenixLib.Caching", "srcs\PhoenixLib.Caching\PhoenixLib.Caching.csproj", "{1E379202-AE6A-4A0A-BFE9-C1C0D4605143}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhoenixLib.Configuration", "srcs\PhoenixLib.Configuration\PhoenixLib.Configuration.csproj", "{45104F9C-A786-442B-8202-B965530AE20E}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhoenixLib.DAL.Abstractions", "srcs\PhoenixLib.DAL.Abstractions\PhoenixLib.DAL.Abstractions.csproj", "{2A8189FE-0E9B-474F-91E7-A92C7C302A43}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhoenixLib.DAL.EFCore.PGSQL", "srcs\PhoenixLib.DAL.EFCore.PGSQL\PhoenixLib.DAL.EFCore.PGSQL.csproj", "{00EE8632-007B-406B-9CF8-F0CE38A5FEB0}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhoenixLib.DAL.MongoDB", "srcs\PhoenixLib.DAL.MongoDB\PhoenixLib.DAL.MongoDB.csproj", "{1565B041-B687-4EF1-B27D-B0F9A927847B}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhoenixLib.DAL.Redis", "srcs\PhoenixLib.DAL.Redis\PhoenixLib.DAL.Redis.csproj", "{E444200C-5B5D-42C0-88C5-D0E2C11A393F}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhoenixLib.Events", "srcs\PhoenixLib.Events\PhoenixLib.Events.csproj", "{28D2FC99-9DB4-4F15-A99E-F5A76DE7B13A}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhoenixLib.Extensions", "srcs\PhoenixLib.Extensions\PhoenixLib.Extensions.csproj", "{F81D94AB-E336-405D-B7E3-8AC97EE4E758}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhoenixLib.Logging", "srcs\PhoenixLib.Logging\PhoenixLib.Logging.csproj", "{192C9441-BA98-41D4-A27E-D7DE95BB46F7}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhoenixLib.Messaging", "srcs\PhoenixLib.Messaging\PhoenixLib.Messaging.csproj", "{D592C239-AFAD-4596-8FE9-BBEA4977B258}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhoenixLib.Multilanguage", "srcs\PhoenixLib.Multilanguage\PhoenixLib.Multilanguage.csproj", "{33AA9C01-0E8E-4960-AE8C-550C3B13F84A}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhoenixLib.Scheduler", "srcs\PhoenixLib.Scheduler\PhoenixLib.Scheduler.csproj", "{FD79C25F-87CD-47C6-87B2-F497AEA4A12D}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhoenixLib.Scheduler.ReactiveX", "srcs\PhoenixLib.Scheduler.ReactiveX\PhoenixLib.Scheduler.ReactiveX.csproj", "{F723B0BB-C5EA-4EBE-B3D5-50682F45EA80}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{15948D13-B424-4DF3-B341-36CCB97D4D9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{15948D13-B424-4DF3-B341-36CCB97D4D9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{15948D13-B424-4DF3-B341-36CCB97D4D9F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{15948D13-B424-4DF3-B341-36CCB97D4D9F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{15948D13-B424-4DF3-B341-36CCB97D4D9F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{15948D13-B424-4DF3-B341-36CCB97D4D9F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{15948D13-B424-4DF3-B341-36CCB97D4D9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{15948D13-B424-4DF3-B341-36CCB97D4D9F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{15948D13-B424-4DF3-B341-36CCB97D4D9F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{15948D13-B424-4DF3-B341-36CCB97D4D9F}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{15948D13-B424-4DF3-B341-36CCB97D4D9F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{15948D13-B424-4DF3-B341-36CCB97D4D9F}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{596A8193-7A5D-4055-BB8C-D2F27E1C6B94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{596A8193-7A5D-4055-BB8C-D2F27E1C6B94}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{596A8193-7A5D-4055-BB8C-D2F27E1C6B94}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{596A8193-7A5D-4055-BB8C-D2F27E1C6B94}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{596A8193-7A5D-4055-BB8C-D2F27E1C6B94}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{596A8193-7A5D-4055-BB8C-D2F27E1C6B94}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{596A8193-7A5D-4055-BB8C-D2F27E1C6B94}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{596A8193-7A5D-4055-BB8C-D2F27E1C6B94}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{596A8193-7A5D-4055-BB8C-D2F27E1C6B94}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{596A8193-7A5D-4055-BB8C-D2F27E1C6B94}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{596A8193-7A5D-4055-BB8C-D2F27E1C6B94}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{596A8193-7A5D-4055-BB8C-D2F27E1C6B94}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{0F1F75EF-8755-499B-8A28-F6653EB29FED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0F1F75EF-8755-499B-8A28-F6653EB29FED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0F1F75EF-8755-499B-8A28-F6653EB29FED}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{0F1F75EF-8755-499B-8A28-F6653EB29FED}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{0F1F75EF-8755-499B-8A28-F6653EB29FED}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{0F1F75EF-8755-499B-8A28-F6653EB29FED}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{0F1F75EF-8755-499B-8A28-F6653EB29FED}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0F1F75EF-8755-499B-8A28-F6653EB29FED}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0F1F75EF-8755-499B-8A28-F6653EB29FED}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{0F1F75EF-8755-499B-8A28-F6653EB29FED}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{0F1F75EF-8755-499B-8A28-F6653EB29FED}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{0F1F75EF-8755-499B-8A28-F6653EB29FED}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{295450C6-C37F-4AA7-A59A-1BFFF352D0EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{295450C6-C37F-4AA7-A59A-1BFFF352D0EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{295450C6-C37F-4AA7-A59A-1BFFF352D0EA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{295450C6-C37F-4AA7-A59A-1BFFF352D0EA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{295450C6-C37F-4AA7-A59A-1BFFF352D0EA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{295450C6-C37F-4AA7-A59A-1BFFF352D0EA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{295450C6-C37F-4AA7-A59A-1BFFF352D0EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{295450C6-C37F-4AA7-A59A-1BFFF352D0EA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{295450C6-C37F-4AA7-A59A-1BFFF352D0EA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{295450C6-C37F-4AA7-A59A-1BFFF352D0EA}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{295450C6-C37F-4AA7-A59A-1BFFF352D0EA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{295450C6-C37F-4AA7-A59A-1BFFF352D0EA}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{05451402-CA5D-4474-A8BE-8E534AD17748}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{05451402-CA5D-4474-A8BE-8E534AD17748}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{05451402-CA5D-4474-A8BE-8E534AD17748}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{05451402-CA5D-4474-A8BE-8E534AD17748}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{05451402-CA5D-4474-A8BE-8E534AD17748}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{05451402-CA5D-4474-A8BE-8E534AD17748}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{05451402-CA5D-4474-A8BE-8E534AD17748}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{05451402-CA5D-4474-A8BE-8E534AD17748}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{05451402-CA5D-4474-A8BE-8E534AD17748}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{05451402-CA5D-4474-A8BE-8E534AD17748}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{05451402-CA5D-4474-A8BE-8E534AD17748}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{05451402-CA5D-4474-A8BE-8E534AD17748}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{D425AE9F-F25D-4B64-9983-FE76778524A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D425AE9F-F25D-4B64-9983-FE76778524A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D425AE9F-F25D-4B64-9983-FE76778524A7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{D425AE9F-F25D-4B64-9983-FE76778524A7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{D425AE9F-F25D-4B64-9983-FE76778524A7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{D425AE9F-F25D-4B64-9983-FE76778524A7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{D425AE9F-F25D-4B64-9983-FE76778524A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D425AE9F-F25D-4B64-9983-FE76778524A7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D425AE9F-F25D-4B64-9983-FE76778524A7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{D425AE9F-F25D-4B64-9983-FE76778524A7}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{D425AE9F-F25D-4B64-9983-FE76778524A7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{D425AE9F-F25D-4B64-9983-FE76778524A7}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{8ED45EF3-0271-4867-9330-80D300029D8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8ED45EF3-0271-4867-9330-80D300029D8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8ED45EF3-0271-4867-9330-80D300029D8C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{8ED45EF3-0271-4867-9330-80D300029D8C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{8ED45EF3-0271-4867-9330-80D300029D8C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{8ED45EF3-0271-4867-9330-80D300029D8C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{8ED45EF3-0271-4867-9330-80D300029D8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8ED45EF3-0271-4867-9330-80D300029D8C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8ED45EF3-0271-4867-9330-80D300029D8C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{8ED45EF3-0271-4867-9330-80D300029D8C}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{8ED45EF3-0271-4867-9330-80D300029D8C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{8ED45EF3-0271-4867-9330-80D300029D8C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{B0D05BBF-F7FA-46EC-9224-182B2B98C4A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B0D05BBF-F7FA-46EC-9224-182B2B98C4A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B0D05BBF-F7FA-46EC-9224-182B2B98C4A3}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{B0D05BBF-F7FA-46EC-9224-182B2B98C4A3}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{B0D05BBF-F7FA-46EC-9224-182B2B98C4A3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{B0D05BBF-F7FA-46EC-9224-182B2B98C4A3}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{B0D05BBF-F7FA-46EC-9224-182B2B98C4A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B0D05BBF-F7FA-46EC-9224-182B2B98C4A3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B0D05BBF-F7FA-46EC-9224-182B2B98C4A3}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{B0D05BBF-F7FA-46EC-9224-182B2B98C4A3}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{B0D05BBF-F7FA-46EC-9224-182B2B98C4A3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{B0D05BBF-F7FA-46EC-9224-182B2B98C4A3}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{3F789B98-5A34-41CF-B4F0-70B03C0C5ED2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{3F789B98-5A34-41CF-B4F0-70B03C0C5ED2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{3F789B98-5A34-41CF-B4F0-70B03C0C5ED2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{3F789B98-5A34-41CF-B4F0-70B03C0C5ED2}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{3F789B98-5A34-41CF-B4F0-70B03C0C5ED2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{3F789B98-5A34-41CF-B4F0-70B03C0C5ED2}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{3F789B98-5A34-41CF-B4F0-70B03C0C5ED2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{3F789B98-5A34-41CF-B4F0-70B03C0C5ED2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{3F789B98-5A34-41CF-B4F0-70B03C0C5ED2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{3F789B98-5A34-41CF-B4F0-70B03C0C5ED2}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{3F789B98-5A34-41CF-B4F0-70B03C0C5ED2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{3F789B98-5A34-41CF-B4F0-70B03C0C5ED2}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{19A46232-72C5-4BB7-AE6B-A9062BE259A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{19A46232-72C5-4BB7-AE6B-A9062BE259A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{19A46232-72C5-4BB7-AE6B-A9062BE259A0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{19A46232-72C5-4BB7-AE6B-A9062BE259A0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{19A46232-72C5-4BB7-AE6B-A9062BE259A0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{19A46232-72C5-4BB7-AE6B-A9062BE259A0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{19A46232-72C5-4BB7-AE6B-A9062BE259A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{19A46232-72C5-4BB7-AE6B-A9062BE259A0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{19A46232-72C5-4BB7-AE6B-A9062BE259A0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{19A46232-72C5-4BB7-AE6B-A9062BE259A0}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{19A46232-72C5-4BB7-AE6B-A9062BE259A0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{19A46232-72C5-4BB7-AE6B-A9062BE259A0}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{42DE9E8C-747E-46A4-9AC5-BB7ED1425BF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{42DE9E8C-747E-46A4-9AC5-BB7ED1425BF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{42DE9E8C-747E-46A4-9AC5-BB7ED1425BF0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{42DE9E8C-747E-46A4-9AC5-BB7ED1425BF0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{42DE9E8C-747E-46A4-9AC5-BB7ED1425BF0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{42DE9E8C-747E-46A4-9AC5-BB7ED1425BF0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{42DE9E8C-747E-46A4-9AC5-BB7ED1425BF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{42DE9E8C-747E-46A4-9AC5-BB7ED1425BF0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{42DE9E8C-747E-46A4-9AC5-BB7ED1425BF0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{42DE9E8C-747E-46A4-9AC5-BB7ED1425BF0}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{42DE9E8C-747E-46A4-9AC5-BB7ED1425BF0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{42DE9E8C-747E-46A4-9AC5-BB7ED1425BF0}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{D4506804-B9A1-4E1A-B97C-0F815814FE62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D4506804-B9A1-4E1A-B97C-0F815814FE62}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D4506804-B9A1-4E1A-B97C-0F815814FE62}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{D4506804-B9A1-4E1A-B97C-0F815814FE62}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{D4506804-B9A1-4E1A-B97C-0F815814FE62}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{D4506804-B9A1-4E1A-B97C-0F815814FE62}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{D4506804-B9A1-4E1A-B97C-0F815814FE62}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D4506804-B9A1-4E1A-B97C-0F815814FE62}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D4506804-B9A1-4E1A-B97C-0F815814FE62}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{D4506804-B9A1-4E1A-B97C-0F815814FE62}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{D4506804-B9A1-4E1A-B97C-0F815814FE62}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{D4506804-B9A1-4E1A-B97C-0F815814FE62}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{D6B19207-B3E3-4B53-9964-19DEFB2089EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D6B19207-B3E3-4B53-9964-19DEFB2089EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D6B19207-B3E3-4B53-9964-19DEFB2089EF}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{D6B19207-B3E3-4B53-9964-19DEFB2089EF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{D6B19207-B3E3-4B53-9964-19DEFB2089EF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{D6B19207-B3E3-4B53-9964-19DEFB2089EF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{D6B19207-B3E3-4B53-9964-19DEFB2089EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D6B19207-B3E3-4B53-9964-19DEFB2089EF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D6B19207-B3E3-4B53-9964-19DEFB2089EF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{D6B19207-B3E3-4B53-9964-19DEFB2089EF}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{D6B19207-B3E3-4B53-9964-19DEFB2089EF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{D6B19207-B3E3-4B53-9964-19DEFB2089EF}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{F839354B-16C3-4EEF-8872-768B6D832BB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F839354B-16C3-4EEF-8872-768B6D832BB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F839354B-16C3-4EEF-8872-768B6D832BB2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{F839354B-16C3-4EEF-8872-768B6D832BB2}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{F839354B-16C3-4EEF-8872-768B6D832BB2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{F839354B-16C3-4EEF-8872-768B6D832BB2}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{F839354B-16C3-4EEF-8872-768B6D832BB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F839354B-16C3-4EEF-8872-768B6D832BB2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{F839354B-16C3-4EEF-8872-768B6D832BB2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{F839354B-16C3-4EEF-8872-768B6D832BB2}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{F839354B-16C3-4EEF-8872-768B6D832BB2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{F839354B-16C3-4EEF-8872-768B6D832BB2}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{5707003E-0835-4E7A-BAD8-E55F4AE0D583}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5707003E-0835-4E7A-BAD8-E55F4AE0D583}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5707003E-0835-4E7A-BAD8-E55F4AE0D583}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{5707003E-0835-4E7A-BAD8-E55F4AE0D583}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{5707003E-0835-4E7A-BAD8-E55F4AE0D583}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{5707003E-0835-4E7A-BAD8-E55F4AE0D583}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{5707003E-0835-4E7A-BAD8-E55F4AE0D583}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5707003E-0835-4E7A-BAD8-E55F4AE0D583}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5707003E-0835-4E7A-BAD8-E55F4AE0D583}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{5707003E-0835-4E7A-BAD8-E55F4AE0D583}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{5707003E-0835-4E7A-BAD8-E55F4AE0D583}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{5707003E-0835-4E7A-BAD8-E55F4AE0D583}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{CA5FFE88-A5FF-42D3-9E88-220A594AC27F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CA5FFE88-A5FF-42D3-9E88-220A594AC27F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CA5FFE88-A5FF-42D3-9E88-220A594AC27F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{CA5FFE88-A5FF-42D3-9E88-220A594AC27F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{CA5FFE88-A5FF-42D3-9E88-220A594AC27F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{CA5FFE88-A5FF-42D3-9E88-220A594AC27F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{CA5FFE88-A5FF-42D3-9E88-220A594AC27F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CA5FFE88-A5FF-42D3-9E88-220A594AC27F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{CA5FFE88-A5FF-42D3-9E88-220A594AC27F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{CA5FFE88-A5FF-42D3-9E88-220A594AC27F}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{CA5FFE88-A5FF-42D3-9E88-220A594AC27F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{CA5FFE88-A5FF-42D3-9E88-220A594AC27F}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{A57EA67A-133B-4217-BE17-9E25622A95DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A57EA67A-133B-4217-BE17-9E25622A95DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A57EA67A-133B-4217-BE17-9E25622A95DD}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{A57EA67A-133B-4217-BE17-9E25622A95DD}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{A57EA67A-133B-4217-BE17-9E25622A95DD}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{A57EA67A-133B-4217-BE17-9E25622A95DD}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{A57EA67A-133B-4217-BE17-9E25622A95DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A57EA67A-133B-4217-BE17-9E25622A95DD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A57EA67A-133B-4217-BE17-9E25622A95DD}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{A57EA67A-133B-4217-BE17-9E25622A95DD}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{A57EA67A-133B-4217-BE17-9E25622A95DD}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{A57EA67A-133B-4217-BE17-9E25622A95DD}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{105F39CC-E6C1-4365-B200-75D451E5747F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{105F39CC-E6C1-4365-B200-75D451E5747F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{105F39CC-E6C1-4365-B200-75D451E5747F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{105F39CC-E6C1-4365-B200-75D451E5747F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{105F39CC-E6C1-4365-B200-75D451E5747F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{105F39CC-E6C1-4365-B200-75D451E5747F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{105F39CC-E6C1-4365-B200-75D451E5747F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{105F39CC-E6C1-4365-B200-75D451E5747F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{105F39CC-E6C1-4365-B200-75D451E5747F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{105F39CC-E6C1-4365-B200-75D451E5747F}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{105F39CC-E6C1-4365-B200-75D451E5747F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{105F39CC-E6C1-4365-B200-75D451E5747F}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{B84EBC46-1CF7-47C9-B38E-F4919A11D6D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B84EBC46-1CF7-47C9-B38E-F4919A11D6D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B84EBC46-1CF7-47C9-B38E-F4919A11D6D0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{B84EBC46-1CF7-47C9-B38E-F4919A11D6D0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{B84EBC46-1CF7-47C9-B38E-F4919A11D6D0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{B84EBC46-1CF7-47C9-B38E-F4919A11D6D0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{B84EBC46-1CF7-47C9-B38E-F4919A11D6D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B84EBC46-1CF7-47C9-B38E-F4919A11D6D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B84EBC46-1CF7-47C9-B38E-F4919A11D6D0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{B84EBC46-1CF7-47C9-B38E-F4919A11D6D0}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{B84EBC46-1CF7-47C9-B38E-F4919A11D6D0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{B84EBC46-1CF7-47C9-B38E-F4919A11D6D0}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{5F2C76AE-81B2-4A24-80D2-086EFC41627B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5F2C76AE-81B2-4A24-80D2-086EFC41627B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5F2C76AE-81B2-4A24-80D2-086EFC41627B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{5F2C76AE-81B2-4A24-80D2-086EFC41627B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{5F2C76AE-81B2-4A24-80D2-086EFC41627B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{5F2C76AE-81B2-4A24-80D2-086EFC41627B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{5F2C76AE-81B2-4A24-80D2-086EFC41627B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5F2C76AE-81B2-4A24-80D2-086EFC41627B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5F2C76AE-81B2-4A24-80D2-086EFC41627B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{5F2C76AE-81B2-4A24-80D2-086EFC41627B}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{5F2C76AE-81B2-4A24-80D2-086EFC41627B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{5F2C76AE-81B2-4A24-80D2-086EFC41627B}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{18F61C6F-997C-459F-A3CE-63AEE311315E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{18F61C6F-997C-459F-A3CE-63AEE311315E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{18F61C6F-997C-459F-A3CE-63AEE311315E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{18F61C6F-997C-459F-A3CE-63AEE311315E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{18F61C6F-997C-459F-A3CE-63AEE311315E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{18F61C6F-997C-459F-A3CE-63AEE311315E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{18F61C6F-997C-459F-A3CE-63AEE311315E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{18F61C6F-997C-459F-A3CE-63AEE311315E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{18F61C6F-997C-459F-A3CE-63AEE311315E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{18F61C6F-997C-459F-A3CE-63AEE311315E}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{18F61C6F-997C-459F-A3CE-63AEE311315E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{18F61C6F-997C-459F-A3CE-63AEE311315E}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{7B5A36CE-10AE-4915-84C2-61C205F4C004}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7B5A36CE-10AE-4915-84C2-61C205F4C004}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7B5A36CE-10AE-4915-84C2-61C205F4C004}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{7B5A36CE-10AE-4915-84C2-61C205F4C004}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{7B5A36CE-10AE-4915-84C2-61C205F4C004}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{7B5A36CE-10AE-4915-84C2-61C205F4C004}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{7B5A36CE-10AE-4915-84C2-61C205F4C004}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7B5A36CE-10AE-4915-84C2-61C205F4C004}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{7B5A36CE-10AE-4915-84C2-61C205F4C004}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{7B5A36CE-10AE-4915-84C2-61C205F4C004}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{7B5A36CE-10AE-4915-84C2-61C205F4C004}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{7B5A36CE-10AE-4915-84C2-61C205F4C004}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{58B6177B-2C56-4B37-88AB-8C454767D427}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{58B6177B-2C56-4B37-88AB-8C454767D427}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{58B6177B-2C56-4B37-88AB-8C454767D427}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{58B6177B-2C56-4B37-88AB-8C454767D427}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{58B6177B-2C56-4B37-88AB-8C454767D427}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{58B6177B-2C56-4B37-88AB-8C454767D427}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{58B6177B-2C56-4B37-88AB-8C454767D427}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{58B6177B-2C56-4B37-88AB-8C454767D427}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{58B6177B-2C56-4B37-88AB-8C454767D427}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{58B6177B-2C56-4B37-88AB-8C454767D427}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{58B6177B-2C56-4B37-88AB-8C454767D427}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{58B6177B-2C56-4B37-88AB-8C454767D427}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{61DB7D8D-7903-47EB-85FA-1E21001C5EAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{61DB7D8D-7903-47EB-85FA-1E21001C5EAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{61DB7D8D-7903-47EB-85FA-1E21001C5EAE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{61DB7D8D-7903-47EB-85FA-1E21001C5EAE}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{61DB7D8D-7903-47EB-85FA-1E21001C5EAE}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{61DB7D8D-7903-47EB-85FA-1E21001C5EAE}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{61DB7D8D-7903-47EB-85FA-1E21001C5EAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{61DB7D8D-7903-47EB-85FA-1E21001C5EAE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{61DB7D8D-7903-47EB-85FA-1E21001C5EAE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{61DB7D8D-7903-47EB-85FA-1E21001C5EAE}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{61DB7D8D-7903-47EB-85FA-1E21001C5EAE}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{61DB7D8D-7903-47EB-85FA-1E21001C5EAE}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{D7F1A231-791A-4B34-A9B2-21282EADA82C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D7F1A231-791A-4B34-A9B2-21282EADA82C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D7F1A231-791A-4B34-A9B2-21282EADA82C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{D7F1A231-791A-4B34-A9B2-21282EADA82C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{D7F1A231-791A-4B34-A9B2-21282EADA82C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{D7F1A231-791A-4B34-A9B2-21282EADA82C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{D7F1A231-791A-4B34-A9B2-21282EADA82C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D7F1A231-791A-4B34-A9B2-21282EADA82C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D7F1A231-791A-4B34-A9B2-21282EADA82C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{D7F1A231-791A-4B34-A9B2-21282EADA82C}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{D7F1A231-791A-4B34-A9B2-21282EADA82C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{D7F1A231-791A-4B34-A9B2-21282EADA82C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{42CDD6F5-A858-4A32-8982-E5A63689CD58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{42CDD6F5-A858-4A32-8982-E5A63689CD58}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{42CDD6F5-A858-4A32-8982-E5A63689CD58}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{42CDD6F5-A858-4A32-8982-E5A63689CD58}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{42CDD6F5-A858-4A32-8982-E5A63689CD58}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{42CDD6F5-A858-4A32-8982-E5A63689CD58}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{42CDD6F5-A858-4A32-8982-E5A63689CD58}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{42CDD6F5-A858-4A32-8982-E5A63689CD58}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{42CDD6F5-A858-4A32-8982-E5A63689CD58}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{42CDD6F5-A858-4A32-8982-E5A63689CD58}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{42CDD6F5-A858-4A32-8982-E5A63689CD58}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{42CDD6F5-A858-4A32-8982-E5A63689CD58}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{779A5516-5209-491C-878A-63086DB3F566}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{779A5516-5209-491C-878A-63086DB3F566}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{779A5516-5209-491C-878A-63086DB3F566}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{779A5516-5209-491C-878A-63086DB3F566}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{779A5516-5209-491C-878A-63086DB3F566}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{779A5516-5209-491C-878A-63086DB3F566}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{779A5516-5209-491C-878A-63086DB3F566}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{779A5516-5209-491C-878A-63086DB3F566}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{779A5516-5209-491C-878A-63086DB3F566}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{779A5516-5209-491C-878A-63086DB3F566}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{779A5516-5209-491C-878A-63086DB3F566}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{779A5516-5209-491C-878A-63086DB3F566}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{8293EEC9-C747-46C3-B3D2-30C552620269}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8293EEC9-C747-46C3-B3D2-30C552620269}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8293EEC9-C747-46C3-B3D2-30C552620269}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{8293EEC9-C747-46C3-B3D2-30C552620269}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{8293EEC9-C747-46C3-B3D2-30C552620269}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{8293EEC9-C747-46C3-B3D2-30C552620269}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{8293EEC9-C747-46C3-B3D2-30C552620269}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8293EEC9-C747-46C3-B3D2-30C552620269}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8293EEC9-C747-46C3-B3D2-30C552620269}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{8293EEC9-C747-46C3-B3D2-30C552620269}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{8293EEC9-C747-46C3-B3D2-30C552620269}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{8293EEC9-C747-46C3-B3D2-30C552620269}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{DA4852B6-2FBD-4E5C-972C-049626152E4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DA4852B6-2FBD-4E5C-972C-049626152E4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DA4852B6-2FBD-4E5C-972C-049626152E4E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{DA4852B6-2FBD-4E5C-972C-049626152E4E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{DA4852B6-2FBD-4E5C-972C-049626152E4E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{DA4852B6-2FBD-4E5C-972C-049626152E4E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{DA4852B6-2FBD-4E5C-972C-049626152E4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DA4852B6-2FBD-4E5C-972C-049626152E4E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DA4852B6-2FBD-4E5C-972C-049626152E4E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{DA4852B6-2FBD-4E5C-972C-049626152E4E}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{DA4852B6-2FBD-4E5C-972C-049626152E4E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{DA4852B6-2FBD-4E5C-972C-049626152E4E}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{CCBE134E-2DAB-4CE7-B9F2-4F315CDEF1FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CCBE134E-2DAB-4CE7-B9F2-4F315CDEF1FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CCBE134E-2DAB-4CE7-B9F2-4F315CDEF1FA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{CCBE134E-2DAB-4CE7-B9F2-4F315CDEF1FA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{CCBE134E-2DAB-4CE7-B9F2-4F315CDEF1FA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{CCBE134E-2DAB-4CE7-B9F2-4F315CDEF1FA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{CCBE134E-2DAB-4CE7-B9F2-4F315CDEF1FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CCBE134E-2DAB-4CE7-B9F2-4F315CDEF1FA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{CCBE134E-2DAB-4CE7-B9F2-4F315CDEF1FA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{CCBE134E-2DAB-4CE7-B9F2-4F315CDEF1FA}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{CCBE134E-2DAB-4CE7-B9F2-4F315CDEF1FA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{CCBE134E-2DAB-4CE7-B9F2-4F315CDEF1FA}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{E4E9646F-7B85-4446-9B6D-846D32BAC3F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E4E9646F-7B85-4446-9B6D-846D32BAC3F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E4E9646F-7B85-4446-9B6D-846D32BAC3F5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{E4E9646F-7B85-4446-9B6D-846D32BAC3F5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{E4E9646F-7B85-4446-9B6D-846D32BAC3F5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{E4E9646F-7B85-4446-9B6D-846D32BAC3F5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{E4E9646F-7B85-4446-9B6D-846D32BAC3F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E4E9646F-7B85-4446-9B6D-846D32BAC3F5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{E4E9646F-7B85-4446-9B6D-846D32BAC3F5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{E4E9646F-7B85-4446-9B6D-846D32BAC3F5}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{E4E9646F-7B85-4446-9B6D-846D32BAC3F5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{E4E9646F-7B85-4446-9B6D-846D32BAC3F5}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{3038B4F6-2CB4-4124-86EB-FE8D7FB3A0D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{3038B4F6-2CB4-4124-86EB-FE8D7FB3A0D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{3038B4F6-2CB4-4124-86EB-FE8D7FB3A0D7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{3038B4F6-2CB4-4124-86EB-FE8D7FB3A0D7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{3038B4F6-2CB4-4124-86EB-FE8D7FB3A0D7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{3038B4F6-2CB4-4124-86EB-FE8D7FB3A0D7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{3038B4F6-2CB4-4124-86EB-FE8D7FB3A0D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{3038B4F6-2CB4-4124-86EB-FE8D7FB3A0D7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{3038B4F6-2CB4-4124-86EB-FE8D7FB3A0D7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{3038B4F6-2CB4-4124-86EB-FE8D7FB3A0D7}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{3038B4F6-2CB4-4124-86EB-FE8D7FB3A0D7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{3038B4F6-2CB4-4124-86EB-FE8D7FB3A0D7}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{E27BC1CD-D6DF-4EEE-B340-5424BE442AC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E27BC1CD-D6DF-4EEE-B340-5424BE442AC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E27BC1CD-D6DF-4EEE-B340-5424BE442AC5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{E27BC1CD-D6DF-4EEE-B340-5424BE442AC5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{E27BC1CD-D6DF-4EEE-B340-5424BE442AC5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{E27BC1CD-D6DF-4EEE-B340-5424BE442AC5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{E27BC1CD-D6DF-4EEE-B340-5424BE442AC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E27BC1CD-D6DF-4EEE-B340-5424BE442AC5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{E27BC1CD-D6DF-4EEE-B340-5424BE442AC5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{E27BC1CD-D6DF-4EEE-B340-5424BE442AC5}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{E27BC1CD-D6DF-4EEE-B340-5424BE442AC5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{E27BC1CD-D6DF-4EEE-B340-5424BE442AC5}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{793F6EA6-6842-4E8F-908D-C7423734E385}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{793F6EA6-6842-4E8F-908D-C7423734E385}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{793F6EA6-6842-4E8F-908D-C7423734E385}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{793F6EA6-6842-4E8F-908D-C7423734E385}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{793F6EA6-6842-4E8F-908D-C7423734E385}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{793F6EA6-6842-4E8F-908D-C7423734E385}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{793F6EA6-6842-4E8F-908D-C7423734E385}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{793F6EA6-6842-4E8F-908D-C7423734E385}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{793F6EA6-6842-4E8F-908D-C7423734E385}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{793F6EA6-6842-4E8F-908D-C7423734E385}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{793F6EA6-6842-4E8F-908D-C7423734E385}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{793F6EA6-6842-4E8F-908D-C7423734E385}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{8D22A73C-581D-4A85-917F-C56DFB386C6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8D22A73C-581D-4A85-917F-C56DFB386C6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8D22A73C-581D-4A85-917F-C56DFB386C6F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{8D22A73C-581D-4A85-917F-C56DFB386C6F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{8D22A73C-581D-4A85-917F-C56DFB386C6F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{8D22A73C-581D-4A85-917F-C56DFB386C6F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{8D22A73C-581D-4A85-917F-C56DFB386C6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8D22A73C-581D-4A85-917F-C56DFB386C6F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8D22A73C-581D-4A85-917F-C56DFB386C6F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{8D22A73C-581D-4A85-917F-C56DFB386C6F}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{8D22A73C-581D-4A85-917F-C56DFB386C6F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{8D22A73C-581D-4A85-917F-C56DFB386C6F}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{DA7F7106-23D0-4DC2-8B71-43A15A6BBDE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DA7F7106-23D0-4DC2-8B71-43A15A6BBDE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DA7F7106-23D0-4DC2-8B71-43A15A6BBDE8}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{DA7F7106-23D0-4DC2-8B71-43A15A6BBDE8}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{DA7F7106-23D0-4DC2-8B71-43A15A6BBDE8}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{DA7F7106-23D0-4DC2-8B71-43A15A6BBDE8}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{DA7F7106-23D0-4DC2-8B71-43A15A6BBDE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DA7F7106-23D0-4DC2-8B71-43A15A6BBDE8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DA7F7106-23D0-4DC2-8B71-43A15A6BBDE8}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{DA7F7106-23D0-4DC2-8B71-43A15A6BBDE8}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{DA7F7106-23D0-4DC2-8B71-43A15A6BBDE8}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{DA7F7106-23D0-4DC2-8B71-43A15A6BBDE8}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{9892ED34-E662-48A2-95AB-F1655412081C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9892ED34-E662-48A2-95AB-F1655412081C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9892ED34-E662-48A2-95AB-F1655412081C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{9892ED34-E662-48A2-95AB-F1655412081C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{9892ED34-E662-48A2-95AB-F1655412081C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{9892ED34-E662-48A2-95AB-F1655412081C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{9892ED34-E662-48A2-95AB-F1655412081C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9892ED34-E662-48A2-95AB-F1655412081C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9892ED34-E662-48A2-95AB-F1655412081C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{9892ED34-E662-48A2-95AB-F1655412081C}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{9892ED34-E662-48A2-95AB-F1655412081C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{9892ED34-E662-48A2-95AB-F1655412081C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{55094771-469E-4E1A-8E7B-C7B915E023D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{55094771-469E-4E1A-8E7B-C7B915E023D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{55094771-469E-4E1A-8E7B-C7B915E023D0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{55094771-469E-4E1A-8E7B-C7B915E023D0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{55094771-469E-4E1A-8E7B-C7B915E023D0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{55094771-469E-4E1A-8E7B-C7B915E023D0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{55094771-469E-4E1A-8E7B-C7B915E023D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{55094771-469E-4E1A-8E7B-C7B915E023D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{55094771-469E-4E1A-8E7B-C7B915E023D0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{55094771-469E-4E1A-8E7B-C7B915E023D0}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{55094771-469E-4E1A-8E7B-C7B915E023D0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{55094771-469E-4E1A-8E7B-C7B915E023D0}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{50A948F4-EF18-4591-8CA9-564909B630A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{50A948F4-EF18-4591-8CA9-564909B630A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{50A948F4-EF18-4591-8CA9-564909B630A1}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{50A948F4-EF18-4591-8CA9-564909B630A1}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{50A948F4-EF18-4591-8CA9-564909B630A1}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{50A948F4-EF18-4591-8CA9-564909B630A1}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{50A948F4-EF18-4591-8CA9-564909B630A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{50A948F4-EF18-4591-8CA9-564909B630A1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{50A948F4-EF18-4591-8CA9-564909B630A1}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{50A948F4-EF18-4591-8CA9-564909B630A1}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{50A948F4-EF18-4591-8CA9-564909B630A1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{50A948F4-EF18-4591-8CA9-564909B630A1}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{A3BDBA44-A720-48FA-8914-736B79830CE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A3BDBA44-A720-48FA-8914-736B79830CE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A3BDBA44-A720-48FA-8914-736B79830CE9}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{A3BDBA44-A720-48FA-8914-736B79830CE9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{A3BDBA44-A720-48FA-8914-736B79830CE9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{A3BDBA44-A720-48FA-8914-736B79830CE9}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{A3BDBA44-A720-48FA-8914-736B79830CE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A3BDBA44-A720-48FA-8914-736B79830CE9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A3BDBA44-A720-48FA-8914-736B79830CE9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{A3BDBA44-A720-48FA-8914-736B79830CE9}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{A3BDBA44-A720-48FA-8914-736B79830CE9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{A3BDBA44-A720-48FA-8914-736B79830CE9}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{192ECC49-3C79-48CD-A49B-296CC47546F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{192ECC49-3C79-48CD-A49B-296CC47546F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{192ECC49-3C79-48CD-A49B-296CC47546F5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{192ECC49-3C79-48CD-A49B-296CC47546F5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{192ECC49-3C79-48CD-A49B-296CC47546F5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{192ECC49-3C79-48CD-A49B-296CC47546F5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{192ECC49-3C79-48CD-A49B-296CC47546F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{192ECC49-3C79-48CD-A49B-296CC47546F5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{192ECC49-3C79-48CD-A49B-296CC47546F5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{192ECC49-3C79-48CD-A49B-296CC47546F5}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{192ECC49-3C79-48CD-A49B-296CC47546F5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{192ECC49-3C79-48CD-A49B-296CC47546F5}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{FAC76C2D-D586-4231-BF60-7766969BBD60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FAC76C2D-D586-4231-BF60-7766969BBD60}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FAC76C2D-D586-4231-BF60-7766969BBD60}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{FAC76C2D-D586-4231-BF60-7766969BBD60}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{FAC76C2D-D586-4231-BF60-7766969BBD60}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{FAC76C2D-D586-4231-BF60-7766969BBD60}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{FAC76C2D-D586-4231-BF60-7766969BBD60}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FAC76C2D-D586-4231-BF60-7766969BBD60}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{FAC76C2D-D586-4231-BF60-7766969BBD60}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{FAC76C2D-D586-4231-BF60-7766969BBD60}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{FAC76C2D-D586-4231-BF60-7766969BBD60}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{FAC76C2D-D586-4231-BF60-7766969BBD60}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{1E379202-AE6A-4A0A-BFE9-C1C0D4605143}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1E379202-AE6A-4A0A-BFE9-C1C0D4605143}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1E379202-AE6A-4A0A-BFE9-C1C0D4605143}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{1E379202-AE6A-4A0A-BFE9-C1C0D4605143}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{1E379202-AE6A-4A0A-BFE9-C1C0D4605143}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{1E379202-AE6A-4A0A-BFE9-C1C0D4605143}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{1E379202-AE6A-4A0A-BFE9-C1C0D4605143}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1E379202-AE6A-4A0A-BFE9-C1C0D4605143}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1E379202-AE6A-4A0A-BFE9-C1C0D4605143}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{1E379202-AE6A-4A0A-BFE9-C1C0D4605143}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{1E379202-AE6A-4A0A-BFE9-C1C0D4605143}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{1E379202-AE6A-4A0A-BFE9-C1C0D4605143}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{45104F9C-A786-442B-8202-B965530AE20E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{45104F9C-A786-442B-8202-B965530AE20E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{45104F9C-A786-442B-8202-B965530AE20E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{45104F9C-A786-442B-8202-B965530AE20E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{45104F9C-A786-442B-8202-B965530AE20E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{45104F9C-A786-442B-8202-B965530AE20E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{45104F9C-A786-442B-8202-B965530AE20E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{45104F9C-A786-442B-8202-B965530AE20E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{45104F9C-A786-442B-8202-B965530AE20E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{45104F9C-A786-442B-8202-B965530AE20E}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{45104F9C-A786-442B-8202-B965530AE20E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{45104F9C-A786-442B-8202-B965530AE20E}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{2A8189FE-0E9B-474F-91E7-A92C7C302A43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{2A8189FE-0E9B-474F-91E7-A92C7C302A43}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{2A8189FE-0E9B-474F-91E7-A92C7C302A43}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{2A8189FE-0E9B-474F-91E7-A92C7C302A43}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{2A8189FE-0E9B-474F-91E7-A92C7C302A43}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{2A8189FE-0E9B-474F-91E7-A92C7C302A43}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{2A8189FE-0E9B-474F-91E7-A92C7C302A43}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{2A8189FE-0E9B-474F-91E7-A92C7C302A43}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{2A8189FE-0E9B-474F-91E7-A92C7C302A43}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{2A8189FE-0E9B-474F-91E7-A92C7C302A43}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{2A8189FE-0E9B-474F-91E7-A92C7C302A43}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{2A8189FE-0E9B-474F-91E7-A92C7C302A43}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{00EE8632-007B-406B-9CF8-F0CE38A5FEB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{00EE8632-007B-406B-9CF8-F0CE38A5FEB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{00EE8632-007B-406B-9CF8-F0CE38A5FEB0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{00EE8632-007B-406B-9CF8-F0CE38A5FEB0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{00EE8632-007B-406B-9CF8-F0CE38A5FEB0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{00EE8632-007B-406B-9CF8-F0CE38A5FEB0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{00EE8632-007B-406B-9CF8-F0CE38A5FEB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{00EE8632-007B-406B-9CF8-F0CE38A5FEB0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{00EE8632-007B-406B-9CF8-F0CE38A5FEB0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{00EE8632-007B-406B-9CF8-F0CE38A5FEB0}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{00EE8632-007B-406B-9CF8-F0CE38A5FEB0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{00EE8632-007B-406B-9CF8-F0CE38A5FEB0}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{1565B041-B687-4EF1-B27D-B0F9A927847B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1565B041-B687-4EF1-B27D-B0F9A927847B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1565B041-B687-4EF1-B27D-B0F9A927847B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{1565B041-B687-4EF1-B27D-B0F9A927847B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{1565B041-B687-4EF1-B27D-B0F9A927847B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{1565B041-B687-4EF1-B27D-B0F9A927847B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{1565B041-B687-4EF1-B27D-B0F9A927847B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1565B041-B687-4EF1-B27D-B0F9A927847B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1565B041-B687-4EF1-B27D-B0F9A927847B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{1565B041-B687-4EF1-B27D-B0F9A927847B}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{1565B041-B687-4EF1-B27D-B0F9A927847B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{1565B041-B687-4EF1-B27D-B0F9A927847B}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{E444200C-5B5D-42C0-88C5-D0E2C11A393F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E444200C-5B5D-42C0-88C5-D0E2C11A393F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E444200C-5B5D-42C0-88C5-D0E2C11A393F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{E444200C-5B5D-42C0-88C5-D0E2C11A393F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{E444200C-5B5D-42C0-88C5-D0E2C11A393F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{E444200C-5B5D-42C0-88C5-D0E2C11A393F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{E444200C-5B5D-42C0-88C5-D0E2C11A393F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E444200C-5B5D-42C0-88C5-D0E2C11A393F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{E444200C-5B5D-42C0-88C5-D0E2C11A393F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{E444200C-5B5D-42C0-88C5-D0E2C11A393F}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{E444200C-5B5D-42C0-88C5-D0E2C11A393F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{E444200C-5B5D-42C0-88C5-D0E2C11A393F}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{28D2FC99-9DB4-4F15-A99E-F5A76DE7B13A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{28D2FC99-9DB4-4F15-A99E-F5A76DE7B13A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{28D2FC99-9DB4-4F15-A99E-F5A76DE7B13A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{28D2FC99-9DB4-4F15-A99E-F5A76DE7B13A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{28D2FC99-9DB4-4F15-A99E-F5A76DE7B13A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{28D2FC99-9DB4-4F15-A99E-F5A76DE7B13A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{28D2FC99-9DB4-4F15-A99E-F5A76DE7B13A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{28D2FC99-9DB4-4F15-A99E-F5A76DE7B13A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{28D2FC99-9DB4-4F15-A99E-F5A76DE7B13A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{28D2FC99-9DB4-4F15-A99E-F5A76DE7B13A}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{28D2FC99-9DB4-4F15-A99E-F5A76DE7B13A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{28D2FC99-9DB4-4F15-A99E-F5A76DE7B13A}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{F81D94AB-E336-405D-B7E3-8AC97EE4E758}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F81D94AB-E336-405D-B7E3-8AC97EE4E758}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F81D94AB-E336-405D-B7E3-8AC97EE4E758}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{F81D94AB-E336-405D-B7E3-8AC97EE4E758}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{F81D94AB-E336-405D-B7E3-8AC97EE4E758}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{F81D94AB-E336-405D-B7E3-8AC97EE4E758}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{F81D94AB-E336-405D-B7E3-8AC97EE4E758}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F81D94AB-E336-405D-B7E3-8AC97EE4E758}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{F81D94AB-E336-405D-B7E3-8AC97EE4E758}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{F81D94AB-E336-405D-B7E3-8AC97EE4E758}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{F81D94AB-E336-405D-B7E3-8AC97EE4E758}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{F81D94AB-E336-405D-B7E3-8AC97EE4E758}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{192C9441-BA98-41D4-A27E-D7DE95BB46F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{192C9441-BA98-41D4-A27E-D7DE95BB46F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{192C9441-BA98-41D4-A27E-D7DE95BB46F7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{192C9441-BA98-41D4-A27E-D7DE95BB46F7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{192C9441-BA98-41D4-A27E-D7DE95BB46F7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{192C9441-BA98-41D4-A27E-D7DE95BB46F7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{192C9441-BA98-41D4-A27E-D7DE95BB46F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{192C9441-BA98-41D4-A27E-D7DE95BB46F7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{192C9441-BA98-41D4-A27E-D7DE95BB46F7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{192C9441-BA98-41D4-A27E-D7DE95BB46F7}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{192C9441-BA98-41D4-A27E-D7DE95BB46F7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{192C9441-BA98-41D4-A27E-D7DE95BB46F7}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{D592C239-AFAD-4596-8FE9-BBEA4977B258}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D592C239-AFAD-4596-8FE9-BBEA4977B258}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D592C239-AFAD-4596-8FE9-BBEA4977B258}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{D592C239-AFAD-4596-8FE9-BBEA4977B258}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{D592C239-AFAD-4596-8FE9-BBEA4977B258}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{D592C239-AFAD-4596-8FE9-BBEA4977B258}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{D592C239-AFAD-4596-8FE9-BBEA4977B258}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D592C239-AFAD-4596-8FE9-BBEA4977B258}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D592C239-AFAD-4596-8FE9-BBEA4977B258}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{D592C239-AFAD-4596-8FE9-BBEA4977B258}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{D592C239-AFAD-4596-8FE9-BBEA4977B258}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{D592C239-AFAD-4596-8FE9-BBEA4977B258}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{33AA9C01-0E8E-4960-AE8C-550C3B13F84A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{33AA9C01-0E8E-4960-AE8C-550C3B13F84A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{33AA9C01-0E8E-4960-AE8C-550C3B13F84A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{33AA9C01-0E8E-4960-AE8C-550C3B13F84A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{33AA9C01-0E8E-4960-AE8C-550C3B13F84A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{33AA9C01-0E8E-4960-AE8C-550C3B13F84A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{33AA9C01-0E8E-4960-AE8C-550C3B13F84A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{33AA9C01-0E8E-4960-AE8C-550C3B13F84A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{33AA9C01-0E8E-4960-AE8C-550C3B13F84A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{33AA9C01-0E8E-4960-AE8C-550C3B13F84A}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{33AA9C01-0E8E-4960-AE8C-550C3B13F84A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{33AA9C01-0E8E-4960-AE8C-550C3B13F84A}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{FD79C25F-87CD-47C6-87B2-F497AEA4A12D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FD79C25F-87CD-47C6-87B2-F497AEA4A12D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FD79C25F-87CD-47C6-87B2-F497AEA4A12D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{FD79C25F-87CD-47C6-87B2-F497AEA4A12D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{FD79C25F-87CD-47C6-87B2-F497AEA4A12D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{FD79C25F-87CD-47C6-87B2-F497AEA4A12D}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{FD79C25F-87CD-47C6-87B2-F497AEA4A12D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FD79C25F-87CD-47C6-87B2-F497AEA4A12D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{FD79C25F-87CD-47C6-87B2-F497AEA4A12D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{FD79C25F-87CD-47C6-87B2-F497AEA4A12D}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{FD79C25F-87CD-47C6-87B2-F497AEA4A12D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{FD79C25F-87CD-47C6-87B2-F497AEA4A12D}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{F723B0BB-C5EA-4EBE-B3D5-50682F45EA80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F723B0BB-C5EA-4EBE-B3D5-50682F45EA80}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F723B0BB-C5EA-4EBE-B3D5-50682F45EA80}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{F723B0BB-C5EA-4EBE-B3D5-50682F45EA80}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{F723B0BB-C5EA-4EBE-B3D5-50682F45EA80}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{F723B0BB-C5EA-4EBE-B3D5-50682F45EA80}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{F723B0BB-C5EA-4EBE-B3D5-50682F45EA80}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F723B0BB-C5EA-4EBE-B3D5-50682F45EA80}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{F723B0BB-C5EA-4EBE-B3D5-50682F45EA80}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{F723B0BB-C5EA-4EBE-B3D5-50682F45EA80}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{F723B0BB-C5EA-4EBE-B3D5-50682F45EA80}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{F723B0BB-C5EA-4EBE-B3D5-50682F45EA80}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{0D88B727-CA5D-48DB-835B-E90C8E282DA7} = {322AAFB0-04E1-42B9-B9F1-B836E12E1FCD}
|
||||||
|
{15948D13-B424-4DF3-B341-36CCB97D4D9F} = {823AD0CF-E764-4113-A166-6A615A4B928F}
|
||||||
|
{404B2AB4-CAFA-4AF7-9593-F231A25A973C} = {322AAFB0-04E1-42B9-B9F1-B836E12E1FCD}
|
||||||
|
{596A8193-7A5D-4055-BB8C-D2F27E1C6B94} = {404B2AB4-CAFA-4AF7-9593-F231A25A973C}
|
||||||
|
{0F1F75EF-8755-499B-8A28-F6653EB29FED} = {404B2AB4-CAFA-4AF7-9593-F231A25A973C}
|
||||||
|
{295450C6-C37F-4AA7-A59A-1BFFF352D0EA} = {404B2AB4-CAFA-4AF7-9593-F231A25A973C}
|
||||||
|
{05451402-CA5D-4474-A8BE-8E534AD17748} = {0D88B727-CA5D-48DB-835B-E90C8E282DA7}
|
||||||
|
{823AD0CF-E764-4113-A166-6A615A4B928F} = {322AAFB0-04E1-42B9-B9F1-B836E12E1FCD}
|
||||||
|
{F872A038-D325-4718-B0A0-4CBCEA3714ED} = {322AAFB0-04E1-42B9-B9F1-B836E12E1FCD}
|
||||||
|
{D425AE9F-F25D-4B64-9983-FE76778524A7} = {404B2AB4-CAFA-4AF7-9593-F231A25A973C}
|
||||||
|
{8ED45EF3-0271-4867-9330-80D300029D8C} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{B0D05BBF-F7FA-46EC-9224-182B2B98C4A3} = {404B2AB4-CAFA-4AF7-9593-F231A25A973C}
|
||||||
|
{3F789B98-5A34-41CF-B4F0-70B03C0C5ED2} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{19A46232-72C5-4BB7-AE6B-A9062BE259A0} = {0D88B727-CA5D-48DB-835B-E90C8E282DA7}
|
||||||
|
{42DE9E8C-747E-46A4-9AC5-BB7ED1425BF0} = {0D88B727-CA5D-48DB-835B-E90C8E282DA7}
|
||||||
|
{D4506804-B9A1-4E1A-B97C-0F815814FE62} = {0D88B727-CA5D-48DB-835B-E90C8E282DA7}
|
||||||
|
{D6B19207-B3E3-4B53-9964-19DEFB2089EF} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{F839354B-16C3-4EEF-8872-768B6D832BB2} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{5707003E-0835-4E7A-BAD8-E55F4AE0D583} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{CA5FFE88-A5FF-42D3-9E88-220A594AC27F} = {404B2AB4-CAFA-4AF7-9593-F231A25A973C}
|
||||||
|
{A57EA67A-133B-4217-BE17-9E25622A95DD} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{105F39CC-E6C1-4365-B200-75D451E5747F} = {0D88B727-CA5D-48DB-835B-E90C8E282DA7}
|
||||||
|
{B84EBC46-1CF7-47C9-B38E-F4919A11D6D0} = {0D88B727-CA5D-48DB-835B-E90C8E282DA7}
|
||||||
|
{5F2C76AE-81B2-4A24-80D2-086EFC41627B} = {0D88B727-CA5D-48DB-835B-E90C8E282DA7}
|
||||||
|
{18F61C6F-997C-459F-A3CE-63AEE311315E} = {0D88B727-CA5D-48DB-835B-E90C8E282DA7}
|
||||||
|
{7B5A36CE-10AE-4915-84C2-61C205F4C004} = {0D88B727-CA5D-48DB-835B-E90C8E282DA7}
|
||||||
|
{58B6177B-2C56-4B37-88AB-8C454767D427} = {0D88B727-CA5D-48DB-835B-E90C8E282DA7}
|
||||||
|
{61DB7D8D-7903-47EB-85FA-1E21001C5EAE} = {0D88B727-CA5D-48DB-835B-E90C8E282DA7}
|
||||||
|
{D7F1A231-791A-4B34-A9B2-21282EADA82C} = {404B2AB4-CAFA-4AF7-9593-F231A25A973C}
|
||||||
|
{42CDD6F5-A858-4A32-8982-E5A63689CD58} = {0D88B727-CA5D-48DB-835B-E90C8E282DA7}
|
||||||
|
{779A5516-5209-491C-878A-63086DB3F566} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{8293EEC9-C747-46C3-B3D2-30C552620269} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{DA4852B6-2FBD-4E5C-972C-049626152E4E} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{CCBE134E-2DAB-4CE7-B9F2-4F315CDEF1FA} = {404B2AB4-CAFA-4AF7-9593-F231A25A973C}
|
||||||
|
{E4E9646F-7B85-4446-9B6D-846D32BAC3F5} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{3038B4F6-2CB4-4124-86EB-FE8D7FB3A0D7} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{E27BC1CD-D6DF-4EEE-B340-5424BE442AC5} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{793F6EA6-6842-4E8F-908D-C7423734E385} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{8D22A73C-581D-4A85-917F-C56DFB386C6F} = {404B2AB4-CAFA-4AF7-9593-F231A25A973C}
|
||||||
|
{DA7F7106-23D0-4DC2-8B71-43A15A6BBDE8} = {823AD0CF-E764-4113-A166-6A615A4B928F}
|
||||||
|
{9892ED34-E662-48A2-95AB-F1655412081C} = {404B2AB4-CAFA-4AF7-9593-F231A25A973C}
|
||||||
|
{55094771-469E-4E1A-8E7B-C7B915E023D0} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{50A948F4-EF18-4591-8CA9-564909B630A1} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{A3BDBA44-A720-48FA-8914-736B79830CE9} = {0D88B727-CA5D-48DB-835B-E90C8E282DA7}
|
||||||
|
{192ECC49-3C79-48CD-A49B-296CC47546F5} = {F872A038-D325-4718-B0A0-4CBCEA3714ED}
|
||||||
|
{368BD8A7-67D2-4F24-B655-C3B82BBDA840} = {322AAFB0-04E1-42B9-B9F1-B836E12E1FCD}
|
||||||
|
{FAC76C2D-D586-4231-BF60-7766969BBD60} = {368BD8A7-67D2-4F24-B655-C3B82BBDA840}
|
||||||
|
{1E379202-AE6A-4A0A-BFE9-C1C0D4605143} = {368BD8A7-67D2-4F24-B655-C3B82BBDA840}
|
||||||
|
{45104F9C-A786-442B-8202-B965530AE20E} = {368BD8A7-67D2-4F24-B655-C3B82BBDA840}
|
||||||
|
{2A8189FE-0E9B-474F-91E7-A92C7C302A43} = {368BD8A7-67D2-4F24-B655-C3B82BBDA840}
|
||||||
|
{00EE8632-007B-406B-9CF8-F0CE38A5FEB0} = {368BD8A7-67D2-4F24-B655-C3B82BBDA840}
|
||||||
|
{1565B041-B687-4EF1-B27D-B0F9A927847B} = {368BD8A7-67D2-4F24-B655-C3B82BBDA840}
|
||||||
|
{E444200C-5B5D-42C0-88C5-D0E2C11A393F} = {368BD8A7-67D2-4F24-B655-C3B82BBDA840}
|
||||||
|
{28D2FC99-9DB4-4F15-A99E-F5A76DE7B13A} = {368BD8A7-67D2-4F24-B655-C3B82BBDA840}
|
||||||
|
{F81D94AB-E336-405D-B7E3-8AC97EE4E758} = {368BD8A7-67D2-4F24-B655-C3B82BBDA840}
|
||||||
|
{192C9441-BA98-41D4-A27E-D7DE95BB46F7} = {368BD8A7-67D2-4F24-B655-C3B82BBDA840}
|
||||||
|
{D592C239-AFAD-4596-8FE9-BBEA4977B258} = {368BD8A7-67D2-4F24-B655-C3B82BBDA840}
|
||||||
|
{33AA9C01-0E8E-4960-AE8C-550C3B13F84A} = {368BD8A7-67D2-4F24-B655-C3B82BBDA840}
|
||||||
|
{FD79C25F-87CD-47C6-87B2-F497AEA4A12D} = {368BD8A7-67D2-4F24-B655-C3B82BBDA840}
|
||||||
|
{F723B0BB-C5EA-4EBE-B3D5-50682F45EA80} = {368BD8A7-67D2-4F24-B655-C3B82BBDA840}
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {63CC6DCC-A35C-402C-8ECB-3CC7EA821994}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
8
nuget.config
Normal file
8
nuget.config
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<packageSources>
|
||||||
|
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||||
|
<add key="myget-discordnet" value="https://www.myget.org/F/discord-net/api/v3/index.json" protocolVersion="3"/>
|
||||||
|
</packageSources>
|
||||||
|
|
||||||
|
</configuration>
|
||||||
BIN
properties_for_visual_studio.zip
Normal file
BIN
properties_for_visual_studio.zip
Normal file
Binary file not shown.
2
scripts/Database/add-migration.ps1
Normal file
2
scripts/Database/add-migration.ps1
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
# comment
|
||||||
|
dotnet ef migrations add $Args[0] --project .\srcs\_plugins\Plugin.DB.EF --startup-project .\srcs\_plugins\Plugin.DB.EF
|
||||||
1
scripts/Database/default-accounts.ps1
Normal file
1
scripts/Database/default-accounts.ps1
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
./dist/toolkit/Toolkit.exe create-accounts
|
||||||
2
scripts/Database/remove-last-migration.ps1
Normal file
2
scripts/Database/remove-last-migration.ps1
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
# comment
|
||||||
|
dotnet ef migrations remove --project .\srcs\_plugins\Plugin.DB.EF --startup-project .\srcs\_plugins\Plugin.DB.EF
|
||||||
2
scripts/Database/update-database.ps1
Normal file
2
scripts/Database/update-database.ps1
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
# comment
|
||||||
|
dotnet ef database update $Args[0] --project .\srcs\_plugins\Plugin.DB.EF --startup-project .\srcs\_plugins\Plugin.DB.EF
|
||||||
1
scripts/Docker/MQTT.ps1
Normal file
1
scripts/Docker/MQTT.ps1
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
docker run -p 18083:18083 -p 1883:1883 --name mqtt-broker --restart always -d eclipse-mosquitto:1.6
|
||||||
1
scripts/Docker/MonboDB.ps1
Normal file
1
scripts/Docker/MonboDB.ps1
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
docker run --name mongodb -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=root --restart always -d mongo:4
|
||||||
1
scripts/Docker/PostgreSQL.ps1
Normal file
1
scripts/Docker/PostgreSQL.ps1
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
docker run -p 5432:5432 --name postgres-SQL -e POSTGRES_PASSWORD=VaNOSilla2022 --restart always -d postgres:13
|
||||||
1
scripts/Docker/Redis.ps1
Normal file
1
scripts/Docker/Redis.ps1
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
docker run -p 6379:6379 --name redis --restart always -d redis:latest redis-server --notify-keyspace-events Kx --appendonly yes
|
||||||
3
scripts/translations-update.ps1
Normal file
3
scripts/translations-update.ps1
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
$wingsemu_translations_directory = "../server-translations"
|
||||||
|
|
||||||
|
./dist/toolkit/Toolkit.exe translations -i $wingsemu_translations_directory -o $wingsemu_translations_directory
|
||||||
73
scripts/update-server-files.ps1
Normal file
73
scripts/update-server-files.ps1
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
# Translations keys #
|
||||||
|
$resources_src = '../server-translations'
|
||||||
|
$files = Get-ChildItem $resources_src -Filter "*.yaml"
|
||||||
|
|
||||||
|
$resources_dst = "./dist/translation-server/translations"
|
||||||
|
New-Item -ItemType Directory -Force -Path $resources_dst
|
||||||
|
Copy-Item $resources_src\en -Destination $resources_dst -Force -Recurse
|
||||||
|
Copy-Item $resources_src\fr -Destination $resources_dst -Force -Recurse
|
||||||
|
Copy-Item $resources_src\pl -Destination $resources_dst -Force -Recurse
|
||||||
|
Copy-Item $resources_src\es -Destination $resources_dst -Force -Recurse
|
||||||
|
Copy-Item $resources_src\it -Destination $resources_dst -Force -Recurse
|
||||||
|
Copy-Item $resources_src\de -Destination $resources_dst -Force -Recurse
|
||||||
|
Copy-Item $resources_src\tr -Destination $resources_dst -Force -Recurse
|
||||||
|
Copy-Item $resources_src\cz -Destination $resources_dst -Force -Recurse
|
||||||
|
Write-Host Copied $files.Count generic translations files to translations
|
||||||
|
|
||||||
|
# Dat
|
||||||
|
$resources_src = '../client-files/dat'
|
||||||
|
$files = Get-ChildItem $resources_src
|
||||||
|
|
||||||
|
$resources_dst = "./dist/game-server/resources/dat"
|
||||||
|
New-Item -ItemType Directory -Force -Path $resources_dst
|
||||||
|
Copy-Item $resources_src\* -Destination $resources_dst -Force -Recurse
|
||||||
|
Write-Host Copied $files.Count .dat files
|
||||||
|
|
||||||
|
$resources_dst = "./dist/bazaar-server/resources/dat"
|
||||||
|
New-Item -ItemType Directory -Force -Path $resources_dst
|
||||||
|
Copy-Item $resources_src\* -Destination $resources_dst -Force -Recurse
|
||||||
|
Write-Host Copied $files.Count .dat files to bazaar
|
||||||
|
|
||||||
|
$resources_dst = "./dist/discord-notifier/resources/dat"
|
||||||
|
New-Item -ItemType Directory -Force -Path $resources_dst
|
||||||
|
Copy-Item $resources_src\* -Destination $resources_dst -Force -Recurse
|
||||||
|
Write-Host Copied $files.Count .dat files to communicator
|
||||||
|
|
||||||
|
|
||||||
|
# Lang keys #
|
||||||
|
$resources_src = '../client-files/lang'
|
||||||
|
$files = Get-ChildItem $resources_src
|
||||||
|
|
||||||
|
$resources_dst = "./dist/game-server/resources/lang"
|
||||||
|
New-Item -ItemType Directory -Force -Path $resources_dst
|
||||||
|
Copy-Item $resources_src\* -Destination $resources_dst -Force -Recurse
|
||||||
|
Write-Host Copied $files.Count langs files to game server
|
||||||
|
|
||||||
|
# Maps
|
||||||
|
$resources_src = '../client-files/maps'
|
||||||
|
$files = Get-ChildItem $resources_src
|
||||||
|
|
||||||
|
$resources_dst = "./dist/game-server/resources/maps"
|
||||||
|
New-Item -ItemType Directory -Force -Path $resources_dst
|
||||||
|
Copy-Item $resources_src\* -Destination $resources_dst -Force -Recurse
|
||||||
|
Write-Host Copied $files.Count maps files
|
||||||
|
|
||||||
|
|
||||||
|
# Config files
|
||||||
|
$server_config_src = '../server-files'
|
||||||
|
$files = Get-ChildItem $server_config_src
|
||||||
|
|
||||||
|
$server_config_target = './dist/game-server/config'
|
||||||
|
New-Item -ItemType Directory -Force -Path $server_config_target
|
||||||
|
Copy-Item $server_config_src\* -Destination $server_config_target -Force -Recurse
|
||||||
|
Write-Host Copied $files.Count config files to Game Server
|
||||||
|
|
||||||
|
$server_config_target = './dist/family-server/config'
|
||||||
|
New-Item -ItemType Directory -Force -Path $server_config_target
|
||||||
|
Copy-Item $server_config_src\* -Destination $server_config_target -Force -Recurse
|
||||||
|
Write-Host Copied $files.Count config files to Family Server
|
||||||
|
|
||||||
|
$server_config_target = './dist/translation-server/config'
|
||||||
|
New-Item -ItemType Directory -Force -Path $server_config_target
|
||||||
|
Copy-Item $server_config_src\* -Destination $server_config_target -Force -Recurse
|
||||||
|
Write-Host Copied $files.Count config files to Translations Server
|
||||||
27
srcs/BazaarServer/BazaarServer.csproj
Normal file
27
srcs/BazaarServer/BazaarServer.csproj
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
<OutputPath>..\..\dist\bazaar-server\</OutputPath>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Grpc.AspNetCore" Version="2.38.0" />
|
||||||
|
<PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Caching\PhoenixLib.Caching.csproj" />
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Events\PhoenixLib.Events.csproj" />
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Logging\PhoenixLib.Logging.csproj" />
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Messaging\PhoenixLib.Messaging.csproj" />
|
||||||
|
<ProjectReference Include="..\WingsEmu.Communication.gRPC\WingsEmu.Communication.gRPC.csproj" />
|
||||||
|
<ProjectReference Include="..\WingsEmu.Health\WingsEmu.Health.csproj" />
|
||||||
|
<ProjectReference Include="..\_plugins\Plugin.ResourceLoader\Plugin.ResourceLoader.csproj" />
|
||||||
|
<ProjectReference Include="..\_plugins\Plugin.DB.EF\Plugin.DB.EF.csproj" />
|
||||||
|
<ProjectReference Include="..\_plugins\WingsEmu.Plugins.DistributedGameEvents\WingsEmu.Plugins.DistributedGameEvents.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using WingsAPI.Communication.Services.Messages;
|
||||||
|
using WingsEmu.Health;
|
||||||
|
|
||||||
|
namespace BazaarServer.Consumers
|
||||||
|
{
|
||||||
|
public class ServiceMaintenanceNotificationMessageConsumer : IMessageConsumer<ServiceMaintenanceNotificationMessage>
|
||||||
|
{
|
||||||
|
private readonly IMaintenanceManager _maintenanceManager;
|
||||||
|
|
||||||
|
public ServiceMaintenanceNotificationMessageConsumer(IMaintenanceManager maintenanceManager) => _maintenanceManager = maintenanceManager;
|
||||||
|
|
||||||
|
public async Task HandleAsync(ServiceMaintenanceNotificationMessage notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
if (notification.TimeLeft <= TimeSpan.FromMinutes(5))
|
||||||
|
{
|
||||||
|
_maintenanceManager.ActivateMaintenance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
srcs/BazaarServer/DockerGracefulStopService.cs
Normal file
54
srcs/BazaarServer/DockerGracefulStopService.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.Loader;
|
||||||
|
using System.Threading;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
|
||||||
|
namespace BazaarServer
|
||||||
|
{
|
||||||
|
public class DockerGracefulStopService : IDisposable
|
||||||
|
{
|
||||||
|
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||||
|
private readonly ManualResetEventSlim _stoppedEvent;
|
||||||
|
|
||||||
|
public DockerGracefulStopService()
|
||||||
|
{
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
_stoppedEvent = new ManualResetEventSlim();
|
||||||
|
|
||||||
|
// SIGINT
|
||||||
|
Console.CancelKeyPress += (sender, eventArgs) =>
|
||||||
|
{
|
||||||
|
Log.Error("[GRACEFUL_SHUTDOWN] SIGINT received", new Exception("[GRACEFUL_SHUTDOWN] SIGINT received"));
|
||||||
|
GracefulStop(_cancellationTokenSource, _stoppedEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
// SIGTERM
|
||||||
|
AssemblyLoadContext.Default.Unloading += context =>
|
||||||
|
{
|
||||||
|
Log.Error("[GRACEFUL_SHUTDOWN] SIGTERM received", new Exception("[GRACEFUL_SHUTDOWN] SIGTERM received"));
|
||||||
|
GracefulStop(_cancellationTokenSource, _stoppedEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
|
||||||
|
{
|
||||||
|
Log.Error("UnhandledException", args.ExceptionObject as Exception);
|
||||||
|
GracefulStop(_cancellationTokenSource, _stoppedEvent);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public CancellationToken CancellationToken => _cancellationTokenSource.Token;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_stoppedEvent.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GracefulStop(CancellationTokenSource cancellationTokenSource, ManualResetEventSlim stoppedEvent)
|
||||||
|
{
|
||||||
|
Log.Info("DockerGracefulStopService Stopping service");
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
stoppedEvent.Wait();
|
||||||
|
Log.Info("DockerGracefulStopService Stop finished");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
186
srcs/BazaarServer/Managers/BazaarManager.cs
Normal file
186
srcs/BazaarServer/Managers/BazaarManager.cs
Normal file
|
|
@ -0,0 +1,186 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PhoenixLib.Caching;
|
||||||
|
using WingsAPI.Communication.Bazaar;
|
||||||
|
using WingsAPI.Data.Bazaar;
|
||||||
|
using WingsAPI.Game.Extensions.Bazaar;
|
||||||
|
using WingsAPI.Packets.Enums.Bazaar;
|
||||||
|
using WingsEmu.Core.Extensions;
|
||||||
|
using WingsEmu.Core.Generics;
|
||||||
|
using WingsEmu.DTOs.Bazaar;
|
||||||
|
|
||||||
|
namespace BazaarServer.Managers
|
||||||
|
{
|
||||||
|
public class BazaarManager
|
||||||
|
{
|
||||||
|
private readonly IBazaarItemDAO _bazaarItemDao;
|
||||||
|
private readonly BazaarSearchManager _bazaarSearchManager;
|
||||||
|
|
||||||
|
private readonly ILongKeyCachedRepository<BazaarItemDTO> _itemsCache;
|
||||||
|
private readonly ConcurrentDictionary<long, ThreadSafeHashSet<long>> _itemsIdsByCharacterId = new();
|
||||||
|
|
||||||
|
private readonly SemaphoreSlim _semaphoreSlim = new(1, 1);
|
||||||
|
|
||||||
|
public BazaarManager(IBazaarItemDAO bazaarItemDao, ILongKeyCachedRepository<BazaarItemDTO> itemsCache, BazaarSearchManager bazaarSearchManager)
|
||||||
|
{
|
||||||
|
_bazaarItemDao = bazaarItemDao;
|
||||||
|
_itemsCache = itemsCache;
|
||||||
|
_bazaarSearchManager = bazaarSearchManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<long> CacheAllItemsInDb()
|
||||||
|
{
|
||||||
|
IEnumerable<BazaarItemDTO> items = await _bazaarItemDao.GetAllNonDeletedBazaarItems();
|
||||||
|
int count = 0;
|
||||||
|
foreach (BazaarItemDTO item in items)
|
||||||
|
{
|
||||||
|
AddToCache(item);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _bazaarSearchManager.Initialize(items);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddToCache(BazaarItemDTO item)
|
||||||
|
{
|
||||||
|
_itemsCache.Set(item.Id, item);
|
||||||
|
_itemsIdsByCharacterId.GetOrAdd(item.CharacterId, new ThreadSafeHashSet<long>()).Add(item.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveFromCache(BazaarItemDTO item)
|
||||||
|
{
|
||||||
|
_itemsCache.Remove(item.Id);
|
||||||
|
_itemsIdsByCharacterId.GetOrDefault(item.CharacterId)?.Remove(item.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BazaarItemDTO> SaveAsync(BazaarItemDTO item)
|
||||||
|
{
|
||||||
|
await _semaphoreSlim.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BazaarItemDTO dto = await _bazaarItemDao.SaveAsync(item);
|
||||||
|
AddToCache(dto);
|
||||||
|
_bazaarSearchManager.AddItem(dto);
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semaphoreSlim.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BazaarItemDTO> GetBazaarItemById(long bazaarItemId)
|
||||||
|
{
|
||||||
|
await _semaphoreSlim.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _itemsCache.Get(bazaarItemId);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semaphoreSlim.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICollection<BazaarItemDTO> GetItemsByCharacterId(long characterId)
|
||||||
|
{
|
||||||
|
return _itemsIdsByCharacterId.GetOrDefault(characterId)?.Select(s => _itemsCache.Get(s)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BazaarItemDTO> DeleteItemWithDto(BazaarItemDTO item, long requesterCharacterId)
|
||||||
|
{
|
||||||
|
await _semaphoreSlim.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BazaarItemDTO cachedItem = _itemsCache.Get(item.Id);
|
||||||
|
if (cachedItem == null || cachedItem.CharacterId != requesterCharacterId)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _bazaarItemDao.DeleteByIdAsync(item.Id);
|
||||||
|
RemoveFromCache(item);
|
||||||
|
_bazaarSearchManager.RemoveItem(item);
|
||||||
|
return cachedItem;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semaphoreSlim.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BazaarItemDTO> ChangeItemPriceWithDto(BazaarItemDTO bazaarItemDto, long characterId, long price, long saleFee)
|
||||||
|
{
|
||||||
|
await _semaphoreSlim.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BazaarItemDTO cachedItem = _itemsCache.Get(bazaarItemDto.Id);
|
||||||
|
if (cachedItem == null || cachedItem.SoldAmount > 0 || characterId != cachedItem.CharacterId || cachedItem.GetBazaarItemStatus() != BazaarListedItemType.ForSale
|
||||||
|
|| BazaarExtensions.PriceOrAmountExceeds(bazaarItemDto.UsedMedal, price, bazaarItemDto.Amount))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedItem.PricePerItem = price;
|
||||||
|
cachedItem.SaleFee = saleFee;
|
||||||
|
BazaarItemDTO savedItem = await _bazaarItemDao.SaveAsync(cachedItem);
|
||||||
|
return savedItem;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semaphoreSlim.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyCollection<BazaarItemDTO> SearchBazaarItems(BazaarSearchContext bazaarSearchContext) => _bazaarSearchManager.SearchBazaarItems(bazaarSearchContext);
|
||||||
|
|
||||||
|
public async Task<BazaarItemDTO> BuyItemWithExpectedValues(long bazaarItemId, long buyerCharacterId, short amount, long pricePerItem)
|
||||||
|
{
|
||||||
|
await _semaphoreSlim.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BazaarItemDTO cachedItem = _itemsCache.Get(bazaarItemId);
|
||||||
|
if (cachedItem == null || buyerCharacterId == cachedItem.CharacterId || amount < 1 || cachedItem.Amount - cachedItem.SoldAmount < amount || pricePerItem != cachedItem.PricePerItem
|
||||||
|
|| cachedItem.IsPackage && amount != cachedItem.Amount || cachedItem.GetBazaarItemStatus() != BazaarListedItemType.ForSale)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedItem.SoldAmount += amount;
|
||||||
|
BazaarItemDTO savedItem = await _bazaarItemDao.SaveAsync(cachedItem);
|
||||||
|
return savedItem;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semaphoreSlim.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<BazaarItemDTO>> UnlistItemsWithVnums(List<BazaarItemDTO> items)
|
||||||
|
{
|
||||||
|
await _semaphoreSlim.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (BazaarItemDTO bazaarItemDto in items)
|
||||||
|
{
|
||||||
|
bazaarItemDto.ExpiryDate = DateTime.UtcNow.AddDays(-1);
|
||||||
|
BazaarItemDTO dto = await _bazaarItemDao.SaveAsync(bazaarItemDto);
|
||||||
|
AddToCache(dto);
|
||||||
|
_bazaarSearchManager.AddItem(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semaphoreSlim.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
719
srcs/BazaarServer/Managers/BazaarSearchManager.cs
Normal file
719
srcs/BazaarServer/Managers/BazaarSearchManager.cs
Normal file
|
|
@ -0,0 +1,719 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PhoenixLib.Caching;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
using WingsAPI.Communication.Bazaar;
|
||||||
|
using WingsAPI.Data.Bazaar;
|
||||||
|
using WingsAPI.Data.GameData;
|
||||||
|
using WingsAPI.Game.Extensions.Bazaar;
|
||||||
|
using WingsAPI.Packets.Enums.Bazaar;
|
||||||
|
using WingsAPI.Packets.Enums.Bazaar.Filter;
|
||||||
|
using WingsAPI.Packets.Enums.Bazaar.SubFilter;
|
||||||
|
using WingsEmu.Core.Generics;
|
||||||
|
using WingsEmu.DTOs.Items;
|
||||||
|
using WingsEmu.Game._enum;
|
||||||
|
using WingsEmu.Packets.Enums;
|
||||||
|
using WingsEmu.Packets.Enums.Battle;
|
||||||
|
using WingsEmu.Packets.Enums.Character;
|
||||||
|
|
||||||
|
namespace BazaarServer.Managers
|
||||||
|
{
|
||||||
|
public class BazaarSearchManager
|
||||||
|
{
|
||||||
|
private readonly ILongKeyCachedRepository<ConcurrentDictionary<long, BazaarItemDTO>> _bazaarItemByItemVnum;
|
||||||
|
private readonly ILongKeyCachedRepository<(ItemDTO, BazaarLevelFilterType)> _cachedItems;
|
||||||
|
private readonly IResourceLoader<ItemDTO> _itemDao;
|
||||||
|
|
||||||
|
private readonly Dictionary<BazaarCategoryFilterType, Dictionary<byte, HashSet<int>>> _itemVnumByCategoryAndSubCategory = new();
|
||||||
|
|
||||||
|
private readonly ThreadSafeHashSet<int> _itemVnumsRegistered = new();
|
||||||
|
|
||||||
|
public BazaarSearchManager(ILongKeyCachedRepository<ConcurrentDictionary<long, BazaarItemDTO>> bazaarItemByItemVnum, IResourceLoader<ItemDTO> itemDao,
|
||||||
|
ILongKeyCachedRepository<(ItemDTO, BazaarLevelFilterType)> cachedItems)
|
||||||
|
{
|
||||||
|
_bazaarItemByItemVnum = bazaarItemByItemVnum;
|
||||||
|
_itemDao = itemDao;
|
||||||
|
_cachedItems = cachedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Initialize(IEnumerable<BazaarItemDTO> bazaarItemDtos)
|
||||||
|
{
|
||||||
|
await CategorizeItems();
|
||||||
|
await CacheSearchableBazaarItems(bazaarItemDtos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CacheSearchableBazaarItems(IEnumerable<BazaarItemDTO> bazaarItemDtos)
|
||||||
|
{
|
||||||
|
Log.Info("[BAZAAR_SEARCH_MANAGER] Caching searchable BazaarItems");
|
||||||
|
int count = 0;
|
||||||
|
foreach (BazaarItemDTO bazaarItemDto in bazaarItemDtos)
|
||||||
|
{
|
||||||
|
if (bazaarItemDto.GetBazaarItemStatus() != BazaarListedItemType.ForSale)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddItem(bazaarItemDto);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Info($"[BAZAAR_SEARCH_MANAGER] Cached: {count} searchable BazaarItems");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CategorizeItems()
|
||||||
|
{
|
||||||
|
Log.Info("[BAZAAR_SEARCH_MANAGER] Categorizing items from DB");
|
||||||
|
IEnumerable<ItemDTO> items = await _itemDao.LoadAsync();
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
var itemsToRegister = new Dictionary<BazaarCategoryFilterType, HashSet<int>>();
|
||||||
|
foreach (ItemDTO item in items)
|
||||||
|
{
|
||||||
|
(BazaarCategoryFilterType category, IEnumerable<byte> subCategories, bool registerForAllSubcategories) = GetCategoryAndSubCategoriesByItemVnum(item);
|
||||||
|
if (registerForAllSubcategories && !itemsToRegister.TryAdd(category, new HashSet<int> { item.Id }))
|
||||||
|
{
|
||||||
|
itemsToRegister[category].Add(item.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_itemVnumByCategoryAndSubCategory.ContainsKey(category))
|
||||||
|
{
|
||||||
|
_itemVnumByCategoryAndSubCategory.Add(category, new Dictionary<byte, HashSet<int>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (byte subCategory in subCategories)
|
||||||
|
{
|
||||||
|
if (!_itemVnumByCategoryAndSubCategory[category].ContainsKey(subCategory))
|
||||||
|
{
|
||||||
|
_itemVnumByCategoryAndSubCategory[category].Add(subCategory, new HashSet<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
_itemVnumByCategoryAndSubCategory[category][subCategory].Add(item.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!_itemVnumByCategoryAndSubCategory[category].ContainsKey(0))
|
||||||
|
{
|
||||||
|
_itemVnumByCategoryAndSubCategory[category].Add(0, new HashSet<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
_itemVnumByCategoryAndSubCategory[category][0].Add(item.Id);
|
||||||
|
|
||||||
|
_cachedItems.Set(item.Id, (item, GetItemLevelFilterByLevel(item.LevelMinimum, item.IsHeroic)));
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (KeyValuePair<BazaarCategoryFilterType, HashSet<int>> itemToRegister in itemsToRegister)
|
||||||
|
{
|
||||||
|
Type type = GetSubCategoryTypeByCategory(itemToRegister.Key);
|
||||||
|
if (type == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (byte value in Enum.GetValues(type))
|
||||||
|
{
|
||||||
|
if (!_itemVnumByCategoryAndSubCategory[itemToRegister.Key].ContainsKey(value))
|
||||||
|
{
|
||||||
|
_itemVnumByCategoryAndSubCategory[itemToRegister.Key].Add(value, new HashSet<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (int vnum in itemToRegister.Value)
|
||||||
|
{
|
||||||
|
_itemVnumByCategoryAndSubCategory[itemToRegister.Key][value].Add(vnum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Info($"[BAZAAR_SEARCH_MANAGER] Categorized: {count.ToString()} items");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Type GetSubCategoryTypeByCategory(BazaarCategoryFilterType category)
|
||||||
|
{
|
||||||
|
switch (category)
|
||||||
|
{
|
||||||
|
case BazaarCategoryFilterType.Specialist:
|
||||||
|
return typeof(BazaarCategorySpecialistSubFilterType);
|
||||||
|
case BazaarCategoryFilterType.Partner:
|
||||||
|
return typeof(BazaarCategoryPartnerSubFilterType);
|
||||||
|
case BazaarCategoryFilterType.Pet:
|
||||||
|
return typeof(BazaarCategoryPetSubFilterType);
|
||||||
|
case BazaarCategoryFilterType.StoreMount:
|
||||||
|
return typeof(BazaarCategoryConsumerItemSubFilterType);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddItem(BazaarItemDTO bazaarItemDto)
|
||||||
|
{
|
||||||
|
ItemInstanceDTO itemInstanceDto = bazaarItemDto?.ItemInstance;
|
||||||
|
if (itemInstanceDto == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentDictionary<long, BazaarItemDTO> dictionary = _bazaarItemByItemVnum.GetOrSet(itemInstanceDto.ItemVNum, () => new ConcurrentDictionary<long, BazaarItemDTO>());
|
||||||
|
dictionary[bazaarItemDto.Id] = bazaarItemDto;
|
||||||
|
_itemVnumsRegistered.Add(itemInstanceDto.ItemVNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveItem(BazaarItemDTO bazaarItemDto)
|
||||||
|
{
|
||||||
|
if (bazaarItemDto == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentDictionary<long, BazaarItemDTO> dictionary = _bazaarItemByItemVnum.GetOrSet(bazaarItemDto.ItemInstance.ItemVNum, () => new ConcurrentDictionary<long, BazaarItemDTO>());
|
||||||
|
dictionary.Remove(bazaarItemDto.Id, out _);
|
||||||
|
if (!dictionary.IsEmpty)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_bazaarItemByItemVnum.Remove(bazaarItemDto.ItemInstance.ItemVNum);
|
||||||
|
_itemVnumsRegistered.Remove(bazaarItemDto.ItemInstance.ItemVNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyCollection<BazaarItemDTO> SearchBazaarItems(BazaarSearchContext bazaarSearchContext)
|
||||||
|
{
|
||||||
|
IReadOnlyCollection<int> desiredItemVNums =
|
||||||
|
(bazaarSearchContext.ItemVNumFilter ?? GetItemVNumsByCategoryAndSubCategory(bazaarSearchContext.CategoryFilterType, bazaarSearchContext.SubTypeFilter)) ??
|
||||||
|
_itemVnumsRegistered.ToArray();
|
||||||
|
|
||||||
|
var itemList = new List<BazaarItemDTO>();
|
||||||
|
var tempItemList = new List<BazaarItemDTO>();
|
||||||
|
|
||||||
|
int ignoreCounter = bazaarSearchContext.Index * bazaarSearchContext.AmountOfItemsPerIndex;
|
||||||
|
int sendCounter = 0;
|
||||||
|
|
||||||
|
foreach (int itemVnum in desiredItemVNums.OrderBy(x => x))
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, BazaarItemDTO> dictionary = _bazaarItemByItemVnum.Get(itemVnum);
|
||||||
|
if (dictionary == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ignoreCounter > dictionary.Count && bazaarSearchContext.LevelFilter == BazaarLevelFilterType.All && bazaarSearchContext.RareFilter == BazaarRarityFilterType.All &&
|
||||||
|
bazaarSearchContext.UpgradeFilter == BazaarUpgradeFilterType.All)
|
||||||
|
{
|
||||||
|
ignoreCounter -= dictionary.Count;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
(ItemDTO itemDto, BazaarLevelFilterType baseItemLevelFilter) = _cachedItems.Get(itemVnum);
|
||||||
|
|
||||||
|
bool itemLevelIsInstanceDependant = false;
|
||||||
|
if (baseItemLevelFilter == BazaarLevelFilterType.All)
|
||||||
|
{
|
||||||
|
itemLevelIsInstanceDependant = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (bazaarSearchContext.LevelFilter != BazaarLevelFilterType.All && !ItemLevelFilterChecker(bazaarSearchContext.LevelFilter, baseItemLevelFilter))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IOrderedEnumerable<KeyValuePair<long, BazaarItemDTO>> values;
|
||||||
|
switch (bazaarSearchContext.OrderFilter)
|
||||||
|
{
|
||||||
|
case BazaarSortFilterType.PriceAscending:
|
||||||
|
values = dictionary.OrderBy(x => x.Value.PricePerItem);
|
||||||
|
break;
|
||||||
|
case BazaarSortFilterType.PriceDescending:
|
||||||
|
values = dictionary.OrderByDescending(x => x.Value.PricePerItem);
|
||||||
|
break;
|
||||||
|
case BazaarSortFilterType.AmountAscending:
|
||||||
|
values = dictionary.OrderBy(x => x.Value.Amount - x.Value.SoldAmount);
|
||||||
|
break;
|
||||||
|
case BazaarSortFilterType.AmountDescending:
|
||||||
|
values = dictionary.OrderByDescending(x => x.Value.Amount - x.Value.SoldAmount);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ((long _, BazaarItemDTO bazaarItemDto) in values)
|
||||||
|
{
|
||||||
|
BazaarListedItemType itemStatus = bazaarItemDto.GetBazaarItemStatus();
|
||||||
|
if (itemStatus != BazaarListedItemType.ForSale)
|
||||||
|
{
|
||||||
|
RemoveItem(bazaarItemDto);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bazaarItemDto.ItemInstance.Type == ItemInstanceType.BoxInstance)
|
||||||
|
{
|
||||||
|
BazaarPerfectionFilterType itemRarityFilter = GetPerfectionFilterByInstance(bazaarItemDto.ItemInstance);
|
||||||
|
|
||||||
|
if ((int)bazaarSearchContext.RareFilter != (int)BazaarPerfectionFilterType.All && (int)itemRarityFilter != (int)bazaarSearchContext.RareFilter)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BazaarRarityFilterType itemRarityFilter = GetRarityFilterByInstance(bazaarItemDto.ItemInstance);
|
||||||
|
|
||||||
|
if (bazaarSearchContext.RareFilter != BazaarRarityFilterType.All && itemRarityFilter != bazaarSearchContext.RareFilter)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BazaarUpgradeFilterType itemUpgradeFilter = GetUpgradeFilterByInstance(bazaarItemDto.ItemInstance);
|
||||||
|
|
||||||
|
if (bazaarSearchContext.UpgradeFilter != BazaarUpgradeFilterType.All && itemUpgradeFilter != bazaarSearchContext.UpgradeFilter)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemLevelIsInstanceDependant)
|
||||||
|
{
|
||||||
|
BazaarLevelFilterType itemLevelFilter = GetItemLevelFilterByInstance(bazaarItemDto.ItemInstance, bazaarItemDto.ItemInstance.Type);
|
||||||
|
|
||||||
|
if (bazaarSearchContext.LevelFilter != BazaarLevelFilterType.All && itemLevelFilter != bazaarSearchContext.LevelFilter)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bazaarSearchContext.SubTypeFilter != 0 && bazaarItemDto.ItemInstance.Type == ItemInstanceType.BoxInstance)
|
||||||
|
{
|
||||||
|
if ((UsableItemSubType)itemDto.ItemSubType != UsableItemSubType.RaidBoxOrSealedJajamaruSpOrSealedSakuraBead)
|
||||||
|
{
|
||||||
|
ItemInstanceDTO boxInstanceDto = bazaarItemDto.ItemInstance;
|
||||||
|
|
||||||
|
if (!GetBoxInstanceSubCategories(boxInstanceDto, itemDto).Contains(bazaarSearchContext.SubTypeFilter))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ignoreCounter > 0)
|
||||||
|
{
|
||||||
|
ignoreCounter--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendCounter >= bazaarSearchContext.AmountOfItemsPerIndex)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendCounter++;
|
||||||
|
|
||||||
|
tempItemList.Add(bazaarItemDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
itemList.AddRange(tempItemList);
|
||||||
|
tempItemList.Clear();
|
||||||
|
|
||||||
|
if (sendCounter >= bazaarSearchContext.AmountOfItemsPerIndex)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<byte> GetBoxInstanceSubCategories(ItemInstanceDTO boxInstanceDto, ItemDTO itemInfo)
|
||||||
|
{
|
||||||
|
var subCategory = new List<byte>();
|
||||||
|
switch ((UsableItemSubType)itemInfo.ItemSubType)
|
||||||
|
{
|
||||||
|
case UsableItemSubType.PetBead:
|
||||||
|
subCategory.Add(
|
||||||
|
(byte)(boxInstanceDto.HoldingVNum is 0 or null ? BazaarCategoryPetSubFilterType.EmptyPetBead : BazaarCategoryPetSubFilterType.PetBead));
|
||||||
|
break;
|
||||||
|
case UsableItemSubType.PartnerBead:
|
||||||
|
if (boxInstanceDto.HoldingVNum is 0 or null)
|
||||||
|
{
|
||||||
|
subCategory.Add((byte)BazaarCategoryPartnerSubFilterType.EmptyPartnerBead);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
subCategory.Add((byte)BazaarCategoryPartnerSubFilterType.PartnerBead);
|
||||||
|
subCategory.Add((byte)GetPartnerSubCategoryOfAttack(_cachedItems.Get(boxInstanceDto.HoldingVNum.Value).Item1));
|
||||||
|
break;
|
||||||
|
case UsableItemSubType.PartnerSpHolder:
|
||||||
|
if (boxInstanceDto.HoldingVNum is 0 or null)
|
||||||
|
{
|
||||||
|
subCategory.Add((byte)BazaarCategoryPartnerSubFilterType.EmptyCardHolder);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
subCategory.Add((byte)GetPartnerSubCategoryOfAttack(_cachedItems.Get(boxInstanceDto.HoldingVNum.Value).Item1));
|
||||||
|
break;
|
||||||
|
case UsableItemSubType.SpecialistHolder:
|
||||||
|
if (boxInstanceDto.HoldingVNum is 0 or null)
|
||||||
|
{
|
||||||
|
subCategory.Add((byte)BazaarCategorySpecialistSubFilterType.EmptyCardHolder);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
subCategory.Add(SpMorphToSubCategory(_cachedItems.Get(boxInstanceDto.HoldingVNum.Value).Item1));
|
||||||
|
break;
|
||||||
|
case UsableItemSubType.VehicleBead:
|
||||||
|
subCategory.Add((byte)(boxInstanceDto.HoldingVNum is 0 or null ? BazaarCategoryStoreMountSubFilterType.EmptyMountBead : BazaarCategoryStoreMountSubFilterType.MountBead));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return subCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private (BazaarCategoryFilterType, IEnumerable<byte>, bool) GetCategoryAndSubCategoriesByItemVnum(ItemDTO itemDto)
|
||||||
|
{
|
||||||
|
BazaarCategoryFilterType category = BazaarCategoryFilterType.Miscellaneous;
|
||||||
|
var subCategories = new List<byte>();
|
||||||
|
bool registerForAllSubcategories = false;
|
||||||
|
switch (itemDto.ItemType)
|
||||||
|
{
|
||||||
|
case ItemType.Weapon:
|
||||||
|
category = BazaarCategoryFilterType.Weapon;
|
||||||
|
subCategories.Add(ItemTypeClassToSubCategory(itemDto.Class));
|
||||||
|
break;
|
||||||
|
case ItemType.Armor:
|
||||||
|
category = BazaarCategoryFilterType.Armour;
|
||||||
|
subCategories.Add(ItemTypeClassToSubCategory(itemDto.Class));
|
||||||
|
break;
|
||||||
|
case ItemType.Fashion:
|
||||||
|
category = BazaarCategoryFilterType.Equipment;
|
||||||
|
subCategories.Add(EquipmentTypeToSubCategory1(itemDto.EquipmentSlot));
|
||||||
|
byte? secondSubCategory = EquipmentTypeToSubCategory2(itemDto.EquipmentSlot, itemDto.Sex switch
|
||||||
|
{
|
||||||
|
0 => GenderType.Unisex,
|
||||||
|
1 => GenderType.Male,
|
||||||
|
_ => GenderType.Female
|
||||||
|
});
|
||||||
|
if (secondSubCategory != null)
|
||||||
|
{
|
||||||
|
subCategories.Add(secondSubCategory.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ItemType.Jewelry:
|
||||||
|
category = BazaarCategoryFilterType.Accessories;
|
||||||
|
subCategories.Add(EquipmentTypeToSubCategory3(itemDto.EquipmentSlot));
|
||||||
|
break;
|
||||||
|
case ItemType.Specialist:
|
||||||
|
category = BazaarCategoryFilterType.Specialist;
|
||||||
|
subCategories.Add(SpMorphToSubCategory(itemDto));
|
||||||
|
break;
|
||||||
|
case ItemType.Box:
|
||||||
|
var usableItemSubType = (UsableItemSubType)itemDto.ItemSubType;
|
||||||
|
switch (usableItemSubType)
|
||||||
|
{
|
||||||
|
case UsableItemSubType.PetBead:
|
||||||
|
category = BazaarCategoryFilterType.Pet;
|
||||||
|
registerForAllSubcategories = true;
|
||||||
|
break;
|
||||||
|
case UsableItemSubType.PartnerBead:
|
||||||
|
category = BazaarCategoryFilterType.Partner;
|
||||||
|
registerForAllSubcategories = true;
|
||||||
|
break;
|
||||||
|
case UsableItemSubType.PartnerSpHolder:
|
||||||
|
category = BazaarCategoryFilterType.Partner;
|
||||||
|
registerForAllSubcategories = true;
|
||||||
|
break;
|
||||||
|
case UsableItemSubType.SpecialistHolder:
|
||||||
|
category = BazaarCategoryFilterType.Specialist;
|
||||||
|
registerForAllSubcategories = true;
|
||||||
|
break;
|
||||||
|
case UsableItemSubType.FairyBead:
|
||||||
|
category = BazaarCategoryFilterType.Accessories;
|
||||||
|
subCategories.Add((byte)BazaarCategoryAccessoriesSubFilterType.Fairy);
|
||||||
|
break;
|
||||||
|
case UsableItemSubType.VehicleBead:
|
||||||
|
category = BazaarCategoryFilterType.StoreMount;
|
||||||
|
registerForAllSubcategories = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ItemType.Shell:
|
||||||
|
category = BazaarCategoryFilterType.Shell;
|
||||||
|
var shellItemSubType = (ShellItemSubType)itemDto.ItemSubType;
|
||||||
|
subCategories.Add(shellItemSubType switch
|
||||||
|
{
|
||||||
|
ShellItemSubType.Weapon => (byte)BazaarCategoryShellSubFilterType.Weapon,
|
||||||
|
ShellItemSubType.Armor => (byte)BazaarCategoryShellSubFilterType.Clothing,
|
||||||
|
_ => default
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case ItemType.Main:
|
||||||
|
category = BazaarCategoryFilterType.MainItem;
|
||||||
|
subCategories.Add((byte)BazaarCategoryMainItemSubFilterType.GeneralItems);
|
||||||
|
break;
|
||||||
|
case ItemType.Upgrade:
|
||||||
|
category = BazaarCategoryFilterType.MainItem;
|
||||||
|
subCategories.Add((byte)BazaarCategoryMainItemSubFilterType.Material);
|
||||||
|
break;
|
||||||
|
case ItemType.Production:
|
||||||
|
category = BazaarCategoryFilterType.MainItem;
|
||||||
|
subCategories.Add((byte)BazaarCategoryMainItemSubFilterType.ProductionItem);
|
||||||
|
break;
|
||||||
|
case ItemType.Special:
|
||||||
|
category = BazaarCategoryFilterType.MainItem;
|
||||||
|
subCategories.Add((byte)BazaarCategoryMainItemSubFilterType.SpecialItems);
|
||||||
|
break;
|
||||||
|
case ItemType.Potion:
|
||||||
|
category = BazaarCategoryFilterType.MainItem;
|
||||||
|
subCategories.Add((byte)BazaarCategoryMainItemSubFilterType.HealingPotion);
|
||||||
|
break;
|
||||||
|
case ItemType.Event:
|
||||||
|
category = BazaarCategoryFilterType.MainItem;
|
||||||
|
subCategories.Add((byte)BazaarCategoryMainItemSubFilterType.Event);
|
||||||
|
break;
|
||||||
|
case ItemType.Title:
|
||||||
|
category = BazaarCategoryFilterType.MainItem;
|
||||||
|
subCategories.Add((byte)BazaarCategoryMainItemSubFilterType.Title);
|
||||||
|
break;
|
||||||
|
case ItemType.Sell:
|
||||||
|
category = BazaarCategoryFilterType.ConsumerItem;
|
||||||
|
subCategories.Add((byte)BazaarCategoryConsumerItemSubFilterType.SaleItem);
|
||||||
|
break;
|
||||||
|
case ItemType.Food:
|
||||||
|
category = BazaarCategoryFilterType.ConsumerItem;
|
||||||
|
subCategories.Add((byte)BazaarCategoryConsumerItemSubFilterType.Food);
|
||||||
|
break;
|
||||||
|
case ItemType.Snack:
|
||||||
|
category = BazaarCategoryFilterType.ConsumerItem;
|
||||||
|
subCategories.Add((byte)BazaarCategoryConsumerItemSubFilterType.Snack);
|
||||||
|
break;
|
||||||
|
case ItemType.Magical:
|
||||||
|
category = BazaarCategoryFilterType.ConsumerItem;
|
||||||
|
subCategories.Add((byte)BazaarCategoryConsumerItemSubFilterType.MagicItem);
|
||||||
|
break;
|
||||||
|
case ItemType.Material:
|
||||||
|
category = BazaarCategoryFilterType.ConsumerItem;
|
||||||
|
subCategories.Add((byte)BazaarCategoryConsumerItemSubFilterType.Ingredients);
|
||||||
|
break;
|
||||||
|
case ItemType.PetPartnerItem:
|
||||||
|
category = BazaarCategoryFilterType.ConsumerItem;
|
||||||
|
subCategories.Add((byte)BazaarCategoryConsumerItemSubFilterType.PartnerItem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (category, subCategories, registerForAllSubcategories);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte ItemTypeClassToSubCategory(byte itemTypeClass)
|
||||||
|
{
|
||||||
|
return itemTypeClass switch
|
||||||
|
{
|
||||||
|
(int)ItemClassType.Adventurer => (byte)BazaarCategoryWeaponArmourSubFilterType.Adventurer,
|
||||||
|
(int)ItemClassType.Swordsman => (byte)BazaarCategoryWeaponArmourSubFilterType.Swordsman,
|
||||||
|
(int)ItemClassType.Archer => (byte)BazaarCategoryWeaponArmourSubFilterType.Archer,
|
||||||
|
(int)ItemClassType.Mage => (byte)BazaarCategoryWeaponArmourSubFilterType.Magician,
|
||||||
|
(int)ItemClassType.MartialArtist => (byte)BazaarCategoryWeaponArmourSubFilterType.MartialArtist,
|
||||||
|
_ => default
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte EquipmentTypeToSubCategory1(EquipmentType equipmentType)
|
||||||
|
{
|
||||||
|
return equipmentType switch
|
||||||
|
{
|
||||||
|
EquipmentType.Hat => (byte)BazaarCategoryEquipmentSubFilterType.Hat,
|
||||||
|
EquipmentType.Mask => (byte)BazaarCategoryEquipmentSubFilterType.Accessory,
|
||||||
|
EquipmentType.Gloves => (byte)BazaarCategoryEquipmentSubFilterType.Gloves,
|
||||||
|
EquipmentType.Boots => (byte)BazaarCategoryEquipmentSubFilterType.Shoes,
|
||||||
|
EquipmentType.CostumeSuit => (byte)BazaarCategoryEquipmentSubFilterType.Costume,
|
||||||
|
EquipmentType.CostumeHat => (byte)BazaarCategoryEquipmentSubFilterType.CostumeHat,
|
||||||
|
EquipmentType.WeaponSkin => (byte)BazaarCategoryEquipmentSubFilterType.CostumeWeapon,
|
||||||
|
EquipmentType.Wings => (byte)BazaarCategoryEquipmentSubFilterType.CostumeWings,
|
||||||
|
_ => default
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte? EquipmentTypeToSubCategory2(EquipmentType equipmentType, GenderType genderType)
|
||||||
|
{
|
||||||
|
if (genderType == GenderType.Unisex)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isMale = genderType == GenderType.Male;
|
||||||
|
|
||||||
|
return equipmentType switch
|
||||||
|
{
|
||||||
|
EquipmentType.CostumeSuit => (byte)(isMale ? BazaarCategoryEquipmentSubFilterType.CostumeMale : BazaarCategoryEquipmentSubFilterType.CostumeFemale),
|
||||||
|
EquipmentType.CostumeHat => (byte)(isMale ? BazaarCategoryEquipmentSubFilterType.CostumeHatMale : BazaarCategoryEquipmentSubFilterType.CostumeHatFemale),
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte EquipmentTypeToSubCategory3(EquipmentType equipmentType)
|
||||||
|
{
|
||||||
|
return equipmentType switch
|
||||||
|
{
|
||||||
|
EquipmentType.Necklace => (byte)BazaarCategoryAccessoriesSubFilterType.Necklace,
|
||||||
|
EquipmentType.Ring => (byte)BazaarCategoryAccessoriesSubFilterType.Ring,
|
||||||
|
EquipmentType.Bracelet => (byte)BazaarCategoryAccessoriesSubFilterType.Bracelet,
|
||||||
|
EquipmentType.Fairy => (byte)BazaarCategoryAccessoriesSubFilterType.Fairy,
|
||||||
|
EquipmentType.Amulet => (byte)BazaarCategoryAccessoriesSubFilterType.Amulet,
|
||||||
|
_ => default
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte SpMorphToSubCategory(ItemDTO item)
|
||||||
|
{
|
||||||
|
int morphId = Convert.ToInt32(item.Morph);
|
||||||
|
if (!Enum.IsDefined(typeof(MorphIdType), morphId))
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var morph = (MorphIdType)morphId;
|
||||||
|
return morph switch
|
||||||
|
{
|
||||||
|
MorphIdType.Pyjama => (byte)BazaarCategorySpecialistSubFilterType.Pyjama,
|
||||||
|
MorphIdType.Warrior => (byte)BazaarCategorySpecialistSubFilterType.Warrior,
|
||||||
|
MorphIdType.Ninja => (byte)BazaarCategorySpecialistSubFilterType.Ninja,
|
||||||
|
MorphIdType.Ranger => (byte)BazaarCategorySpecialistSubFilterType.Ranger,
|
||||||
|
MorphIdType.Assassin => (byte)BazaarCategorySpecialistSubFilterType.Assassin,
|
||||||
|
MorphIdType.RedMagician => (byte)BazaarCategorySpecialistSubFilterType.RedMagician,
|
||||||
|
MorphIdType.HolyMage => (byte)BazaarCategorySpecialistSubFilterType.HolyMage,
|
||||||
|
MorphIdType.Chicken => (byte)BazaarCategorySpecialistSubFilterType.ChickenCostume,
|
||||||
|
MorphIdType.Jajamaru => (byte)BazaarCategorySpecialistSubFilterType.Jajamaru,
|
||||||
|
MorphIdType.Crusader => (byte)BazaarCategorySpecialistSubFilterType.Crusader,
|
||||||
|
MorphIdType.Berserker => (byte)BazaarCategorySpecialistSubFilterType.Berserker,
|
||||||
|
MorphIdType.Destroyer => (byte)BazaarCategorySpecialistSubFilterType.Destroyer,
|
||||||
|
MorphIdType.WildKeeper => (byte)BazaarCategorySpecialistSubFilterType.WildKeeper,
|
||||||
|
MorphIdType.BlueMagician => (byte)BazaarCategorySpecialistSubFilterType.BlueMagician,
|
||||||
|
MorphIdType.DarkGunner => (byte)BazaarCategorySpecialistSubFilterType.DarkGunner,
|
||||||
|
MorphIdType.Pirate => (byte)BazaarCategorySpecialistSubFilterType.Pirate,
|
||||||
|
MorphIdType.Gladiator => (byte)BazaarCategorySpecialistSubFilterType.Gladiator,
|
||||||
|
MorphIdType.FireCannoneer => (byte)BazaarCategorySpecialistSubFilterType.FireCannoneer,
|
||||||
|
MorphIdType.Volcano => (byte)BazaarCategorySpecialistSubFilterType.Volcano,
|
||||||
|
MorphIdType.BattleMonk => (byte)BazaarCategorySpecialistSubFilterType.BattleMonk,
|
||||||
|
MorphIdType.Scout => (byte)BazaarCategorySpecialistSubFilterType.Scout,
|
||||||
|
MorphIdType.TideLord => (byte)BazaarCategorySpecialistSubFilterType.TideLord,
|
||||||
|
MorphIdType.DeathReaper => (byte)BazaarCategorySpecialistSubFilterType.DeathReaper,
|
||||||
|
MorphIdType.DemonHunter => (byte)BazaarCategorySpecialistSubFilterType.DemonHunter,
|
||||||
|
MorphIdType.Seer => (byte)BazaarCategorySpecialistSubFilterType.Seer,
|
||||||
|
MorphIdType.Renegade => (byte)BazaarCategorySpecialistSubFilterType.Renegade,
|
||||||
|
MorphIdType.AvengingAngel => (byte)BazaarCategorySpecialistSubFilterType.AvengingAngel,
|
||||||
|
MorphIdType.Archmage => (byte)BazaarCategorySpecialistSubFilterType.Archmage,
|
||||||
|
MorphIdType.DraconicFist => (byte)BazaarCategorySpecialistSubFilterType.DraconicFist,
|
||||||
|
MorphIdType.MysticArts => (byte)BazaarCategorySpecialistSubFilterType.MysticArts,
|
||||||
|
MorphIdType.WeddingCostume => (byte)BazaarCategorySpecialistSubFilterType.WeddingCostume,
|
||||||
|
MorphIdType.MasterWolf => (byte)BazaarCategorySpecialistSubFilterType.MasterWolf,
|
||||||
|
MorphIdType.DemonWarrior => (byte)BazaarCategorySpecialistSubFilterType.DemonWarrior,
|
||||||
|
_ => default
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private BazaarLevelFilterType GetItemLevelFilterByInstance(ItemInstanceDTO itemInstanceDto, ItemInstanceType instanceType)
|
||||||
|
{
|
||||||
|
switch (instanceType)
|
||||||
|
{
|
||||||
|
case ItemInstanceType.BoxInstance:
|
||||||
|
case ItemInstanceType.SpecialistInstance:
|
||||||
|
ItemInstanceDTO specialistInstanceDto = itemInstanceDto;
|
||||||
|
return GetItemLevelFilterByLevel(specialistInstanceDto.SpLevel, false);
|
||||||
|
default:
|
||||||
|
(ItemDTO itemDto, BazaarLevelFilterType bazaarLevelFilterType) = _cachedItems.Get(itemInstanceDto.ItemVNum);
|
||||||
|
return itemDto.ItemType == ItemType.Shell
|
||||||
|
? GetItemLevelFilterByLevel(itemInstanceDto.Upgrade, false)
|
||||||
|
: bazaarLevelFilterType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BazaarLevelFilterType GetItemLevelFilterByLevel(byte level, bool isHeroic)
|
||||||
|
{
|
||||||
|
if (isHeroic)
|
||||||
|
{
|
||||||
|
if (level is < 1 or > 60)
|
||||||
|
{
|
||||||
|
return BazaarLevelFilterType.ChampionGear;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (BazaarLevelFilterType)(Convert.ToByte(Math.Ceiling(level / 10f)) + (byte)BazaarLevelFilterType.ChampionGear);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level is < 1 or > 99)
|
||||||
|
{
|
||||||
|
return BazaarLevelFilterType.All;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (BazaarLevelFilterType)Convert.ToByte(Math.Ceiling(level / 10f));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ItemLevelFilterChecker(BazaarLevelFilterType demandedFilterType, BazaarLevelFilterType itemFilterType)
|
||||||
|
{
|
||||||
|
return demandedFilterType == itemFilterType || demandedFilterType == BazaarLevelFilterType.All || demandedFilterType == BazaarLevelFilterType.ChampionGear && itemFilterType switch
|
||||||
|
{
|
||||||
|
BazaarLevelFilterType.ChampionLevelOneToTen => true,
|
||||||
|
BazaarLevelFilterType.ChampionLevelElevenToTwenty => true,
|
||||||
|
BazaarLevelFilterType.ChampionLevelTwentyOneToThirty => true,
|
||||||
|
BazaarLevelFilterType.ChampionLevelThirtyOneToForty => true,
|
||||||
|
BazaarLevelFilterType.ChampionLevelFortyOneToFifty => true,
|
||||||
|
BazaarLevelFilterType.ChampionLevelFiftyOneToSixty => true,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BazaarUpgradeFilterType GetUpgradeFilterByInstance(ItemInstanceDTO itemInstanceDto)
|
||||||
|
{
|
||||||
|
if (15 < itemInstanceDto.Upgrade)
|
||||||
|
{
|
||||||
|
return BazaarUpgradeFilterType.All;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (BazaarUpgradeFilterType)(itemInstanceDto.Upgrade + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BazaarRarityFilterType GetRarityFilterByInstance(ItemInstanceDTO itemInstanceDto)
|
||||||
|
{
|
||||||
|
if (itemInstanceDto.Rarity is < 0 or > 8)
|
||||||
|
{
|
||||||
|
return BazaarRarityFilterType.All;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (BazaarRarityFilterType)(itemInstanceDto.Rarity + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BazaarPerfectionFilterType GetPerfectionFilterByInstance(ItemInstanceDTO itemInstanceDto)
|
||||||
|
{
|
||||||
|
const int max = 100;
|
||||||
|
return itemInstanceDto.SpStoneUpgrade switch
|
||||||
|
{
|
||||||
|
< 1 or < max => BazaarPerfectionFilterType.All,
|
||||||
|
max => BazaarPerfectionFilterType.NintyOneToHundred,
|
||||||
|
_ => (BazaarPerfectionFilterType)Convert.ToByte(Math.Ceiling(itemInstanceDto.SpStoneUpgrade / 10f))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BazaarCategoryPartnerSubFilterType GetPartnerSubCategoryOfAttack(ItemDTO item)
|
||||||
|
{
|
||||||
|
return item.PartnerClass switch
|
||||||
|
{
|
||||||
|
(byte)AttackType.Melee => BazaarCategoryPartnerSubFilterType.CloseAttack,
|
||||||
|
(byte)AttackType.Ranged => BazaarCategoryPartnerSubFilterType.RemoteAttack,
|
||||||
|
(byte)AttackType.Magical => BazaarCategoryPartnerSubFilterType.Magic,
|
||||||
|
_ => BazaarCategoryPartnerSubFilterType.All
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyCollection<int> GetItemVNumsByCategoryAndSubCategory(BazaarCategoryFilterType categoryFilterType, byte subTypeFilter)
|
||||||
|
{
|
||||||
|
if (categoryFilterType == BazaarCategoryFilterType.All || !_itemVnumByCategoryAndSubCategory.ContainsKey(categoryFilterType) ||
|
||||||
|
!_itemVnumByCategoryAndSubCategory[categoryFilterType].ContainsKey(subTypeFilter))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _itemVnumByCategoryAndSubCategory[categoryFilterType][subTypeFilter];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
srcs/BazaarServer/Program.cs
Normal file
82
srcs/BazaarServer/Program.cs
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
using PhoenixLib.ServiceBus.MQTT;
|
||||||
|
using Plugin.Database.Mapping;
|
||||||
|
using ProtoBuf.Grpc.Client;
|
||||||
|
|
||||||
|
namespace BazaarServer
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
PrintHeader();
|
||||||
|
NonGameMappingRules.InitializeMapping();
|
||||||
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
|
GrpcClientFactory.AllowUnencryptedHttp2 = true;
|
||||||
|
using var stopService = new DockerGracefulStopService();
|
||||||
|
using IHost host = CreateHostBuilder(args).Build();
|
||||||
|
{
|
||||||
|
await host.StartAsync();
|
||||||
|
IMessagingService messagingService = host.Services.GetRequiredService<IMessagingService>();
|
||||||
|
await messagingService.StartAsync();
|
||||||
|
|
||||||
|
IServiceProvider services = host.Services;
|
||||||
|
Log.Info("BazaarServer started");
|
||||||
|
await host.WaitForShutdownAsync(stopService.CancellationToken);
|
||||||
|
await messagingService.DisposeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PrintHeader()
|
||||||
|
{
|
||||||
|
const string text = @"
|
||||||
|
██╗ ██╗██╗███╗ ██╗ ██████╗ ███████╗███████╗███╗ ███╗██╗ ██╗
|
||||||
|
██║ ██║██║████╗ ██║██╔════╝ ██╔════╝██╔════╝████╗ ████║██║ ██║
|
||||||
|
██║ █╗ ██║██║██╔██╗ ██║██║ ███╗███████╗█████╗ ██╔████╔██║██║ ██║
|
||||||
|
██║███╗██║██║██║╚██╗██║██║ ██║╚════██║██╔══╝ ██║╚██╔╝██║██║ ██║
|
||||||
|
╚███╔███╔╝██║██║ ╚████║╚██████╔╝███████║███████╗██║ ╚═╝ ██║╚██████╔╝
|
||||||
|
╚══╝╚══╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝
|
||||||
|
|
||||||
|
██████╗ █████╗ ███████╗ █████╗ █████╗ ██████╗ ███████╗███████╗██████╗ ██╗ ██╗███████╗██████╗
|
||||||
|
██╔══██╗██╔══██╗╚══███╔╝██╔══██╗██╔══██╗██╔══██╗ ██╔════╝██╔════╝██╔══██╗██║ ██║██╔════╝██╔══██╗
|
||||||
|
██████╔╝███████║ ███╔╝ ███████║███████║██████╔╝█████╗███████╗█████╗ ██████╔╝██║ ██║█████╗ ██████╔╝
|
||||||
|
██╔══██╗██╔══██║ ███╔╝ ██╔══██║██╔══██║██╔══██╗╚════╝╚════██║██╔══╝ ██╔══██╗╚██╗ ██╔╝██╔══╝ ██╔══██╗
|
||||||
|
██████╔╝██║ ██║███████╗██║ ██║██║ ██║██║ ██║ ███████║███████╗██║ ██║ ╚████╔╝ ███████╗██║ ██║
|
||||||
|
╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝
|
||||||
|
|
||||||
|
";
|
||||||
|
string separator = new('=', Console.WindowWidth);
|
||||||
|
string logo = text.Split('\n').Select(s => string.Format("{0," + (Console.WindowWidth / 2 + s.Length / 2) + "}\n", s))
|
||||||
|
.Aggregate("", (current, i) => current + i);
|
||||||
|
|
||||||
|
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||||
|
Console.WriteLine(separator + logo + $"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n" + separator);
|
||||||
|
Console.ForegroundColor = ConsoleColor.White;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional configuration is required to successfully run gRPC on macOS.
|
||||||
|
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
|
||||||
|
private static IHostBuilder CreateHostBuilder(string[] args)
|
||||||
|
{
|
||||||
|
IHostBuilder host = Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureWebHostDefaults(webBuilder =>
|
||||||
|
{
|
||||||
|
webBuilder.ConfigureKestrel(s =>
|
||||||
|
{
|
||||||
|
s.ListenAnyIP(short.Parse(Environment.GetEnvironmentVariable("BAZAAR_SERVER_PORT") ?? "25555"), options => { options.Protocols = HttpProtocols.Http2; });
|
||||||
|
});
|
||||||
|
webBuilder.UseStartup<Startup>();
|
||||||
|
});
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
srcs/BazaarServer/RecurrentJobs/BazaarSystem.cs
Normal file
25
srcs/BazaarServer/RecurrentJobs/BazaarSystem.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BazaarServer.Managers;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
|
||||||
|
namespace BazaarServer.RecurrentJobs
|
||||||
|
{
|
||||||
|
public class BazaarSystem : BackgroundService
|
||||||
|
{
|
||||||
|
private static readonly TimeSpan Interval = TimeSpan.FromSeconds(10);
|
||||||
|
private readonly BazaarManager _bazaarManager;
|
||||||
|
|
||||||
|
public BazaarSystem(BazaarManager bazaarManager) => _bazaarManager = bazaarManager;
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
Log.Info("[BAZAAR_SYSTEM] Caching all items in the database...");
|
||||||
|
long countCachedItems = await _bazaarManager.CacheAllItemsInDb();
|
||||||
|
Log.Info($"[BAZAAR_SYSTEM] Cached: {countCachedItems.ToString()} item/s");
|
||||||
|
Log.Info("[BAZAAR_SYSTEM] Started!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
243
srcs/BazaarServer/Services/BazaarService.cs
Normal file
243
srcs/BazaarServer/Services/BazaarService.cs
Normal file
|
|
@ -0,0 +1,243 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BazaarServer.Managers;
|
||||||
|
using WingsAPI.Communication;
|
||||||
|
using WingsAPI.Communication.Bazaar;
|
||||||
|
using WingsAPI.Data.Bazaar;
|
||||||
|
using WingsEmu.Health;
|
||||||
|
|
||||||
|
namespace BazaarServer.Services
|
||||||
|
{
|
||||||
|
public class BazaarService : IBazaarService
|
||||||
|
{
|
||||||
|
private readonly BazaarManager _bazaarManager;
|
||||||
|
private readonly IMaintenanceManager _maintenanceManager;
|
||||||
|
|
||||||
|
public BazaarService(BazaarManager bazaarManager, IMaintenanceManager maintenanceManager)
|
||||||
|
{
|
||||||
|
_bazaarManager = bazaarManager;
|
||||||
|
_maintenanceManager = maintenanceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MaintenanceMode => _maintenanceManager.IsMaintenanceActive;
|
||||||
|
|
||||||
|
public async ValueTask<BazaarItemResponse> GetBazaarItemById(BazaarGetItemByIdRequest request)
|
||||||
|
{
|
||||||
|
if (MaintenanceMode)
|
||||||
|
{
|
||||||
|
return new BazaarItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.MAINTENANCE_MODE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
BazaarItemDTO bazaarItem = await _bazaarManager.GetBazaarItemById(request.BazaarItemId);
|
||||||
|
return new BazaarItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = bazaarItem == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
BazaarItemDto = bazaarItem
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<BazaarItemResponse> AddItemToBazaar(BazaarAddItemRequest request)
|
||||||
|
{
|
||||||
|
if (MaintenanceMode)
|
||||||
|
{
|
||||||
|
return new BazaarItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.MAINTENANCE_MODE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.MaximumListedItems <= _bazaarManager.GetItemsByCharacterId(request.BazaarItemDto.CharacterId)?.Count)
|
||||||
|
{
|
||||||
|
return new BazaarItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.GENERIC_SERVER_ERROR
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
BazaarItemDTO bazaarItemDto = await _bazaarManager.SaveAsync(request.BazaarItemDto);
|
||||||
|
return new BazaarItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.SUCCESS,
|
||||||
|
BazaarItemDto = bazaarItemDto
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<BazaarItemResponse> RemoveItemFromBazaar(BazaarRemoveItemRequest request)
|
||||||
|
{
|
||||||
|
if (MaintenanceMode)
|
||||||
|
{
|
||||||
|
return new BazaarItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.MAINTENANCE_MODE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
BazaarItemDTO deletedItem = await _bazaarManager.DeleteItemWithDto(request.BazaarItemDto, request.RequesterCharacterId);
|
||||||
|
|
||||||
|
if (deletedItem == null)
|
||||||
|
{
|
||||||
|
return new BazaarItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.GENERIC_SERVER_ERROR
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BazaarItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.SUCCESS,
|
||||||
|
BazaarItemDto = deletedItem
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<BazaarItemResponse> ChangeItemPriceFromBazaar(BazaarChangeItemPriceRequest request)
|
||||||
|
{
|
||||||
|
if (MaintenanceMode)
|
||||||
|
{
|
||||||
|
return new BazaarItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.MAINTENANCE_MODE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
BazaarItemDTO updatedItem = await _bazaarManager.ChangeItemPriceWithDto(request.BazaarItemDto, request.ChangerCharacterId, request.NewPrice, request.NewSaleFee);
|
||||||
|
if (updatedItem == null)
|
||||||
|
{
|
||||||
|
return new BazaarItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.GENERIC_SERVER_ERROR,
|
||||||
|
BazaarItemDto = null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BazaarItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.SUCCESS,
|
||||||
|
BazaarItemDto = updatedItem
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<BazaarGetItemsByCharIdResponse> GetItemsByCharacterIdFromBazaar(BazaarGetItemsByCharIdRequest request)
|
||||||
|
{
|
||||||
|
if (MaintenanceMode)
|
||||||
|
{
|
||||||
|
return new BazaarGetItemsByCharIdResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.MAINTENANCE_MODE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BazaarGetItemsByCharIdResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.SUCCESS,
|
||||||
|
BazaarItems = _bazaarManager.GetItemsByCharacterId(request.CharacterId)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<BazaarRemoveItemsByCharIdResponse> RemoveItemsByCharacterIdFromBazaar(BazaarRemoveItemsByCharIdRequest request)
|
||||||
|
{
|
||||||
|
if (MaintenanceMode)
|
||||||
|
{
|
||||||
|
return new BazaarRemoveItemsByCharIdResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.MAINTENANCE_MODE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ICollection<BazaarItemDTO> items = _bazaarManager.GetItemsByCharacterId(request.CharacterId);
|
||||||
|
|
||||||
|
if (items == null)
|
||||||
|
{
|
||||||
|
return new BazaarRemoveItemsByCharIdResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.SUCCESS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (BazaarItemDTO item in items)
|
||||||
|
{
|
||||||
|
await _bazaarManager.DeleteItemWithDto(item, request.CharacterId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BazaarRemoveItemsByCharIdResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.SUCCESS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<BazaarSearchBazaarItemsResponse> SearchBazaarItems(BazaarSearchBazaarItemsRequest request)
|
||||||
|
{
|
||||||
|
if (MaintenanceMode)
|
||||||
|
{
|
||||||
|
return new BazaarSearchBazaarItemsResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.MAINTENANCE_MODE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BazaarSearchBazaarItemsResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.SUCCESS,
|
||||||
|
BazaarItemDtos = _bazaarManager.SearchBazaarItems(request.BazaarSearchContext)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<BazaarItemResponse> BuyItemFromBazaar(BazaarBuyItemRequest request)
|
||||||
|
{
|
||||||
|
if (MaintenanceMode)
|
||||||
|
{
|
||||||
|
return new BazaarItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.MAINTENANCE_MODE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
BazaarItemDTO cachedItem = await _bazaarManager.BuyItemWithExpectedValues(request.BazaarItemId, request.BuyerCharacterId, request.Amount, request.PricePerItem);
|
||||||
|
if (cachedItem == null)
|
||||||
|
{
|
||||||
|
return new BazaarItemResponse
|
||||||
|
{
|
||||||
|
BazaarItemDto = null,
|
||||||
|
ResponseType = RpcResponseType.GENERIC_SERVER_ERROR
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BazaarItemResponse
|
||||||
|
{
|
||||||
|
BazaarItemDto = cachedItem,
|
||||||
|
ResponseType = RpcResponseType.SUCCESS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<UnlistItemFromBazaarResponse> UnlistItemsFromBazaarWithVnumAsync(UnlistItemFromBazaarRequest request)
|
||||||
|
{
|
||||||
|
IReadOnlyCollection<BazaarItemDTO> itemsToUnlist = _bazaarManager.SearchBazaarItems(new BazaarSearchContext
|
||||||
|
{
|
||||||
|
ItemVNumFilter = request.Vnum,
|
||||||
|
Index = 0,
|
||||||
|
AmountOfItemsPerIndex = 10000
|
||||||
|
});
|
||||||
|
List<BazaarItemDTO> unlistedItems = await _bazaarManager.UnlistItemsWithVnums(itemsToUnlist.ToList());
|
||||||
|
|
||||||
|
return new UnlistItemFromBazaarResponse
|
||||||
|
{
|
||||||
|
UnlistedItems = unlistedItems.Count
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<UnlistItemFromBazaarResponse> UnlistCharacterItemsFromBazaarAsync(UnlistCharacterItemsFromBazaarRequest request)
|
||||||
|
{
|
||||||
|
ICollection<BazaarItemDTO> itemsToUnlist = _bazaarManager.GetItemsByCharacterId(request.Id);
|
||||||
|
|
||||||
|
List<BazaarItemDTO> unlistedItems = await _bazaarManager.UnlistItemsWithVnums(itemsToUnlist.ToList());
|
||||||
|
|
||||||
|
return new UnlistItemFromBazaarResponse
|
||||||
|
{
|
||||||
|
UnlistedItems = unlistedItems.Count
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
srcs/BazaarServer/Startup.cs
Normal file
70
srcs/BazaarServer/Startup.cs
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
using BazaarServer.Consumers;
|
||||||
|
using BazaarServer.Managers;
|
||||||
|
using BazaarServer.RecurrentJobs;
|
||||||
|
using BazaarServer.Services;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using PhoenixLib.Caching;
|
||||||
|
using PhoenixLib.Events;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
using PhoenixLib.ServiceBus.Extensions;
|
||||||
|
using Plugin.Database;
|
||||||
|
using Plugin.ResourceLoader;
|
||||||
|
using ProtoBuf.Grpc.Server;
|
||||||
|
using WingsAPI.Communication.Services.Messages;
|
||||||
|
using WingsEmu.Communication.gRPC.Extensions;
|
||||||
|
using WingsEmu.Health.Extensions;
|
||||||
|
|
||||||
|
namespace BazaarServer
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
|
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddMqttConfigurationFromEnv();
|
||||||
|
services.AddMaintenanceMode();
|
||||||
|
services.AddEventPipeline();
|
||||||
|
services.AddEventHandlersInAssembly<Startup>();
|
||||||
|
new DatabasePlugin().AddDependencies(services);
|
||||||
|
new FileResourceLoaderPlugin().AddDependencies(services);
|
||||||
|
|
||||||
|
services.TryAddSingleton(typeof(ILongKeyCachedRepository<>), typeof(InMemoryCacheRepository<>));
|
||||||
|
services.TryAddSingleton(typeof(IUuidKeyCachedRepository<>), typeof(InMemoryUuidCacheRepository<>));
|
||||||
|
|
||||||
|
services.AddGrpcBazaarServiceClient();
|
||||||
|
services.AddSingleton<BazaarManager>();
|
||||||
|
services.AddSingleton<BazaarSearchManager>();
|
||||||
|
services.AddSingleton<BazaarService>();
|
||||||
|
services.AddSingleton<BazaarSystem>();
|
||||||
|
services.AddHostedService(s => s.GetRequiredService<BazaarSystem>());
|
||||||
|
services.AddPhoenixLogging();
|
||||||
|
|
||||||
|
services.AddCodeFirstGrpc(config =>
|
||||||
|
{
|
||||||
|
config.MaxReceiveMessageSize = null;
|
||||||
|
config.MaxSendMessageSize = null;
|
||||||
|
config.EnableDetailedErrors = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddMessageSubscriber<ServiceMaintenanceNotificationMessage, ServiceMaintenanceNotificationMessageConsumer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
if (env.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseEndpoints(endpoints => { endpoints.MapGrpcService<BazaarService>(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DatabaseServer.Managers;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using WingsAPI.Communication.Services.Messages;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Consumers
|
||||||
|
{
|
||||||
|
public class ServiceFlushAllMessageConsumer : IMessageConsumer<ServiceFlushAllMessage>
|
||||||
|
{
|
||||||
|
private readonly IAccountWarehouseManager _accountWarehouseManager;
|
||||||
|
private readonly ICharacterManager _characterManager;
|
||||||
|
private readonly ITimeSpaceManager _timeSpaceManager;
|
||||||
|
|
||||||
|
public ServiceFlushAllMessageConsumer(ICharacterManager characterManager, IAccountWarehouseManager accountWarehouseManager, ITimeSpaceManager timeSpaceManager)
|
||||||
|
{
|
||||||
|
_characterManager = characterManager;
|
||||||
|
_accountWarehouseManager = accountWarehouseManager;
|
||||||
|
_timeSpaceManager = timeSpaceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleAsync(ServiceFlushAllMessage notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
await _characterManager.FlushCharacterSaves();
|
||||||
|
await _accountWarehouseManager.FlushWarehouseSaves();
|
||||||
|
await _timeSpaceManager.FlushTimeSpaceRecords();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
srcs/DatabaseServer/DatabaseServer.csproj
Normal file
26
srcs/DatabaseServer/DatabaseServer.csproj
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
<OutputPath>..\..\dist\database-server\</OutputPath>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Grpc.AspNetCore" Version="2.38.0" />
|
||||||
|
<PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152" />
|
||||||
|
<PackageReference Include="Z.EntityFramework.Extensions.EFCore" Version="5.13.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Caching\PhoenixLib.Caching.csproj" />
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Logging\PhoenixLib.Logging.csproj" />
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Messaging\PhoenixLib.Messaging.csproj" />
|
||||||
|
<ProjectReference Include="..\WingsEmu.Communication.gRPC\WingsEmu.Communication.gRPC.csproj" />
|
||||||
|
<ProjectReference Include="..\WingsEmu.Health\WingsEmu.Health.csproj" />
|
||||||
|
<ProjectReference Include="..\_plugins\Plugin.DB.EF\Plugin.DB.EF.csproj" />
|
||||||
|
<ProjectReference Include="..\_plugins\WingsEmu.Plugins.DistributedGameEvents\WingsEmu.Plugins.DistributedGameEvents.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
53
srcs/DatabaseServer/DockerGracefulStopService.cs
Normal file
53
srcs/DatabaseServer/DockerGracefulStopService.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.Loader;
|
||||||
|
using System.Threading;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
|
||||||
|
namespace FamilyServer
|
||||||
|
{
|
||||||
|
public class DockerGracefulStopService : IDisposable
|
||||||
|
{
|
||||||
|
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||||
|
private readonly ManualResetEventSlim _stoppedEvent;
|
||||||
|
|
||||||
|
public DockerGracefulStopService()
|
||||||
|
{
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
_stoppedEvent = new ManualResetEventSlim();
|
||||||
|
// SIGINT
|
||||||
|
Console.CancelKeyPress += (sender, eventArgs) =>
|
||||||
|
{
|
||||||
|
Log.Error("[GRACEFUL_SHUTDOWN] SIGINT received", new Exception("[GRACEFUL_SHUTDOWN] SIGINT received"));
|
||||||
|
GracefulStop(_cancellationTokenSource, _stoppedEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
// SIGTERM
|
||||||
|
AssemblyLoadContext.Default.Unloading += context =>
|
||||||
|
{
|
||||||
|
Log.Error("[GRACEFUL_SHUTDOWN] SIGTERM received", new Exception("[GRACEFUL_SHUTDOWN] SIGTERM received"));
|
||||||
|
GracefulStop(_cancellationTokenSource, _stoppedEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
|
||||||
|
{
|
||||||
|
Log.Error("UnhandledException", args.ExceptionObject as Exception);
|
||||||
|
GracefulStop(_cancellationTokenSource, _stoppedEvent);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public CancellationToken CancellationToken => _cancellationTokenSource.Token;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_stoppedEvent.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GracefulStop(CancellationTokenSource cancellationTokenSource, ManualResetEventSlim stoppedEvent)
|
||||||
|
{
|
||||||
|
Log.Info("DockerGracefulStopService Stopping service");
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
stoppedEvent.Wait();
|
||||||
|
Log.Info("DockerGracefulStopService Stop finished");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
srcs/DatabaseServer/EnvironmentConsts.cs
Normal file
10
srcs/DatabaseServer/EnvironmentConsts.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace DatabaseServer
|
||||||
|
{
|
||||||
|
public static class EnvironmentConsts
|
||||||
|
{
|
||||||
|
public const string DbServerSaveIntervalMinutes = "DB_SERVER_SAVE_INTERVAL_MINUTES";
|
||||||
|
public const string DbServerCharTtlMinutes = "DB_SERVER_CHAR_TTL_MINUTES";
|
||||||
|
public const string DbServerCharSaveIntervalSeconds = "DB_SERVER_CHAR_SAVE_INTERVAL_SECONDS";
|
||||||
|
public const string TsServerSaveIntervalMinutes = "TS_SERVER_SAVE_INTERVAL_MINUTES";
|
||||||
|
}
|
||||||
|
}
|
||||||
440
srcs/DatabaseServer/Managers/AccountWarehouseManager.cs
Normal file
440
srcs/DatabaseServer/Managers/AccountWarehouseManager.cs
Normal file
|
|
@ -0,0 +1,440 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Mapster;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using PhoenixLib.Caching;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
using WingsAPI.Data.Account;
|
||||||
|
using WingsAPI.Data.Warehouse;
|
||||||
|
using WingsEmu.Core.Extensions;
|
||||||
|
using WingsEmu.DTOs.Items;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Managers
|
||||||
|
{
|
||||||
|
public class AccountWarehouseManager : BackgroundService, IAccountWarehouseManager
|
||||||
|
{
|
||||||
|
private static readonly TimeSpan Interval = TimeSpan.FromMinutes(Convert.ToUInt32(Environment.GetEnvironmentVariable(EnvironmentConsts.DbServerSaveIntervalMinutes) ?? "5"));
|
||||||
|
private static readonly TimeSpan LifeTime = Interval * 3;
|
||||||
|
|
||||||
|
private readonly IAccountWarehouseItemDao _accountWarehouseItemDao;
|
||||||
|
|
||||||
|
private readonly ILongKeyCachedRepository<Dictionary<short, AccountWarehouseItemDto>> _cachedWarehouseItems;
|
||||||
|
|
||||||
|
private readonly Dictionary<long, Dictionary<short, (AccountWarehouseItemDto dto, bool remove)>> _itemChanges = new();
|
||||||
|
private readonly SemaphoreSlim _itemChangesSemaphore = new(1, 1);
|
||||||
|
private readonly ConcurrentDictionary<long, SemaphoreSlim> _warehouseLocks = new();
|
||||||
|
|
||||||
|
public AccountWarehouseManager(IAccountWarehouseItemDao accountWarehouseItemDao, ILongKeyCachedRepository<Dictionary<short, AccountWarehouseItemDto>> cachedWarehouseItems)
|
||||||
|
{
|
||||||
|
_accountWarehouseItemDao = accountWarehouseItemDao;
|
||||||
|
_cachedWarehouseItems = cachedWarehouseItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<AccountWarehouseItemDto>> GetWarehouse(long accountId)
|
||||||
|
{
|
||||||
|
SemaphoreSlim accountLock = GetAccountLock(accountId);
|
||||||
|
await accountLock.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (await GetAccountWarehouse(accountId))?.Values;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
accountLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AccountWarehouseItemDto> GetWarehouseItem(long accountId, short slot)
|
||||||
|
{
|
||||||
|
SemaphoreSlim accountLock = GetAccountLock(accountId);
|
||||||
|
await accountLock.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (await GetAccountWarehouse(accountId))?.GetValueOrDefault(slot);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
accountLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AddWarehouseItemResult> AddWarehouseItem(AccountWarehouseItemDto warehouseItemDtoToAdd)
|
||||||
|
{
|
||||||
|
long accountId = warehouseItemDtoToAdd.AccountId;
|
||||||
|
|
||||||
|
if (warehouseItemDtoToAdd.ItemInstance.Amount < 1 || 999 < warehouseItemDtoToAdd.ItemInstance.Amount)
|
||||||
|
{
|
||||||
|
return new AddWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
SemaphoreSlim accountLock = GetAccountLock(accountId);
|
||||||
|
await accountLock.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Dictionary<short, AccountWarehouseItemDto> familyWarehouse = await GetAccountWarehouse(accountId);
|
||||||
|
if (familyWarehouse == null)
|
||||||
|
{
|
||||||
|
return new AddWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountWarehouseItemDto alreadyExistentItem = familyWarehouse.GetValueOrDefault(warehouseItemDtoToAdd.Slot);
|
||||||
|
|
||||||
|
if (alreadyExistentItem == null)
|
||||||
|
{
|
||||||
|
familyWarehouse[warehouseItemDtoToAdd.Slot] = warehouseItemDtoToAdd;
|
||||||
|
await SetItemChangeWithLock(warehouseItemDtoToAdd, false);
|
||||||
|
return new AddWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
UpdatedItem = warehouseItemDtoToAdd
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (warehouseItemDtoToAdd.ItemInstance.ItemVNum != alreadyExistentItem.ItemInstance.ItemVNum)
|
||||||
|
{
|
||||||
|
return new AddWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (warehouseItemDtoToAdd.ItemInstance.Type != ItemInstanceType.NORMAL_ITEM || alreadyExistentItem.ItemInstance.Type != ItemInstanceType.NORMAL_ITEM
|
||||||
|
|| warehouseItemDtoToAdd.ItemInstance.Amount + alreadyExistentItem.ItemInstance.Amount > 999)
|
||||||
|
{
|
||||||
|
return new AddWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
alreadyExistentItem.ItemInstance.Amount += warehouseItemDtoToAdd.ItemInstance.Amount;
|
||||||
|
|
||||||
|
await SetItemChangeWithLock(alreadyExistentItem, false);
|
||||||
|
return new AddWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
UpdatedItem = alreadyExistentItem
|
||||||
|
};
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
accountLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WithdrawWarehouseItemResult> WithdrawWarehouseItem(AccountWarehouseItemDto warehouseItemDtoToWithdraw, int amount)
|
||||||
|
{
|
||||||
|
long accountId = warehouseItemDtoToWithdraw.AccountId;
|
||||||
|
|
||||||
|
if (amount < 1 || 999 < amount)
|
||||||
|
{
|
||||||
|
return new WithdrawWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
SemaphoreSlim accountLock = GetAccountLock(accountId);
|
||||||
|
await accountLock.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Dictionary<short, AccountWarehouseItemDto> familyWarehouse = await GetAccountWarehouse(accountId);
|
||||||
|
|
||||||
|
if (familyWarehouse == null)
|
||||||
|
{
|
||||||
|
return new WithdrawWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountWarehouseItemDto alreadyExistentItem = familyWarehouse.GetValueOrDefault(warehouseItemDtoToWithdraw.Slot);
|
||||||
|
if (alreadyExistentItem == null || alreadyExistentItem.ItemInstance.Amount < amount)
|
||||||
|
{
|
||||||
|
return new WithdrawWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
alreadyExistentItem.ItemInstance.Amount -= amount;
|
||||||
|
|
||||||
|
bool toRemove = alreadyExistentItem.ItemInstance.Amount == 0;
|
||||||
|
if (toRemove)
|
||||||
|
{
|
||||||
|
familyWarehouse.Remove(warehouseItemDtoToWithdraw.Slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
await SetItemChangeWithLock(alreadyExistentItem, toRemove);
|
||||||
|
|
||||||
|
ItemInstanceDTO newItemInstance = alreadyExistentItem.ItemInstance.Adapt<ItemInstanceDTO>();
|
||||||
|
newItemInstance.Amount = amount;
|
||||||
|
|
||||||
|
return new WithdrawWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
UpdatedItem = alreadyExistentItem.ItemInstance.Amount == 0 ? null : alreadyExistentItem,
|
||||||
|
WithdrawnItem = newItemInstance
|
||||||
|
};
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
accountLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MoveWarehouseItemResult> MoveWarehouseItem(AccountWarehouseItemDto warehouseItemDtoToMove, int amount, short newSlot)
|
||||||
|
{
|
||||||
|
long accountId = warehouseItemDtoToMove.AccountId;
|
||||||
|
|
||||||
|
if (amount < 1 || 999 < amount)
|
||||||
|
{
|
||||||
|
return new MoveWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
SemaphoreSlim accountLock = GetAccountLock(accountId);
|
||||||
|
await accountLock.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Dictionary<short, AccountWarehouseItemDto> familyWarehouse = await GetAccountWarehouse(accountId);
|
||||||
|
if (familyWarehouse == null)
|
||||||
|
{
|
||||||
|
return new MoveWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountWarehouseItemDto toMoveItem = familyWarehouse.GetValueOrDefault(warehouseItemDtoToMove.Slot);
|
||||||
|
AccountWarehouseItemDto toMergeItem = familyWarehouse.GetValueOrDefault(newSlot);
|
||||||
|
if (toMoveItem == null || toMoveItem.ItemInstance.Amount < amount)
|
||||||
|
{
|
||||||
|
return new MoveWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toMergeItem == null)
|
||||||
|
{
|
||||||
|
if (amount == toMoveItem.ItemInstance.Amount)
|
||||||
|
{
|
||||||
|
familyWarehouse.Remove(toMoveItem.Slot);
|
||||||
|
await SetItemChangeWithLock(new AccountWarehouseItemDto
|
||||||
|
{
|
||||||
|
AccountId = accountId,
|
||||||
|
Slot = toMoveItem.Slot
|
||||||
|
}, true);
|
||||||
|
toMoveItem.Slot = newSlot;
|
||||||
|
familyWarehouse[toMoveItem.Slot] = toMoveItem;
|
||||||
|
await SetItemChangeWithLock(toMoveItem, false);
|
||||||
|
return new MoveWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
OldItem = null,
|
||||||
|
NewItem = toMoveItem
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toMoveItem.ItemInstance.Amount -= amount;
|
||||||
|
await SetItemChangeWithLock(toMoveItem, false);
|
||||||
|
var newItem = new AccountWarehouseItemDto
|
||||||
|
{
|
||||||
|
AccountId = accountId,
|
||||||
|
ItemInstance = toMoveItem.ItemInstance.Adapt<ItemInstanceDTO>(),
|
||||||
|
Slot = newSlot
|
||||||
|
};
|
||||||
|
newItem.ItemInstance.Amount = amount;
|
||||||
|
familyWarehouse[newItem.Slot] = newItem;
|
||||||
|
await SetItemChangeWithLock(newItem, false);
|
||||||
|
return new MoveWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
OldItem = toMoveItem,
|
||||||
|
NewItem = newItem
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toMoveItem.ItemInstance.ItemVNum != toMergeItem.ItemInstance.ItemVNum)
|
||||||
|
{
|
||||||
|
toMergeItem.Slot = toMoveItem.Slot;
|
||||||
|
toMoveItem.Slot = newSlot;
|
||||||
|
familyWarehouse[toMoveItem.Slot] = toMoveItem;
|
||||||
|
await SetItemChangeWithLock(toMoveItem, false);
|
||||||
|
familyWarehouse[toMergeItem.Slot] = toMergeItem;
|
||||||
|
await SetItemChangeWithLock(toMergeItem, false);
|
||||||
|
return new MoveWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
OldItem = toMergeItem,
|
||||||
|
NewItem = toMoveItem
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toMoveItem.ItemInstance.Type != ItemInstanceType.NORMAL_ITEM || toMergeItem.ItemInstance.Type != ItemInstanceType.NORMAL_ITEM || amount + toMergeItem.ItemInstance.Amount > 999)
|
||||||
|
{
|
||||||
|
return new MoveWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toMoveItem.ItemInstance.Amount -= amount;
|
||||||
|
toMergeItem.ItemInstance.Amount += amount;
|
||||||
|
|
||||||
|
bool toRemove = toMoveItem.ItemInstance.Amount == 0;
|
||||||
|
if (toRemove)
|
||||||
|
{
|
||||||
|
familyWarehouse.Remove(toMoveItem.Slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
await SetItemChangeWithLock(toMoveItem, toRemove);
|
||||||
|
await SetItemChangeWithLock(toMergeItem, false);
|
||||||
|
|
||||||
|
return new MoveWarehouseItemResult
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
OldItem = toRemove ? null : toMoveItem,
|
||||||
|
NewItem = toMergeItem
|
||||||
|
};
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
accountLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task FlushWarehouseSaves()
|
||||||
|
{
|
||||||
|
if (_itemChanges.Count < 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _itemChangesSemaphore.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<(AccountWarehouseItemDto dto, bool remove)> unsavedChanges = new();
|
||||||
|
|
||||||
|
var globalWatch = Stopwatch.StartNew();
|
||||||
|
foreach ((long accountId, Dictionary<short, (AccountWarehouseItemDto dto, bool remove)> warehouseChanges) in _itemChanges)
|
||||||
|
{
|
||||||
|
List<AccountWarehouseItemDto> itemsToSave = new();
|
||||||
|
List<AccountWarehouseItemDto> itemsToRemove = new();
|
||||||
|
|
||||||
|
foreach ((short _, (AccountWarehouseItemDto dto, bool remove)) in warehouseChanges)
|
||||||
|
{
|
||||||
|
(remove ? itemsToRemove : itemsToSave).Add(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemsToSave.Count > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int countSavedItems = await _accountWarehouseItemDao.SaveAsync(itemsToSave);
|
||||||
|
Log.Warn($"[ACCOUNT_WAREHOUSE_MANAGER][FLUSH_SAVES][ACCOUNT_ID: {accountId.ToString()}] Saved {countSavedItems.ToString()} warehouseItems");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(
|
||||||
|
$"[ACCOUNT_WAREHOUSE_MANAGER][FLUSH_SAVES][ACCOUNT_ID: {accountId.ToString()}] Error while trying to save {itemsToSave.Count.ToString()} warehouseItems. Re-queueing. ",
|
||||||
|
e);
|
||||||
|
unsavedChanges.AddRange(itemsToSave.Select(x => (x, false)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemsToRemove.Count < 1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _accountWarehouseItemDao.DeleteAsync(itemsToRemove);
|
||||||
|
Log.Warn($"[ACCOUNT_WAREHOUSE_MANAGER][FLUSH_SAVES][ACCOUNT_ID: {accountId.ToString()}] Removed (at maximum) {itemsToRemove.Count.ToString()} warehouseItems");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(
|
||||||
|
$"[ACCOUNT_WAREHOUSE_MANAGER][FLUSH_SAVES][ACCOUNT_ID: {accountId.ToString()}] Error while trying to remove {itemsToRemove.Count.ToString()} warehouseItems. Re-queueing. ",
|
||||||
|
e);
|
||||||
|
unsavedChanges.AddRange(itemsToRemove.Select(x => (x, true)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
globalWatch.Stop();
|
||||||
|
Log.Debug($"[ACCOUNT_WAREHOUSE_MANAGER][FLUSH_SAVES] Saving all warehouses took {globalWatch.ElapsedMilliseconds.ToString()}ms");
|
||||||
|
|
||||||
|
_itemChanges.Clear();
|
||||||
|
|
||||||
|
foreach ((AccountWarehouseItemDto dto, bool remove) in unsavedChanges)
|
||||||
|
{
|
||||||
|
SetItemChange(dto, remove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_itemChangesSemaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await FlushWarehouseSaves();
|
||||||
|
await Task.Delay(Interval, stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SemaphoreSlim GetAccountLock(long accountId) => _warehouseLocks.GetOrAdd(accountId, new SemaphoreSlim(1, 1));
|
||||||
|
|
||||||
|
private async Task SetItemChangeWithLock(AccountWarehouseItemDto dto, bool remove)
|
||||||
|
{
|
||||||
|
await _itemChangesSemaphore.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SetItemChange(dto, remove);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_itemChangesSemaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Not to be used outside SemaphoreSlim
|
||||||
|
/// </summary>
|
||||||
|
private void SetItemChange(AccountWarehouseItemDto dto, bool remove)
|
||||||
|
{
|
||||||
|
_itemChanges.GetOrSetDefault(dto.AccountId, new Dictionary<short, (AccountWarehouseItemDto dto, bool remove)>())[dto.Slot] = (dto, remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Dictionary<short, AccountWarehouseItemDto>> GetAccountWarehouse(long accountId)
|
||||||
|
{
|
||||||
|
Dictionary<short, AccountWarehouseItemDto> cachedItems = _cachedWarehouseItems.Get(accountId);
|
||||||
|
if (cachedItems != null)
|
||||||
|
{
|
||||||
|
return cachedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedItems = (await _accountWarehouseItemDao.GetByAccountIdAsync(accountId))?.ToDictionary(x => x.Slot);
|
||||||
|
_cachedWarehouseItems.Set(accountId, cachedItems ?? new Dictionary<short, AccountWarehouseItemDto>(), LifeTime);
|
||||||
|
return cachedItems;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
srcs/DatabaseServer/Managers/AddWarehouseItemResult.cs
Normal file
11
srcs/DatabaseServer/Managers/AddWarehouseItemResult.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
using WingsAPI.Data.Account;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Managers
|
||||||
|
{
|
||||||
|
public class AddWarehouseItemResult
|
||||||
|
{
|
||||||
|
public bool Success { get; init; }
|
||||||
|
|
||||||
|
public AccountWarehouseItemDto UpdatedItem { get; init; }
|
||||||
|
}
|
||||||
|
}
|
||||||
317
srcs/DatabaseServer/Managers/CharacterManager.cs
Normal file
317
srcs/DatabaseServer/Managers/CharacterManager.cs
Normal file
|
|
@ -0,0 +1,317 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using PhoenixLib.Caching;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
using WingsAPI.Data.Character;
|
||||||
|
using WingsEmu.DTOs.Enums;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Managers
|
||||||
|
{
|
||||||
|
public class CharacterManager : BackgroundService, ICharacterManager
|
||||||
|
{
|
||||||
|
private static readonly TimeSpan Interval = TimeSpan.FromSeconds(Convert.ToUInt32(Environment.GetEnvironmentVariable(EnvironmentConsts.DbServerCharSaveIntervalSeconds) ?? "60"));
|
||||||
|
private static readonly TimeSpan LifeTime = TimeSpan.FromMinutes(Convert.ToUInt32(Environment.GetEnvironmentVariable(EnvironmentConsts.DbServerCharTtlMinutes) ?? "30"));
|
||||||
|
|
||||||
|
private readonly ILongKeyCachedRepository<CharacterDTO> _characterById;
|
||||||
|
|
||||||
|
private readonly ICharacterDAO _characterDao;
|
||||||
|
private readonly IKeyValueCache<long> _characterIdByKey;
|
||||||
|
|
||||||
|
private readonly HashSet<long> _characterIdsToSave = new();
|
||||||
|
private readonly SemaphoreSlim _createCharacterSemaphore = new(1, 1);
|
||||||
|
private readonly SemaphoreSlim _semaphoreSlim = new(1, 1);
|
||||||
|
|
||||||
|
public CharacterManager(ICharacterDAO characterDao, ILongKeyCachedRepository<CharacterDTO> characterById, IKeyValueCache<long> characterIdByKey)
|
||||||
|
{
|
||||||
|
_characterDao = characterDao;
|
||||||
|
_characterById = characterById;
|
||||||
|
_characterIdByKey = characterIdByKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<CharacterDTO>> GetCharactersByAccountId(long accountId) => await _characterDao.LoadByAccountAsync(accountId);
|
||||||
|
|
||||||
|
public async Task<CharacterDTO> GetCharacterBySlot(long accountId, byte slot)
|
||||||
|
{
|
||||||
|
long characterId = _characterIdByKey.Get(GetKeyAccountIdSlot(accountId, slot));
|
||||||
|
|
||||||
|
// check if the cache does have the value
|
||||||
|
if (characterId != default)
|
||||||
|
{
|
||||||
|
Log.Debug($"[CHARACTER_SAVE_SYSTEM][GetCharacterBySlot] CharacterId fetched from cache via AccountId and Slot. AccountId: '{accountId.ToString()}' Slot: '{slot.ToString()}'");
|
||||||
|
return await GetCharacterById(characterId);
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterDTO characterDto = await _characterDao.LoadBySlotAsync(accountId, slot);
|
||||||
|
if (characterDto == null)
|
||||||
|
{
|
||||||
|
// shouldn't happen normally
|
||||||
|
Log.Warn(
|
||||||
|
$"[CHARACTER_SAVE_SYSTEM][GetCharacterBySlot] The given AccountId and Slot does not pertain to any CharacterDTO in the db. AccountId: '{accountId.ToString()}' Slot: '{slot.ToString()}'");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetCharacter(characterDto);
|
||||||
|
Log.Debug($"[CHARACTER_SAVE_SYSTEM][GetCharacterBySlot] fetched by AccountId and Slot. AccountId: '{accountId.ToString()}' Slot: '{slot.ToString()}'");
|
||||||
|
return characterDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CharacterDTO> GetCharacterById(long characterId)
|
||||||
|
{
|
||||||
|
CharacterDTO dto = _characterById.Get(characterId);
|
||||||
|
if (dto != null)
|
||||||
|
{
|
||||||
|
Log.Debug($"[CHARACTER_SAVE_SYSTEM][GetCharacter] CharacterDTO fetched from cache via CharacterId: '{characterId.ToString()}'");
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
dto = await _characterDao.GetByIdAsync(characterId);
|
||||||
|
|
||||||
|
if (dto == null)
|
||||||
|
{
|
||||||
|
Log.Warn($"[CHARACTER_SAVE_SYSTEM][GetCharacter] The given CharacterId does not pertain to any CharacterDTO in the db. CharacterId: '{characterId.ToString()}'");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetCharacter(dto);
|
||||||
|
Log.Debug($"[CHARACTER_SAVE_SYSTEM][GetCharacter] {characterId.ToString()} fetched from DB cause was not existing in cache");
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CharacterDTO> GetCharacterByName(string name)
|
||||||
|
{
|
||||||
|
long characterId = _characterIdByKey.Get(GetKey(name));
|
||||||
|
|
||||||
|
// check if the cache does have the value
|
||||||
|
if (characterId != default)
|
||||||
|
{
|
||||||
|
Log.Debug($"[CHARACTER_SAVE_SYSTEM][GetCharacter] CharacterId fetched from cache through CharacterName. CharacterId: '{characterId.ToString()}' CharacterName: '{name}'");
|
||||||
|
return await GetCharacterById(characterId);
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterDTO characterDto = await _characterDao.LoadByNameAsync(name);
|
||||||
|
if (characterDto == null)
|
||||||
|
{
|
||||||
|
// shouldn't happen normally
|
||||||
|
Log.Warn($"[CHARACTER_SAVE_SYSTEM][GetCharacter] The given CharacterName does not exist in the db. CharacterName: '{name}'");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetCharacter(characterDto);
|
||||||
|
Log.Debug(
|
||||||
|
$"[CHARACTER_SAVE_SYSTEM][GetCharacter] CharacterDTO fetched from DB through CharacterName cause it was not existing in cache. CharacterId: '{characterId.ToString()}' CharacterName: '{name}'");
|
||||||
|
return characterDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CharacterDTO> CreateCharacter(CharacterDTO characterDto, bool ignoreSlotCheck)
|
||||||
|
{
|
||||||
|
await _createCharacterSemaphore.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (await _characterDao.LoadByNameAsync(characterDto.Name) != null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterDTO character;
|
||||||
|
if (ignoreSlotCheck == false)
|
||||||
|
{
|
||||||
|
character = await GetCharacterBySlot(characterDto.AccountId, characterDto.Slot);
|
||||||
|
if (character != null)
|
||||||
|
{
|
||||||
|
Log.Warn("[CHARACTER_SAVE_SYSTEM][CreateCharacter] Found a character already in the desired slot." +
|
||||||
|
$"AccountId: '{character.AccountId.ToString()}' CharacterId: '{character.Id.ToString()}' Slot: '{character.Slot.ToString()}'");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
character = await _characterDao.SaveAsync(characterDto);
|
||||||
|
SetCharacter(character);
|
||||||
|
Log.Debug(
|
||||||
|
$"[CHARACTER_SAVE_SYSTEM][CreateCharacter] Created a new character. AccountId: '{character.AccountId.ToString()}' CharacterId: '{character.Id.ToString()}' Slot: '{character.Slot.ToString()}'");
|
||||||
|
return character;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_createCharacterSemaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddCharacterToSavingQueue(CharacterDTO characterDto)
|
||||||
|
{
|
||||||
|
SetCharacter(characterDto);
|
||||||
|
|
||||||
|
await _semaphoreSlim.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_characterIdsToSave.Add(characterDto.Id);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semaphoreSlim.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Debug($"[CHARACTER_SAVE_SYSTEM][AddCharacterToSavingQueue] Flagged CharacterId for saving. CharacterId: '{characterDto.Id.ToString()}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddCharactersToSavingQueue(IEnumerable<CharacterDTO> characterDtos)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
await _semaphoreSlim.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (CharacterDTO characterDto in characterDtos)
|
||||||
|
{
|
||||||
|
SetCharacter(characterDto);
|
||||||
|
_characterIdsToSave.Add(characterDto.Id);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semaphoreSlim.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Debug($"[CHARACTER_SAVE_SYSTEM][AddCharactersToSavingQueue] Flagged {i.ToString()} characterIds for saving.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> DeleteCharacter(CharacterDTO characterDto)
|
||||||
|
{
|
||||||
|
DeleteResult result = await _characterDao.DeleteByPrimaryKey(characterDto.AccountId, characterDto.Slot);
|
||||||
|
if (result != DeleteResult.Deleted)
|
||||||
|
{
|
||||||
|
Log.Warn(
|
||||||
|
$"[CHARACTER_SAVE_SYSTEM][DeleteCharacter] Tried to delete a character that doesn't exist. AccountId: '{characterDto.AccountId.ToString()}' Slot: '{characterDto.Slot.ToString()}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveCharacter(characterDto);
|
||||||
|
Log.Debug(
|
||||||
|
$"[CHARACTER_SAVE_SYSTEM][DeleteCharacter] Deleted a character. AccountId: '{characterDto.AccountId.ToString()}' CharacterId: '{characterDto.Id.ToString()}' Slot: '{characterDto.Slot.ToString()}'");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> FlushCharacterSaves()
|
||||||
|
{
|
||||||
|
if (_characterIdsToSave.Count < 1)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<long> unsavedCharacterIds = new();
|
||||||
|
long[] characterIds;
|
||||||
|
|
||||||
|
await _semaphoreSlim.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
characterIds = new long[_characterIdsToSave.Count];
|
||||||
|
_characterIdsToSave.CopyTo(characterIds);
|
||||||
|
_characterIdsToSave.Clear();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semaphoreSlim.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
var tmp = Stopwatch.StartNew();
|
||||||
|
var individualWatch = new Stopwatch();
|
||||||
|
foreach (long characterId in characterIds)
|
||||||
|
{
|
||||||
|
CharacterDTO toSave = _characterById.Get(characterId);
|
||||||
|
if (toSave == null)
|
||||||
|
{
|
||||||
|
Log.Error($"[CHARACTER_SAVE_SYSTEM] {characterId.ToString()} could not be retrieved from cache", new DataException($"Desynchronised data for character {characterId.ToString()}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
individualWatch.Restart();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CharacterDTO savedCharacter = await _characterDao.SaveAsync(toSave);
|
||||||
|
count++;
|
||||||
|
Log.Warn($"[CHARACTER_SAVE_SYSTEM] Saved a character successfully in {individualWatch.ElapsedMilliseconds.ToString()}ms. " +
|
||||||
|
$"CharacterName: '{savedCharacter.Name}' CharacterId: '{savedCharacter.Id.ToString()}' AccountId: '{savedCharacter.AccountId.ToString()}'");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
unsavedCharacterIds.Add(characterId);
|
||||||
|
Log.Error($"[CHARACTER_SAVE_SYSTEM] Failed to save a character in {individualWatch.ElapsedMilliseconds.ToString()}ms. Re-queueing the save. " +
|
||||||
|
$"CharacterName: '{toSave.Name}' CharacterId: '{toSave.Id.ToString()}' AccountId: '{toSave.AccountId.ToString()}'", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
individualWatch.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp.Stop();
|
||||||
|
Log.Debug($"[CHARACTER_SAVE_SYSTEM] Saving of saves took in total {tmp.ElapsedMilliseconds.ToString()}ms");
|
||||||
|
|
||||||
|
if (unsavedCharacterIds.Count <= 0)
|
||||||
|
{
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _semaphoreSlim.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (long characterId in unsavedCharacterIds)
|
||||||
|
{
|
||||||
|
_characterIdsToSave.Add(characterId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semaphoreSlim.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Warn($"[CHARACTER_SAVE_SYSTEM] Re-queued {unsavedCharacterIds.Count.ToString()} character saves");
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CharacterDTO> RemoveCachedCharacter(string requestCharacterName)
|
||||||
|
{
|
||||||
|
CharacterDTO characterDto = await GetCharacterByName(requestCharacterName);
|
||||||
|
if (characterDto == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _characterDao.SaveAsync(characterDto);
|
||||||
|
_characterById.Remove(characterDto.Id);
|
||||||
|
return characterDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await FlushCharacterSaves();
|
||||||
|
await Task.Delay(Interval, stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetKey(string name) => $"char:name:{name}";
|
||||||
|
|
||||||
|
private static string GetKeyAccountIdSlot(long accountId, byte slot) => $"char:account-id:{accountId.ToString()}:slot:{slot.ToString()}";
|
||||||
|
|
||||||
|
private void SetCharacter(CharacterDTO characterDto)
|
||||||
|
{
|
||||||
|
_characterById.Set(characterDto.Id, characterDto, LifeTime);
|
||||||
|
_characterIdByKey.Set(GetKey(characterDto.Name), characterDto.Id, LifeTime);
|
||||||
|
_characterIdByKey.Set(GetKeyAccountIdSlot(characterDto.AccountId, characterDto.Slot), characterDto.Id, LifeTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveCharacter(CharacterDTO characterDto)
|
||||||
|
{
|
||||||
|
_characterById.Remove(characterDto.Id);
|
||||||
|
_characterIdByKey.Remove(GetKey(characterDto.Name));
|
||||||
|
_characterIdByKey.Remove(GetKeyAccountIdSlot(characterDto.AccountId, characterDto.Slot));
|
||||||
|
_characterIdsToSave.Remove(characterDto.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
srcs/DatabaseServer/Managers/IAccountWarehouseManager.cs
Normal file
16
srcs/DatabaseServer/Managers/IAccountWarehouseManager.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using WingsAPI.Data.Account;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Managers
|
||||||
|
{
|
||||||
|
public interface IAccountWarehouseManager
|
||||||
|
{
|
||||||
|
public Task<IEnumerable<AccountWarehouseItemDto>> GetWarehouse(long accountId);
|
||||||
|
public Task<AccountWarehouseItemDto> GetWarehouseItem(long accountId, short slot);
|
||||||
|
public Task<AddWarehouseItemResult> AddWarehouseItem(AccountWarehouseItemDto warehouseItemDtoToAdd);
|
||||||
|
public Task<WithdrawWarehouseItemResult> WithdrawWarehouseItem(AccountWarehouseItemDto warehouseItemDtoToWithdraw, int amount);
|
||||||
|
public Task<MoveWarehouseItemResult> MoveWarehouseItem(AccountWarehouseItemDto warehouseItemDtoToMove, int amount, short newSlot);
|
||||||
|
public Task FlushWarehouseSaves();
|
||||||
|
}
|
||||||
|
}
|
||||||
20
srcs/DatabaseServer/Managers/ICharacterManager.cs
Normal file
20
srcs/DatabaseServer/Managers/ICharacterManager.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using WingsAPI.Data.Character;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Managers
|
||||||
|
{
|
||||||
|
public interface ICharacterManager
|
||||||
|
{
|
||||||
|
public Task<IEnumerable<CharacterDTO>> GetCharactersByAccountId(long accountId);
|
||||||
|
public Task<CharacterDTO> GetCharacterBySlot(long accountId, byte slot);
|
||||||
|
public Task<CharacterDTO> GetCharacterById(long characterId);
|
||||||
|
public Task<CharacterDTO> GetCharacterByName(string name);
|
||||||
|
public Task<CharacterDTO> CreateCharacter(CharacterDTO characterDto, bool ignoreSlotCheck);
|
||||||
|
public Task AddCharacterToSavingQueue(CharacterDTO characterDto);
|
||||||
|
public Task AddCharactersToSavingQueue(IEnumerable<CharacterDTO> characterDtos);
|
||||||
|
public Task<bool> DeleteCharacter(CharacterDTO characterDto);
|
||||||
|
public Task<int> FlushCharacterSaves();
|
||||||
|
public Task<CharacterDTO> RemoveCachedCharacter(string requestCharacterName);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
srcs/DatabaseServer/Managers/IRankingManager.cs
Normal file
42
srcs/DatabaseServer/Managers/IRankingManager.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using WingsAPI.Data.Character;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Managers
|
||||||
|
{
|
||||||
|
public interface IRankingManager
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a list of the top 30 characters in compliments
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IReadOnlyList<CharacterDTO>> GetTopCompliment();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a list of the top 30 characters in points
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IReadOnlyList<CharacterDTO>> GetTopPoints();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a list of the top 43 characters in reputation
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IReadOnlyList<CharacterDTO>> GetTopReputation();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to refresh the ranking, in case it fails it will return false
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<RefreshResponse> TryRefreshRanking();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RefreshResponse
|
||||||
|
{
|
||||||
|
public bool Success { get; init; }
|
||||||
|
|
||||||
|
public IReadOnlyList<CharacterDTO> TopCompliment { get; init; }
|
||||||
|
public IReadOnlyList<CharacterDTO> TopPoints { get; init; }
|
||||||
|
public IReadOnlyList<CharacterDTO> TopReputation { get; init; }
|
||||||
|
}
|
||||||
|
}
|
||||||
14
srcs/DatabaseServer/Managers/ITimeSpaceManager.cs
Normal file
14
srcs/DatabaseServer/Managers/ITimeSpaceManager.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using WingsAPI.Data.TimeSpace;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Managers
|
||||||
|
{
|
||||||
|
public interface ITimeSpaceManager
|
||||||
|
{
|
||||||
|
Task<TimeSpaceRecordDto> GetRecordByTimeSpaceId(long tsId);
|
||||||
|
Task FlushTimeSpaceRecords();
|
||||||
|
Task Initialize();
|
||||||
|
void TryAddNewRecord(TimeSpaceRecordDto record);
|
||||||
|
Task<bool> IsNewRecord(long tsId, long points);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
srcs/DatabaseServer/Managers/MoveWarehouseItemResult.cs
Normal file
13
srcs/DatabaseServer/Managers/MoveWarehouseItemResult.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
using WingsAPI.Data.Account;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Managers
|
||||||
|
{
|
||||||
|
public class MoveWarehouseItemResult
|
||||||
|
{
|
||||||
|
public bool Success { get; init; }
|
||||||
|
|
||||||
|
public AccountWarehouseItemDto OldItem { get; init; }
|
||||||
|
|
||||||
|
public AccountWarehouseItemDto NewItem { get; init; }
|
||||||
|
}
|
||||||
|
}
|
||||||
126
srcs/DatabaseServer/Managers/RankingManager.cs
Normal file
126
srcs/DatabaseServer/Managers/RankingManager.cs
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
using WingsAPI.Data.Character;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Managers
|
||||||
|
{
|
||||||
|
public class RankingManager : IRankingManager
|
||||||
|
{
|
||||||
|
private readonly ICharacterDAO _characterDao;
|
||||||
|
|
||||||
|
private IReadOnlyList<CharacterDTO> _topCompliment;
|
||||||
|
private IReadOnlyList<CharacterDTO> _topPoints;
|
||||||
|
private IReadOnlyList<CharacterDTO> _topReputation;
|
||||||
|
|
||||||
|
public RankingManager(ICharacterDAO characterDao) => _characterDao = characterDao;
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<CharacterDTO>> GetTopCompliment()
|
||||||
|
{
|
||||||
|
if (_topCompliment != null)
|
||||||
|
{
|
||||||
|
return _topCompliment;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_topCompliment = await _characterDao.GetTopCompliment();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[RANKING_MANAGER][GET_TOP_COMPLIMENT] Unexpected error:", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _topCompliment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<CharacterDTO>> GetTopPoints()
|
||||||
|
{
|
||||||
|
if (_topPoints != null)
|
||||||
|
{
|
||||||
|
return _topPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_topPoints = await _characterDao.GetTopPoints();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[RANKING_MANAGER][GET_TOP_POINTS] Unexpected error:", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _topPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<CharacterDTO>> GetTopReputation()
|
||||||
|
{
|
||||||
|
if (_topReputation != null)
|
||||||
|
{
|
||||||
|
return _topReputation;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_topReputation = await _characterDao.GetTopReputation();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[RANKING_MANAGER][GET_TOP_REPUTATION] Unexpected error:", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _topReputation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<RefreshResponse> TryRefreshRanking()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_topCompliment = await _characterDao.GetTopCompliment();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[RANKING_MANAGER][TRY_REFRESH_RANKING] Unexpected error:", e);
|
||||||
|
return new RefreshResponse
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_topPoints = await _characterDao.GetTopPoints();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[RANKING_MANAGER][TRY_REFRESH_RANKING] Unexpected error:", e);
|
||||||
|
return new RefreshResponse
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_topReputation = await _characterDao.GetTopReputation();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[RANKING_MANAGER][TRY_REFRESH_RANKING] Unexpected error:", e);
|
||||||
|
return new RefreshResponse
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RefreshResponse
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
TopCompliment = _topCompliment,
|
||||||
|
TopPoints = _topPoints,
|
||||||
|
TopReputation = _topReputation
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
srcs/DatabaseServer/Managers/SaveRequest.cs
Normal file
10
srcs/DatabaseServer/Managers/SaveRequest.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Managers
|
||||||
|
{
|
||||||
|
internal record SaveRequest
|
||||||
|
{
|
||||||
|
public DateTime CreatedAt { get; init; }
|
||||||
|
public long CharacterId { get; init; }
|
||||||
|
}
|
||||||
|
}
|
||||||
119
srcs/DatabaseServer/Managers/TimeSpaceManager.cs
Normal file
119
srcs/DatabaseServer/Managers/TimeSpaceManager.cs
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using PhoenixLib.Caching;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
using WingsAPI.Data.TimeSpace;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Managers
|
||||||
|
{
|
||||||
|
public class TimeSpaceManager : BackgroundService, ITimeSpaceManager
|
||||||
|
{
|
||||||
|
private static readonly TimeSpan Interval = TimeSpan.FromMinutes(Convert.ToUInt32(Environment.GetEnvironmentVariable(EnvironmentConsts.TsServerSaveIntervalMinutes) ?? "5"));
|
||||||
|
private readonly ConcurrentQueue<TimeSpaceRecordDto> _queue = new();
|
||||||
|
private readonly ILongKeyCachedRepository<TimeSpaceRecordDto> _records;
|
||||||
|
|
||||||
|
private readonly ITimeSpaceRecordDao _timeSpaceRecordDao;
|
||||||
|
|
||||||
|
public TimeSpaceManager(ITimeSpaceRecordDao timeSpaceRecordDao, ILongKeyCachedRepository<TimeSpaceRecordDto> records)
|
||||||
|
{
|
||||||
|
_timeSpaceRecordDao = timeSpaceRecordDao;
|
||||||
|
_records = records;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TimeSpaceRecordDto> GetRecordByTimeSpaceId(long tsId)
|
||||||
|
{
|
||||||
|
TimeSpaceRecordDto cached = _records.Get(tsId);
|
||||||
|
if (cached != null)
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeSpaceRecordDto record = await _timeSpaceRecordDao.GetRecordById(tsId);
|
||||||
|
if (record == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_records.Set(tsId, record);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task FlushTimeSpaceRecords()
|
||||||
|
{
|
||||||
|
if (_queue.IsEmpty)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (_queue.TryDequeue(out TimeSpaceRecordDto record))
|
||||||
|
{
|
||||||
|
TimeSpaceRecordDto currentRecord = await GetRecordByTimeSpaceId(record.TimeSpaceId);
|
||||||
|
if (currentRecord == null)
|
||||||
|
{
|
||||||
|
_records.Set(record.TimeSpaceId, record);
|
||||||
|
await _timeSpaceRecordDao.SaveRecord(record);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentRecord.Record >= record.Record)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_records.Set(record.TimeSpaceId, record);
|
||||||
|
await _timeSpaceRecordDao.SaveRecord(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("FlushTimeSpaceRecords", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Initialize()
|
||||||
|
{
|
||||||
|
IEnumerable<TimeSpaceRecordDto> records = await _timeSpaceRecordDao.GetAllRecords();
|
||||||
|
int counter = 0;
|
||||||
|
foreach (TimeSpaceRecordDto recordDto in records)
|
||||||
|
{
|
||||||
|
counter++;
|
||||||
|
_records.Set(recordDto.TimeSpaceId, recordDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Info($"Initialized {counter} time-space records.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TryAddNewRecord(TimeSpaceRecordDto record)
|
||||||
|
{
|
||||||
|
_queue.Enqueue(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IsNewRecord(long tsId, long points)
|
||||||
|
{
|
||||||
|
TimeSpaceRecordDto currentRecord = await GetRecordByTimeSpaceId(tsId);
|
||||||
|
if (currentRecord == null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentRecord.Record < points;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
await Initialize();
|
||||||
|
Log.Info("[TIME_SPACE_MANAGER] Initialized!");
|
||||||
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await FlushTimeSpaceRecords();
|
||||||
|
await Task.Delay(Interval, stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
srcs/DatabaseServer/Managers/WithdrawWarehouseItemResult.cs
Normal file
14
srcs/DatabaseServer/Managers/WithdrawWarehouseItemResult.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
using WingsAPI.Data.Account;
|
||||||
|
using WingsEmu.DTOs.Items;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Managers
|
||||||
|
{
|
||||||
|
public class WithdrawWarehouseItemResult
|
||||||
|
{
|
||||||
|
public bool Success { get; init; }
|
||||||
|
|
||||||
|
public AccountWarehouseItemDto UpdatedItem { get; init; }
|
||||||
|
|
||||||
|
public ItemInstanceDTO WithdrawnItem { get; init; }
|
||||||
|
}
|
||||||
|
}
|
||||||
104
srcs/DatabaseServer/Program.cs
Normal file
104
srcs/DatabaseServer/Program.cs
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DatabaseServer.Managers;
|
||||||
|
using FamilyServer;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Npgsql;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
using PhoenixLib.ServiceBus.MQTT;
|
||||||
|
using Plugin.Database.DB;
|
||||||
|
using Plugin.Database.Extensions;
|
||||||
|
using Plugin.Database.Mapping;
|
||||||
|
using ProtoBuf.Grpc.Client;
|
||||||
|
|
||||||
|
namespace DatabaseServer
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
PrintHeader();
|
||||||
|
NonGameMappingRules.InitializeMapping();
|
||||||
|
GrpcClientFactory.AllowUnencryptedHttp2 = true;
|
||||||
|
using var stopService = new DockerGracefulStopService();
|
||||||
|
using IHost host = CreateHostBuilder(args).Build();
|
||||||
|
IDbContextFactory<GameContext> dbContextFactory = host.Services.GetRequiredService<IDbContextFactory<GameContext>>();
|
||||||
|
|
||||||
|
if (!await dbContextFactory.TryMigrateAsync())
|
||||||
|
{
|
||||||
|
throw new PostgresException("Couldn't migrate the database", "ERROR", "ERROR", "None");
|
||||||
|
}
|
||||||
|
|
||||||
|
await host.StartAsync();
|
||||||
|
IMessagingService messagingService = host.Services.GetService<IMessagingService>();
|
||||||
|
if (messagingService != null)
|
||||||
|
{
|
||||||
|
await messagingService.StartAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Info("Database Server started");
|
||||||
|
|
||||||
|
ICharacterManager characterManager = host.Services.GetRequiredService<ICharacterManager>();
|
||||||
|
IAccountWarehouseManager accountWarehouseManager = host.Services.GetRequiredService<IAccountWarehouseManager>();
|
||||||
|
ITimeSpaceManager timespaceManager = host.Services.GetRequiredService<ITimeSpaceManager>();
|
||||||
|
|
||||||
|
await host.WaitForShutdownAsync(stopService.CancellationToken);
|
||||||
|
if (messagingService != null)
|
||||||
|
{
|
||||||
|
await messagingService.DisposeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
await characterManager.FlushCharacterSaves();
|
||||||
|
await accountWarehouseManager.FlushWarehouseSaves();
|
||||||
|
await timespaceManager.FlushTimeSpaceRecords();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PrintHeader()
|
||||||
|
{
|
||||||
|
const string text = @"
|
||||||
|
██╗ ██╗██╗███╗ ██╗ ██████╗ ███████╗███████╗███╗ ███╗██╗ ██╗
|
||||||
|
██║ ██║██║████╗ ██║██╔════╝ ██╔════╝██╔════╝████╗ ████║██║ ██║
|
||||||
|
██║ █╗ ██║██║██╔██╗ ██║██║ ███╗███████╗█████╗ ██╔████╔██║██║ ██║
|
||||||
|
██║███╗██║██║██║╚██╗██║██║ ██║╚════██║██╔══╝ ██║╚██╔╝██║██║ ██║
|
||||||
|
╚███╔███╔╝██║██║ ╚████║╚██████╔╝███████║███████╗██║ ╚═╝ ██║╚██████╔╝
|
||||||
|
╚══╝╚══╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝
|
||||||
|
|
||||||
|
██████╗ ██████╗ ███████╗███████╗██████╗ ██╗ ██╗███████╗██████╗
|
||||||
|
██╔══██╗██╔══██╗ ██╔════╝██╔════╝██╔══██╗██║ ██║██╔════╝██╔══██╗
|
||||||
|
██║ ██║██████╔╝█████╗███████╗█████╗ ██████╔╝██║ ██║█████╗ ██████╔╝
|
||||||
|
██║ ██║██╔══██╗╚════╝╚════██║██╔══╝ ██╔══██╗╚██╗ ██╔╝██╔══╝ ██╔══██╗
|
||||||
|
██████╔╝██████╔╝ ███████║███████╗██║ ██║ ╚████╔╝ ███████╗██║ ██║
|
||||||
|
╚═════╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝
|
||||||
|
";
|
||||||
|
string separator = new('=', Console.WindowWidth);
|
||||||
|
string logo = text.Split('\n').Select(s => string.Format("{0," + (Console.WindowWidth / 2 + s.Length / 2) + "}\n", s))
|
||||||
|
.Aggregate("", (current, i) => current + i);
|
||||||
|
|
||||||
|
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||||
|
Console.WriteLine(separator + logo + $"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n" + separator);
|
||||||
|
Console.ForegroundColor = ConsoleColor.White;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional configuration is required to successfully run gRPC on macOS.
|
||||||
|
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
|
||||||
|
private static IHostBuilder CreateHostBuilder(string[] args)
|
||||||
|
{
|
||||||
|
IHostBuilder host = Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureWebHostDefaults(webBuilder =>
|
||||||
|
{
|
||||||
|
webBuilder.ConfigureKestrel(s =>
|
||||||
|
{
|
||||||
|
s.ListenAnyIP(short.Parse(Environment.GetEnvironmentVariable("DATABASE_SERVER_PORT") ?? "29999"), options => { options.Protocols = HttpProtocols.Http2; });
|
||||||
|
});
|
||||||
|
webBuilder.UseStartup<Startup>();
|
||||||
|
});
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
173
srcs/DatabaseServer/Services/AccountService.cs
Normal file
173
srcs/DatabaseServer/Services/AccountService.cs
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
using WingsAPI.Communication;
|
||||||
|
using WingsAPI.Communication.DbServer.AccountService;
|
||||||
|
using WingsAPI.Data.Account;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Services
|
||||||
|
{
|
||||||
|
public class AccountService : IAccountService
|
||||||
|
{
|
||||||
|
private readonly IAccountBanDao _accountBanDao;
|
||||||
|
private readonly IAccountDAO _accountDao;
|
||||||
|
private readonly IAccountPenaltyDao _accountPenaltyDao;
|
||||||
|
|
||||||
|
public AccountService(IAccountDAO accountDao, IAccountBanDao accountBanDao, IAccountPenaltyDao accountPenaltyDao)
|
||||||
|
{
|
||||||
|
_accountDao = accountDao;
|
||||||
|
_accountBanDao = accountBanDao;
|
||||||
|
_accountPenaltyDao = accountPenaltyDao;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AccountLoadResponse> LoadAccountByName(AccountLoadByNameRequest request)
|
||||||
|
{
|
||||||
|
AccountDTO dto = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dto = await _accountDao.GetByNameAsync(request.Name);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[ACCOUNT_SERVICE][LOAD_ACCOUNT_BY_NAME] Unexpected error: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AccountLoadResponse
|
||||||
|
{
|
||||||
|
ResponseType = dto == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
AccountDto = dto
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AccountLoadResponse> LoadAccountById(AccountLoadByIdRequest request)
|
||||||
|
{
|
||||||
|
AccountDTO dto = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dto = await _accountDao.GetByIdAsync(request.AccountId);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[ACCOUNT_SERVICE][LOAD_ACCOUNT_BY_ID] Unexpected error: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AccountLoadResponse
|
||||||
|
{
|
||||||
|
ResponseType = dto == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
AccountDto = dto
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AccountSaveResponse> SaveAccount(AccountSaveRequest request)
|
||||||
|
{
|
||||||
|
AccountDTO dto = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dto = await _accountDao.SaveAsync(request.AccountDto);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[ACCOUNT_SERVICE][SAVE_ACCOUNT] Unexpected error: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AccountSaveResponse
|
||||||
|
{
|
||||||
|
ResponseType = dto == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
AccountDto = dto
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AccountBanGetResponse> GetAccountBan(AccountBanGetRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AccountBanDto dto = await _accountBanDao.FindAccountBan(request.AccountId);
|
||||||
|
|
||||||
|
return new AccountBanGetResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.SUCCESS,
|
||||||
|
AccountBanDto = dto
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[ACCOUNT_SERVICE][GET_ACCOUNT_BAN] Unexpected error: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AccountBanGetResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.GENERIC_SERVER_ERROR
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AccountBanSaveResponse> SaveAccountBan(AccountBanSaveRequest request)
|
||||||
|
{
|
||||||
|
AccountBanDto dto = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dto = await _accountBanDao.SaveAsync(request.AccountBanDto);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[ACCOUNT_SERVICE][SAVE_ACCOUNT_BAN] Unexpected error: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AccountBanSaveResponse
|
||||||
|
{
|
||||||
|
ResponseType = dto == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
AccountBanDto = dto
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AccountPenaltyGetAllResponse> GetAccountPenalties(AccountPenaltyGetRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<AccountPenaltyDto> dtos = await _accountPenaltyDao.GetPenaltiesByAccountId(request.AccountId);
|
||||||
|
|
||||||
|
return new AccountPenaltyGetAllResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.SUCCESS,
|
||||||
|
AccountPenaltyDtos = dtos
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[ACCOUNT_SERVICE][GET_ACCOUNT_PENALTIES] Unexpected error: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AccountPenaltyGetAllResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.GENERIC_SERVER_ERROR
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AccountPenaltyMultiSaveResponse> SaveAccountPenalties(AccountPenaltyMultiSaveRequest request)
|
||||||
|
{
|
||||||
|
if (request.AccountPenaltyDtos == null)
|
||||||
|
{
|
||||||
|
return new AccountPenaltyMultiSaveResponse
|
||||||
|
{
|
||||||
|
ResponseType = RpcResponseType.SUCCESS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<AccountPenaltyDto> dtos = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dtos = await _accountPenaltyDao.SaveAsync(request.AccountPenaltyDtos);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[ACCOUNT_SERVICE][SAVE_ACCOUNT_PENALTIES] Unexpected error: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AccountPenaltyMultiSaveResponse
|
||||||
|
{
|
||||||
|
ResponseType = dtos == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
AccountPenaltyDtos = dtos
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
73
srcs/DatabaseServer/Services/AccountWarehouseService.cs
Normal file
73
srcs/DatabaseServer/Services/AccountWarehouseService.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DatabaseServer.Managers;
|
||||||
|
using WingsAPI.Communication;
|
||||||
|
using WingsAPI.Communication.DbServer.WarehouseService;
|
||||||
|
using WingsAPI.Data.Account;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Services
|
||||||
|
{
|
||||||
|
public class AccountWarehouseService : IAccountWarehouseService
|
||||||
|
{
|
||||||
|
private readonly IAccountWarehouseManager _accountWarehouseManager;
|
||||||
|
|
||||||
|
public AccountWarehouseService(IAccountWarehouseManager accountWarehouseManager) => _accountWarehouseManager = accountWarehouseManager;
|
||||||
|
|
||||||
|
public async ValueTask<AccountWarehouseGetItemsResponse> GetItems(AccountWarehouseGetItemsRequest request)
|
||||||
|
{
|
||||||
|
IEnumerable<AccountWarehouseItemDto> warehouseItemDtos = await _accountWarehouseManager.GetWarehouse(request.AccountId);
|
||||||
|
|
||||||
|
return new AccountWarehouseGetItemsResponse
|
||||||
|
{
|
||||||
|
ResponseType = warehouseItemDtos == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
Items = warehouseItemDtos
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<AccountWarehouseGetItemResponse> GetItem(AccountWarehouseGetItemRequest request)
|
||||||
|
{
|
||||||
|
AccountWarehouseItemDto warehouseItemDto = await _accountWarehouseManager.GetWarehouseItem(request.AccountId, request.Slot);
|
||||||
|
|
||||||
|
return new AccountWarehouseGetItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = warehouseItemDto == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
Item = warehouseItemDto
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<AccountWarehouseAddItemResponse> AddItem(AccountWarehouseAddItemRequest request)
|
||||||
|
{
|
||||||
|
AddWarehouseItemResult result = await _accountWarehouseManager.AddWarehouseItem(request.Item);
|
||||||
|
|
||||||
|
return new AccountWarehouseAddItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = result.Success ? RpcResponseType.SUCCESS : RpcResponseType.GENERIC_SERVER_ERROR,
|
||||||
|
Item = result.UpdatedItem
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<AccountWarehouseWithdrawItemResponse> WithdrawItem(AccountWarehouseWithdrawItemRequest request)
|
||||||
|
{
|
||||||
|
WithdrawWarehouseItemResult result = await _accountWarehouseManager.WithdrawWarehouseItem(request.ItemToWithdraw, request.Amount);
|
||||||
|
|
||||||
|
return new AccountWarehouseWithdrawItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = result.Success ? RpcResponseType.SUCCESS : RpcResponseType.GENERIC_SERVER_ERROR,
|
||||||
|
UpdatedItem = result.UpdatedItem,
|
||||||
|
WithdrawnItem = result.WithdrawnItem
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<AccountWarehouseMoveItemResponse> MoveItem(AccountWarehouseMoveItemRequest request)
|
||||||
|
{
|
||||||
|
MoveWarehouseItemResult result = await _accountWarehouseManager.MoveWarehouseItem(request.WarehouseItemDtoToMove, request.Amount, request.NewSlot);
|
||||||
|
|
||||||
|
return new AccountWarehouseMoveItemResponse
|
||||||
|
{
|
||||||
|
ResponseType = result.Success ? RpcResponseType.SUCCESS : RpcResponseType.GENERIC_SERVER_ERROR,
|
||||||
|
OldItem = result.OldItem,
|
||||||
|
NewItem = result.NewItem
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
171
srcs/DatabaseServer/Services/CharacterService.cs
Normal file
171
srcs/DatabaseServer/Services/CharacterService.cs
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DatabaseServer.Managers;
|
||||||
|
using WingsAPI.Communication;
|
||||||
|
using WingsAPI.Communication.DbServer.CharacterService;
|
||||||
|
using WingsAPI.Data.Character;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Services
|
||||||
|
{
|
||||||
|
public class CharacterService : ICharacterService
|
||||||
|
{
|
||||||
|
private readonly ICharacterManager _characterManager;
|
||||||
|
private readonly IRankingManager _rankingManager;
|
||||||
|
|
||||||
|
public CharacterService(ICharacterManager characterManager, IRankingManager rankingManager)
|
||||||
|
{
|
||||||
|
_characterManager = characterManager;
|
||||||
|
_rankingManager = rankingManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DbServerSaveCharactersResponse> SaveCharacters(DbServerSaveCharactersRequest request)
|
||||||
|
{
|
||||||
|
await _characterManager.AddCharactersToSavingQueue(request.Characters);
|
||||||
|
return new DbServerSaveCharactersResponse
|
||||||
|
{
|
||||||
|
RpcResponseType = RpcResponseType.SUCCESS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DbServerSaveCharacterResponse> SaveCharacter(DbServerSaveCharacterRequest request)
|
||||||
|
{
|
||||||
|
await _characterManager.AddCharacterToSavingQueue(request.Character);
|
||||||
|
return new DbServerSaveCharacterResponse
|
||||||
|
{
|
||||||
|
RpcResponseType = RpcResponseType.SUCCESS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DbServerSaveCharacterResponse> CreateCharacter(DbServerSaveCharacterRequest request)
|
||||||
|
{
|
||||||
|
CharacterDTO character = await _characterManager.CreateCharacter(request.Character, request.IgnoreSlotCheck);
|
||||||
|
|
||||||
|
|
||||||
|
return new DbServerSaveCharacterResponse
|
||||||
|
{
|
||||||
|
RpcResponseType = character == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
Character = character
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DbServerGetCharactersResponse> GetCharacters(DbServerGetCharactersRequest request)
|
||||||
|
{
|
||||||
|
IEnumerable<CharacterDTO> characters = await _characterManager.GetCharactersByAccountId(request.AccountId);
|
||||||
|
|
||||||
|
return new DbServerGetCharactersResponse
|
||||||
|
{
|
||||||
|
RpcResponseType = characters == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
Characters = characters
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DbServerGetCharacterResponse> GetCharacterBySlot(DbServerGetCharacterFromSlotRequest fromSlotRequest)
|
||||||
|
{
|
||||||
|
CharacterDTO character = await _characterManager.GetCharacterBySlot(fromSlotRequest.AccountId, fromSlotRequest.Slot);
|
||||||
|
|
||||||
|
return new DbServerGetCharacterResponse
|
||||||
|
{
|
||||||
|
RpcResponseType = character == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
CharacterDto = character
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DbServerGetCharacterResponse> GetCharacterById(DbServerGetCharacterByIdRequest request)
|
||||||
|
{
|
||||||
|
CharacterDTO character = await _characterManager.GetCharacterById(request.CharacterId);
|
||||||
|
|
||||||
|
return new DbServerGetCharacterResponse
|
||||||
|
{
|
||||||
|
RpcResponseType = character == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
CharacterDto = character
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DbServerGetCharacterResponse> GetCharacterByName(DbServerGetCharacterRequestByName request)
|
||||||
|
{
|
||||||
|
CharacterDTO character = await _characterManager.GetCharacterByName(request.CharacterName);
|
||||||
|
|
||||||
|
return new DbServerGetCharacterResponse
|
||||||
|
{
|
||||||
|
RpcResponseType = character == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
CharacterDto = character
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DbServerFlushCharacterSavesResponse> FlushCharacterSaves(DbServerFlushCharacterSavesRequest request)
|
||||||
|
{
|
||||||
|
await _characterManager.FlushCharacterSaves();
|
||||||
|
return new DbServerFlushCharacterSavesResponse
|
||||||
|
{
|
||||||
|
RpcResponseType = RpcResponseType.SUCCESS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DbServerDeleteCharacterResponse> DeleteCharacter(DbServerDeleteCharacterRequest request)
|
||||||
|
{
|
||||||
|
bool success = await _characterManager.DeleteCharacter(request.CharacterDto);
|
||||||
|
|
||||||
|
return new DbServerDeleteCharacterResponse
|
||||||
|
{
|
||||||
|
RpcResponseType = success ? RpcResponseType.SUCCESS : RpcResponseType.GENERIC_SERVER_ERROR
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DbServerGetCharacterResponse> ForceRemoveCharacterFromCache(DbServerGetCharacterRequestByName request)
|
||||||
|
{
|
||||||
|
CharacterDTO character = await _characterManager.RemoveCachedCharacter(request.CharacterName);
|
||||||
|
|
||||||
|
return new DbServerGetCharacterResponse
|
||||||
|
{
|
||||||
|
RpcResponseType = character == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
CharacterDto = character
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<CharacterGetTopResponse> GetTopCompliment(EmptyRpcRequest request)
|
||||||
|
{
|
||||||
|
IReadOnlyList<CharacterDTO> top = await _rankingManager.GetTopCompliment();
|
||||||
|
|
||||||
|
return new CharacterGetTopResponse
|
||||||
|
{
|
||||||
|
ResponseType = top == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
Top = top
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<CharacterGetTopResponse> GetTopPoints(EmptyRpcRequest request)
|
||||||
|
{
|
||||||
|
IReadOnlyList<CharacterDTO> top = await _rankingManager.GetTopPoints();
|
||||||
|
|
||||||
|
return new CharacterGetTopResponse
|
||||||
|
{
|
||||||
|
ResponseType = top == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
Top = top
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<CharacterGetTopResponse> GetTopReputation(EmptyRpcRequest request)
|
||||||
|
{
|
||||||
|
IReadOnlyList<CharacterDTO> top = await _rankingManager.GetTopReputation();
|
||||||
|
|
||||||
|
return new CharacterGetTopResponse
|
||||||
|
{
|
||||||
|
ResponseType = top == null ? RpcResponseType.GENERIC_SERVER_ERROR : RpcResponseType.SUCCESS,
|
||||||
|
Top = top
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<CharacterRefreshRankingResponse> RefreshRanking(EmptyRpcRequest request)
|
||||||
|
{
|
||||||
|
RefreshResponse response = await _rankingManager.TryRefreshRanking();
|
||||||
|
|
||||||
|
return new CharacterRefreshRankingResponse
|
||||||
|
{
|
||||||
|
ResponseType = response.Success ? RpcResponseType.SUCCESS : RpcResponseType.GENERIC_SERVER_ERROR,
|
||||||
|
TopCompliment = response.TopCompliment,
|
||||||
|
TopPoints = response.TopPoints,
|
||||||
|
TopReputation = response.TopReputation
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
srcs/DatabaseServer/Services/TimeSpaceService.cs
Normal file
31
srcs/DatabaseServer/Services/TimeSpaceService.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DatabaseServer.Managers;
|
||||||
|
using WingsAPI.Communication;
|
||||||
|
using WingsAPI.Communication.DbServer.TimeSpaceService;
|
||||||
|
|
||||||
|
namespace DatabaseServer.Services
|
||||||
|
{
|
||||||
|
public class TimeSpaceService : ITimeSpaceService
|
||||||
|
{
|
||||||
|
private readonly ITimeSpaceManager _timeSpaceManager;
|
||||||
|
|
||||||
|
public TimeSpaceService(ITimeSpaceManager timeSpaceManager) => _timeSpaceManager = timeSpaceManager;
|
||||||
|
|
||||||
|
public async ValueTask<TimeSpaceIsNewRecordResponse> IsNewRecord(TimeSpaceIsNewRecordRequest request) => new()
|
||||||
|
{
|
||||||
|
IsNewRecord = await _timeSpaceManager.IsNewRecord(request.TimeSpaceId, request.Record)
|
||||||
|
};
|
||||||
|
|
||||||
|
public async ValueTask<EmptyResponse> SetNewRecord(TimeSpaceNewRecordRequest request)
|
||||||
|
{
|
||||||
|
_timeSpaceManager.TryAddNewRecord(request.TimeSpaceRecordDto);
|
||||||
|
return new EmptyResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<TimeSpaceRecordResponse> GetTimeSpaceRecord(TimeSpaceRecordRequest request) =>
|
||||||
|
new TimeSpaceRecordResponse
|
||||||
|
{
|
||||||
|
TimeSpaceRecordDto = await _timeSpaceManager.GetRecordByTimeSpaceId(request.TimeSpaceId)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
85
srcs/DatabaseServer/Startup.cs
Normal file
85
srcs/DatabaseServer/Startup.cs
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
using DatabaseServer.Consumers;
|
||||||
|
using DatabaseServer.Managers;
|
||||||
|
using DatabaseServer.Services;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using PhoenixLib.Caching;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
using PhoenixLib.ServiceBus.Extensions;
|
||||||
|
using Plugin.Database;
|
||||||
|
using Plugin.Database.Extensions;
|
||||||
|
using ProtoBuf.Grpc.Server;
|
||||||
|
using WingsAPI.Communication.Services.Messages;
|
||||||
|
using WingsEmu.Communication.gRPC.Extensions;
|
||||||
|
using WingsEmu.Health.Extensions;
|
||||||
|
|
||||||
|
namespace DatabaseServer
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
|
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddMqttConfigurationFromEnv();
|
||||||
|
new DatabasePlugin().AddDependencies(services);
|
||||||
|
services.AddMaintenanceMode();
|
||||||
|
|
||||||
|
services.AddSingleton<CharacterService>();
|
||||||
|
services.AddSingleton<CharacterManager>();
|
||||||
|
services.AddHostedService(s => s.GetRequiredService<CharacterManager>());
|
||||||
|
services.AddSingleton<ICharacterManager>(s => s.GetRequiredService<CharacterManager>());
|
||||||
|
|
||||||
|
services.AddSingleton<AccountWarehouseService>();
|
||||||
|
services.AddSingleton<AccountWarehouseManager>();
|
||||||
|
services.AddHostedService(s => s.GetRequiredService<AccountWarehouseManager>());
|
||||||
|
services.AddSingleton<IAccountWarehouseManager>(s => s.GetRequiredService<AccountWarehouseManager>());
|
||||||
|
|
||||||
|
services.AddSingleton<TimeSpaceService>();
|
||||||
|
services.AddSingleton<TimeSpaceManager>();
|
||||||
|
services.AddHostedService(s => s.GetRequiredService<TimeSpaceManager>());
|
||||||
|
services.AddSingleton<ITimeSpaceManager>(s => s.GetRequiredService<TimeSpaceManager>());
|
||||||
|
|
||||||
|
services.AddSingleton<IRankingManager, RankingManager>();
|
||||||
|
|
||||||
|
services.AddPhoenixLogging();
|
||||||
|
services.AddSingleton<AccountService>();
|
||||||
|
|
||||||
|
services.TryAddSingleton(typeof(ILongKeyCachedRepository<>), typeof(InMemoryCacheRepository<>));
|
||||||
|
services.TryAddSingleton(typeof(IKeyValueCache<>), typeof(InMemoryKeyValueCache<>));
|
||||||
|
|
||||||
|
services.AddGrpcDbServerServiceClient();
|
||||||
|
services.AddCodeFirstGrpc(config =>
|
||||||
|
{
|
||||||
|
config.MaxReceiveMessageSize = null;
|
||||||
|
config.MaxSendMessageSize = null;
|
||||||
|
config.EnableDetailedErrors = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddMessageSubscriber<ServiceFlushAllMessage, ServiceFlushAllMessageConsumer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
if (env.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseZEfCoreExtensions();
|
||||||
|
app.UseEndpoints(endpoints =>
|
||||||
|
{
|
||||||
|
endpoints.MapGrpcService<CharacterService>();
|
||||||
|
endpoints.MapGrpcService<AccountWarehouseService>();
|
||||||
|
endpoints.MapGrpcService<TimeSpaceService>();
|
||||||
|
endpoints.MapGrpcService<AccountService>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
// WingsEmu
|
||||||
|
//
|
||||||
|
// Developed by NosWings Team
|
||||||
|
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages.Player;
|
||||||
|
using WingsEmu.Game._playerActionLogs;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Chat
|
||||||
|
{
|
||||||
|
public class LogChatMessageMessageFormatter : IDiscordLogFormatter<LogPlayerChatMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType { get; private set; }
|
||||||
|
|
||||||
|
public bool TryFormat(LogPlayerChatMessage message, out string formattedString)
|
||||||
|
{
|
||||||
|
LogType = message.ChatType switch
|
||||||
|
{
|
||||||
|
ChatType.General => LogType.CHAT_GENERAL,
|
||||||
|
ChatType.HeroChat => LogType.CHAT_GENERAL,
|
||||||
|
ChatType.SpeechBubble => LogType.CHAT_GENERAL,
|
||||||
|
|
||||||
|
ChatType.Whisper => LogType.CHAT_WHISPERS,
|
||||||
|
ChatType.FriendChat => LogType.CHAT_FRIENDS,
|
||||||
|
|
||||||
|
ChatType.Shout => LogType.CHAT_SPEAKERS,
|
||||||
|
|
||||||
|
ChatType.FamilyChat => LogType.CHAT_FAMILIES,
|
||||||
|
ChatType.GroupChat => LogType.CHAT_GROUPS
|
||||||
|
};
|
||||||
|
|
||||||
|
formattedString = $"[ChannelId: '{message.ChannelId.ToString()}']" +
|
||||||
|
$"[ChatType: '{message.ChatType.ToString()}']" +
|
||||||
|
$"[CharacterId: '{message.CharacterId.ToString()}'{(message.TargetCharacterId.HasValue ? $" -> TargetId: '{message.TargetCharacterId.Value.ToString()}'" : string.Empty)}]" +
|
||||||
|
$" {message.Message}";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Discord;
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages.Family;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Family
|
||||||
|
{
|
||||||
|
public class LogFamilyCreatedEmbedMessageFormatter : IDiscordEmbedLogFormatter<LogFamilyCreatedMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.PLAYERS_EVENTS_CHANNEL;
|
||||||
|
|
||||||
|
public bool TryFormat(LogFamilyCreatedMessage message, out List<EmbedBuilder> embeds)
|
||||||
|
{
|
||||||
|
embeds = new List<EmbedBuilder>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Author = new EmbedAuthorBuilder { IconUrl = "https://avatars0.githubusercontent.com/u/40839221?s=200" },
|
||||||
|
Title = "[NosWings] Family created",
|
||||||
|
Description = $"Family {message.FamilyName} has been created",
|
||||||
|
Color = Color.Orange,
|
||||||
|
Footer = new EmbedFooterBuilder().WithIconUrl("https://avatars0.githubusercontent.com/u/40839221?s=200").WithText("discord-notifier microservice"),
|
||||||
|
//ImageUrl = "https://avatars0.githubusercontent.com/u/40839221?s=200",
|
||||||
|
// ThumbnailUrl = "https://avatars0.githubusercontent.com/u/40839221?s=200",
|
||||||
|
Timestamp = DateTimeOffset.Now
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages.Family;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Family
|
||||||
|
{
|
||||||
|
public class LogFamilyCreatedMessageFormatter : IDiscordLogFormatter<LogFamilyCreatedMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.FAMILY_CREATED;
|
||||||
|
|
||||||
|
public bool TryFormat(LogFamilyCreatedMessage message, out string formattedString)
|
||||||
|
{
|
||||||
|
formattedString = $"{message.CreatedAt:yyyy-MM-dd HH:mm:ss} | CHANNEL {message.ChannelId} | HEAD: {message.CharacterName} | DEPUTIES: {string.Join(", ", message.DeputiesIds)} | "
|
||||||
|
+ $"FAM_ID: {message.FamilyId} | FAM_NAME: {message.FamilyName}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages.Family;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Family
|
||||||
|
{
|
||||||
|
public class LogFamilyDisbandedMessageFormatter : IDiscordLogFormatter<LogFamilyDisbandedMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.FAMILY_DISBANDED;
|
||||||
|
|
||||||
|
public bool TryFormat(LogFamilyDisbandedMessage message, out string formattedString)
|
||||||
|
{
|
||||||
|
formattedString = $"{message.CreatedAt:yyyy-MM-dd HH:mm:ss} | CHANNEL {message.ChannelId} | "
|
||||||
|
+ $"PLAYER: {message.CharacterName} | FAM_ID: {message.FamilyId}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages.Family;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Family
|
||||||
|
{
|
||||||
|
public class LogFamilyJoinedMessageFormatter : IDiscordLogFormatter<LogFamilyJoinedMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.FAMILY_JOINED;
|
||||||
|
|
||||||
|
public bool TryFormat(LogFamilyJoinedMessage message, out string formattedString)
|
||||||
|
{
|
||||||
|
formattedString = $"{message.CreatedAt:yyyy-MM-dd HH:mm:ss} | CHANNEL {message.ChannelId} | "
|
||||||
|
+ $"PLAYER: {message.CharacterName} | INVITER_ID: {message.InviterId} | FAM_ID: {message.FamilyId}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages.Family;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Family
|
||||||
|
{
|
||||||
|
public class LogFamilyKickedMessageFormatter : IDiscordLogFormatter<LogFamilyKickedMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.FAMILY_KICK;
|
||||||
|
|
||||||
|
public bool TryFormat(LogFamilyKickedMessage message, out string formattedString)
|
||||||
|
{
|
||||||
|
formattedString = $"{message.CreatedAt:yyyy-MM-dd HH:mm:ss} | CHANNEL {message.ChannelId} | "
|
||||||
|
+ $"PLAYER: {message.CharacterName} | FAM_ID: {message.FamilyId} | KICKED_NAME: {message.KickedMemberName} | KICKED_ID: {message.KickedMemberId}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages.Family;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Family
|
||||||
|
{
|
||||||
|
public class LogFamilyLeftMessageFormatter : IDiscordLogFormatter<LogFamilyLeftMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.FAMILY_LEFT;
|
||||||
|
|
||||||
|
public bool TryFormat(LogFamilyLeftMessage message, out string formattedString)
|
||||||
|
{
|
||||||
|
formattedString = $"{message.CreatedAt:yyyy-MM-dd HH:mm:ss} | CHANNEL {message.ChannelId} | "
|
||||||
|
+ $"PLAYER: {message.CharacterName} | FAM_ID: {message.FamilyId}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages.Family;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Family
|
||||||
|
{
|
||||||
|
public class LogFamilyMessageMessageFormatter : IDiscordLogFormatter<LogFamilyMessageMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.FAMILY_MESSAGES;
|
||||||
|
|
||||||
|
public bool TryFormat(LogFamilyMessageMessage message, out string formattedString)
|
||||||
|
{
|
||||||
|
formattedString = $"[{message.CreatedAt:yyyy-MM-dd HH:mm:ss}] [CHANNEL {message.ChannelId}] [FAM_ID: {message.FamilyId}]\n"
|
||||||
|
+ $"**Player**: {message.CharacterName}\n"
|
||||||
|
+ $"**MessageType**: {message.FamilyMessageType}\n"
|
||||||
|
+ $"**Message**: {message.Message}\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
// WingsEmu
|
||||||
|
//
|
||||||
|
// Developed by NosWings Team
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Discord;
|
||||||
|
using DiscordNotifier.Discord;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using WingsAPI.Communication.InstantBattle;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.GameEvents
|
||||||
|
{
|
||||||
|
public class LogInstantBattleStartDiscordConsumer : IMessageConsumer<InstantBattleStartMessage>
|
||||||
|
{
|
||||||
|
private readonly IDiscordWebhookLogsService _discordWebhook;
|
||||||
|
|
||||||
|
public LogInstantBattleStartDiscordConsumer(IDiscordWebhookLogsService discordWebhook) => _discordWebhook = discordWebhook;
|
||||||
|
|
||||||
|
public async Task HandleAsync(InstantBattleStartMessage notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
if (notification.HasNoDelay)
|
||||||
|
{
|
||||||
|
EmbedFooterBuilder embedFooterBuilder = new EmbedFooterBuilder().WithIconUrl(StaticHardcodedCode.AvatarUrl).WithText("Instant Combat");
|
||||||
|
var embedAuthorBuilder = new EmbedAuthorBuilder { IconUrl = StaticHardcodedCode.AvatarUrl };
|
||||||
|
var embedBuilders = new List<EmbedBuilder>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Author = embedAuthorBuilder,
|
||||||
|
Title = "[INSTANT-COMBAT] An instant combat has started!",
|
||||||
|
Description = "May fate be in your favor",
|
||||||
|
Color = Color.Orange,
|
||||||
|
Footer = embedFooterBuilder,
|
||||||
|
// todo ThumbnailUrl = $"https://friends111.nostale.club/list/ip/iconId here.png",
|
||||||
|
Timestamp = DateTimeOffset.UtcNow
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
await _discordWebhook.PublishLogsEmbedded(LogType.PLAYERS_EVENTS_CHANNEL, embedBuilders);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmbedFooterBuilder embedFooterBuilder = new EmbedFooterBuilder().WithIconUrl(StaticHardcodedCode.AvatarUrl).WithText("Instant Combat");
|
||||||
|
var embedAuthorBuilder = new EmbedAuthorBuilder { IconUrl = StaticHardcodedCode.AvatarUrl };
|
||||||
|
var embedBuilders = new List<EmbedBuilder>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Author = embedAuthorBuilder,
|
||||||
|
Title = "[INSTANT-COMBAT] An instant combat will start in 5 minutes!",
|
||||||
|
Description = "Ready to fight against waves of monsters...?",
|
||||||
|
Color = Color.Orange,
|
||||||
|
Footer = embedFooterBuilder,
|
||||||
|
// todo ThumbnailUrl = $"https://friends111.nostale.club/list/ip/iconId here.png",
|
||||||
|
Timestamp = DateTimeOffset.UtcNow
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
await _discordWebhook.PublishLogsEmbedded(LogType.PLAYERS_EVENTS_CHANNEL, embedBuilders);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages.Upgrade;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Item
|
||||||
|
{
|
||||||
|
public class LogItemGambledMessageFormatter : IDiscordLogFormatter<LogItemGambledMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.ITEM_GAMBLED;
|
||||||
|
|
||||||
|
public bool TryFormat(LogItemGambledMessage message, out string formattedString)
|
||||||
|
{
|
||||||
|
formattedString = $"{message.CreatedAt:yyyy-MM-dd HH:mm:ss} | CHANNEL {message.ChannelId} | PLAYER: {message.CharacterName} | ITEM: {message.ItemVnum} | MODE: {message.Mode} | "
|
||||||
|
+ $"PROTECTION: {message.Protection}{(message.Amulet != null ? " | AMULET: " + message.Amulet : "")} | SUCCEED: {message.Succeed} | RARITY: {message.OriginalRarity}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages.Upgrade;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Item
|
||||||
|
{
|
||||||
|
public class LogItemUpgradedMessageFormatter : IDiscordLogFormatter<LogItemUpgradedMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.ITEM_UPGRADED;
|
||||||
|
|
||||||
|
public bool TryFormat(LogItemUpgradedMessage message, out string formattedString)
|
||||||
|
{
|
||||||
|
formattedString = $"{message.CreatedAt:yyyy-MM-dd HH:mm:ss} | CHANNEL {message.ChannelId} | PLAYER: {message.CharacterName} | ITEM: {message.Item.ItemVNum} | MODE: {message.Mode} | "
|
||||||
|
+ $"PROTECTION: {message.Protection} | HAS_AMULET: {message.HasAmulet} | ORIGINAL_UPGRADE: {message.OriginalUpgrade} | RESULT: {message.Result}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
// WingsEmu
|
||||||
|
//
|
||||||
|
// Developed by NosWings Team
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Discord.Webhook;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using WingsAPI.Communication.Services.Messages;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Maintenance
|
||||||
|
{
|
||||||
|
public class ServiceDownMessageConsumer : IMessageConsumer<ServiceDownMessage>
|
||||||
|
{
|
||||||
|
private static readonly string _webhookUrl = Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_HEALTHCHECK_URL");
|
||||||
|
|
||||||
|
public async Task HandleAsync(ServiceDownMessage notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_webhookUrl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var client = new DiscordWebhookClient(_webhookUrl);
|
||||||
|
await client.SendMessageAsync($"```\n[{notification.LastUpdate:yyyy-MM-dd HH:mm:ss}][HEALTHCHECK] {notification.ServiceName} IS OFFLINE!```");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Discord;
|
||||||
|
using DiscordNotifier.Discord;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using WingsAPI.Communication.Services.Messages;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Maintenance
|
||||||
|
{
|
||||||
|
public class ServiceMaintenanceNotificationMessageConsumer : IMessageConsumer<ServiceMaintenanceNotificationMessage>
|
||||||
|
{
|
||||||
|
private const string ThumbnailUrl = "https://friends111.nostale.club/list/ip/1117.png";
|
||||||
|
|
||||||
|
private static readonly EmbedAuthorBuilder Author = new() { IconUrl = StaticHardcodedCode.AvatarUrl };
|
||||||
|
private static readonly EmbedFooterBuilder DefaultFooter = new EmbedFooterBuilder().WithIconUrl(StaticHardcodedCode.AvatarUrl).WithText("Timestamp:");
|
||||||
|
private static readonly EmbedFooterBuilder FooterForSchedule = new EmbedFooterBuilder().WithIconUrl(StaticHardcodedCode.AvatarUrl).WithText("Maintenance scheduled for:");
|
||||||
|
private readonly IDiscordWebhookLogsService _discordWebhook;
|
||||||
|
|
||||||
|
public ServiceMaintenanceNotificationMessageConsumer(IDiscordWebhookLogsService discordWebhook) => _discordWebhook = discordWebhook;
|
||||||
|
|
||||||
|
public async Task HandleAsync(ServiceMaintenanceNotificationMessage notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
DateTime scheduledMaintenanceDateTime = DateTime.UtcNow + notification.TimeLeft;
|
||||||
|
|
||||||
|
EmbedBuilder embedBuilder = notification.NotificationType switch
|
||||||
|
{
|
||||||
|
ServiceMaintenanceNotificationType.Rescheduled => new EmbedBuilder
|
||||||
|
{
|
||||||
|
Author = Author,
|
||||||
|
Title = "[NosWings Maintenance] The maintenance has been re-scheduled!",
|
||||||
|
Description = GenerateScheduleDescription(scheduledMaintenanceDateTime, notification.Reason),
|
||||||
|
Color = Color.Gold,
|
||||||
|
ThumbnailUrl = ThumbnailUrl,
|
||||||
|
Footer = FooterForSchedule,
|
||||||
|
Timestamp = new DateTimeOffset(scheduledMaintenanceDateTime, TimeSpan.Zero)
|
||||||
|
},
|
||||||
|
ServiceMaintenanceNotificationType.ScheduleStopped => new EmbedBuilder
|
||||||
|
{
|
||||||
|
Author = Author,
|
||||||
|
Title = "[NosWings Maintenance] Maintenance canceled!",
|
||||||
|
Description = string.IsNullOrEmpty(notification.Reason) ? string.Empty : $"Reason: {notification.Reason}",
|
||||||
|
Color = Color.Red,
|
||||||
|
ThumbnailUrl = ThumbnailUrl,
|
||||||
|
Footer = DefaultFooter,
|
||||||
|
Timestamp = new DateTimeOffset(scheduledMaintenanceDateTime, TimeSpan.Zero)
|
||||||
|
},
|
||||||
|
ServiceMaintenanceNotificationType.Executed => new EmbedBuilder
|
||||||
|
{
|
||||||
|
Author = Author,
|
||||||
|
Title = "[NosWings Maintenance] The Server is now in Maintenance mode!",
|
||||||
|
Color = Color.DarkBlue,
|
||||||
|
ThumbnailUrl = ThumbnailUrl,
|
||||||
|
Footer = DefaultFooter,
|
||||||
|
Timestamp = new DateTimeOffset(scheduledMaintenanceDateTime, TimeSpan.Zero)
|
||||||
|
},
|
||||||
|
ServiceMaintenanceNotificationType.EmergencyExecuted => new EmbedBuilder
|
||||||
|
{
|
||||||
|
Author = Author,
|
||||||
|
Title = "[NosWings Maintenance] An emergency maintenance has been called!",
|
||||||
|
Description = $"An unexpected maintenance has been executed, sorry for any inconvenience it may have cause.\n**Reason:** {notification.Reason}",
|
||||||
|
Color = Color.Purple,
|
||||||
|
ThumbnailUrl = ThumbnailUrl,
|
||||||
|
Footer = DefaultFooter,
|
||||||
|
Timestamp = new DateTimeOffset(scheduledMaintenanceDateTime, TimeSpan.Zero)
|
||||||
|
},
|
||||||
|
ServiceMaintenanceNotificationType.Lifted => new EmbedBuilder
|
||||||
|
{
|
||||||
|
Author = Author,
|
||||||
|
Title = "[NosWings Maintenance] The Server's maintenance has been lifted!",
|
||||||
|
Color = Color.Green,
|
||||||
|
ThumbnailUrl = ThumbnailUrl,
|
||||||
|
Footer = DefaultFooter,
|
||||||
|
Timestamp = new DateTimeOffset(scheduledMaintenanceDateTime, TimeSpan.Zero)
|
||||||
|
},
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (embedBuilder == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _discordWebhook.PublishLogEmbedded(LogType.PLAYERS_EVENTS_CHANNEL, embedBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateScheduleDescription(DateTime scheduledMaintenanceDateTime, string reason) => $"**Date and time:** {scheduledMaintenanceDateTime:U} (UTC)\n**Reason:** {reason}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages.Miniland;
|
||||||
|
using WingsEmu.Game.Configurations.Miniland;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Minigame
|
||||||
|
{
|
||||||
|
public class LogMinigameRewardClaimedMessageFormatter : IDiscordLogFormatter<LogMinigameRewardClaimedMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.MINIGAME_REWARD_CLAIMED;
|
||||||
|
|
||||||
|
public bool TryFormat(LogMinigameRewardClaimedMessage log, out string formattedString)
|
||||||
|
{
|
||||||
|
// For now we only want to log on DC the Lv.4 and Lv.5 rewards
|
||||||
|
if (log.RewardLevel != RewardLevel.FourthReward && log.RewardLevel != RewardLevel.FifthReward)
|
||||||
|
{
|
||||||
|
formattedString = string.Empty;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
formattedString =
|
||||||
|
$"{log.CreatedAt:yyyy-MM-dd HH:mm:ss} | CHANNEL {log.ChannelId} | {log.CharacterName} | MINIGAME {log.MinigameType} | OWNER_ID {log.OwnerId} | Lv.{(short)log.RewardLevel + 1} | ITEM {log.ItemVnum} x{log.Amount}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages.Miniland;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Minigame
|
||||||
|
{
|
||||||
|
public class LogMinigameScoreMessageFormatter : IDiscordLogFormatter<LogMinigameScoreMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.MINIGAME_SCORE;
|
||||||
|
|
||||||
|
public bool TryFormat(LogMinigameScoreMessage message, out string formattedString)
|
||||||
|
{
|
||||||
|
formattedString =
|
||||||
|
$"{message.CreatedAt:yyyy-MM-dd HH:mm:ss} | CHANNEL {message.ChannelId} | {message.CharacterName} | MINIGAME {message.MinigameType} | OWNER_ID {message.OwnerId} | SCORE1 {message.Score1} |"
|
||||||
|
+ $"SCORE2 {message.Score2} | VALIDITY {message.ScoreValidity} | COMPLETION_TIME {message.CompletionTime.ToString()}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Player
|
||||||
|
{
|
||||||
|
public class LogGmCommandExecutedMessageFormatter : IDiscordLogFormatter<LogGmCommandExecutedMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.COMMANDS_GM_COMMAND_EXECUTED;
|
||||||
|
|
||||||
|
public bool TryFormat(LogGmCommandExecutedMessage message, out string formattedString)
|
||||||
|
{
|
||||||
|
formattedString =
|
||||||
|
$"{message.CreatedAt:yyyy-MM-dd HH:mm:ss} | CHANNEL {message.ChannelId} | P.Authority: {message.PlayerAuthority} | C.Authority: [{message.CommandAuthority}] {message.CharacterName} | {message.Command}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages.Player;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Player
|
||||||
|
{
|
||||||
|
public class LogPlayerCommandExecutedMessageFormatter : IDiscordLogFormatter<LogPlayerCommandExecutedMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.COMMANDS_PLAYER_COMMAND_EXECUTED;
|
||||||
|
|
||||||
|
public bool TryFormat(LogPlayerCommandExecutedMessage message, out string formattedString)
|
||||||
|
{
|
||||||
|
formattedString = $"{message.CreatedAt:yyyy-MM-dd HH:mm:ss} | CHANNEL {message.ChannelId} | {message.CharacterName} | {message.Command}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages.LevelUp;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Player
|
||||||
|
{
|
||||||
|
public class LogPlayerLevelUpMessageFormatter : IDiscordLogFormatter<LogLevelUpCharacterMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.FARMING_LEVEL_UP;
|
||||||
|
|
||||||
|
public bool TryFormat(LogLevelUpCharacterMessage message, out string formattedString)
|
||||||
|
{
|
||||||
|
formattedString =
|
||||||
|
$"{message.CreatedAt:yyyy-MM-dd HH:mm:ss} | CHANNEL {message.ChannelId} | {message.CharacterName} {message.LevelType.ToUpperInvariant()} {message.Level}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using Plugin.PlayerLogs.Messages;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Consumers.Player
|
||||||
|
{
|
||||||
|
public class LogStrangeBehaviorMessageFormatter : IDiscordLogFormatter<LogStrangeBehaviorMessage>
|
||||||
|
{
|
||||||
|
public LogType LogType => LogType.STRANGE_BEHAVIORS;
|
||||||
|
|
||||||
|
public bool TryFormat(LogStrangeBehaviorMessage message, out string formattedString)
|
||||||
|
{
|
||||||
|
formattedString =
|
||||||
|
$"{message.CreatedAt:yyyy-MM-dd HH:mm:ss} | CHANNEL {message.ChannelId} | {message.CharacterName} | [{message.SeverityType}] -> {message.Message}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Discord
|
||||||
|
{
|
||||||
|
public class DiscordWebhookConfiguration : Dictionary<LogType, string>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
80
srcs/DiscordNotifier/Discord/DiscordWebhookLogsService.cs
Normal file
80
srcs/DiscordNotifier/Discord/DiscordWebhookLogsService.cs
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
// WingsEmu
|
||||||
|
//
|
||||||
|
// Developed by NosWings Team
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Discord;
|
||||||
|
using Discord.Webhook;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Discord
|
||||||
|
{
|
||||||
|
public class DiscordWebhookLogsService : IDiscordWebhookLogsService
|
||||||
|
{
|
||||||
|
private readonly DiscordWebhookConfiguration _configs;
|
||||||
|
|
||||||
|
public DiscordWebhookLogsService(DiscordWebhookConfiguration configs) => _configs = configs;
|
||||||
|
|
||||||
|
public async Task PublishLogMessage(LogType logType, string message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_configs.TryGetValue(logType, out string webhookUrl) || string.IsNullOrEmpty(webhookUrl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var webhookClient = new DiscordWebhookClient(webhookUrl);
|
||||||
|
await webhookClient.SendMessageAsync($"```\n{message}\n```");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error($"[LogsServer] PublishLogEmbedded {logType}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task PublishLogEmbedded(LogType logType, EmbedBuilder embed)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_configs.TryGetValue(logType, out string webhookUrl) || string.IsNullOrEmpty(webhookUrl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var webhookClient = new DiscordWebhookClient(webhookUrl);
|
||||||
|
await webhookClient.SendMessageAsync(embeds: new[] { embed.Build() });
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error($"[LogsServer] PublishLogEmbedded {logType}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task PublishLogsEmbedded(LogType logType, List<EmbedBuilder> embeds)
|
||||||
|
{
|
||||||
|
var builtEmbeds = new List<Embed>();
|
||||||
|
foreach (EmbedBuilder embed in embeds)
|
||||||
|
{
|
||||||
|
builtEmbeds.Add(embed.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_configs.TryGetValue(logType, out string webhookUrl) || string.IsNullOrEmpty(webhookUrl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var webhookClient = new DiscordWebhookClient(webhookUrl);
|
||||||
|
await webhookClient.SendMessageAsync(embeds: builtEmbeds);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error($"[LogsServer] PublishLogsEmbedded {logType}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
srcs/DiscordNotifier/Discord/IDiscordWebhookLogsService.cs
Normal file
13
srcs/DiscordNotifier/Discord/IDiscordWebhookLogsService.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Discord;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Discord
|
||||||
|
{
|
||||||
|
public interface IDiscordWebhookLogsService
|
||||||
|
{
|
||||||
|
Task PublishLogMessage(LogType logType, string message);
|
||||||
|
Task PublishLogEmbedded(LogType logType, EmbedBuilder embed);
|
||||||
|
Task PublishLogsEmbedded(LogType logType, List<EmbedBuilder> embeds);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
srcs/DiscordNotifier/DiscordNotifier.csproj
Normal file
28
srcs/DiscordNotifier/DiscordNotifier.csproj
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
<OutputPath>..\..\dist\discord-notifier\</OutputPath>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Caching\PhoenixLib.Caching.csproj" />
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Configuration\PhoenixLib.Configuration.csproj" />
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Events\PhoenixLib.Events.csproj" />
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Logging\PhoenixLib.Logging.csproj" />
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Messaging\PhoenixLib.Messaging.csproj" />
|
||||||
|
<ProjectReference Include="..\WingsEmu.Health\WingsEmu.Health.csproj" />
|
||||||
|
<ProjectReference Include="..\_plugins\Plugin.FamilyImpl\Plugin.FamilyImpl.csproj" />
|
||||||
|
<ProjectReference Include="..\_plugins\Plugin.PlayerLogs\Plugin.PlayerLogs.csproj" />
|
||||||
|
<ProjectReference Include="..\_plugins\Plugin.DB.EF\Plugin.DB.EF.csproj" />
|
||||||
|
<ProjectReference Include="..\_plugins\Plugin.ResourceLoader\Plugin.ResourceLoader.csproj" />
|
||||||
|
<ProjectReference Include="..\_plugins\WingsEmu.Plugins.DistributedGameEvents\WingsEmu.Plugins.DistributedGameEvents.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Discord.Net.Webhook" Version="2.4.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
54
srcs/DiscordNotifier/DockerGracefulStopService.cs
Normal file
54
srcs/DiscordNotifier/DockerGracefulStopService.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.Loader;
|
||||||
|
using System.Threading;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
|
||||||
|
namespace DiscordNotifier
|
||||||
|
{
|
||||||
|
public class DockerGracefulStopService : IDisposable
|
||||||
|
{
|
||||||
|
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||||
|
private readonly ManualResetEventSlim _stoppedEvent;
|
||||||
|
|
||||||
|
public DockerGracefulStopService()
|
||||||
|
{
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
_stoppedEvent = new ManualResetEventSlim();
|
||||||
|
|
||||||
|
// SIGINT
|
||||||
|
Console.CancelKeyPress += (sender, eventArgs) =>
|
||||||
|
{
|
||||||
|
Log.Error("[GRACEFUL_SHUTDOWN] SIGINT received", new Exception("[GRACEFUL_SHUTDOWN] SIGINT received"));
|
||||||
|
GracefulStop(_cancellationTokenSource, _stoppedEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
// SIGTERM
|
||||||
|
AssemblyLoadContext.Default.Unloading += context =>
|
||||||
|
{
|
||||||
|
Log.Error("[GRACEFUL_SHUTDOWN] SIGTERM received", new Exception("[GRACEFUL_SHUTDOWN] SIGTERM received"));
|
||||||
|
GracefulStop(_cancellationTokenSource, _stoppedEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
|
||||||
|
{
|
||||||
|
Log.Error("UnhandledException", args.ExceptionObject as Exception);
|
||||||
|
GracefulStop(_cancellationTokenSource, _stoppedEvent);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public CancellationToken CancellationToken => _cancellationTokenSource.Token;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_stoppedEvent.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GracefulStop(CancellationTokenSource cancellationTokenSource, ManualResetEventSlim stoppedEvent)
|
||||||
|
{
|
||||||
|
Log.Info("DockerGracefulStopService Stopping service");
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
stoppedEvent.Wait();
|
||||||
|
Log.Info("DockerGracefulStopService Stop finished");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
srcs/DiscordNotifier/Formatting/DiscordLogExtensions.cs
Normal file
25
srcs/DiscordNotifier/Formatting/DiscordLogExtensions.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using PhoenixLib.ServiceBus.Extensions;
|
||||||
|
using Plugin.PlayerLogs;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Formatting
|
||||||
|
{
|
||||||
|
public static class DiscordLogExtensions
|
||||||
|
{
|
||||||
|
public static void AddDiscordFormattedLog<TMessage, TFormatter>(this IServiceCollection services)
|
||||||
|
where TMessage : class, IPlayerActionLogMessage
|
||||||
|
where TFormatter : class, IDiscordLogFormatter<TMessage>
|
||||||
|
{
|
||||||
|
services.AddMessageSubscriber<TMessage, GenericDiscordLogConsumer<TMessage>>();
|
||||||
|
services.AddSingleton<IDiscordLogFormatter<TMessage>, TFormatter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddDiscordEmbedFormattedLog<TMessage, TFormatter>(this IServiceCollection services)
|
||||||
|
where TMessage : class, IPlayerActionLogMessage
|
||||||
|
where TFormatter : class, IDiscordEmbedLogFormatter<TMessage>
|
||||||
|
{
|
||||||
|
services.AddMessageSubscriber<TMessage, GenericDiscordEmbedLogConsumer<TMessage>>();
|
||||||
|
services.AddSingleton<IDiscordEmbedLogFormatter<TMessage>, TFormatter>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Discord;
|
||||||
|
using DiscordNotifier.Discord;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using Plugin.PlayerLogs;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Formatting
|
||||||
|
{
|
||||||
|
public class GenericDiscordEmbedLogConsumer<T> : IMessageConsumer<T> where T : IPlayerActionLogMessage
|
||||||
|
{
|
||||||
|
private readonly IDiscordWebhookLogsService _discordWebhook;
|
||||||
|
private readonly IDiscordEmbedLogFormatter<T> _formatter;
|
||||||
|
|
||||||
|
public GenericDiscordEmbedLogConsumer(IDiscordWebhookLogsService discordWebhook, IDiscordEmbedLogFormatter<T> formatter)
|
||||||
|
{
|
||||||
|
_discordWebhook = discordWebhook;
|
||||||
|
_formatter = formatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleAsync(T notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
if (!_formatter.TryFormat(notification, out List<EmbedBuilder> embeds))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _discordWebhook.PublishLogsEmbedded(_formatter.LogType, embeds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
srcs/DiscordNotifier/Formatting/GenericDiscordLogConsumer.cs
Normal file
30
srcs/DiscordNotifier/Formatting/GenericDiscordLogConsumer.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DiscordNotifier.Discord;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using Plugin.PlayerLogs;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Formatting
|
||||||
|
{
|
||||||
|
public class GenericDiscordLogConsumer<T> : IMessageConsumer<T> where T : IPlayerActionLogMessage
|
||||||
|
{
|
||||||
|
private readonly IDiscordWebhookLogsService _discordWebhook;
|
||||||
|
private readonly IDiscordLogFormatter<T> _formatter;
|
||||||
|
|
||||||
|
public GenericDiscordLogConsumer(IDiscordLogFormatter<T> formatter, IDiscordWebhookLogsService discordWebhook)
|
||||||
|
{
|
||||||
|
_formatter = formatter;
|
||||||
|
_discordWebhook = discordWebhook;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleAsync(T notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
if (!_formatter.TryFormat(notification, out string formattedString))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _discordWebhook.PublishLogMessage(_formatter.LogType, formattedString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
srcs/DiscordNotifier/Formatting/IDiscordEmbedLogFormatter.cs
Normal file
12
srcs/DiscordNotifier/Formatting/IDiscordEmbedLogFormatter.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Discord;
|
||||||
|
using Plugin.PlayerLogs;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Formatting
|
||||||
|
{
|
||||||
|
public interface IDiscordEmbedLogFormatter<TMessage> where TMessage : IPlayerActionLogMessage
|
||||||
|
{
|
||||||
|
LogType LogType { get; }
|
||||||
|
bool TryFormat(TMessage message, out List<EmbedBuilder> embeds);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
srcs/DiscordNotifier/Formatting/IDiscordLogFormatter.cs
Normal file
8
srcs/DiscordNotifier/Formatting/IDiscordLogFormatter.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace DiscordNotifier.Formatting
|
||||||
|
{
|
||||||
|
public interface IDiscordLogFormatter<TMessage>
|
||||||
|
{
|
||||||
|
LogType LogType { get; }
|
||||||
|
bool TryFormat(TMessage message, out string formattedString);
|
||||||
|
}
|
||||||
|
}
|
||||||
45
srcs/DiscordNotifier/LogType.cs
Normal file
45
srcs/DiscordNotifier/LogType.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
// WingsEmu
|
||||||
|
//
|
||||||
|
// Developed by NosWings Team
|
||||||
|
|
||||||
|
namespace DiscordNotifier
|
||||||
|
{
|
||||||
|
public enum LogType
|
||||||
|
{
|
||||||
|
PLAYERS_EVENTS_CHANNEL,
|
||||||
|
|
||||||
|
CHAT_GENERAL, // channel, characterName, message
|
||||||
|
CHAT_WHISPERS, // channel, characterName, message
|
||||||
|
CHAT_FRIENDS, // channel, characterName, message
|
||||||
|
CHAT_FAMILIES, // channel, characterName, message
|
||||||
|
CHAT_SPEAKERS, // channel, characterName, message
|
||||||
|
CHAT_GROUPS, // channel, characterName, groupId, message
|
||||||
|
|
||||||
|
FARMING_LEVEL_UP, // channel, characterName, levelType, level
|
||||||
|
|
||||||
|
FAMILY_CREATED, // channel, characterName, familyId, familyName, deputies
|
||||||
|
FAMILY_DISBANDED, // channel, characterName, familyId
|
||||||
|
FAMILY_JOINED, // channel, characterName, familyId, inviterId
|
||||||
|
FAMILY_LEFT, // channel, characterName, familyId
|
||||||
|
FAMILY_KICK, // channel, characterName, familyId, kickedId, kickedName
|
||||||
|
FAMILY_MESSAGES, // channel, characterName, familyId, messageType, message
|
||||||
|
|
||||||
|
MINIGAME_REWARD_CLAIMED,
|
||||||
|
MINIGAME_SCORE,
|
||||||
|
|
||||||
|
ITEM_GAMBLED,
|
||||||
|
ITEM_UPGRADED,
|
||||||
|
|
||||||
|
GENERAL_ITEM_USAGE, // channel, characterName, itemVnum
|
||||||
|
GENERAL_EXCHANGE, // channel, characterName, tradeCharacterName, tradeInfos (items, gold...)
|
||||||
|
|
||||||
|
EXPLOITS_WRONG_PACKET, // channel, characterName, sent packet
|
||||||
|
EXPLOITS_TRIED_TO_DUPE, // channel, characterName, sent packet
|
||||||
|
EXPLOITS_WARNINGS, // channel, characterName, WarningType, warning arguments
|
||||||
|
|
||||||
|
STRANGE_BEHAVIORS, // channel, behaviorType, message
|
||||||
|
|
||||||
|
COMMANDS_PLAYER_COMMAND_EXECUTED, // channel, characterName
|
||||||
|
COMMANDS_GM_COMMAND_EXECUTED // channel, characterName, authority
|
||||||
|
}
|
||||||
|
}
|
||||||
44
srcs/DiscordNotifier/Managers/ItemManager.cs
Normal file
44
srcs/DiscordNotifier/Managers/ItemManager.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PhoenixLib.Caching;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
using WingsAPI.Data.GameData;
|
||||||
|
using WingsEmu.DTOs.Items;
|
||||||
|
|
||||||
|
namespace DiscordNotifier.Managers
|
||||||
|
{
|
||||||
|
public class ItemManager
|
||||||
|
{
|
||||||
|
private readonly ILongKeyCachedRepository<ItemDTO> _cachedItems;
|
||||||
|
private readonly IResourceLoader<ItemDTO> _itemDao;
|
||||||
|
|
||||||
|
public ItemManager(IResourceLoader<ItemDTO> itemDao, ILongKeyCachedRepository<ItemDTO> cachedItems)
|
||||||
|
{
|
||||||
|
_itemDao = itemDao;
|
||||||
|
_cachedItems = cachedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CacheClientItems()
|
||||||
|
{
|
||||||
|
Log.Info("[ITEM_MANAGER] Caching items from DB");
|
||||||
|
IEnumerable<ItemDTO> items = await _itemDao.LoadAsync();
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
foreach (ItemDTO item in items)
|
||||||
|
{
|
||||||
|
_cachedItems.Set(item.Id, item);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Info($"[ITEM_MANAGER] Cached: {count.ToString()} items");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetItemIconIdByItemId(int itemId)
|
||||||
|
{
|
||||||
|
ItemDTO cachedItem = _cachedItems.Get(itemId);
|
||||||
|
return cachedItem?.IconId ?? default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemDTO GetItemDtoByItemId(int itemId) => _cachedItems.Get(itemId);
|
||||||
|
}
|
||||||
|
}
|
||||||
75
srcs/DiscordNotifier/Program.cs
Normal file
75
srcs/DiscordNotifier/Program.cs
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DiscordNotifier.Managers;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using PhoenixLib.ServiceBus.MQTT;
|
||||||
|
|
||||||
|
namespace DiscordNotifier
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
PrintHeader();
|
||||||
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
|
using var stopService = new DockerGracefulStopService();
|
||||||
|
using IHost host = CreateHostBuilder(args).Build();
|
||||||
|
{
|
||||||
|
IServiceProvider services = host.Services;
|
||||||
|
await host.StartAsync();
|
||||||
|
IMessagingService messagingService = services.GetRequiredService<IMessagingService>();
|
||||||
|
await messagingService.StartAsync();
|
||||||
|
|
||||||
|
ItemManager itemManager = services.GetRequiredService<ItemManager>();
|
||||||
|
await itemManager.CacheClientItems();
|
||||||
|
|
||||||
|
await host.WaitForShutdownAsync(stopService.CancellationToken);
|
||||||
|
await messagingService.DisposeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PrintHeader()
|
||||||
|
{
|
||||||
|
const string text = @"
|
||||||
|
██╗ ██╗██╗███╗ ██╗ ██████╗ ███████╗███████╗███╗ ███╗██╗ ██╗
|
||||||
|
██║ ██║██║████╗ ██║██╔════╝ ██╔════╝██╔════╝████╗ ████║██║ ██║
|
||||||
|
██║ █╗ ██║██║██╔██╗ ██║██║ ███╗███████╗█████╗ ██╔████╔██║██║ ██║
|
||||||
|
██║███╗██║██║██║╚██╗██║██║ ██║╚════██║██╔══╝ ██║╚██╔╝██║██║ ██║
|
||||||
|
╚███╔███╔╝██║██║ ╚████║╚██████╔╝███████║███████╗██║ ╚═╝ ██║╚██████╔╝
|
||||||
|
╚══╝╚══╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝
|
||||||
|
|
||||||
|
██████╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ██████╗ ███╗ ██╗ ██████╗ ████████╗██╗███████╗██╗███████╗██████╗
|
||||||
|
██╔══██╗██║██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔══██╗ ████╗ ██║██╔═══██╗╚══██╔══╝██║██╔════╝██║██╔════╝██╔══██╗
|
||||||
|
██║ ██║██║███████╗██║ ██║ ██║██████╔╝██║ ██║█████╗██╔██╗ ██║██║ ██║ ██║ ██║█████╗ ██║█████╗ ██████╔╝
|
||||||
|
██║ ██║██║╚════██║██║ ██║ ██║██╔══██╗██║ ██║╚════╝██║╚██╗██║██║ ██║ ██║ ██║██╔══╝ ██║██╔══╝ ██╔══██╗
|
||||||
|
██████╔╝██║███████║╚██████╗╚██████╔╝██║ ██║██████╔╝ ██║ ╚████║╚██████╔╝ ██║ ██║██║ ██║███████╗██║ ██║
|
||||||
|
╚═════╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
|
||||||
|
";
|
||||||
|
string separator = new('=', Console.WindowWidth);
|
||||||
|
string logo = text.Split('\n').Select(s => string.Format("{0," + (Console.WindowWidth / 2 + s.Length / 2) + "}\n", s))
|
||||||
|
.Aggregate("", (current, i) => current + i);
|
||||||
|
|
||||||
|
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||||
|
Console.WriteLine(separator + logo + $"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n" + separator);
|
||||||
|
Console.ForegroundColor = ConsoleColor.White;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional configuration is required to successfully run gRPC on macOS.
|
||||||
|
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
|
||||||
|
private static IHostBuilder CreateHostBuilder(string[] args)
|
||||||
|
{
|
||||||
|
IHostBuilder host = Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureWebHostDefaults(webBuilder =>
|
||||||
|
{
|
||||||
|
webBuilder.ConfigureKestrel(s => { s.ListenAnyIP(28000); });
|
||||||
|
webBuilder.UseStartup<Startup>();
|
||||||
|
});
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
127
srcs/DiscordNotifier/Startup.cs
Normal file
127
srcs/DiscordNotifier/Startup.cs
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
using System;
|
||||||
|
using DiscordNotifier.Consumers.Chat;
|
||||||
|
using DiscordNotifier.Consumers.Family;
|
||||||
|
using DiscordNotifier.Consumers.GameEvents;
|
||||||
|
using DiscordNotifier.Consumers.Item;
|
||||||
|
using DiscordNotifier.Consumers.Maintenance;
|
||||||
|
using DiscordNotifier.Consumers.Minigame;
|
||||||
|
using DiscordNotifier.Consumers.Player;
|
||||||
|
using DiscordNotifier.Discord;
|
||||||
|
using DiscordNotifier.Formatting;
|
||||||
|
using DiscordNotifier.Managers;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using PhoenixLib.Caching;
|
||||||
|
using PhoenixLib.Configuration;
|
||||||
|
using PhoenixLib.Events;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
using PhoenixLib.ServiceBus.Extensions;
|
||||||
|
using Plugin.Database;
|
||||||
|
using Plugin.PlayerLogs.Messages;
|
||||||
|
using Plugin.PlayerLogs.Messages.Family;
|
||||||
|
using Plugin.PlayerLogs.Messages.LevelUp;
|
||||||
|
using Plugin.PlayerLogs.Messages.Miniland;
|
||||||
|
using Plugin.PlayerLogs.Messages.Player;
|
||||||
|
using Plugin.PlayerLogs.Messages.Upgrade;
|
||||||
|
using Plugin.ResourceLoader;
|
||||||
|
using WingsAPI.Communication.InstantBattle;
|
||||||
|
using WingsAPI.Communication.Services.Messages;
|
||||||
|
using WingsEmu.Health.Extensions;
|
||||||
|
|
||||||
|
namespace DiscordNotifier
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
|
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddMqttConfigurationFromEnv();
|
||||||
|
services.AddEventPipeline();
|
||||||
|
services.AddEventHandlersInAssembly<Startup>();
|
||||||
|
services.AddMaintenanceMode();
|
||||||
|
services.AddPhoenixLogging();
|
||||||
|
|
||||||
|
services.TryAddSingleton(typeof(ILongKeyCachedRepository<>), typeof(InMemoryCacheRepository<>));
|
||||||
|
|
||||||
|
new FileResourceLoaderPlugin().AddDependencies(services);
|
||||||
|
|
||||||
|
// discord
|
||||||
|
services.AddYamlConfigurationHelper();
|
||||||
|
services.AddSingleton(new DiscordWebhookConfiguration
|
||||||
|
{
|
||||||
|
{ LogType.PLAYERS_EVENTS_CHANNEL, Environment.GetEnvironmentVariable("WINGSEMU_DISCORD_WEBHOOK_URL") },
|
||||||
|
{ LogType.CHAT_FAMILIES, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_CHAT_FAMILIES") },
|
||||||
|
{ LogType.CHAT_FRIENDS, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_CHAT_FRIENDS") },
|
||||||
|
{ LogType.CHAT_GENERAL, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_CHAT_GENERAL") },
|
||||||
|
{ LogType.CHAT_GROUPS, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_CHAT_GROUPS") },
|
||||||
|
{ LogType.CHAT_SPEAKERS, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_CHAT_SPEAKERS") },
|
||||||
|
{ LogType.CHAT_WHISPERS, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_CHAT_WHISPERS") },
|
||||||
|
{ LogType.FARMING_LEVEL_UP, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_FARMING_LEVEL_UP") },
|
||||||
|
{ LogType.COMMANDS_PLAYER_COMMAND_EXECUTED, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_PLAYER_COMMAND_EXECUTED") },
|
||||||
|
{ LogType.COMMANDS_GM_COMMAND_EXECUTED, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_GM_COMMAND_EXECUTED") },
|
||||||
|
{ LogType.FAMILY_CREATED, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_FAMILY_CREATED") },
|
||||||
|
{ LogType.FAMILY_DISBANDED, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_FAMILY_DISBANDED") },
|
||||||
|
{ LogType.FAMILY_JOINED, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_FAMILY_JOINED") },
|
||||||
|
{ LogType.FAMILY_LEFT, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_FAMILY_LEFT") },
|
||||||
|
{ LogType.FAMILY_KICK, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_FAMILY_KICK") },
|
||||||
|
{ LogType.FAMILY_MESSAGES, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_FAMILY_MESSAGES") },
|
||||||
|
{ LogType.MINIGAME_REWARD_CLAIMED, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_MINIGAME_REWARDS_CLAIMED") },
|
||||||
|
{ LogType.MINIGAME_SCORE, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_MINIGAME_SCORE") },
|
||||||
|
{ LogType.ITEM_GAMBLED, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_ITEM_GAMBLED") },
|
||||||
|
{ LogType.ITEM_UPGRADED, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_ITEM_UPGRADED") },
|
||||||
|
{ LogType.STRANGE_BEHAVIORS, Environment.GetEnvironmentVariable("DISCORD_WEBHOOK_STRANGE_BEHAVIORS") }
|
||||||
|
});
|
||||||
|
services.AddSingleton<IDiscordWebhookLogsService, DiscordWebhookLogsService>();
|
||||||
|
services.AddSingleton<ItemManager>();
|
||||||
|
|
||||||
|
new DatabasePlugin().AddDependencies(services);
|
||||||
|
|
||||||
|
services.AddDiscordFormattedLog<LogPlayerChatMessage, LogChatMessageMessageFormatter>();
|
||||||
|
services.AddDiscordFormattedLog<LogLevelUpCharacterMessage, LogPlayerLevelUpMessageFormatter>();
|
||||||
|
services.AddDiscordFormattedLog<LogPlayerCommandExecutedMessage, LogPlayerCommandExecutedMessageFormatter>();
|
||||||
|
services.AddDiscordFormattedLog<LogGmCommandExecutedMessage, LogGmCommandExecutedMessageFormatter>();
|
||||||
|
|
||||||
|
// Family discord
|
||||||
|
services.AddDiscordFormattedLog<LogFamilyCreatedMessage, LogFamilyCreatedMessageFormatter>();
|
||||||
|
services.AddDiscordFormattedLog<LogFamilyDisbandedMessage, LogFamilyDisbandedMessageFormatter>();
|
||||||
|
services.AddDiscordFormattedLog<LogFamilyJoinedMessage, LogFamilyJoinedMessageFormatter>();
|
||||||
|
services.AddDiscordFormattedLog<LogFamilyKickedMessage, LogFamilyKickedMessageFormatter>();
|
||||||
|
services.AddDiscordFormattedLog<LogFamilyLeftMessage, LogFamilyLeftMessageFormatter>();
|
||||||
|
services.AddDiscordFormattedLog<LogFamilyMessageMessage, LogFamilyMessageMessageFormatter>();
|
||||||
|
services.AddDiscordEmbedFormattedLog<LogFamilyCreatedMessage, LogFamilyCreatedEmbedMessageFormatter>();
|
||||||
|
|
||||||
|
// Minigames discord
|
||||||
|
services.AddDiscordFormattedLog<LogMinigameRewardClaimedMessage, LogMinigameRewardClaimedMessageFormatter>();
|
||||||
|
services.AddDiscordFormattedLog<LogMinigameScoreMessage, LogMinigameScoreMessageFormatter>();
|
||||||
|
|
||||||
|
// Items discord
|
||||||
|
services.AddDiscordFormattedLog<LogItemGambledMessage, LogItemGambledMessageFormatter>();
|
||||||
|
services.AddDiscordFormattedLog<LogItemUpgradedMessage, LogItemUpgradedMessageFormatter>();
|
||||||
|
|
||||||
|
services.AddDiscordFormattedLog<LogStrangeBehaviorMessage, LogStrangeBehaviorMessageFormatter>();
|
||||||
|
|
||||||
|
services.AddMessageSubscriber<InstantBattleStartMessage, LogInstantBattleStartDiscordConsumer>();
|
||||||
|
|
||||||
|
// healthcheck
|
||||||
|
services.AddMessageSubscriber<ServiceDownMessage, ServiceDownMessageConsumer>();
|
||||||
|
|
||||||
|
// maintenance
|
||||||
|
services.AddMessageSubscriber<ServiceMaintenanceNotificationMessage, ServiceMaintenanceNotificationMessageConsumer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
if (env.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseRouting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
srcs/DiscordNotifier/StaticHardcodedCode.cs
Normal file
7
srcs/DiscordNotifier/StaticHardcodedCode.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace DiscordNotifier
|
||||||
|
{
|
||||||
|
public static class StaticHardcodedCode
|
||||||
|
{
|
||||||
|
public const string AvatarUrl = "https://avatars0.githubusercontent.com/u/40839221?s=200";
|
||||||
|
}
|
||||||
|
}
|
||||||
11
srcs/FamilyServer/Achievements/FamilyAchievementIncrement.cs
Normal file
11
srcs/FamilyServer/Achievements/FamilyAchievementIncrement.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
using PhoenixLib.Events;
|
||||||
|
|
||||||
|
namespace FamilyServer.Achievements
|
||||||
|
{
|
||||||
|
public class FamilyAchievementIncrement : IAsyncEvent
|
||||||
|
{
|
||||||
|
public long FamilyId { get; set; }
|
||||||
|
public int AchievementId { get; set; }
|
||||||
|
public int ValueToAdd { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PhoenixLib.Events;
|
||||||
|
|
||||||
|
namespace FamilyServer.Achievements
|
||||||
|
{
|
||||||
|
public class FamilyAchievementIncrementEventHandler : IAsyncEventProcessor<FamilyAchievementIncrement>
|
||||||
|
{
|
||||||
|
private readonly FamilyAchievementManager _familyAchievementManager;
|
||||||
|
|
||||||
|
public FamilyAchievementIncrementEventHandler(FamilyAchievementManager familyAchievementManager) => _familyAchievementManager = familyAchievementManager;
|
||||||
|
|
||||||
|
public Task HandleAsync(FamilyAchievementIncrement e, CancellationToken cancellation)
|
||||||
|
{
|
||||||
|
_familyAchievementManager.AddIncrementToQueue(e);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// WingsEmu
|
||||||
|
//
|
||||||
|
// Developed by NosWings Team
|
||||||
|
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using Plugin.FamilyImpl.Achievements;
|
||||||
|
|
||||||
|
namespace FamilyServer.Achievements
|
||||||
|
{
|
||||||
|
public class FamilyAchievementIncrementMessageConsumer : IMessageConsumer<FamilyAchievementIncrementMessage>
|
||||||
|
{
|
||||||
|
private readonly FamilyAchievementManager _familyAchievementManager;
|
||||||
|
|
||||||
|
public FamilyAchievementIncrementMessageConsumer(FamilyAchievementManager familyAchievementManager) => _familyAchievementManager = familyAchievementManager;
|
||||||
|
|
||||||
|
public Task HandleAsync(FamilyAchievementIncrementMessage notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
var incrementRequest = new FamilyAchievementIncrement { AchievementId = notification.AchievementId, FamilyId = notification.FamilyId, ValueToAdd = notification.ValueToAdd };
|
||||||
|
_familyAchievementManager.AddIncrementToQueue(incrementRequest);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
361
srcs/FamilyServer/Achievements/FamilyAchievementManager.cs
Normal file
361
srcs/FamilyServer/Achievements/FamilyAchievementManager.cs
Normal file
|
|
@ -0,0 +1,361 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FamilyServer.Logs;
|
||||||
|
using FamilyServer.Managers;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using PhoenixLib.DAL.Redis.Locks;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using Plugin.FamilyImpl.Achievements;
|
||||||
|
using Plugin.FamilyImpl.Messages;
|
||||||
|
using WingsAPI.Communication.Families;
|
||||||
|
using WingsAPI.Data.Families;
|
||||||
|
using WingsEmu.Packets.Enums.Families;
|
||||||
|
|
||||||
|
namespace FamilyServer.Achievements
|
||||||
|
{
|
||||||
|
public class FamilyAchievementManager : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly Dictionary<int, FamilyAchievementSpecificConfiguration> _achievementsConfiguration;
|
||||||
|
private readonly ConcurrentQueue<FamilyAchievementIncrement> _achievementsQueue = new();
|
||||||
|
private readonly FamilyAchievementsConfiguration _configuration;
|
||||||
|
private readonly IExpirableLockService _expirableLockService;
|
||||||
|
private readonly FamilyExperienceManager _familyExperienceManager;
|
||||||
|
private readonly FamilyLogManager _familyLogManager;
|
||||||
|
private readonly FamilyManager _familyManager;
|
||||||
|
private readonly IFamilyService _familyService;
|
||||||
|
private readonly IMessagePublisher<FamilyUpdateMessage> _familyUpdatePublisher;
|
||||||
|
private readonly Dictionary<int, FamilyMissionSpecificConfiguration> _missionSpecificConfigurations;
|
||||||
|
private readonly ConcurrentQueue<FamilyMissionIncrement> _missionsQueue = new();
|
||||||
|
private readonly IMessagePublisher<FamilyAchievementUnlockedMessage> _unlockedMessagePublisher;
|
||||||
|
|
||||||
|
public FamilyAchievementManager(FamilyManager familyManager, FamilyAchievementsConfiguration configuration, IMessagePublisher<FamilyUpdateMessage> familyUpdatePublisher,
|
||||||
|
IMessagePublisher<FamilyAchievementUnlockedMessage> unlockedMessagePublisher, IFamilyService familyService, FamilyMissionsConfiguration missionsConfiguration,
|
||||||
|
IExpirableLockService expirableLockService, FamilyExperienceManager familyExperienceManager, FamilyLogManager familyLogManager)
|
||||||
|
{
|
||||||
|
_familyManager = familyManager;
|
||||||
|
_configuration = configuration;
|
||||||
|
_familyUpdatePublisher = familyUpdatePublisher;
|
||||||
|
_unlockedMessagePublisher = unlockedMessagePublisher;
|
||||||
|
_familyService = familyService;
|
||||||
|
_expirableLockService = expirableLockService;
|
||||||
|
_familyExperienceManager = familyExperienceManager;
|
||||||
|
_familyLogManager = familyLogManager;
|
||||||
|
_missionSpecificConfigurations = missionsConfiguration.ToDictionary(s => s.MissionId);
|
||||||
|
_achievementsConfiguration = configuration.Counters.ToDictionary(s => s.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TimeSpan RefreshTime => TimeSpan.FromSeconds(Convert.ToInt32(Environment.GetEnvironmentVariable("FAMILY_ACHIEVEMENT_REFRESH_IN_SECONDS") ?? "5"));
|
||||||
|
|
||||||
|
public void AddIncrementToQueue(FamilyAchievementIncrement message)
|
||||||
|
{
|
||||||
|
_achievementsQueue.Enqueue(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddIncrementToQueue(FamilyMissionIncrement message)
|
||||||
|
{
|
||||||
|
_missionsQueue.Enqueue(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
Dictionary<long, FamilyDTO> toUpdate = new();
|
||||||
|
await FlushPendingAchievements(toUpdate);
|
||||||
|
await FlushPendingMissions(toUpdate);
|
||||||
|
|
||||||
|
if (toUpdate.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (FamilyDTO family in toUpdate.Values)
|
||||||
|
{
|
||||||
|
await _familyManager.SaveFamilyAsync(family);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _familyUpdatePublisher.PublishAsync(new FamilyUpdateMessage
|
||||||
|
{
|
||||||
|
Families = toUpdate.Values.ToList(),
|
||||||
|
ChangedInfoFamilyUpdate = ChangedInfoFamilyUpdate.AchievementsAndMissions
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(RefreshTime, stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task FlushPendingMissions(Dictionary<long, FamilyDTO> toUpdate)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_missionsQueue.IsEmpty)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet<(long, int)> unlockedMissions = new();
|
||||||
|
while (_missionsQueue.TryDequeue(out FamilyMissionIncrement message))
|
||||||
|
{
|
||||||
|
FamilyDTO family = await _familyManager.GetFamilyByFamilyIdAsync(message.FamilyId);
|
||||||
|
|
||||||
|
if (family == null)
|
||||||
|
{
|
||||||
|
Log.Debug("Family not found");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int missionId = message.MissionId;
|
||||||
|
int value = message.ValueToAdd;
|
||||||
|
long? characterId = message.CharacterId;
|
||||||
|
Log.Debug($"[FAMILY_MISSIONS] Family {family.Id} adding {value} to mission: {missionId}");
|
||||||
|
|
||||||
|
family.Missions ??= new FamilyMissionsDto();
|
||||||
|
family.Missions.Missions ??= new Dictionary<int, FamilyMissionDto>();
|
||||||
|
|
||||||
|
if (!family.Missions.Missions.TryGetValue(missionId, out FamilyMissionDto progress))
|
||||||
|
{
|
||||||
|
family.Missions.Missions[missionId] = progress = new FamilyMissionDto
|
||||||
|
{
|
||||||
|
Id = missionId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress.CompletionDate.HasValue && progress.CompletionDate < DateTime.UtcNow)
|
||||||
|
{
|
||||||
|
Log.Debug($"[FAMILY_MISSIONS] {progress.CompletionDate.Value} need to wait reset");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_missionSpecificConfigurations.TryGetValue(missionId, out FamilyMissionSpecificConfiguration config))
|
||||||
|
{
|
||||||
|
Log.Error($"Family achievement config {missionId} is not configured", new Exception());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.OncePerPlayerPerDay && !characterId.HasValue)
|
||||||
|
{
|
||||||
|
Log.Debug($"[FAMILY_MISSION] could not increment mission: {missionId} for family: {family.Id} because characterId is missing");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.OncePerPlayerPerDay &&
|
||||||
|
!await _expirableLockService.TryAddTemporaryLockAsync($"game:locks:family:{family.Id}:missions:{missionId}:character:{characterId}", DateTime.UtcNow.Date.AddDays(1)))
|
||||||
|
{
|
||||||
|
Log.Debug($"[FAMILY_MISSION] {characterId} could not increment mission: {missionId} for family: {family.Id}, already done for today");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress.Count += value;
|
||||||
|
toUpdate.TryAdd(family.Id, family);
|
||||||
|
if (progress.Count < config.Value)
|
||||||
|
{
|
||||||
|
Log.Debug($"[FAMILY_MISSIONS] Family {family.Id} tried to increment mission: {missionId} progress.Count < config.Value");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mission done
|
||||||
|
DateTime now = DateTime.UtcNow;
|
||||||
|
progress.CompletionDate = now;
|
||||||
|
progress.CompletionCount++;
|
||||||
|
unlockedMissions.Add((family.Id, missionId));
|
||||||
|
|
||||||
|
if (config.Rewards != null)
|
||||||
|
{
|
||||||
|
foreach (FamilyMissionReward reward in config.Rewards)
|
||||||
|
{
|
||||||
|
if (reward.FamilyXp.HasValue)
|
||||||
|
{
|
||||||
|
_familyExperienceManager.AddExperienceIncrementRequest(new ExperienceIncrementRequest
|
||||||
|
{
|
||||||
|
FamilyId = family.Id,
|
||||||
|
Experience = reward.FamilyXp.Value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress.Count > config.Value)
|
||||||
|
{
|
||||||
|
progress.Count = config.Value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Debug($"[FAMILY_MISSIONS] Family {family.Id} tried to increment mission: {missionId} progress.Count <= config.Value");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ((long familyId, int missionId) in unlockedMissions)
|
||||||
|
{
|
||||||
|
_familyLogManager.SaveFamilyLogs(new[]
|
||||||
|
{
|
||||||
|
new FamilyLogDto
|
||||||
|
{
|
||||||
|
FamilyLogType = FamilyLogType.FamilyMission,
|
||||||
|
FamilyId = familyId,
|
||||||
|
Actor = missionId.ToString(),
|
||||||
|
Timestamp = DateTime.UtcNow
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[FAMILY_ACHIEVEMENT_MANAGER]", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task FlushPendingAchievements(Dictionary<long, FamilyDTO> toUpdate)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_achievementsQueue.IsEmpty)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet<(long, int)> unlockedAchievements = new();
|
||||||
|
while (_achievementsQueue.TryDequeue(out FamilyAchievementIncrement message))
|
||||||
|
{
|
||||||
|
FamilyDTO family = await _familyManager.GetFamilyByFamilyIdAsync(message.FamilyId);
|
||||||
|
|
||||||
|
if (family == null)
|
||||||
|
{
|
||||||
|
Log.Debug("Family not found");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int achievementId = message.AchievementId;
|
||||||
|
int value = message.ValueToAdd;
|
||||||
|
Log.Debug($"[FAMILY_ACHIEVEMENT] Family {family.Id} adding {value} to achievement: {achievementId}");
|
||||||
|
|
||||||
|
|
||||||
|
family.Achievements ??= new FamilyAchievementsDto();
|
||||||
|
family.Achievements.Achievements ??= new Dictionary<int, FamilyAchievementCompletionDto>();
|
||||||
|
family.Achievements.Progress ??= new Dictionary<int, FamilyAchievementProgressDto>();
|
||||||
|
if (family.Achievements?.Achievements?.ContainsKey(achievementId) == true)
|
||||||
|
{
|
||||||
|
AddNextAchievement(achievementId, family, value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!family.Achievements.Progress.TryGetValue(achievementId, out FamilyAchievementProgressDto progress))
|
||||||
|
{
|
||||||
|
family.Achievements.Progress[achievementId] = progress = new FamilyAchievementProgressDto
|
||||||
|
{
|
||||||
|
Id = achievementId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_achievementsConfiguration.TryGetValue(achievementId, out FamilyAchievementSpecificConfiguration config))
|
||||||
|
{
|
||||||
|
Log.Error($"Family achievement config {achievementId} is not configured", new Exception($"[FAMILY_ACHIEVEMENT_CONFIG] {progress.Id} not configured"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.RequiredId.HasValue && !family.Achievements.Achievements.ContainsKey(config.RequiredId.Value))
|
||||||
|
{
|
||||||
|
Log.Debug($"[FAMILY_ACHIEVEMENT] Family {family.Id} tried to increment achievement: {achievementId} but didn't have {config.RequiredId.Value}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress.Count += value;
|
||||||
|
|
||||||
|
toUpdate.TryAdd(family.Id, family);
|
||||||
|
if (progress.Count < config.Value)
|
||||||
|
{
|
||||||
|
Log.Debug($"[FAMILY_ACHIEVEMENT] Family {family.Id} tried to increment achievement: {achievementId} progress.Count < config.Value");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// achievement unlocked
|
||||||
|
family.Achievements.Achievements[achievementId] = new FamilyAchievementCompletionDto
|
||||||
|
{
|
||||||
|
Id = progress.Id,
|
||||||
|
CompletionDate = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
(long Id, int achievementId) key = (family.Id, achievementId);
|
||||||
|
if (!unlockedAchievements.Contains(key))
|
||||||
|
{
|
||||||
|
unlockedAchievements.Add(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.Rewards != null)
|
||||||
|
{
|
||||||
|
foreach (FamilyAchievementReward reward in config.Rewards)
|
||||||
|
{
|
||||||
|
if (reward.FamilyXp.HasValue)
|
||||||
|
{
|
||||||
|
_familyExperienceManager.AddExperienceIncrementRequest(new ExperienceIncrementRequest
|
||||||
|
{
|
||||||
|
FamilyId = family.Id,
|
||||||
|
Experience = reward.FamilyXp.Value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reward.FamilyUpgradeCategory.HasValue && reward.UpgradeValue.HasValue && reward.UpgradeId.HasValue)
|
||||||
|
{
|
||||||
|
await _familyService.TryAddFamilyUpgrade(new FamilyUpgradeRequest
|
||||||
|
{
|
||||||
|
FamilyId = family.Id,
|
||||||
|
FamilyUpgradeType = reward.FamilyUpgradeCategory.Value,
|
||||||
|
UpgradeId = reward.UpgradeId.Value,
|
||||||
|
Value = reward.UpgradeValue.Value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
family.Achievements.Progress.Remove(achievementId);
|
||||||
|
|
||||||
|
int progressCount = achievementId is >= 9018 and <= 9036 ? 0 : progress.Count;
|
||||||
|
|
||||||
|
AddNextAchievement(achievementId, family, progressCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ((long familyId, int achievementId) in unlockedAchievements)
|
||||||
|
{
|
||||||
|
await _unlockedMessagePublisher.PublishAsync(new FamilyAchievementUnlockedMessage
|
||||||
|
{
|
||||||
|
FamilyId = familyId,
|
||||||
|
AchievementId = achievementId
|
||||||
|
});
|
||||||
|
|
||||||
|
_familyLogManager.SaveFamilyLogs(new[]
|
||||||
|
{
|
||||||
|
new FamilyLogDto
|
||||||
|
{
|
||||||
|
FamilyLogType = FamilyLogType.FamilyAchievement,
|
||||||
|
FamilyId = familyId,
|
||||||
|
Actor = achievementId.ToString(),
|
||||||
|
Timestamp = DateTime.UtcNow
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("[FAMILY_ACHIEVEMENT_MANAGER]", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddNextAchievement(int achievementId, FamilyDTO family, int value)
|
||||||
|
{
|
||||||
|
FamilyAchievementSpecificConfiguration tmp = _configuration.Counters.FirstOrDefault(s => s.RequiredId == achievementId);
|
||||||
|
if (tmp == null)
|
||||||
|
{
|
||||||
|
Log.Error($"[FAMILY_ACHIEVEMENT_MANAGER] Couldn't find next achievement for {achievementId} - familyId: {family.Id}", new Exception());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_achievementsQueue.Enqueue(new FamilyAchievementIncrement
|
||||||
|
{
|
||||||
|
FamilyId = family.Id,
|
||||||
|
AchievementId = tmp.Id,
|
||||||
|
ValueToAdd = value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
srcs/FamilyServer/Achievements/FamilyMissionIncrement.cs
Normal file
10
srcs/FamilyServer/Achievements/FamilyMissionIncrement.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace FamilyServer.Achievements
|
||||||
|
{
|
||||||
|
public class FamilyMissionIncrement
|
||||||
|
{
|
||||||
|
public long FamilyId { get; set; }
|
||||||
|
public long? CharacterId { get; set; }
|
||||||
|
public int MissionId { get; set; }
|
||||||
|
public int ValueToAdd { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using Plugin.FamilyImpl.Achievements;
|
||||||
|
|
||||||
|
namespace FamilyServer.Achievements
|
||||||
|
{
|
||||||
|
public class FamilyMissionIncrementMessageConsumer : IMessageConsumer<FamilyMissionIncrementMessage>
|
||||||
|
{
|
||||||
|
private readonly FamilyAchievementManager _familyAchievementManager;
|
||||||
|
|
||||||
|
public FamilyMissionIncrementMessageConsumer(FamilyAchievementManager familyAchievementManager) => _familyAchievementManager = familyAchievementManager;
|
||||||
|
|
||||||
|
public Task HandleAsync(FamilyMissionIncrementMessage notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
var incrementRequest = new FamilyMissionIncrement
|
||||||
|
{
|
||||||
|
MissionId = notification.MissionId,
|
||||||
|
CharacterId = notification.CharacterId,
|
||||||
|
FamilyId = notification.FamilyId,
|
||||||
|
ValueToAdd = notification.ValueToAdd
|
||||||
|
};
|
||||||
|
_familyAchievementManager.AddIncrementToQueue(incrementRequest);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using Plugin.FamilyImpl.Messages;
|
||||||
|
using WingsEmu.Plugins.DistributedGameEvents.PlayerEvents;
|
||||||
|
|
||||||
|
namespace FamilyServer.Consumers
|
||||||
|
{
|
||||||
|
public class FamilyCharacterConnectMessageConsumer : IMessageConsumer<PlayerConnectedOnChannelMessage>
|
||||||
|
{
|
||||||
|
private readonly IMessagePublisher<FamilyCharacterJoinMessage> _messagePublisher;
|
||||||
|
|
||||||
|
public FamilyCharacterConnectMessageConsumer(IMessagePublisher<FamilyCharacterJoinMessage> messagePublisher) => _messagePublisher = messagePublisher;
|
||||||
|
|
||||||
|
public async Task HandleAsync(PlayerConnectedOnChannelMessage notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
await _messagePublisher.PublishAsync(new FamilyCharacterJoinMessage
|
||||||
|
{
|
||||||
|
CharacterId = notification.CharacterId,
|
||||||
|
FamilyId = notification.FamilyId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using Plugin.FamilyImpl.Messages;
|
||||||
|
using WingsAPI.Communication.Families;
|
||||||
|
using WingsEmu.Plugins.DistributedGameEvents.PlayerEvents;
|
||||||
|
|
||||||
|
namespace FamilyServer.Consumers
|
||||||
|
{
|
||||||
|
public class FamilyCharacterDisconnectMessageConsumer : IMessageConsumer<PlayerDisconnectedChannelMessage>
|
||||||
|
{
|
||||||
|
private readonly IFamilyService _familyService;
|
||||||
|
private readonly IMessagePublisher<FamilyCharacterLeaveMessage> _messagePublisher;
|
||||||
|
|
||||||
|
public FamilyCharacterDisconnectMessageConsumer(IMessagePublisher<FamilyCharacterLeaveMessage> messagePublisher, IFamilyService familyService)
|
||||||
|
{
|
||||||
|
_messagePublisher = messagePublisher;
|
||||||
|
_familyService = familyService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleAsync(PlayerDisconnectedChannelMessage e, CancellationToken cancellation)
|
||||||
|
{
|
||||||
|
if (!e.FamilyId.HasValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _familyService.MemberDisconnectedAsync(new FamilyMemberDisconnectedRequest
|
||||||
|
{
|
||||||
|
CharacterId = e.CharacterId,
|
||||||
|
DisconnectionTime = e.DisconnectionTime
|
||||||
|
});
|
||||||
|
;
|
||||||
|
await _messagePublisher.PublishAsync(new FamilyCharacterLeaveMessage
|
||||||
|
{
|
||||||
|
CharacterId = e.CharacterId,
|
||||||
|
FamilyId = e.FamilyId.Value
|
||||||
|
}, cancellation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FamilyServer.Managers;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using Plugin.FamilyImpl.Messages;
|
||||||
|
using WingsEmu.Game.Families;
|
||||||
|
|
||||||
|
namespace FamilyServer.Consumers
|
||||||
|
{
|
||||||
|
public class FamilyDeclareExperienceGainedMessageConsumer : IMessageConsumer<FamilyDeclareExperienceGainedMessage>
|
||||||
|
{
|
||||||
|
private readonly FamilyExperienceManager _familyExperienceManager;
|
||||||
|
|
||||||
|
public FamilyDeclareExperienceGainedMessageConsumer(FamilyExperienceManager familyExperienceManager) => _familyExperienceManager = familyExperienceManager;
|
||||||
|
|
||||||
|
public async Task HandleAsync(FamilyDeclareExperienceGainedMessage e, CancellationToken cancellation)
|
||||||
|
{
|
||||||
|
Dictionary<long, long> dictionary = FuseDifferentXpValues(e.Experiences);
|
||||||
|
|
||||||
|
foreach ((long characterId, long experienceToAdd) in dictionary)
|
||||||
|
{
|
||||||
|
_familyExperienceManager.AddExperienceIncrementRequest(new ExperienceIncrementRequest
|
||||||
|
{
|
||||||
|
CharacterId = characterId,
|
||||||
|
Experience = experienceToAdd
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<long, long> FuseDifferentXpValues(IEnumerable<ExperienceGainedSubMessage> experienceGainedSubMessages)
|
||||||
|
{
|
||||||
|
var dictionary = new Dictionary<long, long>();
|
||||||
|
foreach (ExperienceGainedSubMessage exp in experienceGainedSubMessages)
|
||||||
|
{
|
||||||
|
if (dictionary.ContainsKey(exp.CharacterId))
|
||||||
|
{
|
||||||
|
dictionary[exp.CharacterId] += exp.FamXpGained;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dictionary.Add(exp.CharacterId, exp.FamXpGained);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// WingsEmu
|
||||||
|
//
|
||||||
|
// Developed by NosWings Team
|
||||||
|
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FamilyServer.Logs;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using Plugin.FamilyImpl.Messages;
|
||||||
|
|
||||||
|
namespace FamilyServer.Consumers
|
||||||
|
{
|
||||||
|
public class FamilyDeclareLogsMessageConsumer : IMessageConsumer<FamilyDeclareLogsMessage>
|
||||||
|
{
|
||||||
|
private readonly FamilyLogManager _familyLogManager;
|
||||||
|
|
||||||
|
public FamilyDeclareLogsMessageConsumer(FamilyLogManager familyLogManager) => _familyLogManager = familyLogManager;
|
||||||
|
|
||||||
|
public Task HandleAsync(FamilyDeclareLogsMessage e, CancellationToken cancellation)
|
||||||
|
{
|
||||||
|
_familyLogManager.SaveFamilyLogs(e.Logs);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
srcs/FamilyServer/Consumers/FamilyHeadSexMessageConsumer.cs
Normal file
48
srcs/FamilyServer/Consumers/FamilyHeadSexMessageConsumer.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FamilyServer.Managers;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using Plugin.FamilyImpl.Messages;
|
||||||
|
using WingsAPI.Data.Families;
|
||||||
|
using WingsEmu.Packets.Enums.Character;
|
||||||
|
|
||||||
|
namespace FamilyServer.Consumers
|
||||||
|
{
|
||||||
|
public class FamilyHeadSexMessageConsumer : IMessageConsumer<FamilyHeadSexMessage>
|
||||||
|
{
|
||||||
|
private readonly FamilyManager _familyManager;
|
||||||
|
private readonly IMessagePublisher<FamilyUpdateMessage> _messagePublisher;
|
||||||
|
|
||||||
|
public FamilyHeadSexMessageConsumer(FamilyManager familyManager, IMessagePublisher<FamilyUpdateMessage> messagePublisher)
|
||||||
|
{
|
||||||
|
_familyManager = familyManager;
|
||||||
|
_messagePublisher = messagePublisher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleAsync(FamilyHeadSexMessage notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
long familyId = notification.FamilyId;
|
||||||
|
GenderType genderType = notification.NewGenderType;
|
||||||
|
|
||||||
|
FamilyDTO familyDto = await _familyManager.GetFamilyByFamilyIdAsync(familyId);
|
||||||
|
if (familyDto == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (familyDto.HeadGender == genderType)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
familyDto.HeadGender = genderType;
|
||||||
|
await _familyManager.SaveFamilyAsync(familyDto);
|
||||||
|
|
||||||
|
await _messagePublisher.PublishAsync(new FamilyUpdateMessage
|
||||||
|
{
|
||||||
|
ChangedInfoFamilyUpdate = ChangedInfoFamilyUpdate.HeadSex,
|
||||||
|
Families = new[] { familyDto }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FamilyServer.Managers;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using Plugin.FamilyImpl.Messages;
|
||||||
|
using WingsAPI.Data.Families;
|
||||||
|
|
||||||
|
namespace FamilyServer.Consumers
|
||||||
|
{
|
||||||
|
public class FamilyMemberTodayMessageConsumer : IMessageConsumer<FamilyMemberTodayMessage>
|
||||||
|
{
|
||||||
|
private readonly FamilyMembershipManager _familyMembershipManager;
|
||||||
|
private readonly IMessagePublisher<FamilyMemberUpdateMessage> _messagePublisher;
|
||||||
|
|
||||||
|
public FamilyMemberTodayMessageConsumer(FamilyMembershipManager familyMembershipManager, IMessagePublisher<FamilyMemberUpdateMessage> messagePublisher)
|
||||||
|
{
|
||||||
|
_familyMembershipManager = familyMembershipManager;
|
||||||
|
_messagePublisher = messagePublisher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleAsync(FamilyMemberTodayMessage notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
FamilyMembershipDto familyMember = await _familyMembershipManager.GetFamilyMembershipByCharacterIdAsync(notification.CharacterId);
|
||||||
|
if (familyMember == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
familyMember.DailyMessage = notification.Message;
|
||||||
|
|
||||||
|
await _familyMembershipManager.SaveFamilyMembershipAsync(familyMember);
|
||||||
|
await _messagePublisher.PublishAsync(new FamilyMemberUpdateMessage
|
||||||
|
{
|
||||||
|
ChangedInfoMemberUpdate = ChangedInfoMemberUpdate.DailyMessage,
|
||||||
|
UpdatedMembers = new List<FamilyMembershipDto>
|
||||||
|
{
|
||||||
|
familyMember
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FamilyServer.Services;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using WingsAPI.Communication.Families;
|
||||||
|
|
||||||
|
namespace FamilyServer.Consumers
|
||||||
|
{
|
||||||
|
public class FamilyMissionsResetMessageConsumer : IMessageConsumer<FamilyMissionsResetMessage>
|
||||||
|
{
|
||||||
|
private readonly FamilyService _familyService;
|
||||||
|
|
||||||
|
public FamilyMissionsResetMessageConsumer(FamilyService familyService) => _familyService = familyService;
|
||||||
|
|
||||||
|
public async Task HandleAsync(FamilyMissionsResetMessage notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
await _familyService.ResetFamilyMissions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
srcs/FamilyServer/Consumers/FamilyNoticeMessageConsumer.cs
Normal file
42
srcs/FamilyServer/Consumers/FamilyNoticeMessageConsumer.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FamilyServer.Managers;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using Plugin.FamilyImpl.Messages;
|
||||||
|
using WingsAPI.Data.Families;
|
||||||
|
|
||||||
|
namespace FamilyServer.Consumers
|
||||||
|
{
|
||||||
|
public class FamilyNoticeMessageConsumer : IMessageConsumer<FamilyNoticeMessage>
|
||||||
|
{
|
||||||
|
private readonly FamilyManager _familyManager;
|
||||||
|
private readonly IMessagePublisher<FamilyUpdateMessage> _messagePublisher;
|
||||||
|
|
||||||
|
public FamilyNoticeMessageConsumer(FamilyManager familyManager, IMessagePublisher<FamilyUpdateMessage> messagePublisher)
|
||||||
|
{
|
||||||
|
_familyManager = familyManager;
|
||||||
|
_messagePublisher = messagePublisher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleAsync(FamilyNoticeMessage notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
long familyId = notification.FamilyId;
|
||||||
|
string message = notification.Message;
|
||||||
|
|
||||||
|
FamilyDTO familyDto = await _familyManager.GetFamilyByFamilyIdAsync(familyId);
|
||||||
|
if (familyDto == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
familyDto.Message = message;
|
||||||
|
await _familyManager.SaveFamilyAsync(familyDto);
|
||||||
|
|
||||||
|
await _messagePublisher.PublishAsync(new FamilyUpdateMessage
|
||||||
|
{
|
||||||
|
ChangedInfoFamilyUpdate = ChangedInfoFamilyUpdate.Notice,
|
||||||
|
Families = new[] { familyDto }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PhoenixLib.ServiceBus;
|
||||||
|
using WingsAPI.Communication.Services.Messages;
|
||||||
|
|
||||||
|
namespace FamilyServer.Consumers
|
||||||
|
{
|
||||||
|
public class ServiceFlushAllMessageConsumer : IMessageConsumer<ServiceFlushAllMessage>
|
||||||
|
{
|
||||||
|
private readonly FamilySystem _familySystem;
|
||||||
|
|
||||||
|
public ServiceFlushAllMessageConsumer(FamilySystem familySystem) => _familySystem = familySystem;
|
||||||
|
|
||||||
|
public async Task HandleAsync(ServiceFlushAllMessage notification, CancellationToken token)
|
||||||
|
{
|
||||||
|
await _familySystem.ProcessMain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
srcs/FamilyServer/DockerGracefulStopService.cs
Normal file
54
srcs/FamilyServer/DockerGracefulStopService.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.Loader;
|
||||||
|
using System.Threading;
|
||||||
|
using PhoenixLib.Logging;
|
||||||
|
|
||||||
|
namespace FamilyServer
|
||||||
|
{
|
||||||
|
public class DockerGracefulStopService : IDisposable
|
||||||
|
{
|
||||||
|
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||||
|
private readonly ManualResetEventSlim _stoppedEvent;
|
||||||
|
|
||||||
|
public DockerGracefulStopService()
|
||||||
|
{
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
_stoppedEvent = new ManualResetEventSlim();
|
||||||
|
|
||||||
|
// SIGINT
|
||||||
|
Console.CancelKeyPress += (sender, eventArgs) =>
|
||||||
|
{
|
||||||
|
Log.Error("[GRACEFUL_SHUTDOWN] SIGINT received", new Exception("[GRACEFUL_SHUTDOWN] SIGINT received"));
|
||||||
|
GracefulStop(_cancellationTokenSource, _stoppedEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
// SIGTERM
|
||||||
|
AssemblyLoadContext.Default.Unloading += context =>
|
||||||
|
{
|
||||||
|
Log.Error("[GRACEFUL_SHUTDOWN] SIGTERM received", new Exception("[GRACEFUL_SHUTDOWN] SIGTERM received"));
|
||||||
|
GracefulStop(_cancellationTokenSource, _stoppedEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
|
||||||
|
{
|
||||||
|
Log.Error("UnhandledException", args.ExceptionObject as Exception);
|
||||||
|
GracefulStop(_cancellationTokenSource, _stoppedEvent);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public CancellationToken CancellationToken => _cancellationTokenSource.Token;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_stoppedEvent.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GracefulStop(CancellationTokenSource cancellationTokenSource, ManualResetEventSlim stoppedEvent)
|
||||||
|
{
|
||||||
|
Log.Info("DockerGracefulStopService Stopping service");
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
stoppedEvent.Wait();
|
||||||
|
Log.Info("DockerGracefulStopService Stop finished");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
srcs/FamilyServer/EnvironmentConsts.cs
Normal file
7
srcs/FamilyServer/EnvironmentConsts.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace FamilyServer
|
||||||
|
{
|
||||||
|
public static class EnvironmentConsts
|
||||||
|
{
|
||||||
|
public const string FamilyServerSaveIntervalMinutes = "FAMILY_SERVER_SAVE_INTERVAL_MINUTES";
|
||||||
|
}
|
||||||
|
}
|
||||||
29
srcs/FamilyServer/FamilyServer.csproj
Normal file
29
srcs/FamilyServer/FamilyServer.csproj
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
<OutputPath>..\..\dist\family-server\</OutputPath>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Grpc.AspNetCore" Version="2.38.0" />
|
||||||
|
<PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Caching\PhoenixLib.Caching.csproj" />
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Configuration\PhoenixLib.Configuration.csproj" />
|
||||||
|
<ProjectReference Include="..\PhoenixLib.DAL.Redis\PhoenixLib.DAL.Redis.csproj" />
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Events\PhoenixLib.Events.csproj" />
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Logging\PhoenixLib.Logging.csproj" />
|
||||||
|
<ProjectReference Include="..\PhoenixLib.Messaging\PhoenixLib.Messaging.csproj" />
|
||||||
|
<ProjectReference Include="..\WingsEmu.Communication.gRPC\WingsEmu.Communication.gRPC.csproj" />
|
||||||
|
<ProjectReference Include="..\WingsEmu.Health\WingsEmu.Health.csproj" />
|
||||||
|
<ProjectReference Include="..\_plugins\Plugin.FamilyImpl\Plugin.FamilyImpl.csproj" />
|
||||||
|
<ProjectReference Include="..\_plugins\Plugin.DB.EF\Plugin.DB.EF.csproj" />
|
||||||
|
<ProjectReference Include="..\_plugins\WingsEmu.Plugins.DistributedGameEvents\WingsEmu.Plugins.DistributedGameEvents.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue