Добавьте файлы проекта.
This commit is contained in:
commit
897e3bce74
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
|
||||
363
.gitignore
vendored
Normal file
363
.gitignore
vendored
Normal file
@ -0,0 +1,363 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Oo]ut/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.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
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# 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
|
||||
# Note: 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
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# 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
|
||||
*.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
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# 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
|
||||
orleans.codegen.cs
|
||||
|
||||
# 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)
|
||||
#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
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# 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/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# 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/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
661
LICENSE
Normal file
661
LICENSE
Normal file
@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
73
README.md
Normal file
73
README.md
Normal file
@ -0,0 +1,73 @@
|
||||
|
||||
|
||||

|
||||
|
||||
# DiIiS Project
|
||||
|
||||
DiIiS is a fully-functional open-source server for [Diablo III: Reaper of Souls](https://eu.diablo3.blizzard.com)
|
||||
|
||||
## Features
|
||||
|
||||
- Implemented account creation system, authorization and lobby.
|
||||
- Fully implemented chat system.
|
||||
- Fully implemented clan system.
|
||||
- Opened all cosmetics in the in-game store.
|
||||
- Implemented basic DRLG.
|
||||
- Implemented item generator with in-game affixes.
|
||||
- Implemented the basic mechanics of almost all active abilities for all classes.
|
||||
- Implemented a system of set items.
|
||||
- Implemented all main scripts for all story quests 5 acts.
|
||||
- Implemented basic scripts and generator for "Adventure Mode".
|
||||
- Created the basis for the "Challenge Nephalem Rifts" mode.
|
||||
- Implemented artificial intelligence for 80% of minions.
|
||||
- Implemented personal artificial intelligence for 40% of all monsters.
|
||||
- Implemented personal artificial intelligence for half of the Bosses.
|
||||
- Implemented LAN
|
||||
|
||||
## Installation
|
||||
|
||||
1. Install [PostgreSQL 9.5.25](https://www.enterprisedb.com/downloads/postgres-postgresql-downloads).
|
||||
2. Create databases in PostgreSQL: `diiis` and `worlds`
|
||||
3. Change you account and password in `database.Account.config` and `database.Worlds.conifg`
|
||||
4. Restore `worlds.backup` to `worlds` database
|
||||
5. Compile by [VS 2019/2022](https://visualstudio.microsoft.com/)
|
||||
6. Launch wait until server start, it creates a hierarchy.
|
||||
7. Install certificate `bnetserver.p12`, password - `123` (the game verifies the CA root certificates).
|
||||
8. Use Client Diablo 3 `2.7.3.82785`.
|
||||
9. And add redirects to the `hosts` file (`%WinDir%\System32\drivers\etc\hosts`):
|
||||
`127.0.0.1 us.actual.battle.net`
|
||||
`127.0.0.1 eu.actual.battle.net`
|
||||
10. Enter to Game =)
|
||||
|
||||
## Playing with friends
|
||||
|
||||
1. Create new accounts using the console command:
|
||||
`!account add Login Password Tag`
|
||||
2. Copy the [config.ini](configs/config.ini) file to the server folder (It overwrites the default settings)
|
||||
3. In the IP fields - write your IP within the network. Update the parameter entries: `BindIP` and `PublicIP`.
|
||||
4. Other players must specify your IP address in the `hosts` file (`%WinDir%\System32\drivers\etc\hosts`).
|
||||
Example:
|
||||
`192.168.1.1 us.actual.battle.net`
|
||||
`192.168.1.1 eu.actual.battle.net`
|
||||
5. After that, when creating a game (in client), indicate the creation of a public game.
|
||||
6. "Starting the game"
|
||||
7. Other players, when connecting, must also indicate a public game, and at the start they will connect to you.
|
||||
|
||||
## Flexible configuration
|
||||
|
||||
Using the configuration file you can easily override the [global world parameters](docs/game-world-settings.md).
|
||||
|
||||
## Minimum system requirements
|
||||
|
||||
Minimum system requirements for server
|
||||
|
||||
- CPU: Xeon E5-2620V3 (2.40 GHz and 6 cores)
|
||||
- RAM: 4GB
|
||||
- HDD/SSD: 500MB
|
||||
|
||||
## Screenshots
|
||||
|
||||
You can see more screenshots [here](SCREENSHOTS.md)
|
||||
|
||||

|
||||
|
||||
16
SCREENSHOTS.md
Normal file
16
SCREENSHOTS.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Screenshots
|
||||
|
||||
Character selection
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Unlocked collections
|
||||
|
||||

|
||||
|
||||
LAN party
|
||||
|
||||

|
||||
|
||||
39
configs/config.ini
Normal file
39
configs/config.ini
Normal file
@ -0,0 +1,39 @@
|
||||
; Settings for Bnet
|
||||
[Battle-Server]
|
||||
Enabled = true
|
||||
BindIP = 127.0.0.1
|
||||
WebPort = 9800
|
||||
Port = 1119
|
||||
BindIPv6 = ::1
|
||||
MOTD = Welcome to Diablo 3!
|
||||
|
||||
[IWServer]
|
||||
IWServer = false
|
||||
|
||||
; Settings for REST
|
||||
[REST]
|
||||
IP = 127.0.0.1
|
||||
Public = true
|
||||
PublicIP = 127.0.0.1
|
||||
PORT = 80
|
||||
|
||||
; Settings for game
|
||||
[Game-Server]
|
||||
Enabled = true
|
||||
CoreActive = true
|
||||
BindIP = 127.0.0.1
|
||||
WebPort = 9100
|
||||
Port = 2001
|
||||
BindIPv6 = ::1
|
||||
DRLGemu = true
|
||||
;Modding of game
|
||||
RateExp = 1
|
||||
RateMoney = 1
|
||||
RateDrop = 1
|
||||
RateChangeDrop = 1
|
||||
RateMonsterHP = 1
|
||||
RateMonsterDMG = 1
|
||||
|
||||
[NAT]
|
||||
Enabled = False
|
||||
PublicIP = 127.0.0.1
|
||||
29
docs/game-world-settings.md
Normal file
29
docs/game-world-settings.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Global settings for game
|
||||
|
||||
Using the configuration file you can easily change the parameters of the world.
|
||||
|
||||
## Configuration
|
||||
|
||||
Apply parameters in `config.ini` file to the server folder (It overwrites the default settings)
|
||||
|
||||
```ini
|
||||
[Game-Server]
|
||||
RateExp = 1
|
||||
RateMoney = 1
|
||||
RateDrop = 1
|
||||
RateChangeDrop = 1
|
||||
RateMonsterHP = 1
|
||||
RateMonsterDMG = 1
|
||||
```
|
||||
|
||||
## Description
|
||||
|
||||
| Key | Description |
|
||||
| ---------------- | ------------------------- |
|
||||
| `RateExp` | Experience multiplier |
|
||||
| `RateMoney` | Currency multiplier |
|
||||
| `RateDrop` | Drop quantity multiplier |
|
||||
| `RateChangeDrop` | Drop quality multiplier |
|
||||
| `RateMonsterHP` | Monsters HP multiplier |
|
||||
| `RateMonsterDMG` | Monster damage multiplier |
|
||||
|
||||
BIN
pictures/game-collection-1.png
Normal file
BIN
pictures/game-collection-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
BIN
pictures/ingame-screen-1.png
Normal file
BIN
pictures/ingame-screen-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 MiB |
BIN
pictures/lan-party.png
Normal file
BIN
pictures/lan-party.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
BIN
pictures/logo.png
Normal file
BIN
pictures/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
BIN
pictures/select-char-1.png
Normal file
BIN
pictures/select-char-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
BIN
pictures/select-char-2.png
Normal file
BIN
pictures/select-char-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
31
src/Blizzless-D3.sln
Normal file
31
src/Blizzless-D3.sln
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.1.32210.238
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blizzless", "DiIiS-NA\Blizzless.csproj", "{535AC91E-54D1-4044-B4A5-B78AE1570EE3}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BZNET", "DiIiSNet\BZNET.csproj", "{73D9E87F-1F75-4D94-A47B-29354B9BCF13}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{535AC91E-54D1-4044-B4A5-B78AE1570EE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{535AC91E-54D1-4044-B4A5-B78AE1570EE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{535AC91E-54D1-4044-B4A5-B78AE1570EE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{535AC91E-54D1-4044-B4A5-B78AE1570EE3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{73D9E87F-1F75-4D94-A47B-29354B9BCF13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{73D9E87F-1F75-4D94-A47B-29354B9BCF13}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{73D9E87F-1F75-4D94-A47B-29354B9BCF13}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{73D9E87F-1F75-4D94-A47B-29354B9BCF13}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {20C6ADD1-DDE9-4A25-8C51-B27C47164D5B}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Binary file not shown.
71
src/DiIiS-NA/App.config
Normal file
71
src/DiIiS-NA/App.config
Normal file
@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
|
||||
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
|
||||
</configSections>
|
||||
<!-- database configuration-->
|
||||
<startup>
|
||||
<system.data>
|
||||
<DbProviderFactories>
|
||||
<remove invariant="Microsoft.Data.Sqlite"/>
|
||||
<add name="SQLite Data Provider" invariant="Microsoft.Data.Sqlite" description=".Net Framework Data Provider for SQLite" type="Microsoft.Data.Sqlite.SqliteFactory, Microsoft.Data.Sqlite"/>
|
||||
<remove invariant="Npgsql"/>
|
||||
<add name="Npgsql Data Provider" invariant="Npgsql" description=".Net Data Provider for PostgreSQL" type="Npgsql.NpgsqlFactory, Npgsql, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" support="FF"/>
|
||||
</DbProviderFactories>
|
||||
</system.data>
|
||||
</startup>
|
||||
|
||||
<!-- nhibernate configuration -->
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="NHibernate" publicKeyToken="aa95f207798dfdb4" />
|
||||
<bindingRedirect oldVersion="3.0.0.3001" newVersion="5.3.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.4.1" newVersion="4.0.4.1" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.InteropServices.RuntimeInformation" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
||||
<!-- network-debugging configuration - useful for debugging ssl-connection. -->
|
||||
<!-- remove the line if you want to see TLS connection debugging info!
|
||||
<system.diagnostics>
|
||||
<sources>
|
||||
<source name="System.Net" tracemode="includehex" maxdatasize="1024">
|
||||
<listeners>
|
||||
<add name="System.Net"/>
|
||||
</listeners>
|
||||
</source>
|
||||
<source name="System.Net.Sockets">
|
||||
<listeners>
|
||||
<add name="System.Net"/>
|
||||
</listeners>
|
||||
</source>
|
||||
<source name="System.Net.Cache">
|
||||
<listeners>
|
||||
<add name="System.Net"/>
|
||||
</listeners>
|
||||
</source>
|
||||
</sources>
|
||||
<switches>
|
||||
<add name="System.Net" value="Verbose"/>
|
||||
<add name="System.Net.Sockets" value="Verbose"/>
|
||||
<add name="System.Net.Cache" value="Verbose"/>
|
||||
</switches>
|
||||
<sharedListeners>
|
||||
<add name="System.Net"
|
||||
type="System.Diagnostics.TextWriterTraceListener"
|
||||
initializeData="ssl.log"
|
||||
/>
|
||||
</sharedListeners>
|
||||
<trace autoflush="true"/>
|
||||
</system.diagnostics>
|
||||
-->
|
||||
</configuration>
|
||||
513
src/DiIiS-NA/BGS-Server/AccountsSystem/Account.cs
Normal file
513
src/DiIiS-NA/BGS-Server/AccountsSystem/Account.cs
Normal file
@ -0,0 +1,513 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Objects;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.presence.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage.AccountDataBase.Entities;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Helpers;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Extensions;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Crypthography;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.AccountsSystem
|
||||
{
|
||||
public class Account : PersistentRPCObject
|
||||
{
|
||||
private DBAccount _dbAccount = null; //may be cached forever, as only MooNetServer changes it
|
||||
public DBAccount DBAccount
|
||||
{
|
||||
get
|
||||
{
|
||||
return _dbAccount;
|
||||
}
|
||||
set
|
||||
{
|
||||
_dbAccount = value;
|
||||
}
|
||||
}
|
||||
|
||||
//public D3.PartyMessage.ScreenStatus ScreenStatus { get; set; }
|
||||
|
||||
public ByteStringPresenceField<D3.OnlineService.EntityId> LastPlayedGameAccountIdField
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = new ByteStringPresenceField<D3.OnlineService.EntityId>(FieldKeyHelper.Program.D3, FieldKeyHelper.OriginatingClass.Account, 2, 0, this.LastSelectedGameAccount);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
public ByteStringPresenceField<D3.OnlineService.EntityId> LastPlayedToonIdField
|
||||
{
|
||||
get
|
||||
{
|
||||
ByteStringPresenceField<D3.OnlineService.EntityId> val = null;
|
||||
if (this.GameAccount.CurrentToon != null)
|
||||
val = new ByteStringPresenceField<D3.OnlineService.EntityId>(FieldKeyHelper.Program.D3, FieldKeyHelper.OriginatingClass.Account, 1, 0, this.GameAccount.CurrentToon.D3EntityID);
|
||||
else
|
||||
{
|
||||
var Fake = D3.OnlineService.EntityId.CreateBuilder().SetIdHigh(0).SetIdLow(0);
|
||||
val = new ByteStringPresenceField<D3.OnlineService.EntityId>(FieldKeyHelper.Program.D3, FieldKeyHelper.OriginatingClass.Account, 1, 0, Fake.Build());
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
public StringPresenceField RealIDTagField
|
||||
{
|
||||
get
|
||||
{
|
||||
return new StringPresenceField(FieldKeyHelper.Program.BNet, FieldKeyHelper.OriginatingClass.Account, 1, 0, string.Format(""));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public BoolPresenceField AccountOnlineField
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = new BoolPresenceField(FieldKeyHelper.Program.BNet, FieldKeyHelper.OriginatingClass.Account, 2, 0, this.IsOnline);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
public StringPresenceField AccountBattleTagField
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = new StringPresenceField(FieldKeyHelper.Program.BNet, FieldKeyHelper.OriginatingClass.Account, 4, 0, this.BattleTagName + "#" + HashCode.ToString("D4"));
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
public StringPresenceField BroadcastMessageField
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = new StringPresenceField(FieldKeyHelper.Program.BNet, FieldKeyHelper.OriginatingClass.Account, 2, 0, this.BroadcastMessage);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
public EntityIdPresenceFieldList GameAccountListField
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = new EntityIdPresenceFieldList(FieldKeyHelper.Program.BNet, FieldKeyHelper.OriginatingClass.Account, 3, 0);
|
||||
val.Value.Add(this.GameAccount.BnetEntityId);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
public ulong LastOnline = 1;
|
||||
|
||||
public IntPresenceField LastOnlineField
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = new IntPresenceField(FieldKeyHelper.Program.BNet, FieldKeyHelper.OriginatingClass.Account, 6, 0, 0);
|
||||
val.Value = (long)this.LastOnline;
|
||||
return val;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.LastOnline = (ulong)value.Value;
|
||||
this.DBAccount.LastOnline = (ulong)value.Value;
|
||||
DBSessions.SessionUpdate(this.DBAccount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public bool IsOnline
|
||||
{
|
||||
get
|
||||
{
|
||||
//check if gameAccount is online
|
||||
return GameAccount.IsOnline;
|
||||
}
|
||||
set
|
||||
{
|
||||
GameAccount.IsOnline = value;
|
||||
}
|
||||
}
|
||||
|
||||
public List<ulong> FriendsIds = new List<ulong>();
|
||||
|
||||
public List<ulong> IgnoreIds = new List<ulong>();
|
||||
|
||||
public string Email
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.DBAccount.Email;
|
||||
}
|
||||
private set
|
||||
{
|
||||
}
|
||||
}
|
||||
public string SaltedTicket
|
||||
{
|
||||
get { return this.DBAccount.SaltedTicket; }
|
||||
internal set
|
||||
{
|
||||
this.DBAccount.SaltedTicket = value;
|
||||
DBSessions.SessionUpdate(this.DBAccount);
|
||||
}
|
||||
}
|
||||
public byte[] Salt
|
||||
{
|
||||
get { return this.DBAccount.Salt.ToArray(); }
|
||||
internal set
|
||||
{
|
||||
this.DBAccount.Salt = value;
|
||||
DBSessions.SessionUpdate(this.DBAccount);
|
||||
}
|
||||
} // s- User's salt.
|
||||
public byte[] FullSalt
|
||||
{
|
||||
get { return this.DBAccount.Salt.ToArray(); }
|
||||
} // s- User's salt.
|
||||
|
||||
public byte[] PasswordVerifier
|
||||
{
|
||||
get { return this.DBAccount.PasswordVerifier; }
|
||||
internal set
|
||||
{
|
||||
this.DBAccount.PasswordVerifier = value;
|
||||
DBSessions.SessionUpdate(this.DBAccount);
|
||||
}
|
||||
} // v - password verifier.
|
||||
|
||||
public int HashCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.DBAccount.HashCode;
|
||||
}
|
||||
private set
|
||||
{
|
||||
this.DBAccount.HashCode = value;
|
||||
DBSessions.SessionUpdate(this.DBAccount);
|
||||
}
|
||||
}
|
||||
|
||||
public string BattleTagName
|
||||
{
|
||||
get
|
||||
{
|
||||
bool staff = (this.DBAccount.UserLevel > Account.UserLevels.Tester);
|
||||
//(controller as HandlerController).Client.Account.GameAccount.ProgramField.Value
|
||||
if(this.GameAccount.ProgramField.Value == "APP")
|
||||
return string.Format("{0}", this.DBAccount.BattleTagName);
|
||||
else if (this.GameAccount.ProgramField.Value == "D3")
|
||||
return string.Format("{0}", this.DBAccount.BattleTagName);
|
||||
//return string.Format(staff ? " {{icon:bnet}} {{c_legendary}}{0}{{/c}}" : ("{0}"), this.DBAccount.BattleTagName);
|
||||
else
|
||||
return string.Format("{0}", this.DBAccount.BattleTagName);
|
||||
//return (staff ? " {icon:bnet} " : (premium ? " {icon:gold} " : "")) + dbAcc.BattleTagName;
|
||||
} //{c_blue}{/c}
|
||||
private set
|
||||
{
|
||||
this.DBAccount.BattleTagName = value;
|
||||
DBSessions.SessionUpdate(this.DBAccount);
|
||||
}
|
||||
}
|
||||
|
||||
public string BattleTag
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.BattleTagName + "#" + this.HashCode.ToString("D4");
|
||||
}
|
||||
set
|
||||
{
|
||||
if (!value.Contains('#'))
|
||||
throw new Exception("BattleTag must contain '#'");
|
||||
|
||||
var split = value.Split('#');
|
||||
this.DBAccount.BattleTagName = split[0];
|
||||
this.DBAccount.HashCode = Convert.ToInt32(split[1]);
|
||||
DBSessions.SessionUpdate(this.DBAccount);
|
||||
}
|
||||
}
|
||||
|
||||
public UserLevels UserLevel
|
||||
{
|
||||
get { return this.DBAccount.UserLevel; }
|
||||
internal set
|
||||
{
|
||||
this.DBAccount.UserLevel = value;
|
||||
DBSessions.SessionUpdate(this.DBAccount);
|
||||
}
|
||||
} // user level for account.
|
||||
|
||||
public long MuteTime = 0;
|
||||
public int GiftsSent = 0;
|
||||
|
||||
private GameAccount _currentGameAccount;
|
||||
public ulong CurrentGameAccountId = 0;
|
||||
public GameAccount GameAccount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.CurrentGameAccountId == 0) return null;
|
||||
if (this._currentGameAccount == null)
|
||||
this._currentGameAccount = GameAccountManager.GetAccountByPersistentID(this.CurrentGameAccountId);
|
||||
|
||||
return this._currentGameAccount;
|
||||
}
|
||||
set
|
||||
{
|
||||
this._currentGameAccount = value;
|
||||
this.CurrentGameAccountId = value.PersistentID;
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly D3.OnlineService.EntityId AccountHasNoToons =
|
||||
D3.OnlineService.EntityId.CreateBuilder().SetIdHigh(0).SetIdLow(0).Build();
|
||||
|
||||
public D3.OnlineService.EntityId LastSelectedGameAccount
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.GameAccount.D3GameAccountId;
|
||||
}
|
||||
}
|
||||
|
||||
public string BroadcastMessage = "";
|
||||
|
||||
public Account(DBAccount dbAccount)
|
||||
: base(dbAccount.Id)
|
||||
{
|
||||
this.DBAccount = dbAccount;
|
||||
var account_relations = DBSessions.SessionQueryWhere<DBAccountLists>(dbl => dbl.ListOwner.Id == this.PersistentID);
|
||||
this.FriendsIds = new HashSet<ulong>(account_relations.Where(dbl => dbl.Type == "FRIEND").Select(a => a.ListTarget.Id)).ToList();
|
||||
this.IgnoreIds = new HashSet<ulong>(account_relations.Where(dbl => dbl.Type == "IGNORE").Select(a => a.ListTarget.Id)).ToList();
|
||||
this.LastOnline = dbAccount.LastOnline;
|
||||
SetFields();
|
||||
}
|
||||
|
||||
|
||||
private void SetFields()
|
||||
{
|
||||
this.BnetEntityId = bgs.protocol.EntityId.CreateBuilder().SetHigh((ulong)EntityIdHelper.HighIdType.AccountId).SetLow(this.PersistentID).Build();
|
||||
}
|
||||
|
||||
public void Update(IList<FieldOperation> operations)
|
||||
{
|
||||
List<FieldOperation> operationsToUpdate = new List<FieldOperation>();
|
||||
foreach (var operation in operations)
|
||||
{
|
||||
switch (operation.Operation)
|
||||
{
|
||||
case FieldOperation.Types.OperationType.SET:
|
||||
var op_build = DoSet(operation.Field);
|
||||
if (op_build.HasValue)
|
||||
{
|
||||
var new_op = operation.ToBuilder();
|
||||
new_op.SetField(op_build);
|
||||
operationsToUpdate.Add(new_op.Build());
|
||||
}
|
||||
break;
|
||||
case FieldOperation.Types.OperationType.CLEAR:
|
||||
DoClear(operation.Field);
|
||||
break;
|
||||
default:
|
||||
Logger.Warn("No operation type.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (operationsToUpdate.Count > 0)
|
||||
base.UpdateSubscribers(Subscribers, operationsToUpdate);
|
||||
}
|
||||
|
||||
private Field.Builder DoSet(Field field)
|
||||
{
|
||||
FieldOperation.Builder operation = FieldOperation.CreateBuilder();
|
||||
|
||||
Field.Builder returnField = Field.CreateBuilder().SetKey(field.Key);
|
||||
if (this.GameAccount.LoggedInClient == null) return returnField;
|
||||
|
||||
switch ((FieldKeyHelper.Program)field.Key.Program)
|
||||
{
|
||||
case FieldKeyHelper.Program.D3:
|
||||
returnField.SetValue((field.Value));
|
||||
Logger.Trace("{0} set Unknown D3:{1}:{2} to {3}", this, field.Key.Group, field.Key.Field, field.Value.IntValue);
|
||||
break;
|
||||
case FieldKeyHelper.Program.BNet:
|
||||
returnField.SetValue((field.Value));
|
||||
if (field.Key.Group == 1 && field.Key.Field == 2) // Account's broadcast message
|
||||
{
|
||||
Logger.Trace("{0} set broadcast message to {1}.", this, field.Value.StringValue);
|
||||
this.BroadcastMessage = field.Value.StringValue;
|
||||
}
|
||||
else if (field.Key.Group == 1 && field.Key.Field == 7) // Account's AFK status
|
||||
{
|
||||
Logger.Trace("{0} set AFK to {1}.", this, field.Value.IntValue);
|
||||
}
|
||||
else if (field.Key.Group == 1 && field.Key.Field == 11) // Account is busy (bool)
|
||||
{
|
||||
Logger.Trace("{0} set AwayStatus to {1}.", this, field.Value.BoolValue);
|
||||
|
||||
}
|
||||
else if (field.Key.Group == 1 && field.Key.Field == 12) // Account is busy (bool)
|
||||
{
|
||||
Logger.Trace("{0} set AwayStatus to {1}.", this, field.Value.BoolValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("Account Unknown set-key: {0}, {1}, {2}", field.Key.Program, field.Key.Group, field.Key.Field);
|
||||
}
|
||||
break;
|
||||
}
|
||||
//We only update subscribers on fields that actually change values.
|
||||
return returnField;
|
||||
}
|
||||
|
||||
private void DoClear(Field field)
|
||||
{
|
||||
/*switch ((FieldKeyHelper.Program)field.Key.Program)
|
||||
{
|
||||
case FieldKeyHelper.Program.D3:
|
||||
Logger.Warn("Account: Unknown clear-field: {0}, {1}, {2}", field.Key.Program, field.Key.Group,
|
||||
field.Key.Field);
|
||||
break;
|
||||
case FieldKeyHelper.Program.BNet:
|
||||
Logger.Warn("Account: Unknown clear-field: {0}, {1}, {2}", field.Key.Program, field.Key.Group,
|
||||
field.Key.Field);
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.Field QueryField(bgs.protocol.presence.v1.FieldKey queryKey)
|
||||
{
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(queryKey);
|
||||
|
||||
switch ((FieldKeyHelper.Program)queryKey.Program)
|
||||
{
|
||||
case FieldKeyHelper.Program.D3:
|
||||
if (queryKey.Group == 1 && queryKey.Field == 1) // Account's last selected toon.
|
||||
{
|
||||
if (this.IsOnline) // check if the account is online actually.
|
||||
field.SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(this.GameAccount.LastPlayedHeroId.ToByteString()).Build());
|
||||
}
|
||||
else if (queryKey.Group == 1 && queryKey.Field == 2) // Account's last selected Game Account
|
||||
{
|
||||
if (this.IsOnline) // check if the account is online actually.
|
||||
field.SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(this.LastSelectedGameAccount.ToByteString()).Build());
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("Account Unknown query-key: {0}, {1}, {2}", queryKey.Program, queryKey.Group, queryKey.Field);
|
||||
}
|
||||
break;
|
||||
case FieldKeyHelper.Program.BNet:
|
||||
if (queryKey.Group == 1 && queryKey.Field == 4) // Account's battleTag
|
||||
{
|
||||
field.SetValue(bgs.protocol.Variant.CreateBuilder().SetStringValue(this.BattleTag).Build());
|
||||
}
|
||||
else if (queryKey.Group == 1 && queryKey.Field == 2) // Account's broadcast message
|
||||
{
|
||||
field.SetValue(bgs.protocol.Variant.CreateBuilder().SetStringValue(this.BroadcastMessage).Build());
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("Account Unknown query-key: {0}, {1}, {2}", queryKey.Program, queryKey.Group, queryKey.Field);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return field.HasValue ? field.Build() : null;
|
||||
}
|
||||
|
||||
#region Notifications
|
||||
|
||||
public override void NotifyUpdate()
|
||||
{
|
||||
var operations = ChangedFields.GetChangedFieldList();
|
||||
ChangedFields.ClearChanged();
|
||||
base.UpdateSubscribers(this.Subscribers, operations);
|
||||
}
|
||||
|
||||
//account class generated
|
||||
//D3, Account,1,0 -> D3.OnlineService.EntityId: Last Played Hero
|
||||
//D3, Account,2,0 -> LastSelectedGameAccount
|
||||
//Bnet, Account,1,0 -> RealId Name
|
||||
//Bnet, Account,3,index -> GameAccount EntityIds
|
||||
//Bnet, Account,4,0 -> BattleTag
|
||||
|
||||
public override List<bgs.protocol.presence.v1.FieldOperation> GetSubscriptionNotifications()
|
||||
{
|
||||
//TODO: Create delegate-move this out
|
||||
/*this.GameAccountListField.Value.Clear();
|
||||
foreach (var pair in this.GameAccounts)
|
||||
{
|
||||
this.GameAccountListField.Value.Add(pair.BnetEntityId);
|
||||
}*/
|
||||
|
||||
|
||||
var operationList = new List<bgs.protocol.presence.v1.FieldOperation>();
|
||||
//if (this.LastSelectedHero != AccountHasNoToons)
|
||||
//operationList.Add(this.LastPlayedHeroIdField.GetFieldOperation());
|
||||
if (this.LastSelectedGameAccount != AccountHasNoToons)
|
||||
{
|
||||
operationList.Add(this.LastPlayedToonIdField.GetFieldOperation());
|
||||
operationList.Add(this.LastPlayedGameAccountIdField.GetFieldOperation());
|
||||
}
|
||||
operationList.Add(this.RealIDTagField.GetFieldOperation());
|
||||
operationList.Add(this.AccountOnlineField.GetFieldOperation());
|
||||
operationList.AddRange(this.GameAccountListField.GetFieldOperationList());
|
||||
operationList.Add(this.AccountBattleTagField.GetFieldOperation());
|
||||
operationList.Add(this.BroadcastMessageField.GetFieldOperation());
|
||||
operationList.Add(this.LastOnlineField.GetFieldOperation());
|
||||
|
||||
return operationList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
public bool VerifyPassword(string password)
|
||||
{
|
||||
if (string.IsNullOrEmpty(password))
|
||||
return false;
|
||||
|
||||
if (password.Length < 8 || password.Length > 16)
|
||||
return false;
|
||||
|
||||
var calculatedVerifier = SRP6a.CalculatePasswordVerifierForAccount(this.Email, password, this.FullSalt);
|
||||
return calculatedVerifier.SequenceEqual(this.PasswordVerifier);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("{{ Account: {0} [lowId: {1}] }}", this.Email, this.BnetEntityId.Low);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User-levels.
|
||||
/// </summary>
|
||||
public enum UserLevels : byte
|
||||
{
|
||||
User,
|
||||
Tester,
|
||||
GM,
|
||||
Admin,
|
||||
Owner
|
||||
}
|
||||
}
|
||||
}
|
||||
309
src/DiIiS-NA/BGS-Server/AccountsSystem/AccountManager.cs
Normal file
309
src/DiIiS-NA/BGS-Server/AccountsSystem/AccountManager.cs
Normal file
@ -0,0 +1,309 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Extensions;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage.AccountDataBase.Entities;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Crypthography;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Toons;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Concurrent;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.AccountsSystem
|
||||
{
|
||||
public static class AccountManager
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger("DataBaseSystem");
|
||||
|
||||
public static int TotalAccounts
|
||||
{
|
||||
get { return DBSessions.SessionQuery<DBAccount>().Count(); }
|
||||
}
|
||||
|
||||
public static readonly ConcurrentDictionary<ulong, Account> LoadedAccounts = new ConcurrentDictionary<ulong, Account>();
|
||||
|
||||
public static void PreLoadAccounts()
|
||||
{
|
||||
Logger.Info("Loading accounts...");
|
||||
List<DBAccount> all_accounts = DBSessions.SessionQuery<DBAccount>();
|
||||
foreach (var account in all_accounts)
|
||||
{
|
||||
LoadedAccounts.TryAdd(account.Id, new Account(account));
|
||||
}
|
||||
}
|
||||
|
||||
#region AccountGetter
|
||||
public static Account CreateAccount(string email, string password, string battleTag, Account.UserLevels userLevel = Account.UserLevels.User)
|
||||
{
|
||||
if (password.Length > 16) password = password.Substring(0, 16); // make sure the password does not exceed 16 chars.
|
||||
var hashCode = GetRandomHashCodeForBattleTag();
|
||||
var salt = SRP6a.GetRandomBytes(32);
|
||||
var passwordVerifier = SRP6a.CalculatePasswordVerifierForAccount(email, password, salt);
|
||||
var saltedticket = password + " asa " + email;
|
||||
|
||||
var newDBAccount = new DBAccount
|
||||
{
|
||||
Email = email,
|
||||
Banned = false,
|
||||
Salt = salt,
|
||||
PasswordVerifier = passwordVerifier,
|
||||
SaltedTicket = saltedticket,
|
||||
BattleTagName = battleTag,
|
||||
UserLevel = userLevel,
|
||||
HashCode = hashCode
|
||||
};
|
||||
|
||||
DBSessions.SessionSave(newDBAccount);
|
||||
|
||||
//GenerateReferralCode(email);
|
||||
|
||||
Logger.Warn("Created account {0}", email);
|
||||
return GetAccountByEmail(email);
|
||||
}
|
||||
|
||||
public static bool GenerateReferralCode(string email)
|
||||
{
|
||||
try
|
||||
{
|
||||
var account = GetAccountByEmail(email);
|
||||
account.DBAccount.ReferralCode = ((int)(account.DBAccount.Id * 17) + Core.Helpers.Math.FastRandom.Instance.Next(16));
|
||||
DBSessions.SessionUpdate(account.DBAccount);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SetInvitee(string email, int invitee_code)
|
||||
{
|
||||
try
|
||||
{
|
||||
var invitee_account = DBSessions.SessionQuerySingle<DBAccount>(dba => dba.ReferralCode == invitee_code);
|
||||
var account = GetAccountByEmail(email);
|
||||
account.DBAccount.InviteeAccount = invitee_account;
|
||||
DBSessions.SessionUpdate(account.DBAccount);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Account GetAccountByEmail(string email)
|
||||
{
|
||||
//Logger.Debug("trying GetAccountByEmail {0}", email);
|
||||
email = email.ToLower();
|
||||
if (LoadedAccounts.Any(a => a.Value.Email == email))
|
||||
return LoadedAccounts[LoadedAccounts.Single(a => a.Value.Email == email).Key];
|
||||
else
|
||||
{
|
||||
List<DBAccount> dbAcc = DBSessions.SessionQueryWhere<DBAccount>(dba => dba.Email == email);
|
||||
if (dbAcc.Count() == 0)
|
||||
{
|
||||
Logger.Warn("GetAccountByEmail {0}: DBAccount is null!", email);
|
||||
return null;
|
||||
}
|
||||
if (dbAcc.First() == null)
|
||||
{
|
||||
Logger.Warn("GetAccountByEmail {0}: DBAccount id is null!", email);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
Logger.Debug("GetAccountByEmail id - \"{0}\"", dbAcc.First().Id);
|
||||
return GetAccountByDBAccount(dbAcc.First());
|
||||
}
|
||||
}
|
||||
|
||||
public static Account GetAccountByBattletag(string battletag)
|
||||
{
|
||||
string[] tagparts = battletag.Split(new[] { '#' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
int taghash = Convert.ToInt32(tagparts[1], 10);
|
||||
|
||||
if (tagparts[0].StartsWith(" {icon"))
|
||||
tagparts[0] = tagparts[0].Substring(13);
|
||||
//Logger.Debug("trying GetAccountByBattletag {0}", battletag);
|
||||
if (tagparts[0].StartsWith("{c_legendary"))
|
||||
{
|
||||
tagparts[0] = tagparts[0].Substring(13);
|
||||
tagparts[0] = tagparts[0].Split('{')[0];
|
||||
}
|
||||
List<DBAccount> dbAcc = DBSessions.SessionQueryWhere<DBAccount>(dba => dba.BattleTagName == tagparts[0] && dba.HashCode == taghash);
|
||||
if (dbAcc.Count() == 0)
|
||||
{
|
||||
Logger.Warn("GetAccountByBattletag {0}: DBAccount is null!", battletag);
|
||||
return null;
|
||||
}
|
||||
//else
|
||||
//Logger.Debug("GetAccountByBattletag \"{0}\"", battletag);
|
||||
return GetAccountByDBAccount(dbAcc.First());
|
||||
}
|
||||
|
||||
public static Account GetAccountByName(string btname) //pretty bad to use it
|
||||
{
|
||||
List<DBAccount> dbAcc = DBSessions.SessionQueryWhere<DBAccount>(dba => dba.BattleTagName == btname);
|
||||
if (dbAcc.Count() == 0)
|
||||
{
|
||||
Logger.Warn("GetAccountByName {0}: DBAccount is null!", btname);
|
||||
return null;
|
||||
}
|
||||
return GetAccountByDBAccount(dbAcc.First());
|
||||
}
|
||||
|
||||
public static Account GetAccountByPersistentID(ulong persistentId)
|
||||
{
|
||||
if (LoadedAccounts.ContainsKey(persistentId))
|
||||
return LoadedAccounts[persistentId];
|
||||
else
|
||||
{
|
||||
return GetAccountByDBAccount(DBSessions.SessionGet<DBAccount>(persistentId));
|
||||
}
|
||||
}
|
||||
|
||||
public static Account GetAccountByDBAccount(DBAccount dbAccount)
|
||||
{
|
||||
if (LoadedAccounts.ContainsKey(dbAccount.Id))
|
||||
return LoadedAccounts[dbAccount.Id];
|
||||
else
|
||||
{
|
||||
var account = new Account(dbAccount);
|
||||
LoadedAccounts.TryAdd(dbAccount.Id, account);
|
||||
return account;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Managing Functions, also extending Account
|
||||
|
||||
public static bool UpdatePassword(this Account account, string newPassword)
|
||||
{
|
||||
try
|
||||
{
|
||||
account.PasswordVerifier = SRP6a.CalculatePasswordVerifierForAccount(account.Email, newPassword, account.Salt);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorException(e, "UpdatePassword()");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool UpdateBattleTag(this Account account, string newName)
|
||||
{
|
||||
Logger.Info("Renaming account \"{0}\"", account.Email);
|
||||
try
|
||||
{
|
||||
if (account.DBAccount.HasRename == false) return false;
|
||||
int newHash = GetRandomHashCodeForBattleTag();
|
||||
Logger.RenameAccount("{0}#{1} -> {2}#{3}", account.DBAccount.BattleTagName, account.DBAccount.HashCode, newName, newHash);
|
||||
account.DBAccount.BattleTagName = newName;
|
||||
account.DBAccount.HashCode = newHash;
|
||||
account.DBAccount.HasRename = false;
|
||||
account.DBAccount.RenameCooldown = DateTime.Now.ToExtendedEpoch() + 2592000000000;
|
||||
DBSessions.SessionUpdate(account.DBAccount);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorException(e, "UpdatePassword()");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool UnlockRename(this Account account)
|
||||
{
|
||||
Logger.Info("Rename unlock for account \"{0}\"", account.Email);
|
||||
try
|
||||
{
|
||||
account.DBAccount.HasRename = true;
|
||||
DBSessions.SessionUpdate(account.DBAccount);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorException(e, "UnlockRename()");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Account GetAccountBySaltTicket(string ticket)
|
||||
{
|
||||
if (DBSessions.SessionQueryWhere<DBAccount>(dba => dba.SaltedTicket == ticket).Count() > 0)
|
||||
return LoadedAccounts[LoadedAccounts.Single(a => a.Value.SaltedTicket == ticket).Key];
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool AddMoney(this Account account, int money)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (money <= 0) return false;
|
||||
account.DBAccount.Money += (ulong)money;
|
||||
DBSessions.SessionUpdate(account.DBAccount);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorException(e, "AddMoney()");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SubMoney(this Account account, int money)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (money <= 0) return false;
|
||||
if (account.DBAccount.Money < (ulong)money) return false;
|
||||
account.DBAccount.Money -= (ulong)money;
|
||||
DBSessions.SessionUpdate(account.DBAccount);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorException(e, "SubMoney()");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateUserLevel(this Account account, Account.UserLevels userLevel)
|
||||
{
|
||||
try
|
||||
{
|
||||
account.UserLevel = userLevel;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorException(e, "UpdateUserLevel()");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
private static int GetRandomHashCodeForBattleTag()
|
||||
{
|
||||
var rnd = new Random();
|
||||
return rnd.Next(1, 10000);
|
||||
}
|
||||
}
|
||||
}
|
||||
1316
src/DiIiS-NA/BGS-Server/AccountsSystem/GameAccount.cs
Normal file
1316
src/DiIiS-NA/BGS-Server/AccountsSystem/GameAccount.cs
Normal file
File diff suppressed because it is too large
Load Diff
138
src/DiIiS-NA/BGS-Server/AccountsSystem/GameAccountManager.cs
Normal file
138
src/DiIiS-NA/BGS-Server/AccountsSystem/GameAccountManager.cs
Normal file
@ -0,0 +1,138 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage.AccountDataBase.Entities;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Concurrent;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.AccountsSystem
|
||||
{
|
||||
class GameAccountManager
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger("DataBaseSystem");
|
||||
|
||||
public static readonly ConcurrentDictionary<ulong, GameAccount> LoadedGameAccounts = new ConcurrentDictionary<ulong, GameAccount>();
|
||||
|
||||
public static int TotalAccounts
|
||||
{
|
||||
get { return DBSessions.SessionQuery<DBGameAccount>().Count(); }
|
||||
}
|
||||
|
||||
public static void PreLoadGameAccounts()
|
||||
{
|
||||
Logger.Info("Loading game data...");
|
||||
List<DBGameAccount> all_accounts = DBSessions.SessionQuery<DBGameAccount>();
|
||||
List<DBAchievements> all_achievements = DBSessions.SessionQuery<DBAchievements>();
|
||||
foreach (var account in all_accounts)
|
||||
{
|
||||
var gameAccount = new GameAccount(account, all_achievements);
|
||||
LoadedGameAccounts.TryAdd(account.Id, gameAccount);
|
||||
gameAccount.Owner.GameAccount = gameAccount;
|
||||
}
|
||||
}
|
||||
|
||||
public static GameAccount GetGameAccountByDBGameAccount(DBGameAccount dbGameAccount)
|
||||
{
|
||||
if (LoadedGameAccounts.ContainsKey(dbGameAccount.Id))
|
||||
return LoadedGameAccounts[dbGameAccount.Id];
|
||||
else
|
||||
{
|
||||
var account = new GameAccount(dbGameAccount);
|
||||
LoadedGameAccounts.TryAdd(dbGameAccount.Id, account);
|
||||
return account;
|
||||
}
|
||||
}
|
||||
|
||||
//Not needed... we emulate only D3, or not?
|
||||
/*
|
||||
public static Dictionary<ulong, GameAccount> GetGameAccountsForAccountProgram(Account account, FieldKeyHelper.Program program)
|
||||
{
|
||||
|
||||
return GameAccounts.Where(pair => pair.Value.Owner != null).Where(pair => (pair.Value.Owner.PersistentID == account.PersistentID) && (pair.Value.Program == program)).ToDictionary(pair => pair.Key, pair => pair.Value);
|
||||
}
|
||||
*/
|
||||
public static GameAccount GetAccountByPersistentID(ulong persistentId)
|
||||
{
|
||||
if (LoadedGameAccounts.ContainsKey(persistentId))
|
||||
return LoadedGameAccounts[persistentId];
|
||||
else
|
||||
return GetGameAccountByDBGameAccount(DBSessions.SessionGet<DBGameAccount>(persistentId));
|
||||
}
|
||||
|
||||
public static GameAccount CreateGameAccount(Account account)
|
||||
{
|
||||
var newDBGameAccount = new DBGameAccount
|
||||
{
|
||||
DBAccount = DBSessions.SessionGet<DBAccount>(account.PersistentID),
|
||||
Flags = 0,
|
||||
ParagonLevel = 0,
|
||||
ParagonLevelHardcore = 0,
|
||||
Experience = 7200000,
|
||||
ExperienceHardcore = 7200000,
|
||||
StashSize = 70,
|
||||
HardcoreStashSize = 70,
|
||||
SeasonStashSize = 70,
|
||||
BloodShards = 0,
|
||||
HardcoreBloodShards = 0,
|
||||
BossProgress = new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff },
|
||||
SeenTutorials = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
StashIcons = new byte[] { 0x00, 0x00, 0x00, 0x00 },
|
||||
RmtCurrency = 0,
|
||||
Gold = 0,
|
||||
HardcoreGold = 0,
|
||||
ElitesKilled = 0,
|
||||
TotalKilled = 0,
|
||||
TotalGold = 0,
|
||||
TotalBloodShards = 0,
|
||||
PvPTotalKilled = 0,
|
||||
PvPTotalWins = 0,
|
||||
PvPTotalGold = 0
|
||||
};
|
||||
|
||||
DBSessions.SessionSave(newDBGameAccount);
|
||||
|
||||
CreateArtisanProfile(newDBGameAccount, true, false, "Blacksmith");
|
||||
CreateArtisanProfile(newDBGameAccount, true, false, "Jeweler");
|
||||
CreateArtisanProfile(newDBGameAccount, true, false, "Mystic");
|
||||
|
||||
CreateArtisanProfile(newDBGameAccount, false, false, "Blacksmith");
|
||||
CreateArtisanProfile(newDBGameAccount, false, false, "Jeweler");
|
||||
CreateArtisanProfile(newDBGameAccount, false, false, "Mystic");
|
||||
|
||||
CreateArtisanProfile(newDBGameAccount, true, true, "Blacksmith");
|
||||
CreateArtisanProfile(newDBGameAccount, true, true, "Jeweler");
|
||||
CreateArtisanProfile(newDBGameAccount, true, true, "Mystic");
|
||||
|
||||
CreateArtisanProfile(newDBGameAccount, false, true, "Blacksmith");
|
||||
CreateArtisanProfile(newDBGameAccount, false, true, "Jeweler");
|
||||
CreateArtisanProfile(newDBGameAccount, false, true, "Mystic");
|
||||
|
||||
Logger.Warn("Created gameAccount {0}", account.Email);
|
||||
|
||||
var newGameAccount = GetGameAccountByDBGameAccount(newDBGameAccount);
|
||||
|
||||
account.GameAccount = newGameAccount;
|
||||
return newGameAccount;
|
||||
}
|
||||
|
||||
|
||||
public static void CreateArtisanProfile(DBGameAccount dbGAcc, bool hardcore, bool seasoned, string type)
|
||||
{
|
||||
var crafting = new DBCraft();
|
||||
crafting.Artisan = type;
|
||||
crafting.DBGameAccount = dbGAcc;
|
||||
crafting.isHardcore = hardcore;
|
||||
crafting.isSeasoned = seasoned;
|
||||
crafting.LearnedRecipes = new byte[0];
|
||||
crafting.Level = 1;
|
||||
DBSessions.SessionSave(crafting);
|
||||
}
|
||||
}
|
||||
}
|
||||
70
src/DiIiS-NA/BGS-Server/Base/BNetCodec.cs
Normal file
70
src/DiIiS-NA/BGS-Server/Base/BNetCodec.cs
Normal file
@ -0,0 +1,70 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using DotNetty.Codecs;
|
||||
//Blizzless Project 2022
|
||||
using DotNetty.Buffers;
|
||||
//Blizzless Project 2022
|
||||
using DotNetty.Codecs.Http.WebSockets;
|
||||
//Blizzless Project 2022
|
||||
using DotNetty.Transport.Channels;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Base
|
||||
{
|
||||
public class BNetCodec : MessageToMessageCodec<BinaryWebSocketFrame, BNetPacket>
|
||||
{
|
||||
protected override void Decode(IChannelHandlerContext ctx, BinaryWebSocketFrame msg, List<object> output)
|
||||
{
|
||||
if (msg.Content.ReadableBytes < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int headerSize = msg.Content.ReadUnsignedShort();
|
||||
if (msg.Content.ReadableBytes < headerSize)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byte[] headerBuf = new byte[headerSize];
|
||||
msg.Content.ReadBytes(headerBuf);
|
||||
Header header = Header.ParseFrom(headerBuf);
|
||||
|
||||
int payloadSize = header.HasSize ? (int)header.Size : msg.Content.ReadableBytes;
|
||||
if (msg.Content.ReadableBytes < payloadSize)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byte[] payload = new byte[payloadSize];
|
||||
msg.Content.ReadBytes(payload);
|
||||
|
||||
output.Add(new BNetPacket(header, payload));
|
||||
}
|
||||
|
||||
protected override void Encode(IChannelHandlerContext ctx, BNetPacket msg, List<object> output)
|
||||
{
|
||||
Header header = msg.GetHeader();
|
||||
var payload = msg.GetPayload();
|
||||
int headerSize = header.SerializedSize;
|
||||
int payloadSize = 0;
|
||||
if (payload != null)
|
||||
payloadSize = (payload as IMessage).SerializedSize;
|
||||
|
||||
IByteBuffer packet = DotNetty.Buffers.Unpooled.Buffer(2 + headerSize + payloadSize);
|
||||
|
||||
packet.WriteShort(headerSize);
|
||||
packet.WriteBytes(header.ToByteArray());
|
||||
if (payload != null)
|
||||
packet.WriteBytes((payload as IMessage).ToByteArray());
|
||||
|
||||
|
||||
output.Add(new BinaryWebSocketFrame(packet));
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
31
src/DiIiS-NA/BGS-Server/Base/BNetPacket.cs
Normal file
31
src/DiIiS-NA/BGS-Server/Base/BNetPacket.cs
Normal file
@ -0,0 +1,31 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Base
|
||||
{
|
||||
public class BNetPacket
|
||||
{
|
||||
private Header header;
|
||||
|
||||
private Object payload;
|
||||
|
||||
public BNetPacket(Header h, Object p)
|
||||
{
|
||||
header = h;
|
||||
payload = p;
|
||||
}
|
||||
|
||||
public Header GetHeader()
|
||||
{
|
||||
return header;
|
||||
}
|
||||
|
||||
public Object GetPayload()
|
||||
{
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
}
|
||||
378
src/DiIiS-NA/BGS-Server/Base/ConnectHandler.cs
Normal file
378
src/DiIiS-NA/BGS-Server/Base/ConnectHandler.cs
Normal file
@ -0,0 +1,378 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Battle;
|
||||
//Blizzless Project 2022
|
||||
using DotNetty.Codecs;
|
||||
//Blizzless Project 2022
|
||||
using DotNetty.Codecs.Http;
|
||||
//Blizzless Project 2022
|
||||
using DotNetty.Handlers.Tls;
|
||||
//Blizzless Project 2022
|
||||
using DotNetty.Transport.Channels;
|
||||
//Blizzless Project 2022
|
||||
using DotNetty.Transport.Channels.Sockets;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
|
||||
|
||||
//Blizzless Project 2022
|
||||
using DotNetty.Buffers;
|
||||
//Blizzless Project 2022
|
||||
using DotNetty.Common.Utilities;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading.Tasks;
|
||||
//Blizzless Project 2022
|
||||
using DotNetty.Codecs.Http.WebSockets;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Base
|
||||
{
|
||||
public class ConnectHandler : ChannelInitializer<ISocketChannel>
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger("BlizzLess.Net System");
|
||||
protected override void InitChannel(ISocketChannel socketChannel)
|
||||
{
|
||||
Logger.Info("Request of new Client - IP: - {0}", socketChannel.RemoteAddress);
|
||||
IChannelPipeline p = socketChannel.Pipeline;
|
||||
|
||||
var Certificate = new X509Certificate2("bnetserver.p12", "123");
|
||||
TlsHandler TLS = TlsHandler.Server(Certificate);
|
||||
|
||||
p.AddLast(TLS);
|
||||
p.AddLast(new HttpServerCodec());
|
||||
p.AddLast(new HttpObjectAggregator(8192));
|
||||
//p.AddLast(new WebSocketServerProtocolHandler("/", "jsonrpc.aurora.v1.30.battle.net", true));
|
||||
p.AddLast(new HandshakeHandler("/", "v1.rpc.battle.net", true));
|
||||
|
||||
p.AddLast(new BNetCodec());
|
||||
p.AddLast(new BattleClient(socketChannel, TLS));
|
||||
}
|
||||
private static byte[] StringToByteArray(String hex)
|
||||
{
|
||||
int NumberChars = hex.Length;
|
||||
byte[] bytes = new byte[NumberChars / 2];
|
||||
for (int i = 0; i < NumberChars; i += 2)
|
||||
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
public class HandshakeHandler : WebSocketProtocolHandler
|
||||
{
|
||||
public sealed class HandshakeComplete
|
||||
{
|
||||
private readonly string requestUri;
|
||||
|
||||
private readonly HttpHeaders requestHeaders;
|
||||
|
||||
private readonly string selectedSubprotocol;
|
||||
|
||||
public string RequestUri => requestUri;
|
||||
|
||||
public HttpHeaders RequestHeaders => requestHeaders;
|
||||
|
||||
public string SelectedSubprotocol => selectedSubprotocol;
|
||||
|
||||
internal HandshakeComplete(string requestUri, HttpHeaders requestHeaders, string selectedSubprotocol)
|
||||
{
|
||||
this.requestUri = requestUri;
|
||||
this.requestHeaders = requestHeaders;
|
||||
this.selectedSubprotocol = selectedSubprotocol;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ForbiddenResponseHandler : ChannelHandlerAdapter
|
||||
{
|
||||
public override void ChannelRead(IChannelHandlerContext ctx, object msg)
|
||||
{
|
||||
IFullHttpRequest fullHttpRequest;
|
||||
if ((fullHttpRequest = (msg as IFullHttpRequest)) != null)
|
||||
{
|
||||
fullHttpRequest.Release();
|
||||
DefaultFullHttpResponse message = new DefaultFullHttpResponse(HttpVersion.Http11, HttpResponseStatus.Forbidden);
|
||||
ctx.Channel.WriteAndFlushAsync(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.FireChannelRead(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly AttributeKey<WebSocketServerHandshaker> HandshakerAttrKey = AttributeKey<WebSocketServerHandshaker>.ValueOf("HANDSHAKER");
|
||||
|
||||
private readonly string websocketPath;
|
||||
|
||||
private readonly string subprotocols;
|
||||
|
||||
private readonly bool allowExtensions;
|
||||
|
||||
private readonly int maxFramePayloadLength;
|
||||
|
||||
private readonly bool allowMaskMismatch;
|
||||
|
||||
private readonly bool checkStartsWith;
|
||||
|
||||
public HandshakeHandler(string websocketPath)
|
||||
: this(websocketPath, null, allowExtensions: false)
|
||||
{
|
||||
}
|
||||
|
||||
public HandshakeHandler(string websocketPath, bool checkStartsWith)
|
||||
: this(websocketPath, null, allowExtensions: false, 65536, allowMaskMismatch: false, checkStartsWith)
|
||||
{
|
||||
}
|
||||
|
||||
public HandshakeHandler(string websocketPath, string subprotocols)
|
||||
: this(websocketPath, subprotocols, allowExtensions: false)
|
||||
{
|
||||
}
|
||||
|
||||
public HandshakeHandler(string websocketPath, string subprotocols, bool allowExtensions)
|
||||
: this(websocketPath, subprotocols, allowExtensions, 65536)
|
||||
{
|
||||
}
|
||||
|
||||
public HandshakeHandler(string websocketPath, string subprotocols, bool allowExtensions, int maxFrameSize)
|
||||
: this(websocketPath, subprotocols, allowExtensions, maxFrameSize, allowMaskMismatch: false)
|
||||
{
|
||||
}
|
||||
|
||||
public HandshakeHandler(string websocketPath, string subprotocols, bool allowExtensions, int maxFrameSize, bool allowMaskMismatch)
|
||||
: this(websocketPath, subprotocols, allowExtensions, maxFrameSize, allowMaskMismatch, checkStartsWith: false)
|
||||
{
|
||||
}
|
||||
|
||||
public HandshakeHandler(string websocketPath, string subprotocols, bool allowExtensions, int maxFrameSize, bool allowMaskMismatch, bool checkStartsWith)
|
||||
: this(websocketPath, subprotocols, allowExtensions, maxFrameSize, allowMaskMismatch, checkStartsWith, dropPongFrames: true)
|
||||
{
|
||||
}
|
||||
|
||||
public HandshakeHandler(string websocketPath, string subprotocols, bool allowExtensions, int maxFrameSize, bool allowMaskMismatch, bool checkStartsWith, bool dropPongFrames)
|
||||
: base()// : base(dropPongFrames)
|
||||
{
|
||||
this.websocketPath = websocketPath;
|
||||
this.subprotocols = subprotocols;
|
||||
this.allowExtensions = allowExtensions;
|
||||
maxFramePayloadLength = maxFrameSize;
|
||||
this.allowMaskMismatch = allowMaskMismatch;
|
||||
this.checkStartsWith = checkStartsWith;
|
||||
}
|
||||
|
||||
public override void HandlerAdded(IChannelHandlerContext ctx)
|
||||
{
|
||||
IChannelPipeline pipeline = ctx.Channel.Pipeline;
|
||||
if (pipeline.Get<WebSocketServerProtocolHandshakeHandler>() == null)
|
||||
{
|
||||
ctx.Channel.Pipeline.AddBefore(ctx.Name, "WebSocketServerProtocolHandshakeHandler", new WebSocketServerProtocolHandshakeHandler(websocketPath, subprotocols, allowExtensions, maxFramePayloadLength, allowMaskMismatch, checkStartsWith));
|
||||
}
|
||||
|
||||
if (pipeline.Get<Utf8FrameValidator>() == null)
|
||||
{
|
||||
ctx.Channel.Pipeline.AddBefore(ctx.Name, "Utf8FrameValidator", new Utf8FrameValidator());
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Decode(IChannelHandlerContext ctx, WebSocketFrame frame, List<object> output)
|
||||
{
|
||||
CloseWebSocketFrame frame2;
|
||||
if ((frame2 = (frame as CloseWebSocketFrame)) != null)
|
||||
{
|
||||
WebSocketServerHandshaker handshaker = GetHandshaker(ctx.Channel);
|
||||
if (handshaker != null)
|
||||
{
|
||||
frame.Retain();
|
||||
handshaker.CloseAsync(ctx.Channel, frame2);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.WriteAndFlushAsync(Unpooled.Empty).ContinueWith((Task t, object c) => ((IChannelHandlerContext)c).CloseAsync(), ctx, TaskContinuationOptions.ExecuteSynchronously);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
base.Decode(ctx, frame, output);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ExceptionCaught(IChannelHandlerContext ctx, Exception cause)
|
||||
{
|
||||
if (cause is WebSocketHandshakeException)
|
||||
{
|
||||
DefaultFullHttpResponse message = new DefaultFullHttpResponse(HttpVersion.Http11, HttpResponseStatus.BadRequest, Unpooled.WrappedBuffer(Encoding.ASCII.GetBytes(cause.Message)));
|
||||
ctx.Channel.WriteAndFlushAsync(message).ContinueWith((Task t, object c) => ((IChannelHandlerContext)c).CloseAsync(), ctx, TaskContinuationOptions.ExecuteSynchronously);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.FireExceptionCaught(cause);
|
||||
ctx.CloseAsync();
|
||||
}
|
||||
}
|
||||
|
||||
internal static WebSocketServerHandshaker GetHandshaker(IChannel channel)
|
||||
{
|
||||
return channel.GetAttribute(HandshakerAttrKey).Get();
|
||||
}
|
||||
|
||||
internal static void SetHandshaker(IChannel channel, WebSocketServerHandshaker handshaker)
|
||||
{
|
||||
channel.GetAttribute(HandshakerAttrKey).Set(handshaker);
|
||||
}
|
||||
|
||||
internal static IChannelHandler ForbiddenHttpRequestResponder()
|
||||
{
|
||||
return new ForbiddenResponseHandler();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class WebSocketProtocolHandler : MessageToMessageDecoder<WebSocketFrame>
|
||||
{
|
||||
private readonly bool dropPongFrames;
|
||||
|
||||
internal WebSocketProtocolHandler()
|
||||
: this(dropPongFrames: true)
|
||||
{
|
||||
}
|
||||
|
||||
internal WebSocketProtocolHandler(bool dropPongFrames)
|
||||
{
|
||||
this.dropPongFrames = dropPongFrames;
|
||||
}
|
||||
|
||||
protected override void Decode(IChannelHandlerContext ctx, WebSocketFrame frame, List<object> output)
|
||||
{
|
||||
if (frame is PingWebSocketFrame)
|
||||
{
|
||||
frame.Content.Retain();
|
||||
ctx.Channel.WriteAndFlushAsync(new PongWebSocketFrame(frame.Content));
|
||||
}
|
||||
else if (!(frame is PongWebSocketFrame) || !dropPongFrames)
|
||||
{
|
||||
output.Add(frame.Retain());
|
||||
}
|
||||
}
|
||||
|
||||
public override void ExceptionCaught(IChannelHandlerContext ctx, Exception cause)
|
||||
{
|
||||
ctx.FireExceptionCaught(cause);
|
||||
ctx.CloseAsync();
|
||||
}
|
||||
}
|
||||
internal class WebSocketServerProtocolHandshakeHandler : ChannelHandlerAdapter
|
||||
{
|
||||
private readonly string websocketPath;
|
||||
|
||||
private readonly string subprotocols;
|
||||
|
||||
private readonly bool allowExtensions;
|
||||
|
||||
private readonly int maxFramePayloadSize;
|
||||
|
||||
private readonly bool allowMaskMismatch;
|
||||
|
||||
private readonly bool checkStartsWith;
|
||||
|
||||
internal WebSocketServerProtocolHandshakeHandler(string websocketPath, string subprotocols, bool allowExtensions, int maxFrameSize, bool allowMaskMismatch)
|
||||
: this(websocketPath, subprotocols, allowExtensions, maxFrameSize, allowMaskMismatch, checkStartsWith: false)
|
||||
{
|
||||
}
|
||||
|
||||
internal WebSocketServerProtocolHandshakeHandler(string websocketPath, string subprotocols, bool allowExtensions, int maxFrameSize, bool allowMaskMismatch, bool checkStartsWith)
|
||||
{
|
||||
this.websocketPath = websocketPath;
|
||||
this.subprotocols = subprotocols;
|
||||
this.allowExtensions = allowExtensions;
|
||||
maxFramePayloadSize = maxFrameSize;
|
||||
this.allowMaskMismatch = allowMaskMismatch;
|
||||
this.checkStartsWith = checkStartsWith;
|
||||
}
|
||||
|
||||
public override void ChannelRead(IChannelHandlerContext ctx, object msg)
|
||||
{
|
||||
IFullHttpRequest req = (IFullHttpRequest)msg;
|
||||
if (IsNotWebSocketPath(req))
|
||||
{
|
||||
ctx.FireChannelRead(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!object.Equals(req.Method, HttpMethod.Get))
|
||||
{
|
||||
SendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.Http11, HttpResponseStatus.Forbidden));
|
||||
return;
|
||||
}
|
||||
//v1.rpc.battle.net
|
||||
//
|
||||
WebSocketServerHandshakerFactory webSocketServerHandshakerFactory = new WebSocketServerHandshakerFactory(GetWebSocketLocation(ctx.Channel.Pipeline, req, websocketPath), subprotocols, allowExtensions, maxFramePayloadSize, allowMaskMismatch);
|
||||
WebSocketServerHandshaker handshaker = webSocketServerHandshakerFactory.NewHandshaker(req);
|
||||
if (handshaker == null)
|
||||
{
|
||||
WebSocketServerHandshakerFactory.SendUnsupportedVersionResponse(ctx.Channel);
|
||||
return;
|
||||
}
|
||||
|
||||
handshaker.HandshakeAsync(ctx.Channel, req).ContinueWith(delegate (Task t)
|
||||
{
|
||||
if (t.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
ctx.FireExceptionCaught(t.Exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.FireUserEventTriggered(new HandshakeHandler.HandshakeComplete(req.Uri, req.Headers, handshaker.SelectedSubprotocol));
|
||||
}
|
||||
}, TaskContinuationOptions.ExecuteSynchronously);
|
||||
HandshakeHandler.SetHandshaker(ctx.Channel, handshaker);
|
||||
ctx.Channel.Pipeline.Replace(this, "WS403Responder", HandshakeHandler.ForbiddenHttpRequestResponder());
|
||||
}
|
||||
finally
|
||||
{
|
||||
req.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsNotWebSocketPath(IFullHttpRequest req)
|
||||
{
|
||||
if (!checkStartsWith)
|
||||
{
|
||||
return !req.Uri.Equals(websocketPath);
|
||||
}
|
||||
|
||||
return !req.Uri.StartsWith(websocketPath);
|
||||
}
|
||||
|
||||
private static void SendHttpResponse(IChannelHandlerContext ctx, IHttpRequest req, IHttpResponse res)
|
||||
{
|
||||
Task task = ctx.Channel.WriteAndFlushAsync(res);
|
||||
if (!HttpUtil.IsKeepAlive(req) || res.Status.Code != 200)
|
||||
{
|
||||
task.ContinueWith((Task t, object c) => ((IChannel)c).CloseAsync(), ctx.Channel, TaskContinuationOptions.ExecuteSynchronously);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetWebSocketLocation(IChannelPipeline cp, IHttpRequest req, string path)
|
||||
{
|
||||
string str = "ws";
|
||||
if (cp.Get<TlsHandler>() != null)
|
||||
{
|
||||
str = "wss";
|
||||
}
|
||||
|
||||
string str2 = null;
|
||||
if (req.Headers.TryGet(HttpHeaderNames.Host, out ICharSequence value))
|
||||
{
|
||||
str2 = value.ToString();
|
||||
}
|
||||
|
||||
return str + "://" + str2 + path;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/DiIiS-NA/BGS-Server/Base/HandlerController.cs
Normal file
49
src/DiIiS-NA/BGS-Server/Base/HandlerController.cs
Normal file
@ -0,0 +1,49 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Battle;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Base
|
||||
{
|
||||
public class HandlerController : IRpcController
|
||||
{
|
||||
public BattleClient Client { get; set; }
|
||||
public Header LastCallHeader { get; set; }
|
||||
public uint Status { get; set; }
|
||||
public ulong ListenerId { get; set; }
|
||||
|
||||
public string ErrorText { get; }
|
||||
|
||||
public bool Failed { get; }
|
||||
|
||||
public bool IsCanceled()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void StartCancel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void SetFailed(string reason)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void NotifyOnCancel(Action<object> callback)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/DiIiS-NA/BGS-Server/Base/RPCCallback.cs
Normal file
20
src/DiIiS-NA/BGS-Server/Base/RPCCallback.cs
Normal file
@ -0,0 +1,20 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Base
|
||||
{
|
||||
public class RPCCallBack
|
||||
{
|
||||
public Action<IMessage> Action { get; private set; }
|
||||
public IBuilder Builder { get; private set; }
|
||||
|
||||
public RPCCallBack(Action<IMessage> action, IBuilder builder)
|
||||
{
|
||||
this.Action = action;
|
||||
this.Builder = builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
325
src/DiIiS-NA/BGS-Server/Battle/BattleBackend.cs
Normal file
325
src/DiIiS-NA/BGS-Server/Battle/BattleBackend.cs
Normal file
@ -0,0 +1,325 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage.AccountDataBase.Entities;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.GameServer.AchievementSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.GameServer.GSSystem.ItemsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.GamesSystem;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using WatsonTcp;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Battle
|
||||
{
|
||||
public class BattleBackend
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger("BattleNetEmu");
|
||||
|
||||
public WatsonTcpServer GameServerSocket;
|
||||
|
||||
public struct ServerDescriptor
|
||||
{
|
||||
public string GameIP;
|
||||
public int GamePort;
|
||||
};
|
||||
|
||||
public Dictionary<string, ServerDescriptor> GameServers = new Dictionary<string, ServerDescriptor>();
|
||||
|
||||
public Dictionary<string, ServerDescriptor> PvPGameServers = new Dictionary<string, ServerDescriptor>();
|
||||
|
||||
public BattleBackend(string BattletHost, int BackPort)
|
||||
{
|
||||
this.GameServerSocket = new WatsonTcpServer(BattletHost, BackPort, this._receiverClientConnected, this._receiverClientDisconnected, this._receiverMessageReceived, false);
|
||||
System.Threading.Thread.Sleep(3000);
|
||||
}
|
||||
|
||||
private bool _receiverClientConnected(string ipPort)
|
||||
{
|
||||
Logger.Info("Game server loaded {0} connecting to BlizzLess.Net...", ipPort);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool _receiverClientDisconnected(string ipPort)
|
||||
{
|
||||
Logger.Warn("GameServer at {0} was disconnected!", ipPort);
|
||||
if (this.GameServers.ContainsKey(ipPort)) this.GameServers.Remove(ipPort);
|
||||
if (this.PvPGameServers.ContainsKey(ipPort)) this.PvPGameServers.Remove(ipPort);
|
||||
|
||||
if (this.GameServers.Count == 0)
|
||||
Logger.Warn("GameServers list is empty! Unable to use PvE game activities atm.");
|
||||
if (this.PvPGameServers.Count == 0)
|
||||
Logger.Warn("PvPGameServers list is empty! Unable to use PvP game activities atm.");
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CreateGame(string ipPort, int GameId, int level, int act, int difficulty, int questId, int questStepId, bool isHardcore, int gamemode, bool iSseasoned, int perftest_id = 0)
|
||||
{
|
||||
GameServerSocket.Send(ipPort, Encoding.UTF8.GetBytes(string.Format("diiiscg|{0}/{1}/{2}/{3}/{4}/{5}/{6}/{7}/{8}", GameId, level, act, difficulty, questId, questStepId, isHardcore, gamemode, iSseasoned, perftest_id)));
|
||||
}
|
||||
|
||||
private bool _receiverMessageReceived(string ipPort, byte[] data)
|
||||
{
|
||||
string msg = "";
|
||||
if (data != null && data.Length > 0) msg = Encoding.UTF8.GetString(data);
|
||||
Logger.Trace("Message received from {0}: {1}", ipPort, msg);
|
||||
|
||||
var message = msg.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var args = message[1].Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
switch (message[0])
|
||||
{
|
||||
case "rngsr":
|
||||
if (this.GameServers.ContainsKey(ipPort)) this.GameServers.Remove(ipPort);
|
||||
string rgs_ip = args[0];
|
||||
int rgs_port = int.Parse(args[1].Trim());
|
||||
this.GameServers.Add(ipPort, new ServerDescriptor { GameIP = rgs_ip, GamePort = rgs_port });
|
||||
Logger.Info("Game server was registred for BlizzLess.Net {0}:{1}.", rgs_ip, rgs_port);
|
||||
break;
|
||||
case "rnpvpgsr":
|
||||
if (this.PvPGameServers.ContainsKey(ipPort)) this.PvPGameServers.Remove(ipPort);
|
||||
string rpgs_ip = args[0];
|
||||
int rpgs_port = int.Parse(args[1].Trim());
|
||||
this.PvPGameServers.Add(ipPort, new ServerDescriptor { GameIP = rpgs_ip, GamePort = rpgs_port });
|
||||
Logger.Info("PvP GameServer at {0}:{1} successfully signed and ready to work.", rpgs_ip, rpgs_port);
|
||||
break;
|
||||
case "grachi":
|
||||
ulong gachi_accId = ulong.Parse(args[0].Trim());
|
||||
ulong gachi_achId = ulong.Parse(args[1].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
foreach (var gachi_invokerClient in PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == gachi_accId))
|
||||
AchievementManager.GrantAchievement(gachi_invokerClient, gachi_achId);
|
||||
});
|
||||
break;
|
||||
case "gcrit":
|
||||
ulong gc_accId = ulong.Parse(args[0].Trim());
|
||||
ulong gc_criId = ulong.Parse(args[1].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
foreach (var gc_invokerClient in PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == gc_accId))
|
||||
AchievementManager.GrantCriteria(gc_invokerClient, gc_criId);
|
||||
});
|
||||
break;
|
||||
case "upequt":
|
||||
ulong uq_accId = ulong.Parse(args[0].Trim());
|
||||
ulong uq_achId = ulong.Parse(args[1].Trim());
|
||||
uint uq_addCounter = uint.Parse(args[2].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
foreach (var uq_invokerClient in PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == uq_accId))
|
||||
AchievementManager.UpdateQuantity(uq_invokerClient, uq_achId, uq_addCounter);
|
||||
});
|
||||
break;
|
||||
case "uoacce":
|
||||
ulong uac_accId = ulong.Parse(args[0].Trim());
|
||||
int uac_typeId = int.Parse(args[1].Trim());
|
||||
uint uac_addCounter = uint.Parse(args[2].Trim());
|
||||
int uac_comparand = int.Parse(args[3].Trim());
|
||||
ulong uac_achi = ulong.Parse(args[4].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
if (uac_achi == 0)
|
||||
foreach (var uac_invokerClient in PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == uac_accId))
|
||||
AchievementManager.UpdateAllCounters(uac_invokerClient, uac_typeId, uac_addCounter, uac_comparand);
|
||||
else
|
||||
foreach (var uac_invokerClient in PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == uac_accId))
|
||||
AchievementManager.UpdateAllCounters(uac_invokerClient, uac_typeId, uac_addCounter, uac_comparand);
|
||||
});
|
||||
break;
|
||||
case "upsnaccr": //UpdateSingleAchievementCounter
|
||||
ulong usac_accId = ulong.Parse(args[0].Trim());
|
||||
ulong usac_achId = ulong.Parse(args[1].Trim());
|
||||
uint usac_addCounter = uint.Parse(args[2].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
foreach (var usac_invokerClient in PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == usac_accId))
|
||||
AchievementManager.UpdateQuantity(usac_invokerClient, usac_achId, usac_addCounter);
|
||||
});
|
||||
break;
|
||||
case "cqc": //CheckQuestCriteria
|
||||
ulong cqc_accId = ulong.Parse(args[0].Trim());
|
||||
int cqc_qId = int.Parse(args[1].Trim());
|
||||
bool cqc_isCoop = (args[2].Trim() == "True" ? true : false);
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
foreach (var cqc_invokerClient in PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == cqc_accId))
|
||||
AchievementManager.CheckQuestCriteria(cqc_invokerClient, cqc_qId, cqc_isCoop);
|
||||
});
|
||||
break;
|
||||
case "ckmc": //CheckKillMonsterCriteria
|
||||
ulong ckmc_accId = ulong.Parse(args[0].Trim());
|
||||
int ckmc_actorId = int.Parse(args[1].Trim());
|
||||
int ckmc_type = int.Parse(args[2].Trim());
|
||||
bool ckmc_isHardcore = (args[3].Trim() == "True" ? true : false);
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
foreach (var ckmc_invokerClient in PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == ckmc_accId))
|
||||
AchievementManager.CheckKillMonsterCriteria(ckmc_invokerClient, ckmc_actorId, ckmc_type, ckmc_isHardcore);
|
||||
});
|
||||
break;
|
||||
case "clc": //CheckLevelCap
|
||||
ulong clc_accId = ulong.Parse(args[0].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
foreach (var clc_invokerClient in PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == clc_accId))
|
||||
AchievementManager.CheckLevelCap(clc_invokerClient);
|
||||
});
|
||||
break;
|
||||
case "csic": //CheckSalvageItemCriteria
|
||||
ulong csic_accId = ulong.Parse(args[0].Trim());
|
||||
int csic_itemId = int.Parse(args[1].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
foreach (var csic_invokerClient in PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == csic_accId))
|
||||
AchievementManager.CheckSalvageItemCriteria(csic_invokerClient, csic_itemId);
|
||||
});
|
||||
break;
|
||||
case "clac": //CheckLevelAreaCriteria
|
||||
ulong clac_accId = ulong.Parse(args[0].Trim());
|
||||
int clac_laId = int.Parse(args[1].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
foreach (var clac_invokerClient in PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == clac_accId))
|
||||
AchievementManager.CheckLevelAreaCriteria(clac_invokerClient, clac_laId);
|
||||
});
|
||||
break;
|
||||
case "ccc": //CheckConversationCriteria
|
||||
ulong ccc_accId = ulong.Parse(args[0].Trim());
|
||||
int ccc_cId = int.Parse(args[1].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
foreach (var ccc_invokerClient in PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == ccc_accId))
|
||||
AchievementManager.CheckConversationCriteria(ccc_invokerClient, ccc_cId);
|
||||
});
|
||||
|
||||
break;
|
||||
case "plu": //ParagonLevelUp
|
||||
ulong plu_accId = ulong.Parse(args[0].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
var plr_client = PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == plu_accId).FirstOrDefault();
|
||||
if (plr_client != null && plr_client.Account.GameAccount. Clan != null)
|
||||
plr_client.Account.GameAccount.Clan.ParagonRatings[plr_client.Account.GameAccount] = plr_client.Account.GameAccount.DBGameAccount.ParagonLevel;
|
||||
});
|
||||
break;
|
||||
case "uii": //UniqueItemIdentified
|
||||
ulong uii_accId = ulong.Parse(args[0].Trim());
|
||||
ulong uii_itemId = ulong.Parse(args[1].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
var plr_client = PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == uii_accId).FirstOrDefault();
|
||||
if (plr_client != null && plr_client.Account.GameAccount.Clan != null)
|
||||
{
|
||||
var dbItem = DBSessions.SessionGet<DBInventory>(uii_itemId);
|
||||
if (dbItem != null)
|
||||
{
|
||||
var generator = D3.Items.Generator.CreateBuilder()
|
||||
.SetGbHandle(D3.GameBalance.Handle.CreateBuilder().SetGbid(dbItem.GbId).SetGameBalanceType(2))
|
||||
.SetStackSize(1)
|
||||
.SetDyeType((uint)dbItem.DyeType)
|
||||
.SetItemQualityLevel(dbItem.Quality)
|
||||
.SetFlags((uint)((dbItem.Binding > 0) ? 0 : ((dbItem.Version == 1) ? 2147483647 : 10633))) //0x1 - explored
|
||||
.SetSeed((uint)ItemGenerator.GetSeed(dbItem.Attributes))
|
||||
.SetDurability(509);
|
||||
|
||||
List<string> affixes = dbItem.Affixes.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
foreach (string affix in affixes)
|
||||
{
|
||||
int result = 0;
|
||||
Int32.TryParse(affix, out result);
|
||||
generator.AddBaseAffixes(result);
|
||||
}
|
||||
|
||||
plr_client.Account.GameAccount.Clan.AddNews(plr_client.Account.GameAccount, 0, generator.Build().ToByteArray());
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "uc": //UpdateClient
|
||||
ulong uc_accId = ulong.Parse(args[0].Trim());
|
||||
int uc_level = int.Parse(args[1].Trim());
|
||||
int uc_screen = int.Parse(args[2].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
try
|
||||
{
|
||||
foreach (var uc_invokerClient in PlayerManager.OnlinePlayers.Where(c => c.Account.GameAccount.PersistentID == uc_accId))
|
||||
{
|
||||
if (!uc_invokerClient.Account.IsOnline) continue;
|
||||
uc_invokerClient.Account.GameAccount.ChangedFields.SetPresenceFieldValue(uc_invokerClient.Account.GameAccount.CurrentToon.HeroLevelField);
|
||||
uc_invokerClient.Account.GameAccount.ChangedFields.SetPresenceFieldValue(uc_invokerClient.Account.GameAccount.CurrentToon.HeroParagonLevelField);
|
||||
if (uc_screen != -1) uc_invokerClient.Account.GameAccount.ScreenStatus = D3.PartyMessage.ScreenStatus.CreateBuilder().SetScreen(uc_screen).SetStatus(0).Build();
|
||||
uc_invokerClient.Account.GameAccount.NotifyUpdate();
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
break;
|
||||
case "gpj": //PlayerJoined
|
||||
int gpj_gameId = int.Parse(args[0].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
try
|
||||
{
|
||||
GameFactoryManager.FindGameByDynamicId((ulong)gpj_gameId).PlayersCount++;
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
break;
|
||||
case "gpl": //PlayerLeft
|
||||
int gpl_gameId = int.Parse(args[0].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
try
|
||||
{
|
||||
if (GameFactoryManager.FindGameByDynamicId((ulong)gpl_gameId) != null)
|
||||
GameFactoryManager.FindGameByDynamicId((ulong)gpl_gameId).PlayersCount--;
|
||||
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
break;
|
||||
case "gsp": //SetGamePublic
|
||||
int gsp_gameId = int.Parse(args[0].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
try
|
||||
{
|
||||
GameFactoryManager.FindGameByDynamicId((ulong)gsp_gameId).Public = true;
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
break;
|
||||
case "tsc": //ToonStateChanged
|
||||
int tsc_toonId = int.Parse(args[0].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
try
|
||||
{
|
||||
LoginServer.Toons.ToonManager.GetToonByLowID((ulong)tsc_toonId).StateChanged();
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
break;
|
||||
case "pvpsp": //PvPSaveProgress
|
||||
ulong pvpsp_gAccId = ulong.Parse(args[0].Trim());
|
||||
int pvpsp_kills = int.Parse(args[1].Trim());
|
||||
int pvpsp_wins = int.Parse(args[2].Trim());
|
||||
int pvpsp_gold = int.Parse(args[3].Trim());
|
||||
System.Threading.Tasks.Task.Delay(1).ContinueWith((a) => {
|
||||
var gAcc = GameAccountManager.GetAccountByPersistentID(pvpsp_gAccId);
|
||||
|
||||
lock (gAcc.DBGameAccount)
|
||||
{
|
||||
var dbGAcc = gAcc.DBGameAccount;
|
||||
dbGAcc.PvPTotalKilled += (ulong)pvpsp_kills;
|
||||
dbGAcc.PvPTotalWins += (ulong)pvpsp_wins;
|
||||
dbGAcc.PvPTotalGold += (ulong)pvpsp_gold;
|
||||
DBSessions.SessionUpdate(dbGAcc);
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
Logger.Warn("Unknown message type: {0}|{1}", message[0], message[1]);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
542
src/DiIiS-NA/BGS-Server/Battle/BattleClient.cs
Normal file
542
src/DiIiS-NA/BGS-Server/Battle/BattleClient.cs
Normal file
@ -0,0 +1,542 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Helpers.Hash;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.GameServer.ClientSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.ChannelSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Objects;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.ServicesSystem;
|
||||
//Blizzless Project 2022
|
||||
using DotNetty.Transport.Channels;
|
||||
//Blizzless Project 2022
|
||||
using DotNetty.Transport.Channels.Sockets;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers.DescriptorProtos;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers.Descriptors;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Concurrent;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Net.Security;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Battle
|
||||
{
|
||||
public class BattleClient : SimpleChannelInboundHandler<BNetPacket>, IRpcChannel
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger("F-Client");
|
||||
|
||||
public Dictionary<uint, uint> Services { get; private set; }
|
||||
public ISocketChannel SocketConnection { get; private set; }
|
||||
public IChannelHandlerContext Connect { get; private set; }
|
||||
public bool AuthentificationStatus = false;
|
||||
public ClientLocale ClientLanguage = ClientLocale.enUS;
|
||||
public IRpcController listenercontroller;
|
||||
private uint _tokenCounter = 0;
|
||||
public static bgs.protocol.NO_RESPONSE NO_RESPONSE = bgs.protocol.NO_RESPONSE.CreateBuilder().Build();
|
||||
private Dictionary<int, RPCCallBack> pendingResponses = new Dictionary<int, RPCCallBack>();
|
||||
public bgs.protocol.v2.Attribute AttributeOfServer { get; set; }
|
||||
|
||||
public Account Account { get; set; }
|
||||
public bool MMJoined = false;
|
||||
public const byte ServiceReply = 0xFE;
|
||||
public SslStream ssl = null;
|
||||
private static int REQUEST_SERVICE_ID = 0;
|
||||
private static int RESPONSE_SERVICE_ID = 254;
|
||||
public ulong LastPartitionIdHigh = 0; //HACK: fix it later
|
||||
public ulong LastPartitionIdLow = 0;
|
||||
//public object clientLock = new object();
|
||||
public object serviceLock = new object();
|
||||
public object messageLock = new object();
|
||||
private ulong _listenerId; // last targeted rpc object.
|
||||
public bool MOTDSent { get; private set; }
|
||||
private ConcurrentDictionary<ulong, ulong> MappedObjects { get; set; }
|
||||
public bool GuildChannelsRevealed = false;
|
||||
public string GameTeamTag = "";
|
||||
|
||||
#region Overwatch
|
||||
public byte[] k0, k1, k2, k3 = new byte[64];
|
||||
public ulong CID = 0;
|
||||
#endregion
|
||||
|
||||
#region current channel
|
||||
|
||||
public Dictionary<ulong, Channel> Channels = new Dictionary<ulong, Channel>();
|
||||
|
||||
public List<Channel> ChatChannels = new List<Channel>();
|
||||
public Channel PartyChannel; //Used for all non game related messages
|
||||
public Channel GameChannel; //Used for all game related messages
|
||||
|
||||
public GameClient InGameClient { get; set; }
|
||||
|
||||
private Channel _currentChannel;
|
||||
public Channel CurrentChannel
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentChannel == null)
|
||||
_currentChannel = this.Channels.Values.Where(c => !c.IsChatChannel).FirstOrDefault();
|
||||
return _currentChannel;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
if (_currentChannel != null)
|
||||
this.Channels.Remove(this._currentChannel.DynamicId);
|
||||
//Logger.Trace("Client removed from CurrentChannel: {0}, setting new CurrentChannel to {1}", this._currentChannel, this.Channels.FirstOrDefault().Value);
|
||||
this._currentChannel = Channels.FirstOrDefault().Value;
|
||||
}
|
||||
else if (!Channels.ContainsKey(value.DynamicId))
|
||||
{
|
||||
this.Channels.Add(value.DynamicId, value);
|
||||
this._currentChannel = value;
|
||||
}
|
||||
else
|
||||
this._currentChannel = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void SendServerWhisper(string text)
|
||||
{
|
||||
if (text.Trim() == string.Empty) return;
|
||||
|
||||
var notification = bgs.protocol.notification.v1.Notification.CreateBuilder()
|
||||
.SetTargetId(this.Account.GameAccount.BnetEntityId)
|
||||
.SetType("WHISPER")
|
||||
.SetSenderId(this.Account.GameAccount.BnetEntityId)
|
||||
.SetSenderAccountId(this.Account.BnetEntityId)
|
||||
.AddAttribute(bgs.protocol.Attribute.CreateBuilder().SetName("whisper")
|
||||
.SetValue(Variant.CreateBuilder().SetStringValue(text).Build()).Build()).Build();
|
||||
|
||||
this.MakeRPC((lid) => bgs.protocol.notification.v1.NotificationListener.CreateStub(this).
|
||||
OnNotificationReceived(new HandlerController()
|
||||
{
|
||||
ListenerId = lid
|
||||
}, notification, callback => { }));
|
||||
}
|
||||
|
||||
public void LeaveAllChannels()
|
||||
{
|
||||
List<Channel> _channels = this.Channels.Values.ToList();
|
||||
foreach (var channel in _channels)
|
||||
{
|
||||
try
|
||||
{
|
||||
channel.RemoveMember(this, Channel.RemoveReason.Left);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
this.Channels.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public BattleClient(ISocketChannel socketChannel, DotNetty.Handlers.Tls.TlsHandler TLS)
|
||||
{
|
||||
SocketConnection = socketChannel;
|
||||
Services = new Dictionary<uint, uint>();
|
||||
MappedObjects = new ConcurrentDictionary<ulong, ulong>();
|
||||
this.MOTDSent = false;
|
||||
|
||||
if (SocketConnection.Active)
|
||||
Logger.Trace("Клиент - {0} - успешно зашифровал соединение ", socketChannel.RemoteAddress);
|
||||
}
|
||||
protected override void ChannelRead0(IChannelHandlerContext ctx, BNetPacket msg)
|
||||
{
|
||||
Connect = ctx;
|
||||
Header header = msg.GetHeader();
|
||||
byte[] payload = (byte[])msg.GetPayload();
|
||||
|
||||
if (msg.GetHeader().ServiceId == RESPONSE_SERVICE_ID)
|
||||
{
|
||||
if (pendingResponses.Count == 0) return;
|
||||
RPCCallBack done = pendingResponses[(int)header.Token];
|
||||
if (done != null)
|
||||
{
|
||||
var service = Service.GetByID(header.ServiceId);
|
||||
if (service != null)
|
||||
{
|
||||
IMessage message = DescriptorProto.ParseFrom(payload);
|
||||
done.Action(message);
|
||||
pendingResponses.Remove((int)header.Token);
|
||||
}
|
||||
else
|
||||
Logger.Debug(String.Format("Incoming Response: Unable to identify service (id: %d, hash: 0x%04X)", header.ServiceId, header.ServiceHash));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var service = Service.GetByID(Service.GetByHash(header.ServiceHash));
|
||||
if(header.ServiceHash != 2119327385)
|
||||
if (service != null)
|
||||
{
|
||||
#region Все хэши сервисов
|
||||
/*
|
||||
AccountService - 1658456209
|
||||
AccountNotify - 1423956503
|
||||
AuthenticationClient - 1898188341
|
||||
AuthenticationServer - 233634817
|
||||
ChallengeNotify - 3151632159
|
||||
ChannelService - 3073563442
|
||||
ChannelSubscriber - 3213656212
|
||||
ChannelVoiceService - 2559626750
|
||||
ClubMembershipListener - 724851067
|
||||
ClubMembershipService - 2495170438 - Нужен
|
||||
ConnectionService - 1698982289
|
||||
DiagService - 3111080599
|
||||
FriendsService - 2749215165
|
||||
FriendsNotify - 1864735251
|
||||
PresenceListener - 2299181151
|
||||
PresenceService - 4194801407
|
||||
ReportService - 2091868617
|
||||
Resources - 3971904954
|
||||
SessionListener - 2145610546
|
||||
SessionService - 510168069
|
||||
SocialNetworkService - 1910276758
|
||||
SocialNetworkListener - 3506428651
|
||||
UserManagerService - 1041835658
|
||||
UserManagerNotify - 3162975266
|
||||
WhisperListener - 1072006302
|
||||
WhisperService - 3240634617
|
||||
ChannelMembershipService - 2119327385
|
||||
ChannelMembershipListener - 25167806
|
||||
ChannelService_v2 - 2039298513
|
||||
ChannelListener_v2 - 451225222
|
||||
ReportService_v2 - 977410299
|
||||
VoiceService_v2 - 4117798472
|
||||
|
||||
AccountService - 0x62DA0891
|
||||
AccountNotify - 0x54DFDA17
|
||||
AuthenticationClient - 0x71240E35
|
||||
AuthenticationServer - 0x0DECFC01
|
||||
ChallengeNotify - 0xBBDA171F
|
||||
ChannelService - 0xB732DB32
|
||||
ChannelSubscriber - 0xBF8C8094
|
||||
ChannelVoiceService - 0x9890CDFE
|
||||
ClubMembershipListener - 0x2B34597B
|
||||
ClubMembershipService - 0x94B94786
|
||||
ConnectionService - 0x65446991
|
||||
DiagService - 0xB96F5297
|
||||
FriendsService - 0xA3DDB1BD
|
||||
FriendsNotify - 0x6F259A13
|
||||
PresenceListener - 0x890AB85F
|
||||
PresenceService - 0xFA0796FF
|
||||
ReportService - 0x7CAF61C9
|
||||
Resources - ECBE75BA
|
||||
SessionListener - 0x7FE36B32
|
||||
SessionService - 0x1E688C05
|
||||
SocialNetworkService - 0x71DC8296
|
||||
SocialNetworkListener - 0xD0FFDAEB
|
||||
UserManagerService - 0x3E19268A
|
||||
UserManagerNotify - 0xBC872C22
|
||||
WhisperListener - 0x3FE5849E
|
||||
WhisperService - 0xC12828F9
|
||||
ChannelMembershipService - 0x7E525E99
|
||||
ChannelMembershipListener - 0x018007BE
|
||||
ChannelService_v2 - 0x798D39D1
|
||||
ChannelListener_v2 - 0x1AE52686
|
||||
ReportService_v2 - 0x3A4218FB
|
||||
VoiceService_v2 - 0xF5709E48
|
||||
*/
|
||||
#endregion
|
||||
MethodDescriptor method = service.DescriptorForType.Methods.Single(m => GetMethodId(m) == header.MethodId);
|
||||
IMessage proto = service.GetRequestPrototype(method);
|
||||
IBuilder builder = proto.WeakCreateBuilderForType();
|
||||
IMessage message = builder.WeakMergeFrom(ByteString.CopyFrom(payload)).WeakBuild();
|
||||
try
|
||||
{
|
||||
lock (service)
|
||||
{
|
||||
var controller = new HandlerController();
|
||||
controller.Client = this;
|
||||
controller.LastCallHeader = header;
|
||||
controller.Status = 0;
|
||||
controller.ListenerId = 0;
|
||||
#if DEBUG
|
||||
Logger.Warn("Вызов: {0}, Хэш сервиса: {1}, Метод: {2}, ID: {3}", service.GetType().Name, header.ServiceHash, method.Name, header.MethodId);
|
||||
#endif
|
||||
|
||||
service.CallMethod(method, controller, message, (IMessage m) => { sendResponse(ctx, (int)header.Token, m, controller.Status); });
|
||||
}
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
Logger.Warn("Неимплементированный метод сервиса: {0}.{1}", service.GetType().Name, method.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
Logger.Warn(String.Format("Клиент обращается к неподключенному сервису(id: {0}, hash: {1} Method id: {2})", header.ServiceId, header.ServiceHash, header.MethodId));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Platform enum for clients.
|
||||
/// </summary>
|
||||
public enum ClientPlatform
|
||||
{
|
||||
Unknown,
|
||||
Invalid,
|
||||
Win,
|
||||
Mac
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Locale enum for clients.
|
||||
/// </summary>
|
||||
public enum ClientLocale
|
||||
{
|
||||
/// <summary>
|
||||
/// Unknown client locale state.
|
||||
/// </summary>
|
||||
Unknown,
|
||||
/// <summary>
|
||||
/// Invalid client locale.
|
||||
/// </summary>
|
||||
Invalid,
|
||||
/// <summary>
|
||||
/// Deutsch.
|
||||
/// </summary>
|
||||
deDE,
|
||||
/// <summary>
|
||||
/// English (EU)
|
||||
/// </summary>
|
||||
enGB,
|
||||
/// <summary>
|
||||
/// English (Singapore)
|
||||
/// </summary>
|
||||
enSG,
|
||||
/// <summary>
|
||||
/// English (US)
|
||||
/// </summary>
|
||||
enUS,
|
||||
/// <summary>
|
||||
/// Espanol
|
||||
/// </summary>
|
||||
esES,
|
||||
/// <summary>
|
||||
/// Espanol (Mexico)
|
||||
/// </summary>
|
||||
esMX,
|
||||
/// <summary>
|
||||
/// French
|
||||
/// </summary>
|
||||
frFR,
|
||||
/// <summary>
|
||||
/// Italian
|
||||
/// </summary>
|
||||
itIT,
|
||||
/// <summary>
|
||||
/// Korean
|
||||
/// </summary>
|
||||
koKR,
|
||||
/// <summary>
|
||||
/// Polish
|
||||
/// </summary>
|
||||
plPL,
|
||||
/// <summary>
|
||||
/// Portuguese
|
||||
/// </summary>
|
||||
ptPT,
|
||||
/// <summary>
|
||||
/// Portuguese (Brazil)
|
||||
/// </summary>
|
||||
ptBR,
|
||||
/// <summary>
|
||||
/// Russian
|
||||
/// </summary>
|
||||
ruRU,
|
||||
/// <summary>
|
||||
/// Turkish
|
||||
/// </summary>
|
||||
trTR,
|
||||
/// <summary>
|
||||
/// Chinese
|
||||
/// </summary>
|
||||
zhCN,
|
||||
/// <summary>
|
||||
/// Chinese (Taiwan)
|
||||
/// </summary>
|
||||
zhTW
|
||||
}
|
||||
public virtual void MakeTargetedRPC(RPCObject targetObject, Action<ulong> rpc)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
//lock (this.clientLock)
|
||||
//{
|
||||
try
|
||||
{
|
||||
if (this.SocketConnection == null || !this.SocketConnection.Active) return;
|
||||
var listenerId = this.GetRemoteObjectId(targetObject.DynamicId);
|
||||
#if DEBUG
|
||||
Logger.Trace("[RPC: {0}] Method: {1} Target: {2} [localId: {3}, remoteId: {4}].", this, rpc.Method,
|
||||
targetObject.ToString(), targetObject.DynamicId, listenerId);
|
||||
#endif
|
||||
|
||||
rpc(listenerId);
|
||||
}
|
||||
catch { }
|
||||
//}
|
||||
});
|
||||
}
|
||||
public virtual void MakeRPC(Action<ulong> rpc)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
//lock (this.clientLock)
|
||||
//{
|
||||
try
|
||||
{
|
||||
if (this.SocketConnection == null || !this.SocketConnection.Active) return;
|
||||
#if DEBUG
|
||||
Logger.Trace("[RPC: {0}] Method: {1} Target: N/A", this, rpc.Method);
|
||||
#endif
|
||||
rpc(0);
|
||||
}
|
||||
catch { }
|
||||
//}
|
||||
});
|
||||
}
|
||||
public void CallMethod(MethodDescriptor method, IRpcController controller, IMessage request, IMessage responsePrototype, Action<IMessage> done)
|
||||
{
|
||||
var serviceName = method.Service.FullName;
|
||||
string str = "";
|
||||
|
||||
if (serviceName.ToLower().Contains("gamerequestlistener"))
|
||||
str = "bnet.protocol.matchmaking.GameRequestListener";
|
||||
else
|
||||
str = method.Service.Options.UnknownFields[90000].LengthDelimitedList[0].ToStringUtf8().Remove(0, 2);
|
||||
var serviceHash = StringHashHelper.HashIdentity(str);
|
||||
|
||||
if (!this.Services.ContainsKey(serviceHash))
|
||||
{
|
||||
Logger.Warn("Не найден сервис привязанный к клиенту {0} [0x{1}].", serviceName, serviceHash.ToString("X8"));
|
||||
return;
|
||||
}
|
||||
|
||||
uint status = 0;
|
||||
|
||||
if (controller is HandlerController)
|
||||
{
|
||||
status = (controller as HandlerController).Status;
|
||||
_listenerId = (controller as HandlerController).ListenerId;
|
||||
}
|
||||
|
||||
var serviceId = this.Services[serviceHash];
|
||||
var token = this._tokenCounter++;
|
||||
sendRequest(Connect, serviceHash, GetMethodId(method), token, request, (uint)_listenerId, status);
|
||||
}
|
||||
public static void sendRequest(IChannelHandlerContext ctx, uint serviceHash, uint methodId, uint token, IMessage request, uint listenerId, uint status)
|
||||
{
|
||||
Header.Builder builder = Header.CreateBuilder();
|
||||
builder.SetServiceId((uint)REQUEST_SERVICE_ID);
|
||||
builder.SetServiceHash(serviceHash);
|
||||
builder.SetMethodId(methodId);
|
||||
if (listenerId != 0)
|
||||
builder.SetObjectId(listenerId);
|
||||
builder.SetToken(token);
|
||||
builder.SetSize((uint)request.SerializedSize);
|
||||
builder.SetStatus(status);
|
||||
|
||||
ctx.Channel.WriteAndFlushAsync(new BNetPacket(builder.Build(), request));
|
||||
}
|
||||
/// <param name="localObjectId">The local objectId.</param>
|
||||
/// <param name="remoteObjectId">The remote objectId over client.</param>
|
||||
public void MapLocalObjectID(ulong localObjectId, ulong remoteObjectId)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.MappedObjects[localObjectId] = remoteObjectId;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.WarnException(e, "MapLocalObjectID()");
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="localObjectId"></param>
|
||||
public void UnmapLocalObjectId(ulong localObjectId)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.MappedObjects.TryRemove(localObjectId, out _);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.WarnException(e, "UnmapLocalObjectID()");
|
||||
}
|
||||
}
|
||||
public ulong GetRemoteObjectId(ulong localObjectId)
|
||||
{
|
||||
return localObjectId != 0 ? this.MappedObjects[localObjectId] : 0;
|
||||
}
|
||||
public static uint GetMethodId(MethodDescriptor method)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (uint)method.Options.UnknownFields[90000].LengthDelimitedList[0].ToByteArray()[1];
|
||||
}
|
||||
catch
|
||||
{
|
||||
return (uint)(method.Index) + 1;
|
||||
}
|
||||
}
|
||||
public static void sendResponse(IChannelHandlerContext ctx, int token, IMessage response, uint status)
|
||||
{
|
||||
Header.Builder builder = Header.CreateBuilder();
|
||||
builder.SetServiceId((uint)RESPONSE_SERVICE_ID);
|
||||
builder.SetToken((uint)token);
|
||||
builder.SetStatus(status);
|
||||
if (response != null)
|
||||
builder.SetSize((uint)response.SerializedSize);
|
||||
|
||||
ctx.Channel.WriteAndFlushAsync(new BNetPacket(builder.Build(), response));
|
||||
}
|
||||
public void SendMOTD()
|
||||
{
|
||||
if (this.MOTDSent)
|
||||
return;
|
||||
|
||||
var motd = "Welcome to BlizzLess.Net Alpha-Build Server!";
|
||||
|
||||
this.SendServerWhisper(motd);
|
||||
this.MOTDSent = true;
|
||||
}
|
||||
|
||||
public override void ChannelInactive(IChannelHandlerContext context)
|
||||
{
|
||||
DisconnectClient();
|
||||
base.ChannelInactive(context);
|
||||
}
|
||||
|
||||
private void DisconnectClient()
|
||||
{
|
||||
if (this.Account != null && this.Account.GameAccount != null) this.Account.GameAccount.LoggedInClient = null;
|
||||
PlayerManager.PlayerDisconnected(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
45
src/DiIiS-NA/BGS-Server/Battle/PlayerManager.cs
Normal file
45
src/DiIiS-NA/BGS-Server/Battle/PlayerManager.cs
Normal file
@ -0,0 +1,45 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Battle
|
||||
{
|
||||
public static class PlayerManager
|
||||
{
|
||||
public static readonly List<BattleClient> OnlinePlayers = new List<BattleClient>();
|
||||
|
||||
public static void PlayerConnected(BattleClient client)
|
||||
{
|
||||
var already_logged = OnlinePlayers.Where(cli => cli.Account.Email == client.Account.Email);
|
||||
foreach (var logged in already_logged)
|
||||
{
|
||||
OnlinePlayers.Remove(client);
|
||||
logged.SocketConnection.DisconnectAsync();
|
||||
}
|
||||
|
||||
OnlinePlayers.Add(client);
|
||||
}
|
||||
|
||||
public static BattleClient GetClientbyCID(ulong cid)
|
||||
{
|
||||
foreach (var bc in OnlinePlayers)
|
||||
if (bc.CID == cid)
|
||||
return bc;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void PlayerDisconnected(BattleClient client)
|
||||
{
|
||||
if (OnlinePlayers.Contains(client))
|
||||
OnlinePlayers.Remove(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
581
src/DiIiS-NA/BGS-Server/ChannelSystem/Channel.cs
Normal file
581
src/DiIiS-NA/BGS-Server/ChannelSystem/Channel.cs
Normal file
@ -0,0 +1,581 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Concurrent;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Objects;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Battle;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.GuildSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Helpers;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Extensions;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ChannelSystem
|
||||
{
|
||||
public class Channel : RPCObject
|
||||
{
|
||||
/// <summary>
|
||||
/// D3.OnlineService.EntityId encoded channel Id.
|
||||
/// </summary>
|
||||
public D3.OnlineService.EntityId D3EntityId { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Channel PrivacyLevel.
|
||||
/// </summary>
|
||||
public bgs.protocol.channel.v1.ChannelState.Types.PrivacyLevel PrivacyLevel { get; set; }
|
||||
|
||||
public Dictionary<string, bgs.protocol.Attribute> Attributes = new Dictionary<string, bgs.protocol.Attribute>();
|
||||
|
||||
/// <summary>
|
||||
/// Max number of members.
|
||||
/// </summary>
|
||||
public uint MaxMembers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum number of members.
|
||||
/// </summary>
|
||||
public uint MinMembers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// BucketIndex of channel(only for chat)
|
||||
/// </summary>
|
||||
public uint BucketIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the channel(for public chats).
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum invitations.
|
||||
/// </summary>
|
||||
public uint MaxInvitations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of channel members.
|
||||
/// </summary>
|
||||
public readonly ConcurrentDictionary<BattleClient, Member> Members = new ConcurrentDictionary<BattleClient, Member>();
|
||||
|
||||
public readonly Dictionary<ulong, bgs.protocol.Invitation> Invitations = new Dictionary<ulong, bgs.protocol.Invitation>();
|
||||
|
||||
/// <summary>
|
||||
/// Channel owner.
|
||||
/// </summary>
|
||||
public BattleClient Owner { get; protected set; }
|
||||
|
||||
public bool IsGameChannel { get; set; }
|
||||
|
||||
public bool IsChatChannel { get; set; }
|
||||
|
||||
public bool IsGuildChannel { get; set; }
|
||||
public bool IsGuildChatChannel { get; set; }
|
||||
|
||||
public Guild Guild { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new channel for given client with supplied remote object-id.
|
||||
/// </summary>
|
||||
/// <param name="client">The client channels is created for</param>
|
||||
/// <param name="remoteObjectId">The remove object-id of the client.</param>
|
||||
public Channel(BattleClient client, bool isGameChannel = false, ulong remoteObjectId = 0, bool isChatChannel = false)
|
||||
{
|
||||
this.BnetEntityId = bgs.protocol.EntityId.CreateBuilder().SetHigh((ulong)EntityIdHelper.HighIdType.ChannelId).SetLow(0x0000000100000000L + this.DynamicId).Build();
|
||||
this.D3EntityId = D3.OnlineService.EntityId.CreateBuilder().SetIdHigh((ulong)EntityIdHelper.HighIdType.ChannelId).SetIdLow(this.DynamicId).Build();
|
||||
this.PrivacyLevel = isChatChannel ? bgs.protocol.channel.v1.ChannelState.Types.PrivacyLevel.PRIVACY_LEVEL_OPEN : bgs.protocol.channel.v1.ChannelState.Types.PrivacyLevel.PRIVACY_LEVEL_OPEN_INVITATION;
|
||||
this.MinMembers = isChatChannel ? 0U : 1U;
|
||||
this.MaxMembers = isChatChannel ? 100U : 4U;
|
||||
this.Name = "";
|
||||
this.MaxInvitations = 12000;
|
||||
this.IsGameChannel = isGameChannel;
|
||||
this.IsChatChannel = isChatChannel;
|
||||
this.IsGuildChannel = false;
|
||||
this.IsGuildChatChannel = false;
|
||||
|
||||
if ((client != null) && (remoteObjectId != 0))
|
||||
client.MapLocalObjectID(this.DynamicId, remoteObjectId);
|
||||
|
||||
if (this.IsChatChannel)
|
||||
Program.Watchdog.AddTask(10, new Action(() =>
|
||||
{
|
||||
if (this == null || this._dissolved) throw new Exception("Channel is null");
|
||||
foreach (var member in this.Members.Keys.ToArray())
|
||||
if (member.Account == null || !member.Account.IsOnline)
|
||||
Members.TryRemove(member, out _);
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
#region common methods
|
||||
|
||||
public bool HasUser(BattleClient client)
|
||||
{
|
||||
return this.Members.Any(pair => pair.Key == client);
|
||||
}
|
||||
|
||||
public bool HasMember(GameAccount gameAccount) //check if a given game account is already channels member
|
||||
{
|
||||
return this.Members.Any(pair => pair.Value.Identity.AccountId.Low == gameAccount.BnetEntityId.Low);
|
||||
}
|
||||
|
||||
public Member GetMember(BattleClient client)
|
||||
{
|
||||
return this.Members[client];
|
||||
}
|
||||
|
||||
private bool _dissolved = false;
|
||||
public void Dissolve()
|
||||
{
|
||||
_dissolved = true;
|
||||
ChannelManager.DissolveChannel(this.DynamicId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region owner functionality
|
||||
|
||||
public void SetOwner(BattleClient client)
|
||||
{
|
||||
if (client == this.Owner)
|
||||
return;
|
||||
this.Owner = client;
|
||||
}
|
||||
|
||||
public void RemoveOwner()
|
||||
{
|
||||
if (this.Owner == null) return;
|
||||
|
||||
this.NotifyRoles();
|
||||
|
||||
var owner = this.Members.Keys.FirstOrDefault(c => c != this.Owner && c.SocketConnection != null);
|
||||
|
||||
if (owner != null)
|
||||
{
|
||||
this.SetOwner(owner);
|
||||
this.NotifyRoles();
|
||||
}
|
||||
else
|
||||
this.Owner = null;
|
||||
}
|
||||
|
||||
public void NotifyRole(BattleClient client)
|
||||
{
|
||||
//if (this.Members.Count <= 1) return;
|
||||
var channelMember = bgs.protocol.channel.v1.Member.CreateBuilder();
|
||||
var state = bgs.protocol.channel.v1.MemberState.CreateBuilder();
|
||||
|
||||
state.AddRole((uint)(client == this.Owner ? 2 : 0));
|
||||
|
||||
var identity = bgs.protocol.Identity.CreateBuilder().SetGameAccountId(client.Account.GameAccount.BnetEntityId);
|
||||
|
||||
channelMember.SetIdentity(identity);
|
||||
channelMember.SetState(state);
|
||||
|
||||
var notification = bgs.protocol.channel.v1.UpdateMemberStateNotification.CreateBuilder()
|
||||
.AddStateChange(channelMember)
|
||||
.Build();
|
||||
|
||||
client.MakeTargetedRPC(this, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(client).OnUpdateMemberState(new HandlerController() { ListenerId = lid } , notification, callback => { }));
|
||||
}
|
||||
|
||||
public void NotifyRoles()
|
||||
{
|
||||
if (this.Members.Count <= 1) return;
|
||||
foreach (var member in this.Members.Keys)
|
||||
{
|
||||
var channelMember = bgs.protocol.channel.v1.Member.CreateBuilder();
|
||||
var state = bgs.protocol.channel.v1.MemberState.CreateBuilder();
|
||||
|
||||
state.AddRole((uint)(member == this.Owner ? 2 : 0));
|
||||
|
||||
var identity = bgs.protocol.Identity.CreateBuilder().SetGameAccountId(member.Account.GameAccount.BnetEntityId);
|
||||
|
||||
channelMember.SetIdentity(identity);
|
||||
channelMember.SetState(state);
|
||||
|
||||
var notification = bgs.protocol.channel.v1.UpdateMemberStateNotification.CreateBuilder()
|
||||
.AddStateChange(channelMember)
|
||||
.Build();
|
||||
|
||||
//Notify all Channel members
|
||||
foreach (var n_member in this.Members.Keys)
|
||||
{
|
||||
n_member.MakeTargetedRPC(this, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(n_member).OnUpdateMemberState(new HandlerController() { ListenerId = lid }, notification, callback => { }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region member functinality
|
||||
|
||||
public void Join(BattleClient client, ulong remoteObjectId)
|
||||
{
|
||||
client.MapLocalObjectID(this.DynamicId, remoteObjectId);
|
||||
this.AddMember(client);
|
||||
}
|
||||
|
||||
public void AddMember(BattleClient client)
|
||||
{
|
||||
if (HasUser(client))
|
||||
{
|
||||
Logger.Warn("Attempted to add client {0} to channel when it was already a member of the channel", client.SocketConnection.RemoteAddress.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
bool isOwner = client == this.Owner;
|
||||
|
||||
// Cache the built state and member
|
||||
var channelState = this.State.ToBuilder();
|
||||
|
||||
var addedMember = new Member(this, client.Account.GameAccount, isOwner ? Member.Privilege.Creator : Member.Privilege.JoinedMember);
|
||||
addedMember.AddRole((isOwner) ? Member.Role.ChannelCreator : Member.Role.ChannelMember);
|
||||
|
||||
this.Members.TryAdd(client, addedMember);
|
||||
|
||||
var members = this.Members.Select(member => member.Value.BnetMember).ToList();
|
||||
|
||||
if (this.IsGuildChannel)
|
||||
members = this.Guild.Members.Keys.ToList().Select(account => new Member(this, account, Member.Privilege.JoinedMember).BnetMember).ToList();
|
||||
var joinNotification = bgs.protocol.channel.v1.JoinNotification.CreateBuilder()
|
||||
.SetChannelState(channelState.Build())
|
||||
.SetChannelId(bgs.protocol.channel.v1.ChannelId.CreateBuilder()
|
||||
.SetId((uint)this.D3EntityId.IdLow)
|
||||
.SetType(1)
|
||||
.SetHost(bgs.protocol.ProcessId.CreateBuilder().SetLabel(4041445648).SetEpoch(DateTime.Today.ToUnixTime())))
|
||||
.SetSelf(addedMember.BnetMember)
|
||||
.AddRangeMember(members)
|
||||
.Build();
|
||||
|
||||
client.MakeTargetedRPC(this, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(client).OnJoin(new HandlerController() { ListenerId = lid }, joinNotification, callback => { }));
|
||||
|
||||
|
||||
//client.SendServerWhisper("!PvpDisable");
|
||||
if (!this.IsChatChannel)
|
||||
{
|
||||
if (this.IsGameChannel)
|
||||
client.GameChannel = this;
|
||||
else
|
||||
client.PartyChannel = this;
|
||||
|
||||
client.CurrentChannel = this;
|
||||
if (this.Members.Count < 2) return;
|
||||
}
|
||||
|
||||
var addNotification = bgs.protocol.channel.v1.MemberAddedNotification.CreateBuilder()
|
||||
.SetMember(addedMember.BnetMember).SetChannelId(bgs.protocol.channel.v1.ChannelId.CreateBuilder()
|
||||
.SetId((uint)this.D3EntityId.IdLow)
|
||||
.SetType(1)
|
||||
.SetHost(bgs.protocol.ProcessId.CreateBuilder().SetLabel(4041445648).SetEpoch(DateTime.Today.ToUnixTime()))
|
||||
//.SetChannelState(channelState.Build())
|
||||
//.SetSelf(addedMember.BnetMember)
|
||||
).Build();
|
||||
|
||||
foreach (var pair in this.Members.Where(pair => pair.Value != addedMember)) // only send this to previous members of the channel.
|
||||
{
|
||||
pair.Key.MakeTargetedRPC(this, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(pair.Key).OnMemberAdded(new HandlerController() { ListenerId = lid }, addNotification, callback => { }));
|
||||
}
|
||||
|
||||
if (!this.IsChatChannel) this.NotifyRole(client);
|
||||
}
|
||||
|
||||
public void RemoveAllMembers()
|
||||
{
|
||||
if (!_dissolved)
|
||||
{
|
||||
Dissolve();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var member in this.Members)
|
||||
{
|
||||
RemoveMember(member.Key, RemoveReason.Dissolved);
|
||||
System.Threading.Tasks.Task.Delay(100).Wait();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveMemberByID(bgs.protocol.EntityId memberId, RemoveReason reason)
|
||||
{
|
||||
var client = this.Members.FirstOrDefault(pair => pair.Value.Identity.AccountId == memberId).Key;
|
||||
RemoveMember(client, reason);
|
||||
}
|
||||
|
||||
public void RemoveMember(BattleClient client, RemoveReason reason, bool wentOffline = false)
|
||||
{
|
||||
if (client == null || client.Account == null || client.Account.GameAccount == null)
|
||||
{
|
||||
if (client != null)
|
||||
Logger.Warn("Could not remove client {0} from channel {1}.", client.SocketConnection.RemoteAddress.ToString(), this.ToString());
|
||||
return;
|
||||
}
|
||||
else if (!HasUser(client))
|
||||
{
|
||||
Logger.Warn("Attempted to remove non-member client {0} from channel {1}.", client.SocketConnection.RemoteAddress.ToString(), this.ToString());
|
||||
return;
|
||||
}
|
||||
else if (!client.Channels.ContainsValue(this) && !client.ChatChannels.Contains(this))
|
||||
{
|
||||
Logger.Warn("Client {0} being removed from a channel ({1}) he's not associated with.", client.SocketConnection.RemoteAddress.ToString(), this.ToString());
|
||||
}
|
||||
|
||||
lock (this.Members)
|
||||
{
|
||||
var memberId = this.Members[client].Identity.GameAccountId;
|
||||
var leaveMessage = bgs.protocol.channel.v1.LeaveNotification.CreateBuilder()
|
||||
//.SetAgentId(memberId)
|
||||
//.SetChannelId(bgs.protocol.channel.v1.ChannelId.CreateBuilder() .SetId((uint)this.D3EntityId.IdLow) .SetType(1) .SetHost(bgs.protocol.ProcessId.CreateBuilder().SetLabel(4041445648).SetEpoch(DateTime.Today.ToUnixTime())))
|
||||
.SetMemberId(memberId)
|
||||
//.SetReason((uint)reason)
|
||||
.Build();
|
||||
|
||||
var removeMessage = bgs.protocol.channel.v1.MemberRemovedNotification.CreateBuilder()
|
||||
.SetAgentId(memberId)
|
||||
.SetMemberId(memberId)
|
||||
.SetReason((uint)reason).Build();
|
||||
|
||||
if (this.Members.Count <= 2 && !_dissolved && !this.IsChatChannel)
|
||||
{
|
||||
Dissolve();
|
||||
return;
|
||||
}
|
||||
if (client == this.Owner)
|
||||
this.RemoveOwner();
|
||||
|
||||
foreach (var pair in this.Members)
|
||||
{
|
||||
if (pair.Key != client)
|
||||
pair.Key.MakeTargetedRPC(this, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(pair.Key).OnMemberRemoved(new HandlerController() { ListenerId = lid }, removeMessage, callback => { }));
|
||||
}
|
||||
client.MakeTargetedRPC(this, (lid) => bgs.protocol.channel.v1.ChannelListener.CreateStub(client).OnLeave(new HandlerController() { ListenerId = lid }, leaveMessage, callback =>
|
||||
{
|
||||
client.UnmapLocalObjectId(this.DynamicId); }));
|
||||
|
||||
this.Members.TryRemove(client, out _);
|
||||
//client.UnmapLocalObjectId(this.DynamicId);
|
||||
|
||||
if (!this.IsChatChannel) client.CurrentChannel = null;
|
||||
if (this.IsGameChannel)
|
||||
{
|
||||
client.GameChannel = null;
|
||||
Logger.Warn("Client {0} left game channel {1}.", client, this);
|
||||
}
|
||||
else if (this.IsChatChannel)
|
||||
{
|
||||
client.ChatChannels.Remove(this);
|
||||
Logger.Warn("Client {0} left chat channel {1}.", client, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
client.PartyChannel = null;
|
||||
Logger.Warn("Client {0} left party channel {1}.", client, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region invitation functionality
|
||||
/*public void AddInvitation(bgs.protocol.Invitation invitation)
|
||||
{
|
||||
this.Invitations.Add(invitation.Id, invitation);
|
||||
}*/
|
||||
|
||||
public void SetOpen()
|
||||
{
|
||||
this.PrivacyLevel = bgs.protocol.channel.v1.ChannelState.Types.PrivacyLevel.PRIVACY_LEVEL_OPEN;
|
||||
}
|
||||
|
||||
/*public void RemoveInvitation(bgs.protocol.Invitation invitation)
|
||||
{
|
||||
if (this.Invitations.ContainsKey(invitation.Id))
|
||||
{
|
||||
this.Invitations.Remove(invitation.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("Tried to removed unmapped invitation {0} from channel {1}.", invitation.Id, this);
|
||||
}
|
||||
}*/
|
||||
|
||||
#endregion
|
||||
|
||||
#region channel-messaging
|
||||
|
||||
public void SendMessage(BattleClient client, bgs.protocol.channel.v1.Message message)
|
||||
{
|
||||
if (this.Name != "" && !this.IsGuildChannel && client.Account.MuteTime > DateTime.Now.ToUnixTime())
|
||||
{
|
||||
client.SendServerWhisper(string.Format("Your have been muted in public chat channels by Moderator.\nRemained time: {0} s.", client.Account.MuteTime - DateTime.Now.ToUnixTime()));
|
||||
return;
|
||||
}
|
||||
GameServer.CommandManager.CommandManager.TryParse(message.AttributeList[0].Value.StringValue, client); // try parsing it as a command and respond it if so.
|
||||
|
||||
var notification =
|
||||
bgs.protocol.channel.v1.SendMessageNotification.CreateBuilder()
|
||||
.SetAgentId(client.Account.GameAccount.BnetEntityId)
|
||||
.SetBattleTag(client.Account.BattleTag)
|
||||
.SetRequiredPrivileges(0)
|
||||
.SetMessage(message)
|
||||
.Build();
|
||||
|
||||
if (this.Name == "")
|
||||
Logger.ChatMessage("[Group][{0}]: {1}", client.Account.BattleTagName, message.AttributeList[0].Value.StringValue);
|
||||
else
|
||||
Logger.ChatMessage("[Public][{0}#{1}][{2}]: {3}", this.Name, this.BucketIndex, client.Account.BattleTagName, message.AttributeList[0].Value.StringValue);
|
||||
|
||||
foreach (var pair in this.Members) // send to all members of channel even to the actual one that sent the message else he'll not see his own message.
|
||||
{
|
||||
if (pair.Key.Account.IgnoreIds.Contains(client.Account.PersistentID)) continue;
|
||||
pair.Key.MakeTargetedRPC(this, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(pair.Key).OnSendMessage(new HandlerController() { ListenerId = lid }, notification, callback => { }));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region channel state messages
|
||||
|
||||
public void AddAttribute(bgs.protocol.Attribute attribute)
|
||||
{
|
||||
if (this.Attributes.ContainsKey(attribute.Name))
|
||||
this.Attributes[attribute.Name] = attribute;
|
||||
else
|
||||
this.Attributes.Add(attribute.Name, attribute);
|
||||
}
|
||||
|
||||
public void ClearAttribute(bgs.protocol.Attribute attribute)
|
||||
{
|
||||
if (this.Attributes.ContainsKey(attribute.Name))
|
||||
this.Attributes.Remove(attribute.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// bgs.protocol.channel.v1.ChannelState message.
|
||||
/// </summary>
|
||||
public bgs.protocol.channel.v1.ChannelState State
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.IsGuildChannel)
|
||||
return this.IsGuildChatChannel ? this.Guild.GroupChatChannelState : this.Guild.ChannelState;
|
||||
var state = bgs.protocol.channel.v1.ChannelState.CreateBuilder()
|
||||
.SetMinMembers(this.MinMembers)
|
||||
.SetMaxMembers(this.MaxMembers)
|
||||
.SetPrivacyLevel(this.PrivacyLevel)
|
||||
.SetName(this.Name)
|
||||
.SetChannelType("default")
|
||||
.SetProgram(17459)
|
||||
.SetSubscribeToPresence(true)
|
||||
//.SetName(string.Format("{0}#{1}", this.Name, this.DynamicId))
|
||||
;
|
||||
if (this.IsChatChannel)
|
||||
{
|
||||
var chatState = bgs.protocol.channel.v1.ChatChannelState.CreateBuilder().SetIdentity(string.Format("#{0}", this.DynamicId)).SetLocale(0x55527572).SetBucketIndex(this.BucketIndex).SetPublic(true).Build();
|
||||
state.SetExtension(bgs.protocol.channel.v1.ChatChannelState.ChannelState_, chatState);
|
||||
}
|
||||
|
||||
foreach (var attr in this.Attributes.Values)
|
||||
state.AddAttribute(attr);
|
||||
|
||||
return state.Build();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// bgs.protocol.channel.v1.ChannelDescription message.
|
||||
/// </summary>
|
||||
public bgs.protocol.channel.v1.ChannelDescription Description
|
||||
{
|
||||
get
|
||||
{
|
||||
var builder = bgs.protocol.channel.v1.ChannelDescription.CreateBuilder() // NOTE: Can have extensions
|
||||
.SetChannelId(this.BnetEntityId)
|
||||
.SetState(this.State);
|
||||
|
||||
if (this.Members.Count > 0) // No reason to set a value that defaults to 0
|
||||
builder.SetCurrentMembers((uint)this.Members.Count);
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// bgs.protocol.channel.v1.ChannelInfo message.
|
||||
/// </summary>
|
||||
public bgs.protocol.channel.v1.ChannelInfo Info
|
||||
{
|
||||
get
|
||||
{
|
||||
var builder = bgs.protocol.channel.v1.ChannelInfo.CreateBuilder() // NOTE: Can have extensions
|
||||
.SetDescription(this.Description);
|
||||
|
||||
foreach (var pair in this.Members)
|
||||
{
|
||||
builder.AddMember(pair.Value.BnetMember);
|
||||
}
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region remove-reason helpers
|
||||
|
||||
// Reasons the client tries to remove a member - // TODO: Need more data to complete this
|
||||
public enum RemoveRequestReason : uint
|
||||
{
|
||||
RequestedBySelf = 0x00, // Default; generally when the client quits or leaves a channel (for example, when switching toons)
|
||||
Kicked = 0x01,
|
||||
Dissolved = 0x02,
|
||||
// Kick is probably 0x01 or somesuch
|
||||
}
|
||||
|
||||
// Reasons a member was removed (sent in NotifyRemove)
|
||||
public enum RemoveReason : uint
|
||||
{
|
||||
Left = 0x00,
|
||||
Kicked = 0x01, // The member was kicked
|
||||
Dissolved = 0x02 // The channel was dissolved
|
||||
}
|
||||
|
||||
public static RemoveReason GetRemoveReasonForRequest(RemoveRequestReason reqreason)
|
||||
{
|
||||
switch (reqreason)
|
||||
{
|
||||
case RemoveRequestReason.RequestedBySelf:
|
||||
return RemoveReason.Left;
|
||||
case RemoveRequestReason.Kicked:
|
||||
return RemoveReason.Kicked;
|
||||
case RemoveRequestReason.Dissolved:
|
||||
return RemoveReason.Dissolved;
|
||||
default:
|
||||
Logger.Warn("No RemoveReason for given RemoveRequestReason: {0}", Enum.GetName(typeof(RemoveRequestReason), reqreason));
|
||||
break;
|
||||
}
|
||||
return RemoveReason.Left;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("{{ Channel: [id: {0}] [owner: {1}] }}", this.DynamicId, this.Owner != null ? this.Owner.Account.GameAccount.CurrentToon.ToString() : "N/A");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,361 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Battle;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Helpers;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Objects;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ChannelSystem
|
||||
{
|
||||
public class ChannelInvitationManager : RPCObject
|
||||
{
|
||||
public readonly Dictionary<ulong, bgs.protocol.Invitation> _onGoingInvitations = new Dictionary<ulong, bgs.protocol.Invitation>();
|
||||
public static Dictionary<ulong, bgs.protocol.Invitation> GoingInvitations = new Dictionary<ulong, bgs.protocol.Invitation>();
|
||||
|
||||
public static ulong InvitationIdCounter = 1;
|
||||
|
||||
public ChannelInvitationManager()
|
||||
{
|
||||
// TODO: Hardcoded 1 as channel persistent id in this case...
|
||||
|
||||
this.BnetEntityId = bgs.protocol.EntityId.CreateBuilder().SetHigh((ulong)EntityIdHelper.HighIdType.ChannelId).SetLow(10000000000).Build();
|
||||
}
|
||||
|
||||
public bgs.protocol.Invitation GetInvitationById(ulong Id)
|
||||
{
|
||||
|
||||
if (!this._onGoingInvitations.ContainsKey(Id))
|
||||
{
|
||||
foreach (var inv in _onGoingInvitations.Values)
|
||||
if (inv.GetExtension(bgs.protocol.channel.v1.ChannelInvitation.ChannelInvitationProp).ChannelDescription.ChannelId.Low == Id)
|
||||
return inv;
|
||||
return null;
|
||||
}
|
||||
|
||||
else
|
||||
return this._onGoingInvitations[Id];
|
||||
}
|
||||
|
||||
public void ClearInvitations()
|
||||
{
|
||||
this._onGoingInvitations.Clear();
|
||||
}
|
||||
|
||||
public void HandleInvitation(BattleClient client, bgs.protocol.Invitation invitation)
|
||||
{
|
||||
var invitee = this.Subscribers.FirstOrDefault(subscriber => subscriber.Key.Account.GameAccount.BnetEntityId.Low == invitation.InviteeIdentity.GameAccountId.Low).Key;
|
||||
if (invitee == null) return;
|
||||
|
||||
this._onGoingInvitations.Add(invitation.Id, invitation);
|
||||
GoingInvitations.Add(invitation.Id, invitation);
|
||||
|
||||
var notification = bgs.protocol.channel.v1.InvitationAddedNotification.CreateBuilder().SetInvitation(invitation);
|
||||
|
||||
invitee.MakeTargetedRPC(this, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelInvitationListener.CreateStub(invitee).OnReceivedInvitationAdded(new HandlerController() { ListenerId = lid }, notification.Build(), callback => { }));
|
||||
}
|
||||
|
||||
public Channel HandleAccept(BattleClient client, bgs.protocol.channel.v1.AcceptInvitationRequest request)
|
||||
{
|
||||
Invitation invitation = null;
|
||||
if (!this._onGoingInvitations.ContainsKey(request.InvitationId))
|
||||
{
|
||||
foreach (var inv in _onGoingInvitations.Values)
|
||||
if(inv.GetExtension(bgs.protocol.channel.v1.ChannelInvitation.ChannelInvitationProp).ChannelDescription.ChannelId.Low == request.InvitationId)
|
||||
invitation = inv;
|
||||
|
||||
if(invitation == null)
|
||||
return null;
|
||||
}
|
||||
|
||||
if (invitation == null)
|
||||
invitation = this._onGoingInvitations[request.InvitationId];
|
||||
|
||||
var channel = ChannelManager.GetChannelByEntityId(invitation.GetExtension(bgs.protocol.channel.v1.ChannelInvitation.ChannelInvitationProp).ChannelDescription.ChannelId);
|
||||
|
||||
var notification = bgs.protocol.channel.v1.InvitationRemovedNotification.CreateBuilder().SetInvitation(invitation.ToBuilder()).SetReason((uint)InvitationRemoveReason.Accepted);
|
||||
this._onGoingInvitations.Remove(invitation.Id);
|
||||
GoingInvitations.Remove(request.InvitationId);
|
||||
|
||||
client.MakeTargetedRPC(this, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelInvitationListener.CreateStub(client).OnReceivedInvitationRemoved(new HandlerController() { ListenerId = lid }, notification.Build(), callback => { }));
|
||||
|
||||
channel.Join(client, request.ObjectId);
|
||||
|
||||
var stateNotification = bgs.protocol.channel.v1.UpdateChannelStateNotification.CreateBuilder()
|
||||
.SetAgentId(bgs.protocol.EntityId.CreateBuilder().SetHigh(0).SetLow(0).Build())
|
||||
.SetStateChange(bgs.protocol.channel.v1.ChannelState.CreateBuilder().AddInvitation(invitation).SetReason(0).Build())
|
||||
.Build();
|
||||
|
||||
foreach (var member in channel.Members.Keys)
|
||||
{
|
||||
member.MakeTargetedRPC(channel, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(member).OnUpdateChannelState(new HandlerController() { ListenerId = lid }, stateNotification, callback => { }));
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
public void HandleHardJoin(BattleClient client, bgs.protocol.channel.v1.AcceptInvitationRequest request)
|
||||
{
|
||||
if (!this._onGoingInvitations.ContainsKey(request.InvitationId)) return;
|
||||
|
||||
var invitation = this._onGoingInvitations[request.InvitationId];
|
||||
var channel = ChannelManager.GetChannelByEntityId(invitation.GetExtension(bgs.protocol.channel.v1.ChannelInvitation.ChannelInvitationProp).ChannelDescription.ChannelId);
|
||||
|
||||
this._onGoingInvitations.Remove(invitation.Id);
|
||||
var a = GameAccountManager.GetAccountByPersistentID(invitation.InviteeIdentity.GameAccountId.Low);
|
||||
|
||||
var JoinClient = a.LoggedInClient;
|
||||
|
||||
|
||||
|
||||
#region
|
||||
string GAME_SERVER_IP = Program.GAMESERVERIP;
|
||||
if (GameServer.NATConfig.Instance.Enabled)
|
||||
GAME_SERVER_IP = Program.PUBLICGAMESERVERIP;
|
||||
uint GAME_SERVER_PORT = 2001;
|
||||
|
||||
var member = bgs.protocol.account.v1.GameAccountHandle.CreateBuilder();
|
||||
member.SetId((uint)JoinClient.Account.GameAccount.BnetEntityId.Low).SetProgram(0x00004433).SetRegion(1);
|
||||
|
||||
var notification = bgs.protocol.matchmaking.v1.MatchmakingResultNotification.CreateBuilder();
|
||||
var connectInfo = bgs.protocol.matchmaking.v1.ConnectInfo.CreateBuilder();
|
||||
connectInfo.SetAddress(bgs.protocol.Address.CreateBuilder().SetAddress_(GAME_SERVER_IP).SetPort(GAME_SERVER_PORT));
|
||||
connectInfo.AddAttribute(bgs.protocol.v2.Attribute.CreateBuilder().SetName("GameAccount").SetValue(bgs.protocol.v2.Variant.CreateBuilder().SetBlobValue(member.Build().ToByteString())));
|
||||
connectInfo.AddAttribute(bgs.protocol.v2.Attribute.CreateBuilder().SetName("Token").SetValue(bgs.protocol.v2.Variant.CreateBuilder().SetUintValue(0xEEF4364684EE186E))); // FIXME
|
||||
//connectInfo.AddAttribute(AttributeOfServer); // Настройки игры
|
||||
|
||||
var gh = bgs.protocol.matchmaking.v1.GameHandle.CreateBuilder();
|
||||
gh.SetMatchmaker(bgs.protocol.matchmaking.v1.MatchmakerHandle.CreateBuilder()
|
||||
.SetId((uint)JoinClient.Account.GameAccount.BnetEntityId.Low)
|
||||
.SetAddr(bgs.protocol.matchmaking.v1.HostProxyPair.CreateBuilder()
|
||||
.SetHost(ProcessId.CreateBuilder().SetLabel(1250).SetEpoch(1499729350))
|
||||
.SetProxy(ProcessId.CreateBuilder().SetLabel(0xaa82dfd9).SetEpoch(1497363883))));
|
||||
gh.SetGameServer(bgs.protocol.matchmaking.v1.HostProxyPair.CreateBuilder()
|
||||
.SetHost(ProcessId.CreateBuilder().SetLabel(1277).SetEpoch(1499729371))
|
||||
.SetProxy(ProcessId.CreateBuilder().SetLabel(0xf511871c).SetEpoch(1497363865)));
|
||||
|
||||
//var gameFound = JoinClient.GameChannel as GamesSystem.GameDescriptor;
|
||||
var gameFound = GamesSystem.GameFactoryManager.FindPlayerGame(client);
|
||||
//var clients = (from player in request.Options.PlayerList select GameAccountManager.GetAccountByPersistentID(player.GameAccount.Id) into gameAccount where gameFound != null select gameAccount.LoggedInClient).ToList();
|
||||
List<BattleClient> clients = new List<BattleClient>() { JoinClient };
|
||||
|
||||
if (JoinClient.CurrentChannel != null)
|
||||
{
|
||||
var channelStatePermission = bgs.protocol.channel.v1.ChannelState.CreateBuilder()
|
||||
.AddAttribute(bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.JoinPermissionPreviousToLock")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetIntValue(1).Build())
|
||||
.Build()).Build();
|
||||
|
||||
var notificationPermission = bgs.protocol.channel.v1.UpdateChannelStateNotification.CreateBuilder()
|
||||
.SetAgentId(JoinClient.Account.GameAccount.BnetEntityId)
|
||||
.SetStateChange(channelStatePermission)
|
||||
.Build();
|
||||
|
||||
JoinClient.MakeTargetedRPC(JoinClient.CurrentChannel, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(JoinClient).OnUpdateChannelState(new HandlerController() { ListenerId = lid }, notificationPermission, callback => { }));
|
||||
}
|
||||
gameFound.StartGame(clients, gameFound.DynamicId);
|
||||
|
||||
var notificationFound = bgs.protocol.notification.v1.Notification.CreateBuilder()
|
||||
.SetSenderId(client.Account.GameAccount.BnetEntityId)
|
||||
.SetTargetId(JoinClient.Account.GameAccount.BnetEntityId)
|
||||
.SetType("GO_ENTRY");
|
||||
var attrF = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("game_request_id")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetUintValue(gameFound.RequestId).Build());
|
||||
notificationFound.AddAttribute(attrF);
|
||||
|
||||
JoinClient.MakeRPC((lid) =>
|
||||
bgs.protocol.notification.v1.NotificationListener.CreateStub(JoinClient).OnNotificationReceived(new HandlerController() { ListenerId = lid }, notificationFound.Build(), callback => { }));
|
||||
|
||||
|
||||
gh.SetGameInstanceId((uint)gameFound.BnetEntityId.Low);
|
||||
|
||||
connectInfo.AddAttribute(bgs.protocol.v2.Attribute.CreateBuilder().SetName("SGameId").SetValue(bgs.protocol.v2.Variant.CreateBuilder().SetIntValue((long)gameFound.BnetEntityId.Low)));
|
||||
connectInfo.AddAttribute(bgs.protocol.v2.Attribute.CreateBuilder().SetName("SWorldId").SetValue(bgs.protocol.v2.Variant.CreateBuilder().SetIntValue((long)71150))); // FIXME
|
||||
|
||||
notification.SetRequestId(bgs.protocol.matchmaking.v1.RequestId.CreateBuilder().SetId(0));
|
||||
notification.SetResult(0);
|
||||
notification.SetConnectInfo(connectInfo);
|
||||
notification.SetGameHandle(gh);
|
||||
|
||||
System.Threading.Tasks.Task.Delay(2000).ContinueWith(delegate {
|
||||
JoinClient.MakeRPC((lid) => bgs.protocol.matchmaking.v1.GameRequestListener.CreateStub(JoinClient).OnMatchmakingResult(new HandlerController() { ListenerId = lid }, notification.Build(), callback => { }));
|
||||
});
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
public static ulong FindInvAsForClient(BattleClient client)
|
||||
{
|
||||
foreach (var inv in GoingInvitations.Values)
|
||||
if (inv.InviteeIdentity.GameAccountId.Low == client.Account.GameAccount.BnetEntityId.Low)
|
||||
return inv.Id;
|
||||
|
||||
return System.UInt64.MaxValue;
|
||||
}
|
||||
public static Channel AltConnectToJoin(BattleClient client, bgs.protocol.channel.v1.AcceptInvitationRequest request)
|
||||
{
|
||||
Invitation invitation = null;
|
||||
if (!GoingInvitations.ContainsKey(request.InvitationId))
|
||||
{
|
||||
foreach (var inv in GoingInvitations.Values)
|
||||
if (inv.GetExtension(bgs.protocol.channel.v1.ChannelInvitation.ChannelInvitationProp).ChannelDescription.ChannelId.Low == request.InvitationId)
|
||||
invitation = inv;
|
||||
|
||||
if (invitation == null)
|
||||
return null;
|
||||
}
|
||||
|
||||
if (invitation == null)
|
||||
invitation = GoingInvitations[request.InvitationId];
|
||||
|
||||
var channel = ChannelManager.GetChannelByEntityId(invitation.GetExtension(bgs.protocol.channel.v1.ChannelInvitation.ChannelInvitationProp).ChannelDescription.ChannelId);
|
||||
|
||||
var notification = bgs.protocol.channel.v1.InvitationRemovedNotification.CreateBuilder().SetInvitation(invitation.ToBuilder()).SetReason((uint)InvitationRemoveReason.Accepted);
|
||||
GoingInvitations.Remove(invitation.Id);
|
||||
|
||||
//client.MakeTargetedRPC(this, (lid) =>bgs.protocol.channel.v1.ChannelInvitationListener.CreateStub(client).OnReceivedInvitationRemoved(new HandlerController() { ListenerId = lid }, notification.Build(), callback => { }));
|
||||
|
||||
channel.Join(client, request.ObjectId);
|
||||
|
||||
var stateNotification = bgs.protocol.channel.v1.UpdateChannelStateNotification.CreateBuilder()
|
||||
.SetAgentId(bgs.protocol.EntityId.CreateBuilder().SetHigh(0).SetLow(0).Build())
|
||||
.SetStateChange(bgs.protocol.channel.v1.ChannelState.CreateBuilder().AddInvitation(invitation).SetReason(0).Build())
|
||||
.Build();
|
||||
|
||||
var joinstateNotification = bgs.protocol.channel.v1.JoinNotification.CreateBuilder()
|
||||
.SetChannelState(bgs.protocol.channel.v1.ChannelState.CreateBuilder().AddInvitation(invitation).SetReason(0).Build())
|
||||
.Build();
|
||||
|
||||
foreach (var member in channel.Members.Keys)
|
||||
{
|
||||
member.MakeTargetedRPC(channel, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(member).OnUpdateChannelState(new HandlerController() { ListenerId = lid }, stateNotification, callback => { }));
|
||||
member.MakeTargetedRPC(channel, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(member).OnJoin(new HandlerController() { ListenerId = lid }, joinstateNotification, callback => { }));
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
|
||||
public Channel HandleAcceptAnother(BattleClient client, bgs.protocol.channel.v1.AcceptInvitationRequest request)
|
||||
{
|
||||
if (!this._onGoingInvitations.ContainsKey(request.InvitationId)) return null;
|
||||
|
||||
var invitation = this._onGoingInvitations[request.InvitationId];
|
||||
var channel = ChannelManager.GetChannelByEntityId(invitation.GetExtension(bgs.protocol.channel.v1.ChannelInvitation.ChannelInvitationProp).ChannelDescription.ChannelId);
|
||||
|
||||
var notification = bgs.protocol.channel.v1.InvitationRemovedNotification.CreateBuilder().SetInvitation(invitation.ToBuilder()).SetReason((uint)InvitationRemoveReason.Accepted);
|
||||
this._onGoingInvitations.Remove(invitation.Id);
|
||||
var a = GameAccountManager.GetAccountByPersistentID(invitation.InviteeIdentity.GameAccountId.Low);
|
||||
|
||||
//client.MakeTargetedRPC(this, (lid) =>
|
||||
// bgs.protocol.channel.v1.ChannelInvitationListener.CreateStub(client).OnReceivedInvitationRemoved(new HandlerController() { ListenerId = lid }, notification.Build(), callback => { }));
|
||||
|
||||
var mem = a.LoggedInClient;
|
||||
|
||||
|
||||
|
||||
client.MakeTargetedRPC(this, (lid) => bgs.protocol.channel.v1.ChannelInvitationListener.CreateStub(client).OnReceivedInvitationRemoved(new HandlerController() { ListenerId = lid }, notification.Build(), callback => { }));
|
||||
|
||||
|
||||
channel.Join(mem, request.ObjectId); // add invitee to channel -- so inviter and other members will also be notified too.
|
||||
|
||||
var inviter = GameAccountManager.GetAccountByPersistentID(invitation.InviterIdentity.GameAccountId.Low);
|
||||
|
||||
var stateNotification = bgs.protocol.channel.v1.UpdateChannelStateNotification.CreateBuilder()
|
||||
.SetAgentId(bgs.protocol.EntityId.CreateBuilder().SetHigh(0).SetLow(0).Build())
|
||||
.SetStateChange(bgs.protocol.channel.v1.ChannelState.CreateBuilder().AddInvitation(invitation).SetReason(0).Build())
|
||||
.Build();
|
||||
|
||||
var build = bgs.protocol.channel.v1.JoinNotification.CreateBuilder()
|
||||
.SetChannelState(bgs.protocol.channel.v1.ChannelState.CreateBuilder().AddInvitation(invitation).SetReason(0).Build()).Build();
|
||||
|
||||
foreach (var member in channel.Members.Keys)
|
||||
{
|
||||
member.MakeTargetedRPC(channel, (lid) => bgs.protocol.channel.v1.ChannelListener.CreateStub(member).OnUpdateChannelState(new HandlerController() { ListenerId = lid }, stateNotification, callback => { }));
|
||||
//member.MakeTargetedRPC(channel, (lid) => bgs.protocol.channel.v1.ChannelListener.CreateStub(member).OnJoin(new HandlerController() { ListenerId = lid }, build, callback => { }));
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
public void HandleDecline(BattleClient client, bgs.protocol.channel.v1.DeclineInvitationRequest request)
|
||||
{
|
||||
if (!this._onGoingInvitations.ContainsKey(request.InvitationId)) return;
|
||||
var invitation = this._onGoingInvitations[request.InvitationId];
|
||||
|
||||
var inviter = GameAccountManager.GetAccountByPersistentID(invitation.InviterIdentity.GameAccountId.Low);
|
||||
if (inviter == null || inviter.LoggedInClient == null) return;
|
||||
|
||||
var notification =
|
||||
bgs.protocol.channel.v1.UpdateChannelStateNotification.CreateBuilder()
|
||||
.SetAgentId(bgs.protocol.EntityId.CreateBuilder().SetHigh(0).SetLow(0)) // caps have this set to high: 0 low: 0 /raist.
|
||||
.SetStateChange(bgs.protocol.channel.v1.ChannelState.CreateBuilder().AddInvitation(invitation)
|
||||
.SetReason((uint)InvitationRemoveReason.Declined));
|
||||
|
||||
this._onGoingInvitations.Remove(invitation.Id);
|
||||
GoingInvitations.Remove(request.InvitationId);
|
||||
|
||||
inviter.LoggedInClient.MakeTargetedRPC(inviter.LoggedInClient.CurrentChannel, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(inviter.LoggedInClient).OnUpdateChannelState(new HandlerController() { ListenerId = lid }, notification.Build(), callback => { }));
|
||||
}
|
||||
|
||||
public void Revoke(BattleClient client, bgs.protocol.channel.v1.RevokeInvitationRequest request)
|
||||
{
|
||||
if (!this._onGoingInvitations.ContainsKey(request.InvitationId)) return;
|
||||
this.CheckSubscribers();
|
||||
var invitation = this._onGoingInvitations[request.InvitationId];
|
||||
var inviter = GameAccountManager.GetAccountByPersistentID(invitation.InviterIdentity.GameAccountId.Low);
|
||||
|
||||
var channel = ChannelManager.GetChannelByEntityId(request.ChannelId);
|
||||
|
||||
//notify inviter about revoke
|
||||
var updateChannelNotification =
|
||||
bgs.protocol.channel.v1.UpdateChannelStateNotification.CreateBuilder()
|
||||
.SetAgentId(bgs.protocol.EntityId.CreateBuilder().SetHigh(0).SetLow(0)) // caps have this set to high: 0 low: 0 /dustin
|
||||
.SetStateChange(bgs.protocol.channel.v1.ChannelState.CreateBuilder().AddInvitation(invitation)
|
||||
.SetReason((uint)InvitationRemoveReason.Revoked));
|
||||
|
||||
this._onGoingInvitations.Remove(request.InvitationId);
|
||||
GoingInvitations.Remove(request.InvitationId);
|
||||
|
||||
inviter.LoggedInClient.MakeTargetedRPC(inviter.LoggedInClient.CurrentChannel, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(client).OnUpdateChannelState(new HandlerController() { ListenerId = lid }, updateChannelNotification.Build(), callback => { }));
|
||||
|
||||
//notify invitee about revoke
|
||||
var invitationRemoved =
|
||||
bgs.protocol.channel.v1.InvitationRemovedNotification.CreateBuilder()
|
||||
.SetInvitation(invitation);
|
||||
//.SetReason((uint)InvitationRemoveReason.Declined);
|
||||
|
||||
if (!this.Subscribers.Any(subscriber => subscriber.Key.Account.GameAccount.BnetEntityId.Low == invitation.InviteeIdentity.AccountId.Low)) return;
|
||||
|
||||
client.MakeTargetedRPC(this, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelInvitationListener.CreateStub(client).OnReceivedInvitationRemoved(new HandlerController() { ListenerId = lid }, invitationRemoved.Build(), callback => { }));
|
||||
}
|
||||
|
||||
public enum InvitationRemoveReason : uint
|
||||
{
|
||||
Accepted = 0x0,
|
||||
Declined = 0x1,
|
||||
Revoked = 0x2
|
||||
}
|
||||
}
|
||||
}
|
||||
182
src/DiIiS-NA/BGS-Server/ChannelSystem/ChannelManager.cs
Normal file
182
src/DiIiS-NA/BGS-Server/ChannelSystem/ChannelManager.cs
Normal file
@ -0,0 +1,182 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Battle;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.GamesSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.GuildSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Helpers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ChannelSystem
|
||||
{
|
||||
public static class ChannelManager
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
|
||||
private static Dictionary<string, List<Channel>> ChatChannels = new Dictionary<string, List<Channel>>();
|
||||
|
||||
public readonly static Dictionary<ulong, Channel> Channels =
|
||||
new Dictionary<ulong, Channel>();
|
||||
|
||||
private static string[] chatNames = new string[]{
|
||||
"D3_LookingForGroup",
|
||||
"D3_PVP",
|
||||
"D3_Hardcore",
|
||||
"D3_Trade",
|
||||
"D3_Barbarian",
|
||||
"D3_Crusader",
|
||||
"D3_DemonHunter",
|
||||
"D3_Monk",
|
||||
"D3_GeneralChat",
|
||||
"D3_Wizard",
|
||||
"D3_WitchDoctor",
|
||||
"D3_Necromancer",
|
||||
};
|
||||
|
||||
public static Channel CreateNewChannel(BattleClient client, ulong remoteObjectId)
|
||||
{
|
||||
var channel = new Channel(client, false, remoteObjectId);
|
||||
Channels.Add(channel.DynamicId, channel);
|
||||
return channel;
|
||||
}
|
||||
|
||||
private static void AddPublicChatChannel(string channelName, uint index)
|
||||
{
|
||||
var channel = new Channel(null, false, 0, true);
|
||||
channel.BucketIndex = index;
|
||||
channel.Name = channelName;
|
||||
channel.SetOpen();
|
||||
|
||||
if (!ChatChannels.ContainsKey(channelName))
|
||||
ChatChannels.Add(channelName, new List<Channel>());
|
||||
ChatChannels[channelName].Add(channel);
|
||||
|
||||
Channels.Add(channel.DynamicId, channel);
|
||||
}
|
||||
|
||||
public static Channel CreateGuildChannel(Guild guild)
|
||||
{
|
||||
var channel = new Channel(null, false, 0, true);
|
||||
channel.IsGuildChannel = true;
|
||||
channel.Guild = guild;
|
||||
Channels.Add(channel.DynamicId, channel);
|
||||
return channel;
|
||||
}
|
||||
|
||||
public static Channel CreateGuildGroupChannel(Guild guild)
|
||||
{
|
||||
var channel = new Channel(null, false, 0, true);
|
||||
channel.BucketIndex = 0;
|
||||
channel.Name = guild.Name;
|
||||
channel.SetOpen();
|
||||
channel.IsGuildChannel = true;
|
||||
channel.IsGuildChatChannel = true;
|
||||
channel.Guild = guild;
|
||||
Channels.Add(channel.DynamicId, channel);
|
||||
return channel;
|
||||
}
|
||||
|
||||
static ChannelManager()
|
||||
{
|
||||
uint index = 1;
|
||||
foreach (string chatName in chatNames)
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
AddPublicChatChannel(chatName, index);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Channel> GetChatChannels()
|
||||
{
|
||||
List<Channel> chs = new List<Channel>();
|
||||
foreach (var pair in ChatChannels)
|
||||
foreach (var channel in pair.Value)
|
||||
{
|
||||
if (channel.Members.Count < 99)
|
||||
{
|
||||
chs.Add(channel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return chs;
|
||||
}
|
||||
|
||||
public static void AddGameChannel(Channel channel)
|
||||
{
|
||||
Channels.Add(channel.DynamicId, channel);
|
||||
}
|
||||
|
||||
public static void DissolveChannel(ulong id)
|
||||
{
|
||||
Logger.Debug("Dissolving channel {0}", id);
|
||||
if (!Channels.ContainsKey(id))
|
||||
{
|
||||
Logger.Warn("Attempted to delete a non-existent channel with ID {0}", id);
|
||||
return;
|
||||
}
|
||||
var channel = Channels[id];
|
||||
if (channel.IsGameChannel)
|
||||
GameFactoryManager.DeleteGame(id);
|
||||
channel.RemoveAllMembers();
|
||||
//Channels.Remove(id);
|
||||
}
|
||||
|
||||
public static Channel GetChannelByEntityId(bgs.protocol.EntityId entityId)
|
||||
{
|
||||
if (entityId.GetHighIdType() == EntityIdHelper.HighIdType.ChannelId)
|
||||
{
|
||||
if (Channels.ContainsKey(entityId.Low - 0x0000000100000000L))
|
||||
return Channels[entityId.Low - 0x0000000100000000L];
|
||||
else if(Channels.ContainsKey(entityId.Low))
|
||||
return Channels[entityId.Low];
|
||||
else if (Channels.ContainsKey((uint)entityId.Low))
|
||||
return Channels[(uint)entityId.Low];
|
||||
|
||||
}
|
||||
else
|
||||
Logger.Warn("Given entity ID doesn't look like a channel ID!");
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Channel GetChannelByChannelId(bgs.protocol.channel.v1.ChannelId entityId)
|
||||
{
|
||||
if (Channels.ContainsKey(entityId.Id))
|
||||
return Channels[entityId.Id];
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Channel GetChannelByEntityId(D3.OnlineService.EntityId entityId)
|
||||
{
|
||||
if (entityId.IdHigh == (ulong)EntityIdHelper.HighIdType.ChannelId)
|
||||
{
|
||||
if (Channels.ContainsKey(entityId.IdLow))
|
||||
return Channels[entityId.IdLow];
|
||||
else if (Channels.ContainsKey((uint)entityId.IdLow))
|
||||
return Channels[(uint)entityId.IdLow];
|
||||
}
|
||||
else
|
||||
Logger.Warn("Given entity ID doesn't look like a channel ID!");
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Channel GetChannelByDynamicId(ulong dynamicId)
|
||||
{
|
||||
if (Channels.ContainsKey(dynamicId))
|
||||
return Channels[dynamicId];
|
||||
else if (Channels.ContainsKey((uint)dynamicId))
|
||||
return Channels[(uint)dynamicId];
|
||||
else
|
||||
return Channels[dynamicId];
|
||||
}
|
||||
}
|
||||
}
|
||||
111
src/DiIiS-NA/BGS-Server/ChannelSystem/Member.cs
Normal file
111
src/DiIiS-NA/BGS-Server/ChannelSystem/Member.cs
Normal file
@ -0,0 +1,111 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.channel.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ChannelSystem
|
||||
{
|
||||
public class Member
|
||||
{
|
||||
// TODO: Need moar!
|
||||
public enum Role : uint
|
||||
{
|
||||
ChannelMember = 1,
|
||||
ChannelCreator = 2,
|
||||
PartyMember = 100,
|
||||
PartyLeader = 101 // There's a cap where no member has Role.ChannelCreator (which is plausible since games are actually channels)
|
||||
}
|
||||
|
||||
// TODO: These are flags..
|
||||
[Flags]
|
||||
public enum Privilege : ulong
|
||||
{
|
||||
None = 0,
|
||||
JoinedMember = 56261, // 0x000000000000DBC5
|
||||
UnkJoinedMember = 56261, // 0x000000000000DBC5
|
||||
Creator = 64439, // 0x000000000000FBB7
|
||||
UnkCreator = 64511, // 0x000000000000FBFF
|
||||
Chat = 131072, // 0x0000000000020000
|
||||
UnkMember2 = 199552, // 0x0000000000030B80
|
||||
Member = 199594, // 0x0000000000030BAA
|
||||
UnkMember = 199594, // 0x0000000000030BAA
|
||||
Ultra = 0xffffffff
|
||||
}
|
||||
|
||||
public bgs.protocol.Identity Identity { get; set; }
|
||||
public Channel Channel { get; set; }
|
||||
public GameAccount GameAccount { get; set; }
|
||||
public Privilege Privileges { get; set; }
|
||||
public List<Role> Roles { get; private set; }
|
||||
public MemberAccountInfo Info { get; private set; }
|
||||
|
||||
public MemberState BnetMemberState
|
||||
{
|
||||
get
|
||||
{
|
||||
var builder = MemberState.CreateBuilder();
|
||||
builder.SetInfo(this.Info);
|
||||
foreach (var role in this.Roles)
|
||||
{
|
||||
builder.AddRole((uint)role);
|
||||
}
|
||||
|
||||
if (this.Channel.IsGuildChannel)
|
||||
{
|
||||
var rank = this.Channel.Guild.GetRank(this.Identity.GameAccountId.Low);
|
||||
var note = this.Channel.Guild.GetMemberNote(this.Identity.GameAccountId.Low);
|
||||
builder.AddAttribute(bgs.protocol.Attribute.CreateBuilder().SetName("D3.GuildMember.Rank").SetValue(bgs.protocol.Variant.CreateBuilder().SetIntValue(rank)));
|
||||
builder.AddAttribute(bgs.protocol.Attribute.CreateBuilder().SetName("D3.GuildMember.Note").SetValue(bgs.protocol.Variant.CreateBuilder().SetStringValue(note)));
|
||||
builder.AddAttribute(bgs.protocol.Attribute.CreateBuilder().SetName("D3.GuildMember.AchievementPoints").SetValue(bgs.protocol.Variant.CreateBuilder().SetUintValue(this.GameAccount.AchievementPoints)));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.Privileges != Privilege.None)
|
||||
builder.SetPrivileges((ulong)this.Privileges); // We don't have to set this if it is the default (0)
|
||||
}
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
|
||||
public bgs.protocol.channel.v1.Member BnetMember
|
||||
{
|
||||
get
|
||||
{
|
||||
return bgs.protocol.channel.v1.Member.CreateBuilder()
|
||||
.SetIdentity(this.Identity)
|
||||
.SetState(this.BnetMemberState)
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
|
||||
public Member(Channel channel, GameAccount account, Privilege privs, params Role[] roles)
|
||||
{
|
||||
this.Channel = channel;
|
||||
this.GameAccount = account;
|
||||
this.Identity = bgs.protocol.Identity.CreateBuilder().SetGameAccountId(account.BnetEntityId).Build();
|
||||
this.Privileges = privs;
|
||||
this.Roles = new List<Role>();
|
||||
AddRoles(roles);
|
||||
this.Info = MemberAccountInfo.CreateBuilder()
|
||||
.SetBattleTag(account.Owner.BattleTag)
|
||||
.Build();
|
||||
}
|
||||
|
||||
public void AddRoles(params Role[] roles)
|
||||
{
|
||||
foreach (var role in roles)
|
||||
AddRole(role);
|
||||
}
|
||||
|
||||
public void AddRole(Role role)
|
||||
{
|
||||
if (!this.Roles.Contains(role))
|
||||
this.Roles.Add(role);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/DiIiS-NA/BGS-Server/Config.cs
Normal file
27
src/DiIiS-NA/BGS-Server/Config.cs
Normal file
@ -0,0 +1,27 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DiIiS_NA.LoginServer
|
||||
{
|
||||
public sealed class Config : Core.Config.Config
|
||||
{
|
||||
public bool Enabled { get { return this.GetBoolean("Enabled", true); } set { this.Set("Enabled", value); } }
|
||||
public string BindIP { get { return this.GetString("BindIP", "127.0.0.1"); } set { this.Set("BindIP", value); } }
|
||||
public int WebPort { get { return this.GetInt("WebPort", 9000); } set { this.Set("WebPort", value); } }
|
||||
public int Port { get { return this.GetInt("Port", 1119); } set { this.Set("Port", value); } }
|
||||
public string BindIPv6 { get { return this.GetString("BindIPv6", "::1"); } set { this.Set("BindIPv6", value); } }
|
||||
|
||||
private static readonly Config _instance = new Config();
|
||||
public static Config Instance { get { return _instance; } }
|
||||
private Config() : base("Battle-Server") { }
|
||||
}
|
||||
}
|
||||
60
src/DiIiS-NA/BGS-Server/Crypthography/ARC4.cs
Normal file
60
src/DiIiS-NA/BGS-Server/Crypthography/ARC4.cs
Normal file
@ -0,0 +1,60 @@
|
||||
//Blizzless Project 2022
|
||||
namespace DiIiS_NA.LoginServer.Crypthography
|
||||
{
|
||||
public class ARC4
|
||||
{
|
||||
private readonly byte[] _state;
|
||||
private byte x, y;
|
||||
|
||||
public ARC4(byte[] key)
|
||||
{
|
||||
_state = new byte[256];
|
||||
x = y = 0;
|
||||
KeySetup(key);
|
||||
}
|
||||
|
||||
public int Process(byte[] buffer, int start, int count)
|
||||
{
|
||||
return InternalTransformBlock(buffer, start, count, buffer, start);
|
||||
}
|
||||
|
||||
private void KeySetup(byte[] key)
|
||||
{
|
||||
byte index1 = 0;
|
||||
byte index2 = 0;
|
||||
|
||||
for (int counter = 0; counter < 256; counter++)
|
||||
{
|
||||
_state[counter] = (byte)counter;
|
||||
}
|
||||
x = 0;
|
||||
y = 0;
|
||||
for (int counter = 0; counter < 256; counter++)
|
||||
{
|
||||
index2 = (byte)(key[index1] + _state[counter] + index2);
|
||||
// swap byte
|
||||
byte tmp = _state[counter];
|
||||
_state[counter] = _state[index2];
|
||||
_state[index2] = tmp;
|
||||
index1 = (byte)((index1 + 1) % key.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private int InternalTransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
|
||||
{
|
||||
for (int counter = 0; counter < inputCount; counter++)
|
||||
{
|
||||
x = (byte)(x + 1);
|
||||
y = (byte)(_state[x] + y);
|
||||
// swap byte
|
||||
byte tmp = _state[x];
|
||||
_state[x] = _state[y];
|
||||
_state[y] = tmp;
|
||||
|
||||
byte xorIndex = (byte)(_state[x] + _state[y]);
|
||||
outputBuffer[outputOffset + counter] = (byte)(inputBuffer[inputOffset + counter] ^ _state[xorIndex]);
|
||||
}
|
||||
return inputCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
241
src/DiIiS-NA/BGS-Server/Crypthography/SRP6a.cs
Normal file
241
src/DiIiS-NA/BGS-Server/Crypthography/SRP6a.cs
Normal file
@ -0,0 +1,241 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Extensions;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Numerics;
|
||||
//Blizzless Project 2022
|
||||
using System.Security.Cryptography;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Crypthography
|
||||
{
|
||||
public class SRP6a
|
||||
{
|
||||
// The following is a description of SRP-6 and 6a, the latest versions of SRP:
|
||||
// ---------------------------------------------------------------------------
|
||||
// N A large safe prime (N = 2q+1, where q is prime)
|
||||
// All arithmetic is done modulo N.
|
||||
// g A generator modulo N
|
||||
// k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6)
|
||||
// s User's salt
|
||||
// I Username
|
||||
// p Cleartext Password
|
||||
// H() One-way hash function
|
||||
// ^ (Modular) Exponentiation
|
||||
// u Random scrambling parameter
|
||||
// a,b Secret ephemeral values
|
||||
// A,B Public ephemeral values
|
||||
// x Private key (derived from p and s)
|
||||
// v Password verifier
|
||||
// ---------------------------------------------------------------------------
|
||||
// specification: http://srp.stanford.edu/design.html
|
||||
// article: http://en.wikipedia.org/wiki/Secure_Remote_Password_protocol
|
||||
// contains code from tomrus88 (https://github.com/tomrus88/d3proto/blob/master/Core/SRP.cs
|
||||
|
||||
private static readonly SHA256Managed H = new SHA256Managed(); // H() One-way hash function.
|
||||
|
||||
/// <summary>
|
||||
/// Account used within SRP6-a authentication.
|
||||
/// </summary>
|
||||
public Account Account { get; private set; }
|
||||
|
||||
|
||||
public string IdentitySalt { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// K = H(S) - Shared, strong session key.
|
||||
/// </summary>
|
||||
public byte[] SessionKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Server's secret ephemeral value.
|
||||
/// </summary>
|
||||
private readonly BigInteger b;
|
||||
|
||||
/// <summary>
|
||||
/// Server's public ephemeral value
|
||||
/// </summary>
|
||||
private readonly BigInteger B;
|
||||
|
||||
/// <summary>
|
||||
/// Returns server's logon challenge message.
|
||||
/// command = 0
|
||||
/// byte identity salt [32]; - identity-salt - generated by hashing account email [static value per account] (skipped when command == 1)
|
||||
/// byte password salt[32]; - account-salt - generated on account creation [static value per account]
|
||||
/// byte serverChallenge[128]; - changes every login - server's public ephemeral value (B)
|
||||
/// byte secondChallenge[128]; - extra challenge
|
||||
/// </summary>
|
||||
public byte[] LogonChallenge { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns logon proof.
|
||||
/// command == 3 - server sends proof of session key to client
|
||||
/// byte M_server[32] - server's proof of session key.
|
||||
/// byte secondProof[128]; // for veriyfing second challenge.
|
||||
/// </summary>
|
||||
public byte[] LogonProof { get; private set; }
|
||||
|
||||
public SRP6a(Account account)
|
||||
{
|
||||
this.Account = account;
|
||||
this.IdentitySalt = H.ComputeHash(Encoding.ASCII.GetBytes(this.Account.Email)).ToHexString();
|
||||
// calculate server's public ephemeral value.
|
||||
this.b = GetRandomBytes(128).ToBigInteger(); // server's secret ephemeral value.
|
||||
var gModb = BigInteger.ModPow(g, b, N); // pow(g, b, N)
|
||||
var k = H.ComputeHash(new byte[0].Concat(N.ToArray()).Concat(g.ToArray()).ToArray()).ToBigInteger(); // Multiplier parameter (k = H(N, g) in SRP-6a
|
||||
this.B = BigInteger.Remainder((this.Account.PasswordVerifier.ToBigInteger() * k) + gModb, N); // B = (k * v + pow(g, b, N)) % N
|
||||
|
||||
// cook the logon challenge message
|
||||
this.LogonChallenge = new byte[0]
|
||||
.Concat(new byte[] { 0 }) // command = 0
|
||||
.Concat(this.IdentitySalt.ToByteArray()) // identity-salt - generated by hashing account email.
|
||||
.Concat(this.Account.Salt) // account-salt - generated on account creation.
|
||||
.Concat(B.ToArray(128)) // server's public ephemeral value (B)
|
||||
.Concat(b.ToArray(128)) // second challenge
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates password verifier for given email, password and salt.
|
||||
/// </summary>
|
||||
/// <param name="email">The account email.</param>
|
||||
/// <param name="password">The password.</param>
|
||||
/// <param name="salt">The generated salt.</param>
|
||||
/// <returns></returns>
|
||||
public static byte[] CalculatePasswordVerifierForAccount(string email, string password, byte[] salt)
|
||||
{
|
||||
// x = H(s, p) -> s: randomly choosen salt
|
||||
// v = g^x (computes password verifier)
|
||||
|
||||
// TODO: it seems hashing identity-salt + password bugs for passwords with >11 chars or so.
|
||||
// we need to get rid of that identity-salt in pBytes /raist.
|
||||
|
||||
var identitySalt = H.ComputeHash(Encoding.ASCII.GetBytes(email)).ToHexString();
|
||||
var pBytes = H.ComputeHash(Encoding.ASCII.GetBytes(identitySalt.ToUpper() + ":" + password.ToUpper())); // p (identitySalt + password)
|
||||
var x = H.ComputeHash(new byte[0].Concat(salt).Concat(pBytes).ToArray()).ToBigInteger(); // x = H(s, p)
|
||||
|
||||
return BigInteger.ModPow(g, x, N).ToArray(128);
|
||||
}
|
||||
|
||||
|
||||
public bool Verify(byte[] A_bytes, byte[] M_client, byte[] seed)
|
||||
{
|
||||
var A = A_bytes.ToBigInteger(); // client's public ephemeral
|
||||
var u = H.ComputeHash(new byte[0].Concat(A_bytes).Concat(B.ToArray(128)).ToArray()).ToBigInteger(); // Random scrambling parameter - u = H(A, B)
|
||||
|
||||
var S_s = BigInteger.ModPow(A * BigInteger.ModPow(this.Account.PasswordVerifier.ToBigInteger(), u, N), b, N); // calculate server session key - S = (Av^u) ^ b
|
||||
this.SessionKey = Calc_K(S_s.ToArray(128)); // K = H(S) - Shared, strong session key.
|
||||
byte[] K_s = this.SessionKey;
|
||||
|
||||
var hashgxorhashN = Hash_g_and_N_and_xor_them().ToBigInteger(); // H(N) ^ H(g)
|
||||
var hashedIdentitySalt = H.ComputeHash(Encoding.ASCII.GetBytes(this.IdentitySalt)); // H(I)
|
||||
|
||||
var M = H.ComputeHash(new byte[0] // verify client M_client - H(H(N) ^ H(g), H(I), s, A, B, K_c)
|
||||
.Concat(hashgxorhashN.ToArray(32))
|
||||
.Concat(hashedIdentitySalt)
|
||||
.Concat(this.Account.Salt.ToArray())
|
||||
.Concat(A_bytes)
|
||||
.Concat(B.ToArray(128))
|
||||
.Concat(K_s)
|
||||
.ToArray());
|
||||
|
||||
// We can basically move m_server, secondproof and logonproof calculation behind the M.CompareTo(M_client) check, but as we have an option DisablePasswordChecks
|
||||
// which allows authentication without the correct password, they should be also calculated for wrong-passsword auths. /raist.
|
||||
|
||||
// calculate server proof of session key
|
||||
var M_server = H.ComputeHash(new byte[0] // M_server = H(A, M_client, K)
|
||||
.Concat(A_bytes)
|
||||
.Concat(M_client)
|
||||
.Concat(K_s)
|
||||
.ToArray());
|
||||
|
||||
// cook logon proof message.
|
||||
LogonProof = new byte[0]
|
||||
.Concat(new byte[] { 3 }) // command = 3 - server sends proof of session key to client
|
||||
.Concat(M_server) // server's proof of session key
|
||||
.Concat(B.ToArray(128)) // second proof
|
||||
.ToArray();
|
||||
|
||||
if (M.CompareTo(M_client)) // successful authentication session.
|
||||
return true;
|
||||
else // authentication failed because of invalid credentals.
|
||||
return false;
|
||||
}
|
||||
|
||||
public static byte[] GetRandomBytes(int count)
|
||||
{
|
||||
var rnd = new Random();
|
||||
var result = new byte[count];
|
||||
rnd.NextBytes(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Interleave SHA256 Key
|
||||
private byte[] Calc_K(byte[] S)
|
||||
{
|
||||
var K = new byte[64];
|
||||
|
||||
var half_S = new byte[64];
|
||||
|
||||
for (int i = 0; i < 64; ++i)
|
||||
half_S[i] = S[i * 2];
|
||||
|
||||
var p1 = H.ComputeHash(half_S);
|
||||
|
||||
for (int i = 0; i < 32; ++i)
|
||||
K[i * 2] = p1[i];
|
||||
|
||||
for (int i = 0; i < 64; ++i)
|
||||
half_S[i] = S[i * 2 + 1];
|
||||
|
||||
var p2 = H.ComputeHash(half_S);
|
||||
|
||||
for (int i = 0; i < 32; ++i)
|
||||
K[i * 2 + 1] = p2[i];
|
||||
|
||||
return K;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// H(N) ^ H(g)
|
||||
/// </summary>
|
||||
/// <returns>byte[]</returns>
|
||||
private byte[] Hash_g_and_N_and_xor_them()
|
||||
{
|
||||
var hash_N = H.ComputeHash(N.ToArray());
|
||||
var hash_g = H.ComputeHash(g.ToArray());
|
||||
|
||||
for (var i = 0; i < 32; ++i)
|
||||
hash_N[i] ^= hash_g[i];
|
||||
|
||||
return hash_N;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A generator modulo N
|
||||
/// </summary>
|
||||
private static readonly BigInteger g = new byte[] { 0x02 }.ToBigInteger();
|
||||
|
||||
/// <summary>
|
||||
/// A large safe prime (N = 2q+1, where q is prime)
|
||||
/// </summary>
|
||||
private static readonly BigInteger N = new byte[]
|
||||
{
|
||||
0xAB, 0x24, 0x43, 0x63, 0xA9, 0xC2, 0xA6, 0xC3, 0x3B, 0x37, 0xE4, 0x61, 0x84, 0x25, 0x9F, 0x8B,
|
||||
0x3F, 0xCB, 0x8A, 0x85, 0x27, 0xFC, 0x3D, 0x87, 0xBE, 0xA0, 0x54, 0xD2, 0x38, 0x5D, 0x12, 0xB7,
|
||||
0x61, 0x44, 0x2E, 0x83, 0xFA, 0xC2, 0x21, 0xD9, 0x10, 0x9F, 0xC1, 0x9F, 0xEA, 0x50, 0xE3, 0x09,
|
||||
0xA6, 0xE5, 0x5E, 0x23, 0xA7, 0x77, 0xEB, 0x00, 0xC7, 0xBA, 0xBF, 0xF8, 0x55, 0x8A, 0x0E, 0x80,
|
||||
0x2B, 0x14, 0x1A, 0xA2, 0xD4, 0x43, 0xA9, 0xD4, 0xAF, 0xAD, 0xB5, 0xE1, 0xF5, 0xAC, 0xA6, 0x13,
|
||||
0x1C, 0x69, 0x78, 0x64, 0x0B, 0x7B, 0xAF, 0x9C, 0xC5, 0x50, 0x31, 0x8A, 0x23, 0x08, 0x01, 0xA1,
|
||||
0xF5, 0xFE, 0x31, 0x32, 0x7F, 0xE2, 0x05, 0x82, 0xD6, 0x0B, 0xED, 0x4D, 0x55, 0x32, 0x41, 0x94,
|
||||
0x29, 0x6F, 0x55, 0x7D, 0xE3, 0x0F, 0x77, 0x19, 0xE5, 0x6C, 0x30, 0xEB, 0xDE, 0xF6, 0xA7, 0x86
|
||||
}.ToBigInteger();
|
||||
}
|
||||
}
|
||||
70
src/DiIiS-NA/BGS-Server/FriendsSystem/FriendManager.cs
Normal file
70
src/DiIiS-NA/BGS-Server/FriendsSystem/FriendManager.cs
Normal file
@ -0,0 +1,70 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage.AccountDataBase.Entities;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Battle;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Helpers;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Objects;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.FriendsSystem
|
||||
{
|
||||
public class FriendManager : RPCObject
|
||||
{
|
||||
private static readonly FriendManager _instance = new FriendManager();
|
||||
public static FriendManager Instance { get { return _instance; } }
|
||||
|
||||
public static readonly Dictionary<ulong, bgs.protocol.friends.v1.ReceivedInvitation> OnGoingInvitations =
|
||||
new Dictionary<ulong, bgs.protocol.friends.v1.ReceivedInvitation>();
|
||||
|
||||
public static ulong InvitationIdCounter = 1;
|
||||
|
||||
static FriendManager()
|
||||
{
|
||||
_instance.BnetEntityId = bgs.protocol.EntityId.CreateBuilder().SetHigh((ulong)EntityIdHelper.HighIdType.Unknown).SetLow(0x0000000110000000L + 1).Build();
|
||||
}
|
||||
|
||||
|
||||
public static void HandleIgnore(BattleClient client, bgs.protocol.friends.v1.IgnoreInvitationRequest request)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static void HandleAccept(BattleClient client, bgs.protocol.friends.v1.AcceptInvitationRequest request)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static void HandleDecline(BattleClient client, bgs.protocol.friends.v1.DeclineInvitationRequest request)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static void HandleRemove(BattleClient client, bgs.protocol.friends.v1.RemoveFriendRequest request)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public enum InvitationRemoveReason : uint
|
||||
{
|
||||
Accepted = 0x0,
|
||||
Declined = 0x1,
|
||||
Ignored = 0x3
|
||||
}
|
||||
}
|
||||
116
src/DiIiS-NA/BGS-Server/FriendsSystem/UserManager.cs
Normal file
116
src/DiIiS-NA/BGS-Server/FriendsSystem/UserManager.cs
Normal file
@ -0,0 +1,116 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage.AccountDataBase.Entities;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Battle;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Helpers;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Objects;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.FriendsSystem
|
||||
{
|
||||
public class UserManager : RPCObject
|
||||
{
|
||||
private static readonly UserManager _instance = new UserManager();
|
||||
public static UserManager Instance { get { return _instance; } }
|
||||
|
||||
public static void BlockAccount(BattleClient client, bgs.protocol.user_manager.v1.BlockPlayerRequest request)
|
||||
{
|
||||
var blocked = GameAccountManager.GetAccountByPersistentID(request.TargetId.Low);
|
||||
var blocker = client.Account;
|
||||
|
||||
if (!blocker.IgnoreIds.Contains(blocked.Owner.PersistentID))
|
||||
blocker.IgnoreIds.Add(blocked.Owner.PersistentID);
|
||||
AddIgnoreToDB(blocker, blocked.Owner);
|
||||
|
||||
var blockedPlayer = bgs.protocol.user_manager.v1.BlockedPlayer.CreateBuilder()
|
||||
.SetAccountId(bgs.protocol.EntityId.CreateBuilder().SetHigh((ulong)EntityIdHelper.HighIdType.AccountId).SetLow(blocked.Owner.PersistentID))
|
||||
.SetName(blocked.Owner.BattleTag)
|
||||
//.AddRole(0)
|
||||
.Build();
|
||||
|
||||
var notifyBlock = bgs.protocol.user_manager.v1.BlockedPlayerAddedNotification.CreateBuilder();
|
||||
notifyBlock.SetPlayer(blockedPlayer);
|
||||
notifyBlock.SetGameAccountId(blocked.BnetEntityId);
|
||||
|
||||
client.MakeTargetedRPC(UserManager.Instance, (lid) =>
|
||||
bgs.protocol.user_manager.v1.UserManagerListener.CreateStub(client).OnBlockedPlayerAdded(new HandlerController() { ListenerId = lid }, notifyBlock.Build(), callback => { }));
|
||||
}
|
||||
|
||||
public static void UnblockAccount(BattleClient client, bgs.protocol.user_manager.v1.UnblockPlayerRequest request)
|
||||
{
|
||||
var blocked = AccountManager.GetAccountByPersistentID(request.TargetId.Low);
|
||||
var blocker = client.Account;
|
||||
|
||||
|
||||
if (blocker.IgnoreIds.Contains(blocked.PersistentID))
|
||||
blocker.IgnoreIds.Remove(blocked.PersistentID);
|
||||
RemoveIgnoreFromDB(blocker, blocked);
|
||||
|
||||
var blockedPlayer = bgs.protocol.user_manager.v1.BlockedPlayer.CreateBuilder()
|
||||
.SetAccountId(bgs.protocol.EntityId.CreateBuilder().SetHigh((ulong)EntityIdHelper.HighIdType.AccountId).SetLow(blocked.PersistentID))
|
||||
.SetName(blocked.BattleTag)
|
||||
.Build();
|
||||
|
||||
var notifyUnblock = bgs.protocol.user_manager.v1.BlockedPlayerRemovedNotification.CreateBuilder();
|
||||
notifyUnblock.SetPlayer(blockedPlayer);
|
||||
|
||||
client.MakeTargetedRPC(UserManager.Instance, (lid) =>
|
||||
bgs.protocol.user_manager.v1.UserManagerListener.CreateStub(client).OnBlockedPlayerRemoved(new HandlerController() { ListenerId = lid }, notifyUnblock.Build(), callback => { }));
|
||||
}
|
||||
|
||||
private static void AddIgnoreToDB(Account owner, Account target)
|
||||
{
|
||||
Logger.Trace("AddIgnoreToDB(): owner {0}, target {1}", owner.PersistentID, target.PersistentID);
|
||||
try
|
||||
{
|
||||
if (DBSessions.SessionQueryWhere<DBAccountLists>(dbl => dbl.ListOwner.Id == owner.PersistentID && dbl.ListTarget.Id == target.PersistentID && dbl.Type == "IGNORE").Count() > 0) return;
|
||||
|
||||
var blockRecord = new DBAccountLists
|
||||
{
|
||||
ListOwner = owner.DBAccount,
|
||||
ListTarget = target.DBAccount,
|
||||
Type = "IGNORE"
|
||||
};
|
||||
DBSessions.SessionSave(blockRecord);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorException(e, "UserManager.AddIgnoreToDB()");
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveIgnoreFromDB(Account owner, Account target)
|
||||
{
|
||||
Logger.Trace("RemoveIgnoreFromDB(): owner {0}, target {1}", owner.PersistentID, target.PersistentID);
|
||||
try
|
||||
{
|
||||
var blockRecords = DBSessions.SessionQueryWhere<DBAccountLists>(dbl => dbl.ListOwner.Id == owner.PersistentID && dbl.ListTarget.Id == target.PersistentID && dbl.Type == "IGNORE");
|
||||
foreach (var rec in blockRecords)
|
||||
DBSessions.SessionDelete(rec);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorException(e, "UserManager.RemoveIgnoreFromDB()");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
186
src/DiIiS-NA/BGS-Server/GamesSystem/GameDescriptor.cs
Normal file
186
src/DiIiS-NA/BGS-Server/GamesSystem/GameDescriptor.cs
Normal file
@ -0,0 +1,186 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.matchmaking.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Battle;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.ChannelSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Helpers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.GamesSystem
|
||||
{
|
||||
public class GameDescriptor : Channel
|
||||
{
|
||||
public int PlayersCount = 0;
|
||||
public bgs.protocol.games.v1.GameHandle GameHandle { get; private set; }
|
||||
|
||||
public D3.OnlineService.GameCreateParams GameCreateParams { get; private set; }
|
||||
|
||||
public string Version = "2.7.3";
|
||||
public ulong FactoryID { get; private set; }
|
||||
public KeyValuePair<string, BattleBackend.ServerDescriptor> GServer { get; private set; }
|
||||
public ulong RequestId { get; private set; }
|
||||
public bool Started { get; private set; }
|
||||
public bool Public { get; set; }
|
||||
|
||||
public GameDescriptor(BattleClient owner, GameMatchmakingOptions request, ulong requestId)
|
||||
: base(owner, true)
|
||||
{
|
||||
this.Started = false;
|
||||
this.Public = false;
|
||||
this.Owner = owner;
|
||||
this.RequestId = requestId;
|
||||
this.GServer = ChooseGameServer();
|
||||
this.Owner.GameChannel = this;
|
||||
this.BnetEntityId = bgs.protocol.EntityId.CreateBuilder().SetHigh((ulong)EntityIdHelper.HighIdType.GameId).SetLow(this.DynamicId)
|
||||
.Build();
|
||||
this.GameHandle = bgs.protocol.games.v1.GameHandle.CreateBuilder().SetFactoryId(this.FactoryID).SetGameId(this.BnetEntityId).Build();
|
||||
|
||||
foreach (bgs.protocol.v2.Attribute attribute in request.CreationProperties.AttributeList)
|
||||
{
|
||||
if (attribute.Name == "GameCreateParams")
|
||||
{
|
||||
this.GameCreateParams = D3.OnlineService.GameCreateParams.ParseFrom(attribute.Value.BlobValue);
|
||||
if (this.GameCreateParams.CreationFlags == 256 || this.GameCreateParams.CreationFlags == 262400) this.Public = true;
|
||||
lock (owner.Account.GameAccount.CurrentToon.DBToon)
|
||||
{
|
||||
var toonByClient = owner.Account.GameAccount.CurrentToon.DBToon;
|
||||
toonByClient.CurrentAct = this.GameCreateParams.CampaignOrAdventureMode.Act;
|
||||
toonByClient.CurrentQuestId = (this.GameCreateParams.CampaignOrAdventureMode.SnoQuest == 0 ? 87700 : this.GameCreateParams.CampaignOrAdventureMode.SnoQuest);
|
||||
toonByClient.CurrentQuestStepId = (this.GameCreateParams.CampaignOrAdventureMode.QuestStepId == 0 ? -1 : this.GameCreateParams.CampaignOrAdventureMode.QuestStepId);
|
||||
toonByClient.CurrentDifficulty = this.GameCreateParams.CampaignOrAdventureMode.HandicapLevel;
|
||||
DBSessions.SessionUpdate(toonByClient);
|
||||
}
|
||||
}
|
||||
else if (attribute.Name == "version")
|
||||
this.Version = attribute.Value.StringValue;
|
||||
}
|
||||
|
||||
foreach (bgs.protocol.v2.Attribute attribute in request.MatchmakerFilter.AttributeList)
|
||||
{
|
||||
if (attribute.Name != "version")
|
||||
;
|
||||
else
|
||||
this.Version = attribute.Value.StringValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static KeyValuePair<string, BattleBackend.ServerDescriptor> ChooseGameServer()
|
||||
{
|
||||
return Program.BattleBackend.GameServers.First();
|
||||
}
|
||||
|
||||
public void StartGame(List<BattleClient> clients, ulong objectId)
|
||||
{
|
||||
Logger.Trace("StartGame(): objectId: {0}", objectId);
|
||||
var owner = this.Owner.Account.GameAccount.CurrentToon.DBToon;
|
||||
|
||||
if (Program.BattleBackend.GameServers.Count == 0) return;
|
||||
|
||||
Program.BattleBackend.CreateGame(
|
||||
this.GServer.Key,
|
||||
(int)this.DynamicId,
|
||||
owner.Level,
|
||||
owner.CurrentAct,
|
||||
owner.CurrentDifficulty,
|
||||
owner.CurrentQuestId,
|
||||
owner.CurrentQuestStepId,
|
||||
owner.isHardcore,
|
||||
this.GameCreateParams.GameType,
|
||||
owner.isSeasoned
|
||||
);
|
||||
|
||||
foreach (var client in clients)
|
||||
{
|
||||
client.MapLocalObjectID(this.DynamicId, objectId);
|
||||
this.SendConnectionInfo(client);
|
||||
client.Account.GameAccount.ScreenStatus = D3.PartyMessage.ScreenStatus.CreateBuilder().SetScreen(0).SetStatus(0).Build();
|
||||
client.Account.GameAccount.NotifyUpdate();
|
||||
}
|
||||
|
||||
this.Started = true;
|
||||
|
||||
}
|
||||
|
||||
public void JoinGame(List<BattleClient> clients, ulong objectId)
|
||||
{
|
||||
foreach (var client in clients)
|
||||
{
|
||||
client.MapLocalObjectID(this.DynamicId, objectId);
|
||||
this.SendConnectionInfo(client);
|
||||
client.Account.GameAccount.ScreenStatus = D3.PartyMessage.ScreenStatus.CreateBuilder().SetScreen(1).SetStatus(1).Build();
|
||||
client.Account.GameAccount.NotifyUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public bgs.protocol.games.v2.ConnectInfo GetConnectionInfoForClient(BattleClient client)
|
||||
{
|
||||
return bgs.protocol.games.v2.ConnectInfo.CreateBuilder()
|
||||
.SetAddress(bgs.protocol.Address.CreateBuilder().SetAddress_(this.GServer.Value.GameIP).SetPort((uint)this.GServer.Value.GamePort))
|
||||
.SetToken(Google.ProtocolBuffers.ByteString.CopyFrom(new byte[] { 0x31, 0x33, 0x38, 0x38, 0x35, 0x34, 0x33, 0x33, 0x32, 0x30, 0x38, 0x34, 0x30, 0x30, 0x38, 0x38, 0x35, 0x37, 0x39, 0x36 }))
|
||||
.AddAttribute(bgs.protocol.Attribute.CreateBuilder().SetName("Token").SetValue(bgs.protocol.Variant.CreateBuilder().SetUintValue(0xee34d06ffe821c43L)))
|
||||
|
||||
.AddAttribute(bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("SGameId").SetValue(bgs.protocol.Variant.CreateBuilder().SetIntValue((long)this.DynamicId).Build()))
|
||||
.AddAttribute(bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("GameCreateParams").SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(this.GameCreateParams.ToByteString()).Build()))
|
||||
.Build();
|
||||
}
|
||||
|
||||
private void SendConnectionInfo(BattleClient client)
|
||||
{
|
||||
if (client.CurrentChannel != null)
|
||||
{
|
||||
var channelStatePrivacyLevel = bgs.protocol.channel.v1.ChannelState.CreateBuilder()
|
||||
.SetPrivacyLevel(bgs.protocol.channel.v1.ChannelState.Types.PrivacyLevel.PRIVACY_LEVEL_OPEN).Build();
|
||||
|
||||
var notificationPrivacyLevel = bgs.protocol.channel.v1.UpdateChannelStateNotification.CreateBuilder()
|
||||
.SetAgentId(client.Account.GameAccount.BnetEntityId)
|
||||
.SetStateChange(channelStatePrivacyLevel)
|
||||
.Build();
|
||||
|
||||
var altPrivacyLevel = bgs.protocol.channel.v1.JoinNotification.CreateBuilder()
|
||||
.SetChannelState(channelStatePrivacyLevel)
|
||||
.Build();
|
||||
|
||||
client.MakeTargetedRPC(client.CurrentChannel, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(client).OnUpdateChannelState(new HandlerController() { ListenerId = lid }, notificationPrivacyLevel, callback => { }));
|
||||
|
||||
|
||||
var channelStatePartyLock = bgs.protocol.channel.v1.ChannelState.CreateBuilder()
|
||||
.AddAttribute(bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.LockReasons")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetIntValue(0).Build())
|
||||
.Build()).Build();
|
||||
|
||||
|
||||
var notificationPartyLock = bgs.protocol.channel.v1.UpdateChannelStateNotification.CreateBuilder()
|
||||
.SetAgentId(client.Account.GameAccount.BnetEntityId)
|
||||
.SetStateChange(channelStatePartyLock)
|
||||
.Build();
|
||||
|
||||
var altPartyLock = bgs.protocol.channel.v1.JoinNotification.CreateBuilder()
|
||||
.SetChannelState(channelStatePartyLock)
|
||||
.Build();
|
||||
|
||||
client.MakeTargetedRPC(client.CurrentChannel, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(client).OnUpdateChannelState(new HandlerController() { ListenerId = lid }, notificationPartyLock, callback => { }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
323
src/DiIiS-NA/BGS-Server/GamesSystem/GameFactoryManager.cs
Normal file
323
src/DiIiS-NA/BGS-Server/GamesSystem/GameFactoryManager.cs
Normal file
@ -0,0 +1,323 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.matchmaking.v1;
|
||||
//Blizzless Project 2022
|
||||
using D3.OnlineService;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Battle;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.ChannelSystem;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.GamesSystem
|
||||
{
|
||||
public static class GameFactoryManager
|
||||
{
|
||||
private static readonly Dictionary<ulong, GameDescriptor> GameCreators =
|
||||
new Dictionary<ulong, GameDescriptor>();
|
||||
|
||||
public static int GamesOnline
|
||||
{
|
||||
get
|
||||
{
|
||||
return GameCreators.Values.Where(game => game.PlayersCount > 0).Count();
|
||||
}
|
||||
set { }
|
||||
}
|
||||
|
||||
public static ulong RequestIdCounter = 1;
|
||||
|
||||
public static GameDescriptor JoinGame(BattleClient client, bgs.protocol.games.v1.JoinGameRequest request, ulong requetId)
|
||||
{
|
||||
var game = FindGameByEntityId(request.GameHandle.GameId);
|
||||
return game;
|
||||
}
|
||||
|
||||
public static GameDescriptor CreateGame(BattleClient owner, GameMatchmakingOptions request, ulong requestId)
|
||||
{
|
||||
var gameDescriptor = new GameDescriptor(owner, request, requestId);
|
||||
GameCreators.Add(gameDescriptor.DynamicId, gameDescriptor);
|
||||
ChannelManager.AddGameChannel(gameDescriptor);
|
||||
|
||||
return gameDescriptor;
|
||||
}
|
||||
|
||||
public static GameDescriptor FindGameByEntityId(bgs.protocol.EntityId entityId)
|
||||
{
|
||||
foreach (GameDescriptor game in GameCreators.Values)
|
||||
{
|
||||
if (game.BnetEntityId.Low == entityId.Low)
|
||||
return game;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static GameDescriptor FindGameByDynamicId(ulong gameId)
|
||||
{
|
||||
foreach (GameDescriptor game in GameCreators.Values)
|
||||
{
|
||||
if (game.DynamicId == gameId)
|
||||
return game;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void DeleteGame(ulong dynId)
|
||||
{
|
||||
GameCreators.Remove(dynId);
|
||||
}
|
||||
|
||||
public static GameDescriptor FindGame(BattleClient client, QueueMatchmakingRequest request, ulong requestId)
|
||||
{
|
||||
string request_type = "";
|
||||
string ServerPool = "";
|
||||
bgs.protocol.v2.Attribute AttributeOfServer = null;
|
||||
GameCreateParams gameCreateParams;
|
||||
foreach (var attr in request.Options.CreationProperties.AttributeList)
|
||||
{
|
||||
switch (attr.Name)
|
||||
{
|
||||
case "GameCreateParams":
|
||||
gameCreateParams = GameCreateParams.ParseFrom(attr.Value.BlobValue);
|
||||
AttributeOfServer = attr;
|
||||
break;
|
||||
case "ServerPool":
|
||||
ServerPool = attr.Value.StringValue;
|
||||
break;
|
||||
case "request_type":
|
||||
request_type = attr.Value.StringValue;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
List<GameDescriptor> matchingGames = FindMatchingGames(request);
|
||||
|
||||
GameDescriptor TagGame = null;
|
||||
if (client.GameTeamTag != "") TagGame = FindTagGame(client.GameTeamTag);
|
||||
|
||||
var rand = new Random();
|
||||
GameDescriptor gameDescriptor = null;
|
||||
|
||||
if(TagGame != null)
|
||||
gameDescriptor = TagGame;
|
||||
else if (request_type == "find" && matchingGames.Count > 0)
|
||||
gameDescriptor = matchingGames[rand.Next(matchingGames.Count)];
|
||||
else
|
||||
gameDescriptor = CreateGame(client, request.Options, requestId);
|
||||
|
||||
return gameDescriptor;
|
||||
}
|
||||
|
||||
public static GameDescriptor FindPlayerGame()
|
||||
{
|
||||
foreach (GameDescriptor game in GameCreators.Values)
|
||||
{
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static GameDescriptor FindPlayerGame(BattleClient ToClient)
|
||||
{
|
||||
foreach (GameDescriptor game in GameCreators.Values)
|
||||
{
|
||||
if (game.D3EntityId == ToClient.GameChannel.D3EntityId)
|
||||
return game;
|
||||
;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static GameDescriptor FindTagGame(string GameTag)
|
||||
{
|
||||
GameDescriptor taggame = null;
|
||||
foreach (GameDescriptor game in GameCreators.Values)
|
||||
if (game.PlayersCount > 0 && game.PlayersCount < 8)
|
||||
if (game.Owner.GameTeamTag == GameTag)
|
||||
taggame = game;
|
||||
|
||||
return taggame;
|
||||
}
|
||||
|
||||
private static List<GameDescriptor> FindMatchingGames(QueueMatchmakingRequest request)
|
||||
{
|
||||
String version = String.Empty;
|
||||
int monster_level = 0;
|
||||
int difficulty = 0;
|
||||
int currentQuest = 0;
|
||||
int currentAct = 0;
|
||||
bool ChallengeRift = false;
|
||||
|
||||
|
||||
D3.OnlineService.GameCreateParams Params = D3.OnlineService.GameCreateParams.ParseFrom(request.Options.CreationProperties.GetAttribute(0).Value.BlobValue);
|
||||
|
||||
foreach (var attribute in request.Options.MatchmakerFilter.AttributeList)
|
||||
{
|
||||
switch (attribute.Name)
|
||||
{
|
||||
case "MonsterLevel":
|
||||
difficulty = (int)attribute.Value.IntValue;
|
||||
break;
|
||||
case "version":
|
||||
version = attribute.Value.StringValue;
|
||||
break;
|
||||
case "Game.MonsterLevel":
|
||||
monster_level = (int)attribute.Value.IntValue;
|
||||
break;
|
||||
case "HandicapLevel":
|
||||
difficulty = (int)attribute.Value.IntValue;
|
||||
break;
|
||||
case "Game.CurrentQuest":
|
||||
currentQuest = (int)attribute.Value.IntValue;
|
||||
break;
|
||||
case "Game.CurrentAct":
|
||||
currentAct = (int)attribute.Value.IntValue;
|
||||
break;
|
||||
case "MatchmakingPartition":
|
||||
if ((int)attribute.Value.IntValue == 9)
|
||||
ChallengeRift = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
List<GameDescriptor> matches = new List<GameDescriptor>();
|
||||
foreach (GameDescriptor game in GameCreators.Values)
|
||||
{
|
||||
if (game.PlayersCount > 0 && game.Public && game.PlayersCount < 50)
|
||||
{
|
||||
{
|
||||
|
||||
if (ChallengeRift)
|
||||
{
|
||||
if (game.GameCreateParams.GameType == 9)
|
||||
matches.Add(game);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Params.CampaignOrAdventureMode.HandicapLevel == game.GameCreateParams.CampaignOrAdventureMode.HandicapLevel)
|
||||
if (Params.CampaignOrAdventureMode.Act == game.GameCreateParams.CampaignOrAdventureMode.Act)
|
||||
{
|
||||
if (Params.CampaignOrAdventureMode.SnoQuest == game.GameCreateParams.CampaignOrAdventureMode.SnoQuest)
|
||||
{
|
||||
matches.Add(game);
|
||||
}
|
||||
else if (Params.CampaignOrAdventureMode.SnoQuest == -1)
|
||||
{
|
||||
matches.Add(game);
|
||||
}
|
||||
}
|
||||
else if (Params.CampaignOrAdventureMode.Act == -1)
|
||||
{
|
||||
matches.Add(game);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
public enum Mode
|
||||
{
|
||||
Campaign = 1,
|
||||
Bounties = 2,
|
||||
Portals = 9
|
||||
}
|
||||
public static D3.GameMessage.MatchmakingStatsBucket GetStatsBucketWithFilter(D3.GameMessage.MatchmakingGetStats request)
|
||||
{
|
||||
String version = String.Empty;
|
||||
version = request.Version;
|
||||
int GameMode = request.Partition;
|
||||
|
||||
int Difficulty = 0;
|
||||
int handicap = 0;
|
||||
uint GameQuest = 0;
|
||||
int GameAct = 0;
|
||||
string GameTag = "";
|
||||
var response = D3.GameMessage.MatchmakingStatsBucket.CreateBuilder();
|
||||
uint games = 0;
|
||||
|
||||
uint players = 0;
|
||||
|
||||
switch (request.Partition)
|
||||
{
|
||||
case 1: //Кампания
|
||||
handicap = request.HandicapLevel;
|
||||
Difficulty = request.MonsterLevel;
|
||||
GameTag = request.GameTag;
|
||||
GameAct = request.GameAct;
|
||||
GameQuest = request.GameQuest;
|
||||
break;
|
||||
case 2: //Приключения
|
||||
Difficulty = request.HandicapLevel;
|
||||
GameTag = request.GameTag;
|
||||
break;
|
||||
case 3: //Порталы дерзаний
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (GameDescriptor game in GameCreators.Values)
|
||||
{
|
||||
if (game != null)
|
||||
{
|
||||
switch ((Mode)GameMode)
|
||||
{
|
||||
case Mode.Campaign:
|
||||
if (game.GameCreateParams.CampaignOrAdventureMode.HandicapLevel == handicap)
|
||||
{
|
||||
if (request.HasGameAct)
|
||||
{
|
||||
if (game.PlayersCount > 0)
|
||||
if (game.GameCreateParams.CampaignOrAdventureMode.SnoQuest == GameQuest)
|
||||
{
|
||||
games++;
|
||||
players += (uint)game.PlayersCount;
|
||||
}
|
||||
else if (!request.HasGameQuest)
|
||||
{
|
||||
games++;
|
||||
players += (uint)game.PlayersCount;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
games++;
|
||||
players += (uint)game.PlayersCount;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Mode.Bounties:
|
||||
if (game.GameCreateParams.CampaignOrAdventureMode.HandicapLevel == handicap)
|
||||
{
|
||||
games++;
|
||||
players += (uint)game.PlayersCount;
|
||||
}
|
||||
break;
|
||||
|
||||
case Mode.Portals:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.SetPlayersInOpenGamesTotal(players)
|
||||
.SetOpenGamesTotal(games)
|
||||
.SetWaitingPlayers(0)
|
||||
.SetFormingGames(0)
|
||||
;
|
||||
|
||||
return response.Build();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
626
src/DiIiS-NA/BGS-Server/GuildSystem/Guild.cs
Normal file
626
src/DiIiS-NA/BGS-Server/GuildSystem/Guild.cs
Normal file
@ -0,0 +1,626 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Extensions;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage.AccountDataBase.Entities;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.ChannelSystem;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using DateTime = System.DateTime;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.GuildSystem
|
||||
{
|
||||
public class Guild
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
|
||||
/// <summary>
|
||||
/// D3.GameMessage.GuildId encoded guild Id.
|
||||
/// </summary>
|
||||
public D3.GameMessage.GuildId GuildId { get; protected set; }
|
||||
|
||||
public ulong PersistentId { get; protected set; }
|
||||
|
||||
public DBGuild DBGuild
|
||||
{
|
||||
get
|
||||
{
|
||||
return DBSessions.SessionGet<DBGuild>(this.PersistentId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Max number of members.
|
||||
/// </summary>
|
||||
public uint MaxMembers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the guild
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
public string FullName
|
||||
{
|
||||
get
|
||||
{
|
||||
//return this.IsClan ? string.Format("{0} [{1}]", this.Name, this.ParagonRatings.Values.ToList().OrderByDescending(x => x).Take(12).Sum()) : this.Name;
|
||||
return this.Name;
|
||||
}
|
||||
}
|
||||
|
||||
public uint Flags
|
||||
{
|
||||
get
|
||||
{
|
||||
var flags = GuildFlags.None;
|
||||
if (!this.Disbanded) flags |= GuildFlags.Enabled;
|
||||
if (this.IsLFM) flags |= GuildFlags.LookingForMembers;
|
||||
if (this.IsInviteRequired) flags |= GuildFlags.InviteOnly;
|
||||
return (uint)flags;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prefix of the guild (clans)
|
||||
/// </summary>
|
||||
private string _Prefix = "";
|
||||
|
||||
public string Prefix
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._Prefix;
|
||||
}
|
||||
set
|
||||
{
|
||||
this._Prefix = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string Description { get; set; }
|
||||
public string MOTD { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Category of the guild (clans are always 0)
|
||||
/// </summary>
|
||||
public uint Category { get; set; }
|
||||
public uint Language { get; set; }
|
||||
|
||||
public Dictionary<uint, RankDescriptor> Ranks { get; set; }
|
||||
|
||||
public byte[] BnetRanks
|
||||
{
|
||||
get
|
||||
{
|
||||
var builder = D3.Guild.RankList.CreateBuilder();
|
||||
foreach (var rank in this.Ranks)
|
||||
{
|
||||
builder.AddRanks(D3.Guild.Rank.CreateBuilder().SetRankId(rank.Key).SetRankOrder(rank.Key).SetName(rank.Value.RankName).SetPermissions(rank.Value.Privileges));
|
||||
}
|
||||
return builder.Build().ToByteString().ToByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Guild channel (common one)
|
||||
/// </summary>
|
||||
public Channel Channel { get; set; }
|
||||
public Channel GroupChatChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of guild members + their ranks.
|
||||
/// </summary>
|
||||
public readonly Dictionary<GameAccount, DBGuildMember> Members = new Dictionary<GameAccount, DBGuildMember>();
|
||||
public List<D3.Guild.Invite> GuildSuggestions = new List<D3.Guild.Invite>();
|
||||
|
||||
/// <summary>
|
||||
/// Guild owner.
|
||||
/// </summary>
|
||||
public GameAccount Owner { get; set; }
|
||||
|
||||
public bool IsClan { get; set; }
|
||||
public bool IsLFM { get; set; }
|
||||
public bool IsInviteRequired { get; set; }
|
||||
public Dictionary<GameAccount, int> ParagonRatings { get; set; }
|
||||
public int RatingPoints { get; set; }
|
||||
|
||||
public ulong NewsTime { get; set; }
|
||||
public ulong InviteTime { get; set; }
|
||||
|
||||
public bool Disbanded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new guild instance
|
||||
/// </summary>
|
||||
public Guild(DBGuild dbGuild)
|
||||
{
|
||||
this.PersistentId = dbGuild.Id;
|
||||
this.GuildId = D3.GameMessage.GuildId.CreateBuilder().SetGuildId_(dbGuild.Id).Build();
|
||||
this.IsClan = (dbGuild.Category == 0);
|
||||
this.IsLFM = dbGuild.IsLFM;
|
||||
this.IsInviteRequired = dbGuild.IsInviteRequired;
|
||||
this.Prefix = dbGuild.Tag;
|
||||
this.Name = dbGuild.Name;
|
||||
this.Description = dbGuild.Description;
|
||||
this.MOTD = dbGuild.MOTD;
|
||||
this.RatingPoints = dbGuild.Rating;
|
||||
var ranks = D3.Guild.RankList.ParseFrom(dbGuild.Ranks);
|
||||
this.Ranks = new Dictionary<uint, RankDescriptor>();
|
||||
foreach (var rank in ranks.RanksList)
|
||||
this.Ranks.Add(rank.RankId, new RankDescriptor() { RankName = rank.Name, Privileges = rank.Permissions });
|
||||
this.NewsTime = 0;
|
||||
this.InviteTime = 0;
|
||||
this.Owner = GameAccountManager.GetGameAccountByDBGameAccount(dbGuild.Creator);
|
||||
this.MaxMembers = this.IsClan ? (16U) : 100U;
|
||||
this.Category = (uint)dbGuild.Category;
|
||||
this.Language = (uint)dbGuild.Language;
|
||||
this.Disbanded = dbGuild.Disbanded;
|
||||
this.Channel = ChannelManager.CreateGuildChannel(this);
|
||||
this.GroupChatChannel = ChannelManager.CreateGuildGroupChannel(this);
|
||||
this.ParagonRatings = new Dictionary<GameAccount, int>();
|
||||
}
|
||||
|
||||
public void Disband()
|
||||
{
|
||||
this.RemoveAllMembers();
|
||||
this.Disbanded = true;
|
||||
lock (this.DBGuild)
|
||||
{
|
||||
var dbGuild = this.DBGuild;
|
||||
dbGuild.Disbanded = true;
|
||||
DBSessions.SessionUpdate(dbGuild);
|
||||
}
|
||||
}
|
||||
|
||||
public void ActivatePremium()
|
||||
{
|
||||
if (this.Ranks.Count < 4)
|
||||
this.Ranks.Add(3, new RankDescriptor() { RankName = "Advanced", Privileges = 212943U });
|
||||
lock (this.DBGuild)
|
||||
{
|
||||
var dbGuild = this.DBGuild;
|
||||
dbGuild.Ranks = this.BnetRanks;
|
||||
DBSessions.SessionUpdate(dbGuild);
|
||||
}
|
||||
this.UpdateChannelAttributes();
|
||||
}
|
||||
|
||||
#region member functionality
|
||||
|
||||
public bool HasMember(GameAccount account)
|
||||
{
|
||||
return this.Members.Keys.Any(a => a == account);
|
||||
}
|
||||
|
||||
public void AddMember(GameAccount account)
|
||||
{
|
||||
if (HasMember(account))
|
||||
{
|
||||
Logger.Warn("Attempted to add account {0} to guild when it was already a member of the guild", account.Owner.BattleTag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.IsClan && account.Clan != null)
|
||||
{
|
||||
Logger.Warn("Attempted to add account {0} to clan when it was already a member of the another clan", account.Owner.BattleTag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.Members.Count + 1 > this.MaxMembers) return;
|
||||
|
||||
var newDBGuildMember = new DBGuildMember
|
||||
{
|
||||
DBGuild = this.DBGuild,
|
||||
DBGameAccount = account.DBGameAccount,
|
||||
Note = "",
|
||||
Rank = (account.PersistentID == this.Owner.PersistentID ? 1 : 4)
|
||||
};
|
||||
DBSessions.SessionSave(newDBGuildMember);
|
||||
|
||||
this.Members.Add(account, newDBGuildMember);
|
||||
if (this.IsClan)
|
||||
{
|
||||
this.ParagonRatings.Add(account, account.DBGameAccount.ParagonLevel);
|
||||
this.AddNews(account, 2);
|
||||
}
|
||||
this.NotifyChannels(account);
|
||||
}
|
||||
|
||||
public void RemoveAllMembers()
|
||||
{
|
||||
List<GameAccount> _members = this.Members.Keys.ToList();
|
||||
foreach (var account in _members)
|
||||
{
|
||||
RemoveMember(account);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveMember(GameAccount account)
|
||||
{
|
||||
if (account == null) return;
|
||||
|
||||
if (!HasMember(account))
|
||||
{
|
||||
Logger.Warn("Attempted to remove non-member account {0} from guild {1}#{2}", account.Owner.BattleTag, this.Name, this.PersistentId);
|
||||
return;
|
||||
}
|
||||
else if (account.Clan != this && !account.Communities.Contains(this))
|
||||
{
|
||||
Logger.Warn("Client {0} being removed from a guild ({1}#{2}) he's not associated with.", account.Owner.BattleTag, this.Name, this.PersistentId);
|
||||
}
|
||||
|
||||
this.Members.Remove(account);
|
||||
this.Channel.RemoveMember(account.LoggedInClient, Channel.RemoveReason.Left);
|
||||
if (this.IsClan)
|
||||
{
|
||||
this.ParagonRatings.Remove(account);
|
||||
this.AddNews(account, 3);
|
||||
}
|
||||
var dbRow = DBSessions.SessionQuerySingle<DBGuildMember>(m => m.DBGuild.Id == this.PersistentId && m.DBGameAccount.Id == account.PersistentID);
|
||||
DBSessions.SessionDelete(dbRow);
|
||||
}
|
||||
|
||||
public void AddSuggestion(GameAccount account, GameAccount inviter)
|
||||
{
|
||||
var invite = D3.Guild.Invite.CreateBuilder()
|
||||
.SetAccountId(account.PersistentID)
|
||||
.SetInviterId(inviter.PersistentID)
|
||||
.SetInviteTime(DateTime.Now.ToUnixTime())
|
||||
.SetInviteType(1)
|
||||
.SetExpireTime(3600);
|
||||
this.GuildSuggestions.Add(invite.Build());
|
||||
|
||||
this.InviteTime = DateTime.Now.ToBlizzardEpoch();
|
||||
this.UpdateChannelAttributes();
|
||||
}
|
||||
|
||||
public void RemoveSuggestion(GameAccount account)
|
||||
{
|
||||
this.GuildSuggestions.RemoveAll(s => s.AccountId == account.PersistentID);
|
||||
}
|
||||
|
||||
public uint GetRank(ulong GameAccountId)
|
||||
{
|
||||
if (this.Members.Keys.Any(m => m.PersistentID == GameAccountId))
|
||||
{
|
||||
return (uint)this.Members.Single(m => m.Key.PersistentID == GameAccountId).Value.Rank;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("Client {0} called GetRank() on a guild ({1}#{2}) he's not associated with.", GameAccountId, this.Name, this.PersistentId);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetMemberNote(ulong GameAccountId)
|
||||
{
|
||||
if (this.Members.Keys.Any(m => m.PersistentID == GameAccountId))
|
||||
{
|
||||
return this.Members.Single(m => m.Key.PersistentID == GameAccountId).Value.Note;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("Client {0} called GetMemberNote() on a guild ({1}#{2}) he's not associated with.", GameAccountId, this.Name, this.PersistentId);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public void SetMemberNote(GameAccount account, string note)
|
||||
{
|
||||
if (!this.HasMember(account)) return;
|
||||
|
||||
this.Members[account].Note = note;
|
||||
DBSessions.SessionUpdate(this.Members[account]);
|
||||
this.UpdateMemberAttributes(account);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ranks functionality
|
||||
|
||||
public bool HasPermission(GameAccount account, GuildPrivilegeFlags permission)
|
||||
{
|
||||
if (!this.HasMember(account)) return false;
|
||||
|
||||
var rank = (uint)this.Members[account].Rank;
|
||||
|
||||
if (!this.Ranks.ContainsKey(rank)) return false;
|
||||
|
||||
var permissions = (GuildPrivilegeFlags)this.Ranks[rank].Privileges;
|
||||
|
||||
return permissions.HasFlag(permission);
|
||||
}
|
||||
|
||||
public void SetPermissions(uint rank, string name, uint permissions)
|
||||
{
|
||||
if (!this.Ranks.ContainsKey(rank)) return;
|
||||
|
||||
this.Ranks[rank].RankName = name;
|
||||
this.Ranks[rank].Privileges = permissions;
|
||||
|
||||
lock (this.DBGuild)
|
||||
{
|
||||
var dbGuild = this.DBGuild;
|
||||
dbGuild.Ranks = this.BnetRanks;
|
||||
DBSessions.SessionUpdate(dbGuild);
|
||||
}
|
||||
this.UpdateChannelAttributes();
|
||||
}
|
||||
|
||||
public void PromoteMember(GameAccount account, bool setLeader = false)
|
||||
{
|
||||
if (!this.HasMember(account)) return;
|
||||
|
||||
var rank = (uint)this.Members[account].Rank;
|
||||
|
||||
if (!this.Ranks.ContainsKey(rank)) return;
|
||||
if (rank <= 2 && !setLeader) return;
|
||||
|
||||
this.Members[account].Rank--;
|
||||
if (this.Members[account].Rank == 3)
|
||||
this.Members[account].Rank--;
|
||||
|
||||
DBSessions.SessionUpdate(this.Members[account]);
|
||||
this.UpdateMemberAttributes(account);
|
||||
if (setLeader)
|
||||
this.AddNews(account, 7);
|
||||
else
|
||||
this.AddNews(account, 4);
|
||||
}
|
||||
|
||||
public void DemoteMember(GameAccount account)
|
||||
{
|
||||
if (!this.HasMember(account)) return;
|
||||
|
||||
var rank = (uint)this.Members[account].Rank;
|
||||
|
||||
if (!this.Ranks.ContainsKey(rank)) return;
|
||||
if (rank >= this.Ranks.Count) return;
|
||||
|
||||
this.Members[account].Rank++;
|
||||
if (this.Members[account].Rank == 3)
|
||||
this.Members[account].Rank++;
|
||||
|
||||
DBSessions.SessionUpdate(this.Members[account]);
|
||||
this.UpdateMemberAttributes(account);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region guild channel
|
||||
|
||||
/// <summary>
|
||||
/// bnet.protocol.channel.ChannelState message.
|
||||
/// </summary>
|
||||
public bgs.protocol.channel.v1.ChannelState ChannelState
|
||||
{
|
||||
get
|
||||
{
|
||||
var state = bgs.protocol.channel.v1.ChannelState.CreateBuilder()
|
||||
.SetMinMembers(0)
|
||||
.SetMaxMembers(this.MaxMembers)
|
||||
.SetName(this.Name)
|
||||
.SetPrivacyLevel(bgs.protocol.channel.v1.ChannelState.Types.PrivacyLevel.PRIVACY_LEVEL_OPEN)
|
||||
.SetChannelType(this.IsClan ? "clan" : "group")
|
||||
.SetProgram(17459)
|
||||
.SetSubscribeToPresence(true);
|
||||
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.GuildId").SetValue(Variant.CreateBuilder().SetUintValue(this.PersistentId)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.Name").SetValue(Variant.CreateBuilder().SetStringValue(this.FullName)));
|
||||
if (this.IsClan)
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.Tag").SetValue(Variant.CreateBuilder().SetStringValue(this.Prefix)));
|
||||
else
|
||||
{
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.TotalMembers").SetValue(Variant.CreateBuilder().SetUintValue((ulong)this.Members.Count)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.TotalMembersInChat").SetValue(Variant.CreateBuilder().SetUintValue((ulong)this.GroupChatChannel.Members.Count)));
|
||||
}
|
||||
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.Ranks").SetValue(Variant.CreateBuilder().SetMessageValue(ByteString.CopyFrom(this.BnetRanks))));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.Motd").SetValue(Variant.CreateBuilder().SetStringValue(this.MOTD)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.Description").SetValue(Variant.CreateBuilder().SetStringValue(this.Description)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.Category").SetValue(Variant.CreateBuilder().SetUintValue(this.Category)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.Creator").SetValue(Variant.CreateBuilder().SetUintValue(this.Owner.PersistentID)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.Language").SetValue(Variant.CreateBuilder().SetUintValue(this.Language)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.InviteTime").SetValue(Variant.CreateBuilder().SetUintValue(this.InviteTime)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.Flags").SetValue(Variant.CreateBuilder().SetUintValue(this.Flags)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.SearchCategory").SetValue(Variant.CreateBuilder().SetUintValue(this.Category)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.PostNewsTime").SetValue(Variant.CreateBuilder().SetUintValue(0)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.NewsTime").SetValue(Variant.CreateBuilder().SetUintValue(this.NewsTime)));
|
||||
|
||||
return state.Build();
|
||||
}
|
||||
}
|
||||
|
||||
public bgs.protocol.channel.v1.ChannelState GroupChatChannelState
|
||||
{
|
||||
get
|
||||
{
|
||||
var state = bgs.protocol.channel.v1.ChannelState.CreateBuilder()
|
||||
.SetMinMembers(0)
|
||||
.SetMaxMembers(this.MaxMembers)
|
||||
.SetName(this.Name)
|
||||
.SetPrivacyLevel(bgs.protocol.channel.v1.ChannelState.Types.PrivacyLevel.PRIVACY_LEVEL_OPEN)
|
||||
.SetChannelType("group_chat")
|
||||
.SetProgram(17459)
|
||||
.SetSubscribeToPresence(true);
|
||||
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.GuildId").SetValue(Variant.CreateBuilder().SetUintValue(this.PersistentId)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.Guild.Name").SetValue(Variant.CreateBuilder().SetStringValue(this.Name)));
|
||||
|
||||
|
||||
return state.Build();
|
||||
}
|
||||
}
|
||||
|
||||
public D3.Guild.GuildSummary Summary
|
||||
{
|
||||
get
|
||||
{
|
||||
var summary = D3.Guild.GuildSummary.CreateBuilder()
|
||||
.SetGuildId(this.PersistentId)
|
||||
.SetGuildName(this.Name)
|
||||
.SetGuildTag(this.Prefix)
|
||||
.SetGuildFlags(this.Flags);
|
||||
return summary.Build();
|
||||
}
|
||||
}
|
||||
|
||||
public void NotifyChannels(GameAccount account)
|
||||
{
|
||||
var guildChannelId = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("channel_id")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetEntityIdValue(
|
||||
this.Channel.BnetEntityId
|
||||
).Build())
|
||||
.Build();
|
||||
|
||||
var notificationBuilder = bgs.protocol.notification.v1.Notification.CreateBuilder()
|
||||
.SetTargetId(account.BnetEntityId)
|
||||
.SetType("P_SUBSCRIBE_TO_CHANNEL")
|
||||
.AddAttribute(guildChannelId)
|
||||
.SetSenderAccountId(account.Owner.BnetEntityId)
|
||||
.SetTargetAccountId(account.Owner.BnetEntityId)
|
||||
.Build();
|
||||
|
||||
account.LoggedInClient.MakeRPC((lid) =>
|
||||
bgs.protocol.notification.v1.NotificationListener.CreateStub(account.LoggedInClient).OnNotificationReceived(new HandlerController() { ListenerId = lid
|
||||
}, notificationBuilder, callback => { }));
|
||||
|
||||
if (!this.IsClan)
|
||||
{
|
||||
var guildChatChannelId = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("channel_id")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetEntityIdValue(
|
||||
this.GroupChatChannel.BnetEntityId
|
||||
).Build())
|
||||
.Build();
|
||||
|
||||
var chatNotificationBuilder = bgs.protocol.notification.v1.Notification.CreateBuilder()
|
||||
.SetTargetId(account.BnetEntityId)
|
||||
.SetType("P_SUBSCRIBE_TO_CHANNEL")
|
||||
.AddAttribute(guildChatChannelId)
|
||||
.SetSenderAccountId(account.Owner.BnetEntityId)
|
||||
.SetTargetAccountId(account.Owner.BnetEntityId)
|
||||
.Build();
|
||||
|
||||
account.LoggedInClient.MakeRPC((lid) =>
|
||||
bgs.protocol.notification.v1.NotificationListener.CreateStub(account.LoggedInClient).OnNotificationReceived(new HandlerController() { ListenerId = lid
|
||||
}, chatNotificationBuilder, callback => { }));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region guild news
|
||||
|
||||
public void AddNews(GameAccount account, int type, byte[] data = null)
|
||||
{
|
||||
var newDBGuildNews = new DBGuildNews
|
||||
{
|
||||
DBGuild = this.DBGuild,
|
||||
DBGameAccount = account.DBGameAccount,
|
||||
Type = type,
|
||||
Time = DateTime.Now.ToBlizzardEpoch(),
|
||||
Data = data
|
||||
};
|
||||
DBSessions.SessionSave(newDBGuildNews);
|
||||
|
||||
this.NewsTime = DateTime.Now.ToBlizzardEpoch();
|
||||
this.UpdateChannelAttributes();
|
||||
}
|
||||
|
||||
public void UpdateChannelAttributes()
|
||||
{
|
||||
var notification = bgs.protocol.channel.v1.UpdateChannelStateNotification.CreateBuilder().SetStateChange(this.ChannelState).Build();
|
||||
var altnotification = bgs.protocol.channel.v1.JoinNotification.CreateBuilder().SetChannelState(this.ChannelState).Build();
|
||||
|
||||
foreach (var member in this.Channel.Members)
|
||||
//member.Key.MakeTargetedRPC(this.Channel, (lid) => bgs.protocol.channel.v1.ChannelListener.CreateStub(member.Key).OnUpdateChannelState(new HandlerController() { ListenerId = lid }, notification, callback => { }));
|
||||
member.Key.MakeTargetedRPC(this.Channel, (lid) => bgs.protocol.channel.v1.ChannelListener.CreateStub(member.Key).OnJoin(new HandlerController() { ListenerId = lid }, altnotification, callback => { }));
|
||||
}
|
||||
|
||||
public void UpdateMemberAttributes(GameAccount member)
|
||||
{
|
||||
if (!this.HasMember(member)) return;
|
||||
|
||||
var channelMember = bgs.protocol.channel.v1.Member.CreateBuilder();
|
||||
var state = bgs.protocol.channel.v1.MemberState.CreateBuilder();
|
||||
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.GuildMember.Rank").SetValue(Variant.CreateBuilder().SetIntValue(this.Members[member].Rank)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.GuildMember.Note").SetValue(Variant.CreateBuilder().SetStringValue(this.Members[member].Note)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.GuildMember.Hardcore").SetValue(bgs.protocol.Variant.CreateBuilder().SetBoolValue(true)));
|
||||
state.AddAttribute(Attribute.CreateBuilder().SetName("D3.GuildMember.AchievementPoints").SetValue(Variant.CreateBuilder().SetUintValue(member.AchievementPoints)));
|
||||
|
||||
var identity = bgs.protocol.Identity.CreateBuilder().SetGameAccountId(member.BnetEntityId);
|
||||
|
||||
channelMember.SetIdentity(identity);
|
||||
channelMember.SetState(state);
|
||||
|
||||
var notification = bgs.protocol.channel.v1.UpdateMemberStateNotification.CreateBuilder()
|
||||
.AddStateChange(channelMember)
|
||||
.Build();
|
||||
|
||||
foreach (var mbr in this.Channel.Members)
|
||||
mbr.Key.MakeTargetedRPC(this.Channel, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(mbr.Key).OnUpdateMemberState(new HandlerController() { ListenerId = lid }, notification, callback => { }));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[System.Flags]
|
||||
public enum GuildFlags : uint
|
||||
{
|
||||
None = 0x00,
|
||||
LookingForMembers = 0x01,
|
||||
InviteOnly = 0x02,
|
||||
Enabled = 0x04
|
||||
}
|
||||
|
||||
[System.Flags]
|
||||
public enum GuildPrivilegeFlags : uint
|
||||
{
|
||||
None = 0x00,
|
||||
Enabled = 0x01,
|
||||
ClanChatSpeak = 0x02,
|
||||
IsOfficer = 0x04,
|
||||
OfficerChatSpeak = 0x08,
|
||||
Promote = 0x10,
|
||||
Demote = 0x20,
|
||||
Invite = 0x40,
|
||||
Kick = 0x80,
|
||||
SetMOTD = 0x100,
|
||||
AddNews = 0x200,
|
||||
EditMemberNotesOther = 0x400,
|
||||
EditMemberNotesSelf = 0x800,
|
||||
SeeNotes = 0x1000,
|
||||
ModifyPermissions = 0x2000,
|
||||
EditInfo = 0x8000,
|
||||
SeeInvites = 0x10000,
|
||||
CancelInvite = 0x20000,
|
||||
SetLanguage = 0x40000,
|
||||
SetPrivacy = 0x80000,
|
||||
}
|
||||
|
||||
public class RankDescriptor
|
||||
{
|
||||
public string RankName;
|
||||
public uint Privileges;
|
||||
}
|
||||
}
|
||||
}
|
||||
112
src/DiIiS-NA/BGS-Server/GuildSystem/GuildManager.cs
Normal file
112
src/DiIiS-NA/BGS-Server/GuildSystem/GuildManager.cs
Normal file
@ -0,0 +1,112 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage.AccountDataBase.Entities;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.GuildSystem
|
||||
{
|
||||
public static class GuildManager
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
|
||||
public readonly static Dictionary<ulong, Guild> Guilds =
|
||||
new Dictionary<ulong, Guild>();
|
||||
|
||||
public static void PreLoadGuilds()
|
||||
{
|
||||
//Logger.Info("Loading Diablo III guilds system...");
|
||||
List<DBGuild> all_guilds = DBSessions.SessionQuery<DBGuild>();
|
||||
List<DBGuildMember> all_guild_members = DBSessions.SessionQuery<DBGuildMember>();
|
||||
List<DBGuildNews> all_guild_news = DBSessions.SessionQuery<DBGuildNews>();
|
||||
foreach (var dbGuild in all_guilds)
|
||||
{
|
||||
var guild = new Guild(dbGuild);
|
||||
Guilds.Add(guild.PersistentId, guild);
|
||||
foreach (var dbGuildMember in all_guild_members)
|
||||
if (dbGuildMember.DBGuild.Id == guild.PersistentId)
|
||||
{
|
||||
var gacc = GameAccountManager.GetGameAccountByDBGameAccount(dbGuildMember.DBGameAccount);
|
||||
if (guild.IsClan)
|
||||
guild.ParagonRatings.Add(gacc, gacc.DBGameAccount.ParagonLevel);
|
||||
guild.Members.Add(gacc, dbGuildMember);
|
||||
}
|
||||
foreach (var dbGuildNews in all_guild_news)
|
||||
if (dbGuildNews.DBGuild.Id == guild.PersistentId)
|
||||
{
|
||||
if (dbGuildNews.Time > guild.NewsTime)
|
||||
guild.NewsTime = dbGuildNews.Time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Guild CreateNewGuild(GameAccount owner, string name, string tag, bool isClan, uint category, bool isLFM, uint language)
|
||||
{
|
||||
if (DBSessions.SessionQueryWhere<DBGuild>(g => g.Name == name || (g.Tag != "" && g.Tag == tag)).Count > 0) return null;
|
||||
var newDBGuild = new DBGuild
|
||||
{
|
||||
Name = name,
|
||||
Tag = tag,
|
||||
Description = "",
|
||||
MOTD = "",
|
||||
Category = (int)category,
|
||||
Language = (int)language,
|
||||
IsLFM = isLFM,
|
||||
IsInviteRequired = isClan,
|
||||
Rating = 0,
|
||||
Creator = owner.DBGameAccount,
|
||||
Ranks = D3.Guild.RankList.CreateBuilder()
|
||||
.AddRanks(D3.Guild.Rank.CreateBuilder().SetRankId(1).SetRankOrder(1).SetName("Leader").SetPermissions(4294967295))
|
||||
.AddRanks(D3.Guild.Rank.CreateBuilder().SetRankId(2).SetRankOrder(2).SetName("Officer").SetPermissions(212943))
|
||||
.AddRanks(D3.Guild.Rank.CreateBuilder().SetRankId(4).SetRankOrder(4).SetName("Member").SetPermissions(4163))
|
||||
.Build().ToByteString().ToByteArray(),
|
||||
Disbanded = false
|
||||
};
|
||||
DBSessions.SessionSave(newDBGuild);
|
||||
|
||||
var guild = new Guild(newDBGuild);
|
||||
Guilds.Add(guild.PersistentId, guild);
|
||||
guild.AddMember(owner);
|
||||
return guild;
|
||||
}
|
||||
|
||||
public static Guild GetGuildById(ulong id)
|
||||
{
|
||||
return Guilds.ContainsKey(id) ? Guilds[id] : null;
|
||||
}
|
||||
|
||||
public static Guild GetGuildByName(string name)
|
||||
{
|
||||
return Guilds.Values.Where(g => g.Name == name).FirstOrDefault();
|
||||
}
|
||||
|
||||
public static List<Guild> GetClans()
|
||||
{
|
||||
return Guilds.Values.Where(g => g.IsClan).ToList();
|
||||
}
|
||||
|
||||
public static List<Guild> GetCommunities()
|
||||
{
|
||||
return Guilds.Values.Where(g => !g.IsClan).ToList();
|
||||
}
|
||||
|
||||
public static void ReplicateGuilds(GameAccount account)
|
||||
{
|
||||
if (account.Clan != null)
|
||||
{
|
||||
account.Clan.NotifyChannels(account);
|
||||
}
|
||||
|
||||
foreach (var guild in account.Communities)
|
||||
guild.NotifyChannels(account);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
src/DiIiS-NA/BGS-Server/Helpers/EntityIdHelper.cs
Normal file
43
src/DiIiS-NA/BGS-Server/Helpers/EntityIdHelper.cs
Normal file
@ -0,0 +1,43 @@
|
||||
//Blizzless Project 2022
|
||||
namespace DiIiS_NA.LoginServer.Helpers
|
||||
{
|
||||
public static class EntityIdHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns high-id type for given bnet.protocol.EntityId
|
||||
/// </summary>
|
||||
/// <param name="id">The bnet.protocol.EntityId</param>
|
||||
/// <returns><see cref="HighIdType"/></returns>
|
||||
public static HighIdType GetHighIdType(this bgs.protocol.EntityId id)
|
||||
{
|
||||
switch (id.High >> 48)
|
||||
{
|
||||
case 0x0100:
|
||||
return HighIdType.AccountId;
|
||||
case 0x0200:
|
||||
return HighIdType.GameAccountId;
|
||||
case 0x0300:
|
||||
return HighIdType.ItemId;
|
||||
case 0x0000:
|
||||
return HighIdType.ToonId;
|
||||
case 0x0600:
|
||||
return HighIdType.ChannelId;
|
||||
}
|
||||
return HighIdType.Unknown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// High id types for bnet.protocol.EntityId high-id.
|
||||
/// </summary>
|
||||
public enum HighIdType : ulong
|
||||
{
|
||||
Unknown = 0x0,
|
||||
AccountId = 0x100000000000000,
|
||||
GameAccountId = 0x200000000000000,
|
||||
ItemId = 0x300000000000000,
|
||||
ToonId = 0x000000000000000,
|
||||
GameId = 0x600000000000000,
|
||||
ChannelId = 0x600000000000000
|
||||
}
|
||||
}
|
||||
}
|
||||
90
src/DiIiS-NA/BGS-Server/Helpers/FieldKeyHelper.cs
Normal file
90
src/DiIiS-NA/BGS-Server/Helpers/FieldKeyHelper.cs
Normal file
@ -0,0 +1,90 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.presence.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Objects;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Helpers
|
||||
{
|
||||
public class FieldKeyHelper
|
||||
{
|
||||
public enum Program : uint
|
||||
{
|
||||
BNet = 16974,
|
||||
D3 = 17459,
|
||||
S2 = 21298,
|
||||
WoW = 5730135,
|
||||
}
|
||||
|
||||
public enum OriginatingClass : uint
|
||||
{
|
||||
Account = 1,
|
||||
GameAccount = 2,
|
||||
Hero = 3,
|
||||
Party = 4,
|
||||
GameSession = 5
|
||||
}
|
||||
|
||||
public static FieldKey Create(Program program, OriginatingClass originatingClass, uint field, ulong index)
|
||||
{
|
||||
return
|
||||
FieldKey.CreateBuilder().SetProgram((uint)program).SetGroup((uint)originatingClass).SetField(
|
||||
field).SetUniqueId(index).Build();
|
||||
}
|
||||
|
||||
|
||||
private HashSet<FieldKey> _changedFields = new HashSet<FieldKey>();
|
||||
private Dictionary<FieldKey, FieldOperation> _FieldValues = new Dictionary<FieldKey, FieldOperation>();
|
||||
|
||||
public void SetFieldValue(FieldKey key, FieldOperation operation)
|
||||
{
|
||||
if (!_changedFields.Contains(key))
|
||||
_changedFields.Add(key);
|
||||
|
||||
_FieldValues[key] = operation;
|
||||
}
|
||||
|
||||
//TODO: Use covariance and refactor this
|
||||
public void SetPresenceFieldValue(IPresenceField field)
|
||||
{
|
||||
if (field != null)
|
||||
{
|
||||
SetFieldValue(field.GetFieldKey(), field.GetFieldOperation());
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Use covariance and refactor this
|
||||
public void SetIntPresenceFieldValue(IntPresenceField field)
|
||||
{
|
||||
if (field != null)
|
||||
{
|
||||
var key = Create(field.Program, field.OriginatingClass, field.FieldNumber, field.Index);
|
||||
this.SetFieldValue(key, field.GetFieldOperation());
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Use covariance and refactor this
|
||||
public void SetStringPresenceFieldValue(StringPresenceField field)
|
||||
{
|
||||
if (field != null)
|
||||
{
|
||||
var key = Create(field.Program, field.OriginatingClass, field.FieldNumber, field.Index);
|
||||
this.SetFieldValue(key, field.GetFieldOperation());
|
||||
}
|
||||
}
|
||||
|
||||
public List<FieldOperation> GetChangedFieldList()
|
||||
{
|
||||
return new List<FieldOperation>(_FieldValues.Values);
|
||||
}
|
||||
|
||||
public void ClearChanged()
|
||||
{
|
||||
this._changedFields.Clear();
|
||||
this._FieldValues.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
37
src/DiIiS-NA/BGS-Server/Helpers/NotificationTypeHelper.cs
Normal file
37
src/DiIiS-NA/BGS-Server/Helpers/NotificationTypeHelper.cs
Normal file
@ -0,0 +1,37 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Helpers
|
||||
{
|
||||
public static class NotificationTypeHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the NotificationType for the given notification.
|
||||
/// </summary>
|
||||
/// <param name="notification">The notification</param>
|
||||
/// <returns><see cref="NotificationType"/></returns>
|
||||
public static NotificationType GetNotificationType(this bgs.protocol.notification.v1.Notification notification)
|
||||
{
|
||||
switch (notification.Type)
|
||||
{
|
||||
case "WHISPER":
|
||||
return NotificationType.Whisper;
|
||||
}
|
||||
return NotificationType.Unknown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notification types
|
||||
/// </summary>
|
||||
public enum NotificationType
|
||||
{
|
||||
Unknown,
|
||||
Whisper
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/DiIiS-NA/BGS-Server/Objects/PersistentRPCObject.cs
Normal file
17
src/DiIiS-NA/BGS-Server/Objects/PersistentRPCObject.cs
Normal file
@ -0,0 +1,17 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Objects
|
||||
{
|
||||
public class PersistentRPCObject : RPCObject
|
||||
{
|
||||
|
||||
[DataMemberAttribute]
|
||||
public ulong PersistentID { get; private set; }
|
||||
protected PersistentRPCObject(ulong persistentId)
|
||||
{
|
||||
this.PersistentID = persistentId;
|
||||
}
|
||||
}
|
||||
}
|
||||
234
src/DiIiS-NA/BGS-Server/Objects/PresenceField.cs
Normal file
234
src/DiIiS-NA/BGS-Server/Objects/PresenceField.cs
Normal file
@ -0,0 +1,234 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Helpers;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Objects
|
||||
{
|
||||
public class EntityIdPresenceFieldList
|
||||
{
|
||||
public List<bgs.protocol.EntityId> Value = new List<bgs.protocol.EntityId>();
|
||||
|
||||
protected FieldKeyHelper.Program _program;
|
||||
protected FieldKeyHelper.OriginatingClass _originatingClass;
|
||||
protected uint _fieldNumber;
|
||||
protected uint _index;
|
||||
|
||||
public EntityIdPresenceFieldList(FieldKeyHelper.Program Program, FieldKeyHelper.OriginatingClass OriginatingClass, uint FieldNumber, uint Index)
|
||||
{
|
||||
_fieldNumber = FieldNumber;
|
||||
_index = Index;
|
||||
_program = Program;
|
||||
_originatingClass = OriginatingClass;
|
||||
}
|
||||
|
||||
public List<bgs.protocol.presence.v1.FieldOperation> GetFieldOperationList()
|
||||
{
|
||||
var operationList = new List<bgs.protocol.presence.v1.FieldOperation>();
|
||||
|
||||
foreach (var id in Value)
|
||||
{
|
||||
var Key = FieldKeyHelper.Create(FieldKeyHelper.Program.BNet, FieldKeyHelper.OriginatingClass.Account, _fieldNumber, id.High);
|
||||
var Field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(Key).SetValue(bgs.protocol.Variant.CreateBuilder().SetEntityIdValue(id).Build()).Build();
|
||||
operationList.Add(bgs.protocol.presence.v1.FieldOperation.CreateBuilder().SetField(Field).Build());
|
||||
}
|
||||
return operationList;
|
||||
}
|
||||
}
|
||||
|
||||
public class BoolPresenceField : PresenceField<bool>, IPresenceField
|
||||
{
|
||||
public BoolPresenceField(FieldKeyHelper.Program Program, FieldKeyHelper.OriginatingClass OriginatingClass, uint FieldNumber, uint Index, bool defaultValue = default(bool))
|
||||
: base(Program, OriginatingClass, FieldNumber, Index, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.Field GetField()
|
||||
{
|
||||
var fieldKey = FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetBoolValue(Value).Build()).Build();
|
||||
return field;
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.FieldOperation GetFieldOperation()
|
||||
{
|
||||
var fieldKey = FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetBoolValue(Value).Build()).Build();
|
||||
return bgs.protocol.presence.v1.FieldOperation.CreateBuilder().SetField(field).Build();
|
||||
}
|
||||
}
|
||||
|
||||
public class EntityIdPresenceField : PresenceField<bgs.protocol.EntityId>, IPresenceField
|
||||
{
|
||||
public EntityIdPresenceField(FieldKeyHelper.Program Program, FieldKeyHelper.OriginatingClass OriginatingClass, uint FieldNumber, uint Index, bgs.protocol.EntityId defaultValue = default(bgs.protocol.EntityId))
|
||||
: base(Program, OriginatingClass, FieldNumber, Index, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.Field GetField()
|
||||
{
|
||||
var fieldKey = FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetEntityIdValue(Value).Build()).Build();
|
||||
return field;
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.FieldOperation GetFieldOperation()
|
||||
{
|
||||
var fieldKey = FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetEntityIdValue(Value).Build()).Build();
|
||||
return bgs.protocol.presence.v1.FieldOperation.CreateBuilder().SetField(field).Build();
|
||||
}
|
||||
}
|
||||
|
||||
public class UintPresenceField : PresenceField<ulong>, IPresenceField
|
||||
{
|
||||
public UintPresenceField(FieldKeyHelper.Program Program, FieldKeyHelper.OriginatingClass OriginatingClass, uint FieldNumber, uint Index, ulong defaultValue = default(ulong))
|
||||
: base(Program, OriginatingClass, FieldNumber, Index, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.Field GetField()
|
||||
{
|
||||
var fieldKey = FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetUintValue(Value).Build()).Build();
|
||||
return field;
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.FieldOperation GetFieldOperation()
|
||||
{
|
||||
var fieldKey = FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetUintValue(Value).Build()).Build();
|
||||
return bgs.protocol.presence.v1.FieldOperation.CreateBuilder().SetField(field).Build();
|
||||
}
|
||||
}
|
||||
|
||||
public class IntPresenceField : PresenceField<long>, IPresenceField
|
||||
{
|
||||
public IntPresenceField(FieldKeyHelper.Program Program, FieldKeyHelper.OriginatingClass OriginatingClass, uint FieldNumber, uint Index, long defaultValue = default(long))
|
||||
: base(Program, OriginatingClass, FieldNumber, Index, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.Field GetField()
|
||||
{
|
||||
var fieldKey = FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetIntValue(Value).Build()).Build();
|
||||
return field;
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.FieldOperation GetFieldOperation()
|
||||
{
|
||||
var fieldKey = FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetIntValue(Value).Build()).Build();
|
||||
return bgs.protocol.presence.v1.FieldOperation.CreateBuilder().SetField(field).Build();
|
||||
}
|
||||
}
|
||||
|
||||
public class FourCCPresenceField : PresenceField<String>, IPresenceField
|
||||
{
|
||||
public FourCCPresenceField(FieldKeyHelper.Program Program, FieldKeyHelper.OriginatingClass OriginatingClass, uint FieldNumber, uint Index, string defaultValue = default(string))
|
||||
: base(Program, OriginatingClass, FieldNumber, Index, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.Field GetField()
|
||||
{
|
||||
var fieldKey = FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetFourccValue(Value).Build()).Build();
|
||||
return field;
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.FieldOperation GetFieldOperation()
|
||||
{
|
||||
var fieldKey = FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetFourccValue(Value).Build()).Build();
|
||||
return bgs.protocol.presence.v1.FieldOperation.CreateBuilder().SetField(field).Build();
|
||||
}
|
||||
}
|
||||
|
||||
public class StringPresenceField : PresenceField<String>, IPresenceField
|
||||
{
|
||||
public StringPresenceField(FieldKeyHelper.Program Program, FieldKeyHelper.OriginatingClass OriginatingClass, uint FieldNumber, uint Index, string defaultValue = default(string))
|
||||
: base(Program, OriginatingClass, FieldNumber, Index, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.Field GetField()
|
||||
{
|
||||
var fieldKey = FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetStringValue(Value).Build()).Build();
|
||||
return field;
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.FieldOperation GetFieldOperation()
|
||||
{
|
||||
var fieldKey = FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetStringValue(Value).Build()).Build();
|
||||
return bgs.protocol.presence.v1.FieldOperation.CreateBuilder().SetField(field).Build();
|
||||
}
|
||||
}
|
||||
|
||||
public class ByteStringPresenceField<T> : PresenceField<T>, IPresenceField where T : IMessageLite<T> //Used IMessageLite to get ToByteString(), might need refactoring later
|
||||
{
|
||||
public ByteStringPresenceField(FieldKeyHelper.Program Program, FieldKeyHelper.OriginatingClass OriginatingClass, uint FieldNumber, uint Index, T defaultValue = default(T))
|
||||
: base(Program, OriginatingClass, FieldNumber, Index, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.Field GetField()
|
||||
{
|
||||
var fieldKey = FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder().SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(Value.ToByteString()).Build()).Build();
|
||||
return field;
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.FieldOperation GetFieldOperation()
|
||||
{
|
||||
var fieldKey = FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
var field = bgs.protocol.presence.v1.Field.CreateBuilder();
|
||||
if (Value == null)
|
||||
field.SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(ByteString.Empty).Build()).Build();
|
||||
else
|
||||
field.SetKey(fieldKey).SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(Value.ToByteString()).Build()).Build();
|
||||
return bgs.protocol.presence.v1.FieldOperation.CreateBuilder().SetField(field).Build();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class PresenceField<T>
|
||||
{
|
||||
public T Value;
|
||||
|
||||
public FieldKeyHelper.Program Program { get; private set; }
|
||||
public FieldKeyHelper.OriginatingClass OriginatingClass { get; private set; }
|
||||
public uint FieldNumber { get; private set; }
|
||||
public uint Index { get; private set; }
|
||||
|
||||
public PresenceField(FieldKeyHelper.Program program, FieldKeyHelper.OriginatingClass originatingClass, uint fieldNumber, uint index, T defaultValue)
|
||||
{
|
||||
Value = defaultValue;
|
||||
FieldNumber = fieldNumber;
|
||||
Index = index;
|
||||
Program = program;
|
||||
OriginatingClass = originatingClass;
|
||||
}
|
||||
|
||||
public bgs.protocol.presence.v1.FieldKey GetFieldKey()
|
||||
{
|
||||
return FieldKeyHelper.Create(Program, OriginatingClass, FieldNumber, Index);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IPresenceField
|
||||
{
|
||||
bgs.protocol.presence.v1.Field GetField();
|
||||
bgs.protocol.presence.v1.FieldOperation GetFieldOperation();
|
||||
bgs.protocol.presence.v1.FieldKey GetFieldKey();
|
||||
}
|
||||
}
|
||||
154
src/DiIiS-NA/BGS-Server/Objects/RPCObject.cs
Normal file
154
src/DiIiS-NA/BGS-Server/Objects/RPCObject.cs
Normal file
@ -0,0 +1,154 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Battle;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Concurrent;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Objects
|
||||
{
|
||||
public class RPCObject
|
||||
{
|
||||
protected static readonly Logger Logger = LogManager.CreateLogger("RPCTransfer");
|
||||
|
||||
public ulong DynamicId { get; set; }
|
||||
public bgs.protocol.EntityId BnetEntityId;
|
||||
public ConcurrentDictionary<BattleClient, byte> Subscribers { get; private set; }
|
||||
protected RPCObject()
|
||||
{
|
||||
RPCObjectManager.Init(this);
|
||||
this.Subscribers = new ConcurrentDictionary<BattleClient, byte>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a client subscriber to object, which will eventually be notified whenever the object changes state.
|
||||
/// </summary>
|
||||
/// <param name="client">The client to add as a subscriber.</param>
|
||||
/// <param name="remoteObjectId">The client's dynamic ID.</param>
|
||||
public void AddSubscriber(BattleClient client, ulong remoteObjectId)
|
||||
{
|
||||
if (this.Subscribers.ContainsKey(client)) return;
|
||||
|
||||
this.Subscribers.TryAdd(client, 0);
|
||||
client.MapLocalObjectID(this.DynamicId, remoteObjectId);
|
||||
|
||||
if (client.SocketConnection.Active)
|
||||
{
|
||||
var operations = GetSubscriptionNotifications();
|
||||
if (operations.Count > 0)
|
||||
MakeRPC(client, operations);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveSubscriber(BattleClient client)
|
||||
{
|
||||
if (!this.Subscribers.ContainsKey(client)) return;
|
||||
|
||||
client.UnmapLocalObjectId(this.DynamicId);
|
||||
this.Subscribers.TryRemove(client, out _);
|
||||
}
|
||||
|
||||
public virtual List<bgs.protocol.presence.v1.FieldOperation> GetSubscriptionNotifications()
|
||||
{
|
||||
return new List<bgs.protocol.presence.v1.FieldOperation>();
|
||||
}
|
||||
|
||||
public Helpers.FieldKeyHelper ChangedFields = new Helpers.FieldKeyHelper();
|
||||
|
||||
protected void NotifySubscriptionAdded(BattleClient client)
|
||||
{
|
||||
var operations = GetSubscriptionNotifications();
|
||||
if (operations.Count > 0)
|
||||
MakeRPC(client, operations);
|
||||
}
|
||||
|
||||
public virtual void NotifyUpdate() { }
|
||||
|
||||
public void CheckSubscribers()
|
||||
{
|
||||
foreach (var subscriber in this.Subscribers)
|
||||
{
|
||||
if (!subscriber.Key.SocketConnection.Active)
|
||||
{
|
||||
Logger.Trace("Removing disconnected subscriber {0}", subscriber.Key);
|
||||
this.Subscribers.TryRemove(subscriber.Key, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSubscribers(ConcurrentDictionary<BattleClient, byte> subscribers, List<bgs.protocol.presence.v1.FieldOperation> operations)
|
||||
{
|
||||
this.CheckSubscribers();
|
||||
|
||||
foreach (var subscriber in subscribers.Where(c => c.Key.Account.GameAccount != null))
|
||||
{
|
||||
var gameAccount = subscriber.Key.Account.GameAccount;
|
||||
|
||||
var state = bgs.protocol.presence.v1.ChannelState.CreateBuilder().SetEntityId(this.BnetEntityId);
|
||||
|
||||
state = state.AddRangeFieldOperation(operations);
|
||||
|
||||
var channelState = bgs.protocol.channel.v1.ChannelState.CreateBuilder().SetExtension(bgs.protocol.presence.v1.ChannelState.Presence, state.Build());
|
||||
var notification = bgs.protocol.channel.v1.UpdateChannelStateNotification.CreateBuilder().SetStateChange(channelState);
|
||||
var altnotification = bgs.protocol.channel.v1.JoinNotification.CreateBuilder().SetChannelState(channelState);
|
||||
if (gameAccount.LoggedInClient != null)
|
||||
{
|
||||
gameAccount.LoggedInClient.MakeTargetedRPC(this, (lid) => bgs.protocol.channel.v1.ChannelListener.CreateStub(gameAccount.LoggedInClient).OnUpdateChannelState(new HandlerController() { ListenerId = lid }, notification.Build(), callback => { }));
|
||||
gameAccount.LoggedInClient.MakeTargetedRPC(this, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(gameAccount.LoggedInClient).OnJoin(new HandlerController() { ListenerId = lid }, altnotification.Build(), callback => { }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void MakeRPC(BattleClient client, List<bgs.protocol.presence.v1.FieldOperation> operations)
|
||||
{
|
||||
if (!client.SocketConnection.Active) return;
|
||||
var state = bgs.protocol.presence.v1.ChannelState.CreateBuilder().SetEntityId(this.BnetEntityId).AddRangeFieldOperation(operations).Build();
|
||||
var channelState = bgs.protocol.channel.v1.ChannelState.CreateBuilder().SetExtension(bgs.protocol.presence.v1.ChannelState.Presence, state);
|
||||
var builder = bgs.protocol.channel.v1.JoinNotification.CreateBuilder().SetChannelState(channelState);
|
||||
var notification = bgs.protocol.channel.v1.UpdateChannelStateNotification.CreateBuilder().SetStateChange(channelState);
|
||||
client.MakeTargetedRPC(this, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(client).OnJoin(new HandlerController() { ListenerId = lid }, builder.Build(), callback => { }));
|
||||
|
||||
client.MakeTargetedRPC(this, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(client).OnUpdateChannelState(new HandlerController() { ListenerId = lid }, notification.Build(), callback => { }));
|
||||
|
||||
}
|
||||
|
||||
#region de-ctor
|
||||
|
||||
private bool _disposed = false;
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (this._disposed) return;
|
||||
if (disposing) { }
|
||||
|
||||
RPCObjectManager.Release(this);
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
~RPCObject()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
43
src/DiIiS-NA/BGS-Server/Objects/RPCObjectManager.cs
Normal file
43
src/DiIiS-NA/BGS-Server/Objects/RPCObjectManager.cs
Normal file
@ -0,0 +1,43 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Objects
|
||||
{
|
||||
public static class RPCObjectManager
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
|
||||
private static ulong _nextId = 10000;
|
||||
public static readonly Dictionary<ulong, RPCObject> Objects = new Dictionary<ulong, RPCObject>();
|
||||
|
||||
static RPCObjectManager()
|
||||
{ }
|
||||
|
||||
public static void Init(RPCObject obj)
|
||||
{
|
||||
if (Objects.ContainsKey(obj.DynamicId))
|
||||
throw new Exception("Given object was already initialized");
|
||||
ulong id = Next();
|
||||
obj.DynamicId = id;
|
||||
|
||||
Objects.Add(id, obj);
|
||||
}
|
||||
|
||||
public static void Release(RPCObject obj)
|
||||
{
|
||||
Logger.Trace("Releasing object {0}", obj.DynamicId);
|
||||
Objects.Remove(obj.DynamicId);
|
||||
}
|
||||
|
||||
public static ulong Next()
|
||||
{
|
||||
while (Objects.ContainsKey(++_nextId)) ;
|
||||
return _nextId;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/DiIiS-NA/BGS-Server/ServicesSystem/IServerService.cs
Normal file
9
src/DiIiS-NA/BGS-Server/ServicesSystem/IServerService.cs
Normal file
@ -0,0 +1,9 @@
|
||||
//Blizzless Project 2022
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem
|
||||
{
|
||||
public interface IServerService
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
69
src/DiIiS-NA/BGS-Server/ServicesSystem/Service.cs
Normal file
69
src/DiIiS-NA/BGS-Server/ServicesSystem/Service.cs
Normal file
@ -0,0 +1,69 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Helpers.Hash;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Reflection;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ServiceAttribute : Attribute
|
||||
{
|
||||
public uint ServiceID { get; private set; }
|
||||
public uint Hash { get; private set; }
|
||||
|
||||
public ServiceAttribute(uint serviceID, uint serviceHash)
|
||||
{
|
||||
this.ServiceID = serviceID;
|
||||
this.Hash = serviceHash;
|
||||
}
|
||||
|
||||
public ServiceAttribute(uint serviceID, string serviceName)
|
||||
: this(serviceID, (uint)StringHashHelper.HashIdentity(serviceName))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static class Service
|
||||
{
|
||||
private static uint _notImplementedServiceCounter = 99;
|
||||
public readonly static Dictionary<Type, ServiceAttribute> ProvidedServices = new Dictionary<Type, ServiceAttribute>();
|
||||
public readonly static Dictionary<Type, IService> Services = new Dictionary<Type, IService>();
|
||||
|
||||
static Service()
|
||||
{
|
||||
foreach (var type in Assembly.GetExecutingAssembly().GetTypes().Where(type => type.GetInterface("IServerService") != null))
|
||||
{
|
||||
object[] attributes = type.GetCustomAttributes(typeof(ServiceAttribute), true);
|
||||
if (attributes.Length == 0) return;
|
||||
|
||||
ProvidedServices.Add(type, (ServiceAttribute)attributes[0]);
|
||||
Services.Add(type, (IService)Activator.CreateInstance(type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static IService GetByID(uint serviceID)
|
||||
{
|
||||
return (from pair in ProvidedServices let serviceInfo = pair.Value where serviceInfo.ServiceID == serviceID select Services[pair.Key]).FirstOrDefault();
|
||||
}
|
||||
|
||||
public static uint GetByHash(uint serviceHash)
|
||||
{
|
||||
foreach (var serviceInfo in ProvidedServices.Select(pair => pair.Value).Where(serviceInfo => serviceInfo.Hash == serviceHash))
|
||||
{
|
||||
return serviceInfo.ServiceID;
|
||||
}
|
||||
|
||||
return _notImplementedServiceCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,133 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.account.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x2, serviceHash: 0x62DA0891)]
|
||||
|
||||
public class AccountService : bgs.protocol.account.v1.AccountService, IServerService
|
||||
{
|
||||
|
||||
|
||||
public override void GetAccountState(IRpcController controller, GetAccountStateRequest request, Action<GetAccountStateResponse> done)
|
||||
{
|
||||
|
||||
GetAccountStateResponse.Builder builder = GetAccountStateResponse.CreateBuilder();
|
||||
var AccState = AccountState.CreateBuilder();
|
||||
|
||||
if (request.EntityId.Low == (controller as HandlerController).Client.Account.BnetEntityId.Low)
|
||||
{
|
||||
if (request.Options.FieldPrivacyInfo)
|
||||
{
|
||||
var prv = PrivacyInfo.CreateBuilder();
|
||||
prv.SetIsUsingRid(true);
|
||||
prv.SetIsVisibleForViewFriends(true);
|
||||
prv.SetIsHiddenFromFriendFinder(false);
|
||||
AccState.SetPrivacyInfo(prv);
|
||||
prv.SetGameInfoPrivacy(PrivacyInfo.Types.GameInfoPrivacy.PRIVACY_EVERYONE);
|
||||
}
|
||||
if (request.Options.FieldAccountLevelInfo)
|
||||
{
|
||||
AccountLevelInfo.Builder level = AccountLevelInfo.CreateBuilder();
|
||||
|
||||
level.AddLicenses(new AccountLicense.Builder().SetId(167));
|
||||
level.AddLicenses(new AccountLicense.Builder().SetId(168));
|
||||
level.AddLicenses(new AccountLicense.Builder().SetId(0));
|
||||
level.AddLicenses(new AccountLicense.Builder().SetId(1));
|
||||
level.AddLicenses(new AccountLicense.Builder().SetId(2));
|
||||
level.AddLicenses(new AccountLicense.Builder().SetId(4));
|
||||
level.AddLicenses(new AccountLicense.Builder().SetId(10));
|
||||
level.AddLicenses(new AccountLicense.Builder().SetId(15));
|
||||
level.AddLicenses(new AccountLicense.Builder().SetId(20));
|
||||
|
||||
level.SetDefaultCurrency(5395778);
|
||||
level.SetCountry("RUS");
|
||||
level.SetPreferredRegion(1);
|
||||
level.SetFullName("Name LastName");
|
||||
level.SetBattleTag((controller as HandlerController).Client.Account.BattleTag);
|
||||
level.SetAccountPaidAny(true);
|
||||
level.SetEmail((controller as HandlerController).Client.Account.Email).SetHeadlessAccount(false);
|
||||
|
||||
AccState.SetAccountLevelInfo(level);
|
||||
|
||||
builder.SetTags(AccountFieldTags.CreateBuilder().SetAccountLevelInfoTag(3827081107));
|
||||
}
|
||||
}
|
||||
|
||||
builder.SetState(AccState);
|
||||
done(builder.Build());
|
||||
}
|
||||
|
||||
public override void GetAuthorizedData(IRpcController controller, GetAuthorizedDataRequest request, Action<GetAuthorizedDataResponse> done)
|
||||
{
|
||||
var Data = AuthorizedData.CreateBuilder();//.SetData("a");
|
||||
//Data.AddLicense(17459); // Diablo 3
|
||||
//Data.AddLicense(21298); // Starcraft 2 Wings of Liberty
|
||||
//Data.AddLicense(5730135); // World of Warcraft without last update
|
||||
var builder = GetAuthorizedDataResponse.CreateBuilder().AddData(Data);
|
||||
done(builder.Build());
|
||||
}
|
||||
|
||||
public override void GetCAISInfo(IRpcController controller, GetCAISInfoRequest request, Action<GetCAISInfoResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetGameAccountState(IRpcController controller, GetGameAccountStateRequest request, Action<GetGameAccountStateResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetGameSessionInfo(IRpcController controller, GetGameSessionInfoRequest request, Action<GetGameSessionInfoResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetGameTimeRemainingInfo(IRpcController controller, GetGameTimeRemainingInfoRequest request, Action<GetGameTimeRemainingInfoResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetLicenses(IRpcController controller, GetLicensesRequest request, Action<GetLicensesResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetSignedAccountState(IRpcController controller, GetSignedAccountStateRequest request, Action<GetSignedAccountStateResponse> done)
|
||||
{
|
||||
done(GetSignedAccountStateResponse.CreateBuilder().SetToken("eyJ0eXAiOiJKV1QiLCJlbnYiOiJwcm9kLmV1IiwiYWxnIjoiUlMyNTYiLCJraWQiOiJmMDE5NzgzMi0zMWMwLTQzN2MtOTc2NC1iMzliOTM5MDJlNWMiLCJrdHkiOiJSU0EifQ").Build());
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void ResolveAccount(IRpcController controller, ResolveAccountRequest request, Action<ResolveAccountResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Subscribe(IRpcController controller, SubscriptionUpdateRequest request, Action<SubscriptionUpdateResponse> done)
|
||||
{
|
||||
;
|
||||
var builder = SubscriptionUpdateResponse.CreateBuilder()
|
||||
.AddRef(request.GetRef(0));//.AddRef(request.GetRef(41));
|
||||
done(builder.Build());
|
||||
}
|
||||
|
||||
public override void Unsubscribe(IRpcController controller, SubscriptionUpdateRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,235 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.authentication.v1;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.challenge.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Extensions;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Battle;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x1, serviceHash: 233634817)]
|
||||
public class AuthenticationService : bgs.protocol.authentication.v1.AuthenticationService, IServerService
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
|
||||
public static int CounterLogged = 0;
|
||||
public static bool switcher = false;
|
||||
|
||||
|
||||
public override void GenerateSSOToken(IRpcController controller, GenerateSSOTokenRequest request, Action<GenerateSSOTokenResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GenerateWebCredentials(IRpcController controller, GenerateWebCredentialsRequest request, Action<GenerateWebCredentialsResponse> done)
|
||||
{
|
||||
uint Product = request.Program;
|
||||
string a = ByteString.CopyFromUtf8("CN-97e2280792852a25e356fc9897f8bcd5-525139407").ToStringUtf8();
|
||||
|
||||
var Response = GenerateWebCredentialsResponse.CreateBuilder().SetWebCredentials(ByteString.CopyFromUtf8(a));
|
||||
done(Response.Build());
|
||||
}
|
||||
|
||||
public override void Logon(IRpcController controller, LogonRequest request, Action<NoData> done)
|
||||
{
|
||||
//Error 28 - Появилось обновление Diablo III, клиент закрывается.
|
||||
//Error 33 - Профилактические работы
|
||||
//Error 35 - Служба Battle.net - Отключена
|
||||
//Error 36 - Не удалось загрузить модуль аутентификации
|
||||
//Error 37 - Служба аутентификации получает слишком много обращений.
|
||||
//Error 38 - Для игры требуется получить BattleTag
|
||||
//Error 42 - Вы подключаетесь к неверному серверу (Неверная последовательность действий)
|
||||
//Error 43 - Вы заблокировали свою учетную запись с мобильного телефона.
|
||||
//Error 44 - Невозможно выполнить это действие. Учетная запись лишена функции голосового общения.
|
||||
//Error 50 - Предоплаченное для учетной записи время игры истекло.
|
||||
//Error 51 - Подписка для данной учетной записи истекла.
|
||||
//Error 52 - Данная учетная запись была заблокирована в связи с многочисленными нарушениями условий использования службы Battle.net
|
||||
//Error 53 - Действие данной учетной записи было приостановлено в связи с нарушениями условий использования службы Batle.net.
|
||||
|
||||
int VersionRetail = 82785; //74291 - 2.7.0, 76761 - 2.7.1, 79575 - 2.7.2;
|
||||
int VersionPTR = 79151;
|
||||
string version = "";
|
||||
int a = request.ApplicationVersion;
|
||||
Logger.Info("----------------------------------------------------------------");
|
||||
string game = "-----";
|
||||
switch (request.Program.ToLower())
|
||||
{
|
||||
case "d3": game = "Diablo 3"; break;
|
||||
case "osi": game = "Diablo 2 Ressurected"; break;
|
||||
case "odin": game = "Call of Duty: Warzone"; break;
|
||||
case "pro": game = "Overwatch"; break;
|
||||
case "proc": game = "Overwatch Tournament"; break;
|
||||
case "app": game = "Battle.net Launcher"; break;
|
||||
case "s2": game = "Starcraft 2"; break;
|
||||
case "hero": game = "Heroes of the Storm"; break;
|
||||
case "fen": game = "Diablo IV"; break;
|
||||
}
|
||||
if (request.ApplicationVersion == 0)
|
||||
{
|
||||
var Parts = request.Version.Split('"');
|
||||
if (Parts.Length > 1)
|
||||
version = Parts[1];
|
||||
}
|
||||
else
|
||||
version = request.ApplicationVersion.ToString();
|
||||
Logger.Info("Game: {0} | Version: {1} | Platform: {2} | Locale: {3}", game, version, request.Platform, request.Locale);
|
||||
if (request.Program.ToLower() == "d3")
|
||||
if (request.ApplicationVersion != VersionRetail & request.ApplicationVersion != VersionPTR)
|
||||
{
|
||||
Logger.Error("Подключение не правильной версии клиента!");
|
||||
var ercomplete = LogonResult.CreateBuilder().SetErrorCode(28);
|
||||
(controller as HandlerController).Client.MakeRPC((lid) => AuthenticationListener.CreateStub((controller as HandlerController).Client).OnLogonComplete(controller, ercomplete.Build(), callback => { }));
|
||||
}
|
||||
switch (request.Locale)
|
||||
{
|
||||
case "deDE": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.deDE; break;
|
||||
case "enGB": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.enGB; break;
|
||||
case "enSG": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.enSG; break;
|
||||
case "enUS": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.enUS; break;
|
||||
case "esES": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.esES; break;
|
||||
case "esMX": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.esMX; break;
|
||||
case "frFR": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.frFR; break;
|
||||
case "itIT": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.itIT; break;
|
||||
case "koKR": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.koKR; break;
|
||||
case "plPL": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.plPL; break;
|
||||
case "ptBR": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.ptBR; break;
|
||||
case "ptPT": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.ptPT; break;
|
||||
case "ruRU": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.ruRU; break;
|
||||
case "trTR": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.trTR; break;
|
||||
case "zhCN": (controller as HandlerController).Client.ClientLanguage = Battle.BattleClient.ClientLocale.zhCN; break;
|
||||
}
|
||||
done(NoData.CreateBuilder().Build());
|
||||
Logger.Info("----------------------------------------------------------------");
|
||||
var builder = ChallengeExternalRequest.CreateBuilder();
|
||||
var complete = LogonResult.CreateBuilder();
|
||||
switch (request.Program.ToLower())
|
||||
{
|
||||
case "d3":
|
||||
//if (!request.HasCachedWebCredentials)
|
||||
{
|
||||
#region Процедура аутентификации через WEB
|
||||
if (request.HasCachedWebCredentials)
|
||||
VerifyWebCredentials(controller, VerifyWebCredentialsRequest.CreateBuilder().SetWebCredentials(request.CachedWebCredentials).Build(), callback => { });
|
||||
builder.SetPayloadType("web_auth_url");
|
||||
if (REST.Config.Instance.Public)
|
||||
builder.SetPayload(ByteString.CopyFromUtf8(String.Format("http://{0}:{1}/battlenet/login", REST.Config.Instance.PublicIP, REST.Config.Instance.PORT)));
|
||||
else
|
||||
builder.SetPayload(ByteString.CopyFromUtf8(String.Format("http://{0}:{1}/battlenet/login", Program.RESTSERVERIP, REST.Config.Instance.PORT)));
|
||||
|
||||
(controller as HandlerController).Client.MakeRPC((lid) => ChallengeListener.CreateStub((controller as HandlerController).Client).OnExternalChallenge(controller, builder.Build(), callback => { }));
|
||||
#endregion
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Logger.Error("Authorization not implemeted for Game: {0}", game);
|
||||
Logger.Info("----------------------------------------------------------------");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void ModuleMessage(IRpcController controller, ModuleMessageRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void ModuleNotify(IRpcController controller, ModuleNotification request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SelectGameAccount(IRpcController controller, SelectGameAccountRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SelectGameAccountDEPRECATED(IRpcController controller, EntityId request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void VerifyWebCredentials(IRpcController controller, VerifyWebCredentialsRequest request, Action<NoData> done)
|
||||
{
|
||||
done(NoData.CreateBuilder().Build());
|
||||
#region Завершение аутентификации
|
||||
if (request.WebCredentials.ToStringUtf8().ToLower().Contains("eu-"))
|
||||
{
|
||||
(controller as HandlerController).Client.Account = AccountManager.GetAccountByPersistentID(1);
|
||||
|
||||
var comple = LogonResult.CreateBuilder()
|
||||
.SetAccountId((controller as HandlerController).Client.Account.BnetEntityId)
|
||||
.SetEmail("TEST@MAIL.DU")
|
||||
.SetBattleTag("Test#0000")
|
||||
.SetSessionKey(ByteString.CopyFrom("7CB18EDA470F96A4DD70C70B9307CBBA2A4131043075648D8B2F55EE0E383132025D3CC3BA43406DC0740D776B1E5C366BD1123D16E6D6759075B475C28C4022".ToByteArray()))
|
||||
.AddAvailableRegion(1)
|
||||
.AddAvailableRegion(2)
|
||||
.AddAvailableRegion(3)
|
||||
.SetConnectedRegion(1)
|
||||
.SetGeoipCountry("RU")
|
||||
.SetRestrictedMode(false)
|
||||
.SetErrorCode(0);
|
||||
comple.AddGameAccountId((controller as HandlerController).Client.Account.GameAccount.BnetEntityId);
|
||||
(controller as HandlerController).Client.Account.GameAccount.LoggedInClient = (controller as HandlerController).Client;
|
||||
(controller as HandlerController).Client.MakeRPC((lid) => AuthenticationListener.CreateStub((controller as HandlerController).Client).OnLogonComplete(controller, comple.Build(), callback => { }));
|
||||
(controller as HandlerController).Client.Account.GameAccount.ProgramField.Value = "FEN";
|
||||
PlayerManager.PlayerConnected((controller as HandlerController).Client);
|
||||
var ga1selected = GameAccountSelectedRequest.CreateBuilder().SetResult(0).SetGameAccountId((controller as HandlerController).Client.Account.GameAccount.BnetEntityId);
|
||||
(controller as HandlerController).Client.MakeRPC((lid) =>
|
||||
AuthenticationListener.CreateStub((controller as HandlerController).Client).OnGameAccountSelected(new HandlerController() { ListenerId = lid }, ga1selected.Build(), callback => { }));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
(controller as HandlerController).Client.Account = AccountManager.GetAccountBySaltTicket(request.WebCredentials.ToStringUtf8());
|
||||
|
||||
if ((controller as HandlerController).Client.Account == null)
|
||||
{
|
||||
var complete = LogonResult.CreateBuilder().SetErrorCode(2);
|
||||
(controller as HandlerController).Client.MakeRPC((lid) => AuthenticationListener.CreateStub((controller as HandlerController).Client).OnLogonComplete(controller, complete.Build(), callback => { }));
|
||||
(controller as HandlerController).Client.SocketConnection.CloseAsync();
|
||||
(controller as HandlerController).Client.Connect.CloseAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("Client connected - {0}#{1}", (controller as HandlerController).Client.Account.DBAccount.BattleTagName, (controller as HandlerController).Client.Account.HashCode);
|
||||
Logger.Info("----------------------------------------------------------------");
|
||||
var complete = LogonResult.CreateBuilder()
|
||||
.SetAccountId((controller as HandlerController).Client.Account.BnetEntityId)
|
||||
.SetEmail((controller as HandlerController).Client.Account.Email)
|
||||
.SetBattleTag((controller as HandlerController).Client.Account.BattleTag)
|
||||
.AddAvailableRegion(1)
|
||||
.SetConnectedRegion(1)
|
||||
.SetGeoipCountry("RU")
|
||||
.SetRestrictedMode(false)
|
||||
.SetErrorCode(0);
|
||||
complete.AddGameAccountId((controller as HandlerController).Client.Account.GameAccount.BnetEntityId); //D3
|
||||
(controller as HandlerController).Client.Account.GameAccount.LoggedInClient = (controller as HandlerController).Client;
|
||||
(controller as HandlerController).Client.MakeRPC((lid) => AuthenticationListener.CreateStub((controller as HandlerController).Client).OnLogonComplete(controller, complete.Build(), callback => { }));
|
||||
|
||||
PlayerManager.PlayerConnected((controller as HandlerController).Client);
|
||||
|
||||
var gaselected = GameAccountSelectedRequest.CreateBuilder().SetResult(0).SetGameAccountId((controller as HandlerController).Client.Account.GameAccount.BnetEntityId);
|
||||
(controller as HandlerController).Client.MakeRPC((lid) =>
|
||||
AuthenticationListener.CreateStub((controller as HandlerController).Client).OnGameAccountSelected(new HandlerController() { ListenerId = lid }, gaselected.Build(), callback => { }));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.challenge.v1;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x78, serviceHash: 0xBBDA171F)]
|
||||
public class ChallengeService : bgs.protocol.challenge.v1.ChallengeService, IServerService
|
||||
{
|
||||
public override void ChallengeAnswered(IRpcController controller, ChallengeAnsweredRequest request, Action<ChallengeAnsweredResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void ChallengeCancelled(IRpcController controller, ChallengeCancelledRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void ChallengePicked(IRpcController controller, ChallengePickedRequest request, Action<ChallengePickedResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SendChallengeToUser(IRpcController controller, SendChallengeToUserRequest request, Action<SendChallengeToUserResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,160 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.channel.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Extensions;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.ChannelSystem;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x33, serviceName: "bnet.protocol.channel_invitation.ChannelInvitationService")]
|
||||
public class ChannelInvitationService : bgs.protocol.channel.v1.ChannelInvitationService, IServerService
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
|
||||
public readonly ChannelInvitationManager _invitationManager = new ChannelInvitationManager();
|
||||
|
||||
public override void Subscribe(Google.ProtocolBuffers.IRpcController controller, bgs.protocol.channel.v1.SubscribeRequest request, Action<NoData> done)
|
||||
{
|
||||
Logger.Trace("Subscribe() {0}", ((controller as HandlerController).Client));
|
||||
|
||||
this._invitationManager.AddSubscriber(((controller as HandlerController).Client), request.ObjectId);
|
||||
|
||||
done(NoData.DefaultInstance);
|
||||
}
|
||||
|
||||
public override void AcceptInvitation(Google.ProtocolBuffers.IRpcController controller, AcceptInvitationRequest request, Action<AcceptInvitationResponse> done)
|
||||
{
|
||||
var channel = ChannelManager.GetChannelByEntityId(_invitationManager.GetInvitationById(request.InvitationId).GetExtension(ChannelInvitation.ChannelInvitationProp).ChannelDescription.ChannelId);
|
||||
var response = AcceptInvitationResponse.CreateBuilder().SetObjectId(channel.DynamicId).Build();
|
||||
done(response);
|
||||
|
||||
this._invitationManager.HandleAccept(((controller as HandlerController).Client), request);
|
||||
}
|
||||
|
||||
public override void DeclineInvitation(Google.ProtocolBuffers.IRpcController controller, DeclineInvitationRequest request, Action<NoData> done)
|
||||
{
|
||||
var respone = NoData.CreateBuilder();
|
||||
done(respone.Build());
|
||||
|
||||
this._invitationManager.HandleDecline(((controller as HandlerController).Client), request);
|
||||
}
|
||||
|
||||
public override void RevokeInvitation(Google.ProtocolBuffers.IRpcController controller, RevokeInvitationRequest request, Action<NoData> done)
|
||||
{
|
||||
var builder = NoData.CreateBuilder();
|
||||
done(builder.Build());
|
||||
|
||||
this._invitationManager.Revoke(((controller as HandlerController).Client), request);
|
||||
}
|
||||
|
||||
public override void SendInvitation(Google.ProtocolBuffers.IRpcController controller, SendInvitationRequest request, Action<NoData> done)
|
||||
{
|
||||
var invitee = GameAccountManager.GetAccountByPersistentID(request.TargetId.Low);
|
||||
|
||||
if (invitee.Owner.IgnoreIds.Contains((controller as HandlerController).Client.Account.PersistentID))
|
||||
{
|
||||
((controller as HandlerController).Status) = 403;
|
||||
done(NoData.CreateBuilder().Build());
|
||||
return;
|
||||
}
|
||||
|
||||
var extensionBytes = request.Params.UnknownFields.FieldDictionary[105].LengthDelimitedList[0].ToByteArray();
|
||||
var channelInvitationInfo = ChannelInvitationParams.ParseFrom(extensionBytes);
|
||||
|
||||
var channel = ChannelManager.GetChannelByEntityId(channelInvitationInfo.ChannelId);
|
||||
|
||||
var channelDescription = ChannelDescription.CreateBuilder()
|
||||
.SetChannelId(channelInvitationInfo.ChannelId)
|
||||
.SetCurrentMembers((uint)channel.Members.Count)
|
||||
.SetState(channel.State)
|
||||
;
|
||||
|
||||
var channelInvitation = ChannelInvitation.CreateBuilder()
|
||||
.SetChannelDescription(channelDescription)
|
||||
.SetReserved(false)
|
||||
.SetServiceType(1)
|
||||
.SetRejoin(false)
|
||||
.Build();
|
||||
|
||||
var invitation = Invitation.CreateBuilder();
|
||||
invitation.SetId(ChannelInvitationManager.InvitationIdCounter++)
|
||||
.SetInviterIdentity(Identity.CreateBuilder().SetGameAccountId((controller as HandlerController).Client.Account.GameAccount.BnetEntityId).Build())
|
||||
.SetInviterName((controller as HandlerController).Client.Account.GameAccount.Owner.BattleTag)
|
||||
.SetInviteeIdentity(Identity.CreateBuilder().SetGameAccountId(request.TargetId).Build())
|
||||
.SetInviteeName(invitee.Owner.BattleTag)
|
||||
.SetInvitationMessage(request.Params.InvitationMessage)
|
||||
.SetCreationTime(DateTime.Now.ToExtendedEpoch())
|
||||
.SetExpirationTime(DateTime.Now.ToUnixTime() + request.Params.ExpirationTime)
|
||||
.SetExtension(ChannelInvitation.ChannelInvitationProp, channelInvitation)
|
||||
;
|
||||
|
||||
var respone = NoData.CreateBuilder();
|
||||
done(respone.Build());
|
||||
|
||||
var notification = UpdateChannelStateNotification.CreateBuilder()
|
||||
.SetAgentId(EntityId.CreateBuilder().SetHigh(0).SetLow(1))
|
||||
.SetStateChange(ChannelState.CreateBuilder().AddInvitation(invitation.Clone()));
|
||||
|
||||
var builder = JoinNotification.CreateBuilder().SetChannelState(ChannelState.CreateBuilder().AddInvitation(invitation.Clone()));
|
||||
|
||||
(controller as HandlerController).Client.MakeTargetedRPC(channel, (lid) => ChannelListener.CreateStub((controller as HandlerController).Client)
|
||||
.OnUpdateChannelState(controller, notification.Build(), callback => { }));
|
||||
(controller as HandlerController).Client.MakeTargetedRPC(channel, (lid) =>
|
||||
ChannelListener.CreateStub((controller as HandlerController).Client).OnJoin(new HandlerController() { ListenerId = lid }, builder.Build(), callback => { }));
|
||||
|
||||
this._invitationManager.HandleInvitation((controller as HandlerController).Client, invitation.Build());
|
||||
|
||||
|
||||
}
|
||||
public override void SuggestInvitation(Google.ProtocolBuffers.IRpcController controller, bgs.protocol.channel.v1.SuggestInvitationRequest request, Action<NoData> done)
|
||||
{
|
||||
var suggester = GameAccountManager.GetAccountByPersistentID(request.TargetId.Low);
|
||||
var suggestee = GameAccountManager.GetAccountByPersistentID(request.ApprovalId.Low);
|
||||
if (suggestee == null) return;
|
||||
|
||||
if (suggestee.Owner.IgnoreIds.Contains(suggester.Owner.PersistentID))
|
||||
{
|
||||
((controller as HandlerController).Status) = 403;
|
||||
done(NoData.CreateBuilder().Build());
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Debug("{0} suggested {1} to invite him.", suggester, suggestee);
|
||||
var respone = NoData.CreateBuilder();
|
||||
done(respone.Build());
|
||||
|
||||
var suggestion = InvitationSuggestion.CreateBuilder()
|
||||
.SetChannelId(request.ChannelId)
|
||||
.SetSuggesterId(suggester.BnetEntityId)
|
||||
.SetSuggesterName(suggester.Owner.BattleTag)
|
||||
.SetSuggesteeId(suggester.BnetEntityId)
|
||||
.SetSuggesteeName(suggester.Owner.BattleTag)
|
||||
.Build();
|
||||
|
||||
var notification = SuggestionAddedNotification.CreateBuilder().SetSuggestion(suggestion);
|
||||
|
||||
suggestee.LoggedInClient.MakeTargetedRPC(this._invitationManager, (lid) =>
|
||||
ChannelInvitationListener.CreateStub(suggestee.LoggedInClient).OnReceivedSuggestionAdded(new HandlerController() { ListenerId = lid }, notification.Build(), callback => { }));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void ListChannelCount(Google.ProtocolBuffers.IRpcController controller, ListChannelCountRequest request, Action<ListChannelCountResponse> done)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.channel.v2.membership;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x26, serviceHash: 2119327385)]
|
||||
public class ChannelMembershipService_ : bgs.protocol.channel.v2.membership.ChannelMembershipService, IServerService
|
||||
{
|
||||
public override void GetState(IRpcController controller, GetStateRequest request, Action<GetStateResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Subscribe(IRpcController controller, SubscribeRequest request, Action<SubscribeResponse> done)
|
||||
{
|
||||
done(SubscribeResponse.CreateBuilder().Build());
|
||||
}
|
||||
|
||||
public override void Unsubscribe(IRpcController controller, UnsubscribeRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.channel.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.ChannelSystem;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x31, serviceName: "bnet.protocol.channel.ChannelOwner")]
|
||||
public class ChannelOwnerService : bgs.protocol.channel.v1.ChannelOwnerService, IServerService
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
|
||||
public override void CreateChannel(Google.ProtocolBuffers.IRpcController controller, bgs.protocol.channel.v1.CreateChannelRequest request, System.Action<bgs.protocol.channel.v1.CreateChannelResponse> done)
|
||||
{
|
||||
var channel = ChannelManager.CreateNewChannel(((controller as HandlerController).Client), request.ObjectId);
|
||||
var builder = bgs.protocol.channel.v1.CreateChannelResponse.CreateBuilder()
|
||||
.SetObjectId(channel.DynamicId)
|
||||
.SetChannelId(channel.BnetEntityId)
|
||||
;
|
||||
|
||||
done(builder.Build());
|
||||
channel.SetOwner(((controller as HandlerController).Client));
|
||||
channel.AddMember(((controller as HandlerController).Client));
|
||||
}
|
||||
|
||||
public override void ListChannels(IRpcController controller, ListChannelsRequest request, Action<ListChannelsResponse> done)
|
||||
{
|
||||
List<Channel> chatChannels = ChannelManager.GetChatChannels();
|
||||
var builder = ListChannelsResponse.CreateBuilder();
|
||||
|
||||
foreach (Channel channel in chatChannels)
|
||||
{
|
||||
if (!channel.HasUser(((controller as HandlerController).Client)) && (request.Options.HasName ? request.Options.Name == channel.Name : true) && channel.MaxMembers > channel.Members.Count)
|
||||
builder.AddChannel(bgs.protocol.channel.v1.ChannelDescription.CreateBuilder().SetCurrentMembers((uint)channel.Members.Count)
|
||||
.SetChannelId(bgs.protocol.EntityId.CreateBuilder().SetHigh(channel.BnetEntityId.High).SetLow(channel.BnetEntityId.Low))
|
||||
.SetState(channel.State));
|
||||
}
|
||||
|
||||
done(builder.Build());
|
||||
}
|
||||
public override void GetChannelInfo(Google.ProtocolBuffers.IRpcController controller, bgs.protocol.channel.v1.GetChannelInfoRequest request, System.Action<bgs.protocol.channel.v1.GetChannelInfoResponse> done)
|
||||
{
|
||||
var builder = bgs.protocol.channel.v1.GetChannelInfoResponse.CreateBuilder();
|
||||
var channel = ChannelManager.GetChannelByEntityId(request.ChannelId);
|
||||
|
||||
if (channel != null)
|
||||
builder.SetChannelInfo(channel.Info);
|
||||
else
|
||||
Logger.Warn("Channel does not exist!");
|
||||
|
||||
done(builder.Build());
|
||||
}
|
||||
|
||||
public override void JoinChannel(Google.ProtocolBuffers.IRpcController controller, bgs.protocol.channel.v1.JoinChannelRequest request, System.Action<bgs.protocol.channel.v1.JoinChannelResponse> done)
|
||||
{
|
||||
var channel = ChannelManager.GetChannelByEntityId(request.ChannelId);
|
||||
|
||||
channel.Join(((controller as HandlerController).Client), request.ObjectId);
|
||||
var builder = bgs.protocol.channel.v1.JoinChannelResponse.CreateBuilder().SetObjectId(channel.DynamicId).SetMemberId((controller as HandlerController).Client.Account.BnetEntityId);
|
||||
|
||||
((controller as HandlerController).Client).ChatChannels.Add(channel);
|
||||
|
||||
done(builder.Build());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void SubscribeChannel(Google.ProtocolBuffers.IRpcController controller, bgs.protocol.channel.v1.SubscribeChannelRequest request, Action<bgs.protocol.channel.v1.SubscribeChannelResponse> done)
|
||||
{
|
||||
var channel = ChannelManager.GetChannelByEntityId(request.ChannelId);
|
||||
var builder = bgs.protocol.channel.v1.SubscribeChannelResponse.CreateBuilder();
|
||||
|
||||
builder.SetObjectId(channel.DynamicId);
|
||||
done(builder.Build());
|
||||
|
||||
((controller as HandlerController).Client).ChatChannels.Add(channel);
|
||||
channel.Join(((controller as HandlerController).Client), request.ObjectId);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,423 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Extensions;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage.AccountDataBase.Entities;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.GameServer.CommandManager;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.ChannelSystem;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x10, serviceName: "bnet.protocol.channel.Channel")]
|
||||
public class ChannelService : bgs.protocol.channel.v1.ChannelService, IServerService
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
|
||||
|
||||
public override void Dissolve(Google.ProtocolBuffers.IRpcController controller, bgs.protocol.channel.v1.DissolveRequest request, System.Action<bgs.protocol.NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void RemoveMember(Google.ProtocolBuffers.IRpcController controller, bgs.protocol.channel.v1.RemoveMemberRequest request, System.Action<bgs.protocol.NoData> done)
|
||||
{
|
||||
Logger.Trace("RemoveMember()");
|
||||
var channel = ChannelManager.GetChannelByDynamicId(((controller as HandlerController).LastCallHeader).ObjectId);
|
||||
var gameAccount = GameAccountManager.GetAccountByPersistentID(request.MemberId.Low);
|
||||
|
||||
var builder = bgs.protocol.NoData.CreateBuilder();
|
||||
done(builder.Build());
|
||||
|
||||
channel.RemoveMember(gameAccount.LoggedInClient, Channel.GetRemoveReasonForRequest((Channel.RemoveRequestReason)request.Reason));
|
||||
if (request.Reason == 0)
|
||||
{
|
||||
ulong invId = ChannelInvitationManager.FindInvAsForClient((controller as HandlerController).Client);
|
||||
if (invId != System.UInt64.MaxValue)
|
||||
ChannelInvitationManager.AltConnectToJoin((controller as HandlerController).Client, bgs.protocol.channel.v1.AcceptInvitationRequest.CreateBuilder().SetInvitationId(invId).SetObjectId(0).Build());
|
||||
//ServicesSystem.Services.ChannelInvitationService.CreateStub((controller as HandlerController).Client).AcceptInvitation(controller, bgs.protocol.channel.v1.AcceptInvitationRequest.CreateBuilder().SetInvitationId(invId).SetObjectId(0).Build(), callback => { });
|
||||
}
|
||||
}
|
||||
|
||||
public override void SendMessage(Google.ProtocolBuffers.IRpcController controller, bgs.protocol.channel.v1.SendMessageRequest request, System.Action<bgs.protocol.NoData> done)
|
||||
{
|
||||
var channel = ChannelManager.GetChannelByDynamicId(((controller as HandlerController).LastCallHeader).ObjectId);
|
||||
//Logger.Trace("{0} sent a message to channel {1}.", ((controller as HandlerController).Client).Account.GameAccount.CurrentToon, channel);
|
||||
|
||||
var builder = bgs.protocol.NoData.CreateBuilder();
|
||||
done(builder.Build());
|
||||
|
||||
if (!request.HasMessage)
|
||||
return; // only continue if the request actually contains a message.
|
||||
|
||||
if (request.Message.AttributeCount == 0 || !request.Message.AttributeList.First().HasValue)
|
||||
return; // check if it has attributes.
|
||||
|
||||
var parsedAsCommand = CommandManager.TryParse(request.Message.AttributeList[0].Value.StringValue, ((controller as HandlerController).Client)); // try parsing the message as a command
|
||||
|
||||
if (!parsedAsCommand)
|
||||
channel.SendMessage(((controller as HandlerController).Client), request.Message); // if it's not parsed as an command - let channel itself to broadcast message to it's members.
|
||||
}
|
||||
|
||||
public override void UpdateChannelState(Google.ProtocolBuffers.IRpcController controller, bgs.protocol.channel.v1.UpdateChannelStateRequest request, System.Action<bgs.protocol.NoData> done)
|
||||
{
|
||||
/*
|
||||
if (this._loggedInClient.CurrentChannel != channel)
|
||||
{
|
||||
var request = bgs.protocol.channel.v1.AcceptInvitationRequest.CreateBuilder().SetInvitationId(channel.BnetEntityId.Low);
|
||||
|
||||
ServicesSystem.Services.ChannelInvitationService.CreateStub(this.LoggedInClient).AcceptInvitation(null, request.Build(), callback => { });
|
||||
}
|
||||
//*/
|
||||
Channel channel = ChannelManager.GetChannelByDynamicId(((controller as HandlerController).LastCallHeader).ObjectId);
|
||||
|
||||
Logger.Trace("UpdateChannelState(): {0}", request.ToString());
|
||||
|
||||
foreach (bgs.protocol.Attribute attribute in request.StateChange.AttributeList)
|
||||
{
|
||||
if (attribute.Name == "D3.Party.GameCreateParams")
|
||||
{
|
||||
if (attribute.HasValue && !attribute.Value.MessageValue.IsEmpty) //Sometimes not present -Egris
|
||||
{
|
||||
var gameCreateParams = D3.OnlineService.GameCreateParams.ParseFrom(attribute.Value.MessageValue);
|
||||
Logger.Trace("D3.Party.GameCreateParams: {0}", gameCreateParams.ToString());
|
||||
//D3.OnlineService.EntityId hero = gameCreateParams.Coop.ResumeFromSaveHeroId;
|
||||
bool clear_quests = ((controller as HandlerController).Client.GameChannel != null && gameCreateParams.CampaignOrAdventureMode.QuestStepId == -1 &&
|
||||
(gameCreateParams.CampaignOrAdventureMode.SnoQuest == 87700 ||
|
||||
gameCreateParams.CampaignOrAdventureMode.SnoQuest == 80322 ||
|
||||
gameCreateParams.CampaignOrAdventureMode.SnoQuest == 93595 ||
|
||||
gameCreateParams.CampaignOrAdventureMode.SnoQuest == 112498 ||
|
||||
gameCreateParams.CampaignOrAdventureMode.SnoQuest == 251355));
|
||||
var paramsBuilder = D3.OnlineService.GameCreateParams.CreateBuilder(gameCreateParams);
|
||||
var Mode = D3.OnlineService.CampaignOrAdventureModeCreateParams.CreateBuilder(gameCreateParams.CampaignOrAdventureMode);
|
||||
|
||||
lock ((controller as HandlerController).Client.Account.GameAccount.CurrentToon.DBToon)
|
||||
{
|
||||
DBToon toonByClient = ((controller as HandlerController).Client).Account.GameAccount.CurrentToon.DBToon;
|
||||
if(toonByClient.CurrentAct == 400)
|
||||
toonByClient.CurrentAct = gameCreateParams.CampaignOrAdventureMode.Act;
|
||||
if (!clear_quests)
|
||||
{
|
||||
if (toonByClient.CurrentQuestId == 251355)
|
||||
{
|
||||
toonByClient.CurrentQuestId = (gameCreateParams.CampaignOrAdventureMode.SnoQuest == 0 ? 87700 : gameCreateParams.CampaignOrAdventureMode.SnoQuest);
|
||||
toonByClient.CurrentQuestStepId = (gameCreateParams.CampaignOrAdventureMode.QuestStepId == 0 ? -1 : gameCreateParams.CampaignOrAdventureMode.QuestStepId);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mode.SetAct(toonByClient.CurrentAct)
|
||||
.SetSnoQuest(toonByClient.CurrentQuestId)
|
||||
.SetQuestStepId(toonByClient.CurrentQuestStepId);
|
||||
}
|
||||
}
|
||||
|
||||
toonByClient.CurrentDifficulty = gameCreateParams.CampaignOrAdventureMode.HandicapLevel;
|
||||
DBSessions.SessionUpdate(toonByClient);
|
||||
}
|
||||
paramsBuilder.SetCampaignOrAdventureMode(Mode);
|
||||
|
||||
|
||||
//paramsBuilder.SetGameType(16);
|
||||
//paramsBuilder.SetCreationFlags(0xFFFFFFFF);
|
||||
//paramsBuilder.ClearCoop();
|
||||
//paramsBuilder.SetPvp(D3.OnlineService.PvPCreateParams.CreateBuilder().SetSnoWorld(79100));
|
||||
|
||||
/*var toon = ((controller as HandlerController).Client).Account.GameAccount.CurrentToon.DBToon;
|
||||
paramsBuilder.SetCoop(D3.OnlineService.CoopCreateParams.CreateBuilder()
|
||||
.SetDifficultyLevel(toon.CurrentDifficulty)
|
||||
.SetAct(toon.CurrentAct)
|
||||
.SetSnoQuest(toon.CurrentQuestId)
|
||||
.SetQuestStepId(toon.CurrentQuestStepId)
|
||||
.SetOpenToFriends(true)
|
||||
.SetOpenToFriendsMessage("TestGame")
|
||||
);
|
||||
*/
|
||||
|
||||
gameCreateParams = paramsBuilder.Build(); //some magic
|
||||
|
||||
var attr = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.GameCreateParams")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(gameCreateParams.ToByteString()).Build()).Build();
|
||||
|
||||
channel.AddAttribute(attr);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var gameCreateParamsBuilder = D3.OnlineService.GameCreateParams.CreateBuilder();
|
||||
var toon = ((controller as HandlerController).Client).Account.GameAccount.CurrentToon;
|
||||
var dbToon = ((controller as HandlerController).Client).Account.GameAccount.CurrentToon.DBToon;
|
||||
gameCreateParamsBuilder.SetGameType(1);
|
||||
gameCreateParamsBuilder.SetCreationFlags(0);
|
||||
gameCreateParamsBuilder.SetCampaignOrAdventureMode(D3.OnlineService.CampaignOrAdventureModeCreateParams.CreateBuilder()
|
||||
.SetHandicapLevel(dbToon.CurrentDifficulty)
|
||||
.SetAct(dbToon.CurrentAct)
|
||||
.SetSnoQuest(dbToon.CurrentQuestId)
|
||||
.SetQuestStepId(dbToon.CurrentQuestStepId)
|
||||
.SetResumeFromSaveHeroId(toon.D3EntityID)
|
||||
.SetDeprecatedOpenToFriends(true)
|
||||
.SetDeprecatedOpenToFriendsMessage("TestGame")
|
||||
);
|
||||
gameCreateParamsBuilder.SetName(dbToon.Name);
|
||||
var attr = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.GameCreateParams")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(gameCreateParamsBuilder.Build().ToByteString()).Build());
|
||||
|
||||
channel.AddAttribute(attr.Build());
|
||||
}
|
||||
}
|
||||
else if (attribute.Name == "D3.Party.SearchForPublicGame.Params")
|
||||
{
|
||||
// TODO: Find a game that fits the clients params and join /raist.
|
||||
var publicGameParams = D3.PartyMessage.SearchForPublicGameParams.ParseFrom(attribute.Value.MessageValue);
|
||||
Logger.Trace("SearchForPublicGameParams: {0}", publicGameParams.ToString());
|
||||
var attr = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.SearchForPublicGame.Params")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(publicGameParams.ToByteString()).Build());
|
||||
|
||||
channel.AddAttribute(attr.Build());
|
||||
}
|
||||
else if (attribute.Name == "D3.Party.ScreenStatus")
|
||||
{
|
||||
if (!attribute.HasValue || attribute.Value.MessageValue.IsEmpty) //Sometimes not present -Egris
|
||||
{
|
||||
var attr = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.ScreenStatus")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder());
|
||||
|
||||
channel.AddAttribute(attr.Build());
|
||||
Logger.Trace("D3.Party.ScreenStatus = null");
|
||||
}
|
||||
else
|
||||
{
|
||||
var oldScreen = D3.PartyMessage.ScreenStatus.ParseFrom(attribute.Value.MessageValue);
|
||||
((controller as HandlerController).Client).Account.GameAccount.ScreenStatus = oldScreen;
|
||||
|
||||
// TODO: save screen status for use with friends -Egris
|
||||
var attr = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.ScreenStatus")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(oldScreen.ToByteString()));
|
||||
|
||||
channel.AddAttribute(attr.Build());
|
||||
Logger.Trace("Client moving to Screen: {0}, with Status: {1}", oldScreen.Screen, oldScreen.Status);
|
||||
}
|
||||
}
|
||||
else if (attribute.Name == "D3.Party.JoinPermissionPreviousToLock")
|
||||
{
|
||||
// 0 - CLOSED
|
||||
// 1 - ASK_TO_JOIN
|
||||
|
||||
var joinPermission = attribute.Value;
|
||||
var attr = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.JoinPermissionPreviousToLock")
|
||||
.SetValue(joinPermission);
|
||||
|
||||
channel.AddAttribute(attr.Build());
|
||||
Logger.Trace("D3.Party.JoinPermissionPreviousToLock = {0}", joinPermission.IntValue);
|
||||
}
|
||||
else if (attribute.Name == "D3.Party.JoinPermissionPreviousToClose")
|
||||
{
|
||||
// 0 - CLOSED
|
||||
// 1 - ASK_TO_JOIN
|
||||
|
||||
var joinPermission = attribute.Value;
|
||||
var attr = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.JoinPermissionPreviousToClose")
|
||||
.SetValue(joinPermission);
|
||||
|
||||
channel.AddAttribute(attr.Build());
|
||||
Logger.Trace("D3.Party.JoinPermissionPreviousToClose = {0}", joinPermission.IntValue);
|
||||
}
|
||||
else if (attribute.Name == "D3.Party.LockReasons")
|
||||
{
|
||||
// 0 - CREATING_GAME
|
||||
// 2 - MATCHMAKER_SEARCHING
|
||||
|
||||
var lockReason = attribute.Value;
|
||||
var attr = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.LockReasons")
|
||||
.SetValue(lockReason);
|
||||
|
||||
channel.AddAttribute(attr.Build());
|
||||
Logger.Trace("D3.Party.LockReasons = {0}", lockReason.IntValue);
|
||||
}
|
||||
else if (attribute.Name == "D3.Party.GameId")
|
||||
{
|
||||
if (attribute.HasValue && !attribute.Value.MessageValue.IsEmpty) //Sometimes not present -Egris
|
||||
{
|
||||
var gameId = D3.OnlineService.GameId.ParseFrom(attribute.Value.MessageValue);
|
||||
var attr = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.GameId")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(gameId.ToByteString()).Build());
|
||||
|
||||
channel.AddAttribute(attr.Build());
|
||||
Logger.Trace("D3.Party.GameId = {0}", gameId.GameInstanceId);
|
||||
}
|
||||
else
|
||||
Logger.Trace("D3.Party.GameId = null");
|
||||
|
||||
}
|
||||
else if (attribute.Name == "D3.Party.EnterGame.Members")
|
||||
{
|
||||
if (attribute.HasValue && !attribute.Value.MessageValue.IsEmpty) //Sometimes not present -Egris
|
||||
{
|
||||
var members = D3.PartyMessage.EnterGamePartyMemberList.ParseFrom(attribute.Value.MessageValue);
|
||||
var attr = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.EnterGame.Members")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(members.ToByteString()).Build());
|
||||
|
||||
channel.AddAttribute(attr.Build());
|
||||
Logger.Trace("D3.Party.EnterGame.Members = {0}", members.ToString());
|
||||
}
|
||||
else
|
||||
Logger.Trace("D3.Party.EnterGame.Members = null");
|
||||
|
||||
}
|
||||
else if (attribute.Name == "D3.Party.JoinPermission")
|
||||
{
|
||||
if (attribute.HasValue && !attribute.Value.MessageValue.IsEmpty) //Sometimes not present -Egris
|
||||
{
|
||||
var permission = D3.PartyMessage.EnterGamePartyMemberList.ParseFrom(attribute.Value.MessageValue);
|
||||
var attr = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.JoinPermission")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(permission.ToByteString()).Build());
|
||||
|
||||
channel.AddAttribute(attr.Build());
|
||||
Logger.Trace("D3.Party.JoinPermission = {0}", permission.ToString());
|
||||
}
|
||||
else
|
||||
Logger.Trace("D3.Party.JoinPermission = null");
|
||||
|
||||
}
|
||||
else if (attribute.Name == "D3.Party.EnterGame.Leader.AtQueueStart")
|
||||
{
|
||||
if (attribute.HasValue && !attribute.Value.MessageValue.IsEmpty) //Sometimes not present -Egris
|
||||
{
|
||||
var queueStart = D3.PartyMessage.EnterGamePartyMemberList.ParseFrom(attribute.Value.MessageValue);
|
||||
var attr = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.EnterGame.Leader.AtQueueStart")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(queueStart.ToByteString()).Build());
|
||||
|
||||
channel.AddAttribute(attr.Build());
|
||||
Logger.Trace("D3.Party.EnterGame.Leader.AtQueueStart = {0}", queueStart.ToString());
|
||||
}
|
||||
else
|
||||
Logger.Trace("D3.Party.EnterGame.Leader.AtQueueStart = null");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("UpdateChannelState(): Unknown attribute: {0}", attribute.Name);
|
||||
}
|
||||
}
|
||||
|
||||
if (request.StateChange.HasPrivacyLevel)
|
||||
channel.PrivacyLevel = request.StateChange.PrivacyLevel;
|
||||
|
||||
var builder = bgs.protocol.NoData.CreateBuilder();
|
||||
done(builder.Build());
|
||||
|
||||
var notification = bgs.protocol.channel.v1.UpdateChannelStateNotification.CreateBuilder()
|
||||
.SetAgentId(((controller as HandlerController).Client).Account.GameAccount.BnetEntityId)
|
||||
/*
|
||||
.SetChannelId(bgs.protocol.channel.v1.ChannelId.CreateBuilder().SetId((uint)channel.BnetEntityId.Low))
|
||||
.SetSubscriber(bgs.protocol.account.v1.Identity.CreateBuilder()
|
||||
.SetAccount(bgs.protocol.account.v1.AccountId.CreateBuilder().SetId((uint)((controller as HandlerController).Client).Account.BnetEntityId.Low))
|
||||
.SetGameAccount(bgs.protocol.account.v1.GameAccountHandle.CreateBuilder()
|
||||
.SetId((uint)((controller as HandlerController).Client).Account.GameAccount.BnetEntityId.Low)
|
||||
.SetProgram(17459)
|
||||
.SetRegion(1))
|
||||
.SetProcess(bgs.protocol.ProcessId.CreateBuilder().SetLabel(0).SetEpoch(DateTime.Today.ToUnixTime())))
|
||||
//*/
|
||||
.SetStateChange(channel.State) //channelState
|
||||
.Build();
|
||||
|
||||
var altnotif = bgs.protocol.channel.v1.JoinNotification.CreateBuilder().SetChannelState(channel.State).Build();
|
||||
|
||||
var client = (controller as HandlerController).Client;
|
||||
|
||||
//Notify all Channel members
|
||||
foreach (var member in channel.Members.Keys)
|
||||
{
|
||||
member.MakeTargetedRPC(channel, (lid) => bgs.protocol.channel.v1.ChannelListener.CreateStub(member).OnUpdateChannelState(new HandlerController() { ListenerId = lid }, notification, callback => { }));
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateMemberState(Google.ProtocolBuffers.IRpcController controller, bgs.protocol.channel.v1.UpdateMemberStateRequest request, System.Action<bgs.protocol.NoData> done)
|
||||
{
|
||||
var channel = ChannelManager.GetChannelByDynamicId(((controller as HandlerController).LastCallHeader).ObjectId);
|
||||
var builder = bgs.protocol.NoData.CreateBuilder();
|
||||
done(builder.Build());
|
||||
|
||||
var channelMember = bgs.protocol.channel.v1.Member.CreateBuilder();
|
||||
var state = bgs.protocol.channel.v1.MemberState.CreateBuilder();
|
||||
foreach (bgs.protocol.Attribute attribute in request.GetStateChange(0).State.AttributeList)
|
||||
{
|
||||
if (attribute.Name == "D3.PartyMember.GameId")
|
||||
if (attribute.HasValue && !attribute.Value.MessageValue.IsEmpty) //Sometimes not present -Egris
|
||||
{
|
||||
var gameId = D3.OnlineService.GameId.ParseFrom(attribute.Value.MessageValue);
|
||||
var attr = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.PartyMember.GameId")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetMessageValue(gameId.ToByteString()).Build());
|
||||
state.AddAttribute(attr);
|
||||
Logger.Trace("D3.PartyMember.GameId = {0}", gameId.GameInstanceId);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Trace("D3.PartyMember.GameId = null");
|
||||
channel.RemoveMember((controller as HandlerController).Client, Channel.GetRemoveReasonForRequest((Channel.RemoveRequestReason)2));
|
||||
}
|
||||
}
|
||||
|
||||
if (request.GetStateChange(0).State.RoleCount > 0)
|
||||
{
|
||||
state.AddRangeRole(request.GetStateChange(0).State.RoleList);
|
||||
}
|
||||
channelMember.SetIdentity(request.GetStateChange(0).Identity);
|
||||
channelMember.SetState(state);
|
||||
|
||||
var notification = bgs.protocol.channel.v1.UpdateMemberStateNotification.CreateBuilder()
|
||||
.SetAgentId(((controller as HandlerController).Client).Account.GameAccount.BnetEntityId)
|
||||
.AddStateChange(channelMember)
|
||||
.Build();
|
||||
|
||||
try
|
||||
{
|
||||
if (request.GetStateChange(0).State.RoleCount > 0 && request.GetStateChange(0).State.GetRole(0) == 2)
|
||||
{
|
||||
channel.SetOwner(GameAccountManager.GetAccountByPersistentID(request.GetStateChange(0).Identity.GameAccountId.Low).LoggedInClient);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
//Notify all Channel members
|
||||
foreach (var member in channel.Members.Keys)
|
||||
{
|
||||
member.MakeTargetedRPC(channel, (lid) =>
|
||||
bgs.protocol.channel.v1.ChannelListener.CreateStub(member).OnUpdateMemberState(new HandlerController() { ListenerId = lid }, notification, callback => { }));
|
||||
}
|
||||
//*/
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.authentication.v1;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.connection.v1;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Extensions;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x0, serviceHash: 1698982289)]
|
||||
public class ConnectionSerivce : ConnectionService, IServerService
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
public override void Bind(IRpcController controller, BindRequest request, Action<BindResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Connect(IRpcController controller, ConnectRequest request, Action<ConnectResponse> done)
|
||||
{
|
||||
var builder = ConnectResponse.CreateBuilder()
|
||||
.SetServerId(ProcessId.CreateBuilder().SetLabel(0).SetEpoch(DateTime.Now.ToUnixTime()))
|
||||
.SetServerTime(DateTime.Now.ToUnixTime())
|
||||
.SetClientId(ProcessId.CreateBuilder().SetLabel(1).SetEpoch(DateTime.Now.ToUnixTime()));
|
||||
if (request.HasUseBindlessRpc)
|
||||
builder.SetUseBindlessRpc(true);
|
||||
|
||||
|
||||
|
||||
(controller as HandlerController).Client.Services.Add(0x54DFDA17, 0x01);
|
||||
(controller as HandlerController).Client.Services.Add(0xD4DCD093, 0x02);
|
||||
(controller as HandlerController).Client.Services.Add(0x71240E35, 0x03);
|
||||
(controller as HandlerController).Client.Services.Add(0xBBDA171F, 0x04);
|
||||
(controller as HandlerController).Client.Services.Add(0xF084FC20, 0x05);
|
||||
(controller as HandlerController).Client.Services.Add(0xBF8C8094, 0x06);
|
||||
(controller as HandlerController).Client.Services.Add(0x166FE4A1, 0x07);
|
||||
(controller as HandlerController).Client.Services.Add(0xB96F5297, 0x08);
|
||||
(controller as HandlerController).Client.Services.Add(0x6F259A13, 0x09);
|
||||
(controller as HandlerController).Client.Services.Add(0xE1CB2EA8, 0x0A);
|
||||
(controller as HandlerController).Client.Services.Add(0xBC872C22, 0x0B);
|
||||
(controller as HandlerController).Client.Services.Add(0x7FE36B32, 0x0C);
|
||||
(controller as HandlerController).Client.Services.Add(233634817, 0x0D);
|
||||
(controller as HandlerController).Client.Services.Add(0x62DA0891, 0x0E); //AccountService
|
||||
(controller as HandlerController).Client.Services.Add(510168069, 0x0F);
|
||||
(controller as HandlerController).Client.Services.Add(0x45E59C4D, 0x10);
|
||||
(controller as HandlerController).Client.Services.Add(0x135185EF, 0x11);
|
||||
(controller as HandlerController).Client.Services.Add(1910276758, 0x51);
|
||||
//(controller as HandlerController).Client.Services.Add(2495170438, 0x25);
|
||||
(controller as HandlerController).Client.Services.Add(2119327385, 0x26);
|
||||
|
||||
done(builder.Build());
|
||||
Logger.Info("Connect with BlizzLess.Net established. Client - {0}", (controller as HandlerController).Client.SocketConnection.RemoteAddress);
|
||||
}
|
||||
|
||||
public override void Echo(IRpcController controller, EchoRequest request, Action<EchoResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void Encrypt(IRpcController controller, EncryptRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void ForceDisconnect(IRpcController controller, DisconnectNotification request, Action<NO_RESPONSE> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void KeepAlive(IRpcController controller, NoData request, Action<NO_RESPONSE> done)
|
||||
{
|
||||
var builder = LogonUpdateRequest.CreateBuilder().SetErrorCode(0);
|
||||
|
||||
done(NO_RESPONSE.CreateBuilder().Build());
|
||||
}
|
||||
|
||||
public override void RequestDisconnect(IRpcController controller, DisconnectRequest request, Action<NO_RESPONSE> done)
|
||||
{
|
||||
Console.WriteLine("Клиент - {0} , отключен", (controller as HandlerController).Client.SocketConnection.RemoteAddress);
|
||||
this.DisconnectClient(controller as HandlerController);
|
||||
if ((controller as HandlerController).Client.Account != null)
|
||||
(controller as HandlerController).Client.Account.GameAccount.Logined = false;
|
||||
((controller as HandlerController).Client).Connect.CloseAsync();
|
||||
(controller as HandlerController).Client.SocketConnection.CloseAsync();
|
||||
/*
|
||||
if ((controller as HandlerController).Client.Account != null)
|
||||
{
|
||||
(controller as HandlerController).Client.Account.CurrentGameAccount.Logined = false;
|
||||
AccountManager.SaveToDB((controller as HandlerController).Client.Account);
|
||||
if ((controller as HandlerController).Client.Account.CurrentGameAccount != null)
|
||||
{
|
||||
GameAccountManager.SaveToDB((controller as HandlerController).Client.Account.CurrentGameAccount);
|
||||
(controller as HandlerController).Client.SocketConnection.CloseAsync();
|
||||
(controller as HandlerController).Client.Connect.CloseAsync();
|
||||
}
|
||||
}
|
||||
//*/
|
||||
|
||||
}
|
||||
private void DisconnectClient(HandlerController controller)
|
||||
{
|
||||
if (controller.Client.Account != null && controller.Client.Account.GameAccount != null) controller.Client.Account.GameAccount.LoggedInClient = null;
|
||||
LoginServer.Battle.PlayerManager.PlayerDisconnected(controller.Client);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.friends.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x36, serviceName: "bnet.protocol.friends.FriendsService")]
|
||||
public class FriendService : bgs.protocol.friends.v1.FriendsService, IServerService
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
|
||||
|
||||
|
||||
public override void AcceptInvitation(IRpcController controller, AcceptInvitationRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void CreateFriendship(IRpcController controller, CreateFriendshipRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void DeclineInvitation(IRpcController controller, DeclineInvitationRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void GetFriendList(IRpcController controller, GetFriendListRequest request, Action<GetFriendListResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void IgnoreInvitation(IRpcController controller, IgnoreInvitationRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void RemoveFriend(IRpcController controller, RemoveFriendRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void RevokeAllInvitations(IRpcController controller, RevokeAllInvitationsRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void RevokeInvitation(IRpcController controller, RevokeInvitationRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void SendInvitation(IRpcController controller, SendInvitationRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void SetAttribute(IRpcController controller, SetAttributeRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void Subscribe(IRpcController controller, SubscribeRequest request, Action<SubscribeResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void Unsubscribe(IRpcController controller, UnsubscribeRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void UpdateFriendState(IRpcController controller, UpdateFriendStateRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void ViewFriends(IRpcController controller, ViewFriendsRequest request, Action<ViewFriendsResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.games.v1;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x77, serviceName: "bnet.protocol.games.GameMaster")]
|
||||
public class GameMasterService : bgs.protocol.games.v1.GameMasterService, IServerService
|
||||
{
|
||||
public override void CancelGameEntry(IRpcController controller, CancelGameEntryRequest request, Action<CancelGameEntryResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void ChangeGame(IRpcController controller, ChangeGameRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void FindGame(IRpcController controller, FindGameRequest request, Action<FindGameResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GameEnded(IRpcController controller, GameEndedNotification request, Action<NO_RESPONSE> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetFactoryInfo(IRpcController controller, GetFactoryInfoRequest request, Action<GetFactoryInfoResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetFindGameRequests(IRpcController controller, GetFindGameRequestsRequest request, Action<GetFindGameRequestsResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetGameStats(IRpcController controller, GetGameStatsRequest request, Action<GetGameStatsResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetGameStatsBuckets(IRpcController controller, GetGameStatsBucketsRequest request, Action<GetGameStatsBucketsResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void JoinGame(IRpcController controller, JoinGameRequest request, Action<JoinGameResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void ListFactories(IRpcController controller, ListFactoriesRequest request, Action<ListFactoriesResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void OnGameCreated(IRpcController controller, GameCreatedNotification request, Action<NO_RESPONSE> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void PlayerLeft(IRpcController controller, PlayerLeftNotification request, Action<NO_RESPONSE> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void RegisterServer(IRpcController controller, RegisterServerRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void RegisterUtilities(IRpcController controller, RegisterUtilitiesRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void RequestGames(IRpcController controller, RequestGamesRequest request, Action<RequestGamesResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetGameSlots(IRpcController controller, SetGameSlotsRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Subscribe(IRpcController controller, SubscribeRequest request, Action<SubscribeResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void UnregisterServer(IRpcController controller, UnregisterServerRequest request, Action<NO_RESPONSE> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void UnregisterUtilities(IRpcController controller, UnregisterUtilitiesRequest request, Action<NO_RESPONSE> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Unsubscribe(IRpcController controller, UnsubscribeRequest request, Action<NO_RESPONSE> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,170 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.matchmaking.v1;
|
||||
//Blizzless Project 2022
|
||||
using D3.OnlineService;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.GamesSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Helpers;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x57, serviceName: "bnet.protocol.matchmaking.GameRequest")]
|
||||
public class GameRequestService : bgs.protocol.matchmaking.v1.GameRequestService, IServerService
|
||||
{
|
||||
public static ulong Counter = 1;
|
||||
public override void CancelMatchmaking(IRpcController controller, CancelMatchmakingRequest request, Action<NoData> done)
|
||||
{
|
||||
done(NoData.DefaultInstance);
|
||||
}
|
||||
public override void JoinGame(IRpcController controller, JoinGameRequest request, Action<JoinGameResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override void QueueMatchmaking(IRpcController controller, QueueMatchmakingRequest request, Action<QueueMatchmakingResponse> done)
|
||||
{
|
||||
#region Инициализация игры
|
||||
|
||||
var id = RequestId.CreateBuilder().SetId(Counter); Counter++;
|
||||
|
||||
done(QueueMatchmakingResponse.CreateBuilder().SetRequestId(id).Build());
|
||||
#endregion
|
||||
string request_type = "";
|
||||
string ServerPool = "";
|
||||
bgs.protocol.v2.Attribute AttributeOfServer = null;
|
||||
GameCreateParams gameCreateParams = null;
|
||||
|
||||
int Difficulty = 0;
|
||||
int CurrentAct = 0;
|
||||
int CurrentQuest = 0;
|
||||
int CurrentStep = 0;
|
||||
string GameTag = "";
|
||||
foreach (var attr in request.Options.CreationProperties.AttributeList)
|
||||
{
|
||||
switch (attr.Name)
|
||||
{
|
||||
case "GameCreateParams":
|
||||
gameCreateParams = GameCreateParams.ParseFrom(attr.Value.BlobValue);
|
||||
AttributeOfServer = attr;
|
||||
break;
|
||||
case "ServerPool":
|
||||
ServerPool = attr.Value.StringValue;
|
||||
break;
|
||||
case "request_type":
|
||||
request_type = attr.Value.StringValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Difficulty = gameCreateParams.CampaignOrAdventureMode.HandicapLevel;
|
||||
CurrentAct = gameCreateParams.CampaignOrAdventureMode.Act;
|
||||
CurrentQuest = gameCreateParams.CampaignOrAdventureMode.SnoQuest;
|
||||
CurrentStep = gameCreateParams.CampaignOrAdventureMode.QuestStepId;
|
||||
|
||||
#region Ставим в очередь
|
||||
QueueWaitTimes.Builder timers = QueueWaitTimes.CreateBuilder();
|
||||
timers.SetMinWait(0).SetMaxWait(120).SetAvgWait(60).SetStdDevWait(0);
|
||||
|
||||
var member = bgs.protocol.account.v1.GameAccountHandle.CreateBuilder();
|
||||
member.SetId((uint)(controller as HandlerController).Client.Account.GameAccount.BnetEntityId.Low).SetProgram(0x00004433).SetRegion(1);
|
||||
|
||||
QueueEntryNotification.Builder qen = QueueEntryNotification.CreateBuilder();
|
||||
qen.SetRequestId(id).SetWaitTimes(timers).AddMember(member).SetRequestInitiator(member);
|
||||
(controller as HandlerController).Client.MakeRPC((lid) => GameRequestListener.CreateStub((controller as HandlerController).Client).OnQueueEntry(new HandlerController() { ListenerId = lid }, qen.Build(), callback => { }));
|
||||
#endregion
|
||||
|
||||
#region Обновление очереди
|
||||
QueueUpdateNotification.Builder qun = QueueUpdateNotification.CreateBuilder();
|
||||
qun.SetRequestId(id)
|
||||
.SetWaitTimes(timers)
|
||||
.SetIsMatchmaking(true);
|
||||
(controller as HandlerController).Client.MakeRPC((lid) => GameRequestListener.CreateStub((controller as HandlerController).Client).OnQueueUpdate(new HandlerController() { ListenerId = lid }, qun.Build(), callback => { }));
|
||||
#endregion
|
||||
|
||||
|
||||
string GAME_SERVER_IP = Program.GAMESERVERIP;
|
||||
if (GameServer.NATConfig.Instance.Enabled)
|
||||
GAME_SERVER_IP = Program.PUBLICGAMESERVERIP;
|
||||
uint GAME_SERVER_PORT = 2001;
|
||||
|
||||
MatchmakingResultNotification.Builder notification = MatchmakingResultNotification.CreateBuilder();
|
||||
ConnectInfo.Builder connectInfo = ConnectInfo.CreateBuilder();
|
||||
connectInfo.SetAddress(Address.CreateBuilder().SetAddress_(GAME_SERVER_IP).SetPort(GAME_SERVER_PORT));
|
||||
connectInfo.AddAttribute(bgs.protocol.v2.Attribute.CreateBuilder().SetName("GameAccount").SetValue(bgs.protocol.v2.Variant.CreateBuilder().SetBlobValue(member.Build().ToByteString())));
|
||||
connectInfo.AddAttribute(bgs.protocol.v2.Attribute.CreateBuilder().SetName("Token").SetValue(bgs.protocol.v2.Variant.CreateBuilder().SetUintValue(0xEEF4364684EE186E))); // FIXME
|
||||
connectInfo.AddAttribute(AttributeOfServer); // Настройки игры
|
||||
|
||||
GameHandle.Builder gh = GameHandle.CreateBuilder();
|
||||
gh.SetMatchmaker(MatchmakerHandle.CreateBuilder()
|
||||
.SetId((uint)(controller as HandlerController).Client.Account.GameAccount.BnetEntityId.Low)
|
||||
.SetAddr(HostProxyPair.CreateBuilder()
|
||||
.SetHost(ProcessId.CreateBuilder().SetLabel(1250).SetEpoch(1499729350))
|
||||
.SetProxy(ProcessId.CreateBuilder().SetLabel(0xaa82dfd9).SetEpoch(1497363883))));
|
||||
gh.SetGameServer(HostProxyPair.CreateBuilder()
|
||||
.SetHost(ProcessId.CreateBuilder().SetLabel(1277).SetEpoch(1499729371))
|
||||
.SetProxy(ProcessId.CreateBuilder().SetLabel(0xf511871c).SetEpoch(1497363865)));
|
||||
|
||||
var gameFound = GameFactoryManager.FindGame((controller as HandlerController).Client, request, ++GameFactoryManager.RequestIdCounter);
|
||||
var clients = (from player in request.Options.PlayerList select GameAccountManager.GetAccountByPersistentID(player.GameAccount.Id) into gameAccount where gameFound != null select gameAccount.LoggedInClient).ToList();
|
||||
|
||||
if (((controller as HandlerController).Client).CurrentChannel != null)
|
||||
{
|
||||
var channelStatePermission = bgs.protocol.channel.v1.ChannelState.CreateBuilder()
|
||||
.AddAttribute(bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("D3.Party.JoinPermissionPreviousToLock")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetIntValue(1).Build())
|
||||
.Build()).Build();
|
||||
|
||||
var notificationPermission = bgs.protocol.channel.v1.UpdateChannelStateNotification.CreateBuilder()
|
||||
.SetAgentId(((controller as HandlerController).Client).Account.GameAccount.BnetEntityId)
|
||||
.SetStateChange(channelStatePermission)
|
||||
.Build();
|
||||
|
||||
var JoinPerm = bgs.protocol.channel.v1.JoinNotification.CreateBuilder().SetChannelState(channelStatePermission).Build();
|
||||
|
||||
((controller as HandlerController).Client).MakeTargetedRPC(((controller as HandlerController).Client).CurrentChannel, (lid) => bgs.protocol.channel.v1.ChannelListener.CreateStub(((controller as HandlerController).Client)).OnUpdateChannelState(new HandlerController() { ListenerId = lid }, notificationPermission, callback => { }));
|
||||
}
|
||||
gameFound.StartGame(clients, gameFound.DynamicId);
|
||||
|
||||
var notificationFound = bgs.protocol.notification.v1.Notification.CreateBuilder()
|
||||
.SetSenderId(bgs.protocol.EntityId.CreateBuilder().SetHigh((ulong)EntityIdHelper.HighIdType.GameAccountId).SetLow(0).Build())
|
||||
.SetTargetId(((controller as HandlerController).Client).Account.GameAccount.BnetEntityId)
|
||||
.SetType("GQ_ENTRY");
|
||||
var attrF = bgs.protocol.Attribute.CreateBuilder()
|
||||
.SetName("game_request_id")
|
||||
.SetValue(bgs.protocol.Variant.CreateBuilder().SetUintValue(gameFound.RequestId).Build());
|
||||
notificationFound.AddAttribute(attrF);
|
||||
|
||||
((controller as HandlerController).Client).MakeRPC((lid) =>
|
||||
bgs.protocol.notification.v1.NotificationListener.CreateStub(((controller as HandlerController).Client)).OnNotificationReceived(new HandlerController() { ListenerId = lid }, notificationFound.Build(), callback => { }));
|
||||
|
||||
|
||||
gh.SetGameInstanceId((uint)gameFound.BnetEntityId.Low);
|
||||
|
||||
connectInfo.AddAttribute(bgs.protocol.v2.Attribute.CreateBuilder().SetName("SGameId").SetValue(bgs.protocol.v2.Variant.CreateBuilder().SetIntValue((long)gameFound.BnetEntityId.Low)));
|
||||
connectInfo.AddAttribute(bgs.protocol.v2.Attribute.CreateBuilder().SetName("SWorldId").SetValue(bgs.protocol.v2.Variant.CreateBuilder().SetIntValue((long)71150))); // FIXME
|
||||
|
||||
notification.SetRequestId(id);
|
||||
notification.SetResult(0);
|
||||
notification.SetConnectInfo(connectInfo);
|
||||
notification.SetGameHandle(gh);
|
||||
|
||||
System.Threading.Tasks.Task.Delay(2000).ContinueWith(delegate {
|
||||
(controller as HandlerController).Client.MakeRPC((lid) => GameRequestListener.CreateStub((controller as HandlerController).Client).OnMatchmakingResult(new HandlerController() { ListenerId = lid }, notification.Build(), callback => { }));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,81 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.notification.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.GameServer.CommandManager;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Helpers;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0xa, serviceName: "bnet.protocol.notification.NotificationService")]
|
||||
public class NotificationService : bgs.protocol.notification.v1.NotificationService, IServerService
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
|
||||
public override void Publish(IRpcController controller, PublishRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SendNotification(IRpcController controller, Notification request, Action<NoData> done)
|
||||
{
|
||||
|
||||
switch (request.GetNotificationType())
|
||||
{
|
||||
case NotificationTypeHelper.NotificationType.Whisper:
|
||||
|
||||
var targetAccount = GameAccountManager.GetAccountByPersistentID(request.TargetId.Low);
|
||||
Logger.Trace(string.Format("NotificationRequest.Whisper by {0} to {1}", (controller as HandlerController).Client.Account.GameAccount, targetAccount));
|
||||
|
||||
if (targetAccount.LoggedInClient == null) return;
|
||||
|
||||
if (targetAccount == (controller as HandlerController).Client.Account.GameAccount)
|
||||
CommandManager.TryParse(request.AttributeList[0].Value.StringValue, (controller as HandlerController).Client); // try parsing it as a command and respond it if so.
|
||||
else
|
||||
{
|
||||
var notification = bgs.protocol.notification.v1.Notification.CreateBuilder(request)
|
||||
.SetSenderId((controller as HandlerController).Client.Account.GameAccount.BnetEntityId)
|
||||
.SetSenderAccountId((controller as HandlerController).Client.Account.BnetEntityId)
|
||||
.Build();
|
||||
|
||||
targetAccount.LoggedInClient.MakeRPC((lid) =>
|
||||
NotificationListener.CreateStub(targetAccount.LoggedInClient).OnNotificationReceived(controller, notification, callback => { }));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Logger.Warn("Unhandled notification type: {0}", request.Type);
|
||||
break;
|
||||
}
|
||||
//*/
|
||||
var builder = bgs.protocol.NoData.CreateBuilder();
|
||||
done(builder.Build());
|
||||
}
|
||||
|
||||
public override void Subscribe(IRpcController controller, SubscribeRequest request, Action<NoData> done)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
var builder = bgs.protocol.NoData.CreateBuilder();
|
||||
done(builder.Build());
|
||||
}
|
||||
|
||||
public override void Unsubscribe(IRpcController controller, UnsubscribeRequest request, Action<NoData> done)
|
||||
{
|
||||
var builder = bgs.protocol.NoData.CreateBuilder();
|
||||
done(builder.Build());
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,214 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.presence.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Helpers;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0xb, serviceName: "bnet.protocol.presence.PresenceService")]//: )]
|
||||
public class PresenceService : bgs.protocol.presence.v1.PresenceService, IServerService
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
|
||||
public override void BatchSubscribe(IRpcController controller, BatchSubscribeRequest request, Action<BatchSubscribeResponse> done)
|
||||
{
|
||||
|
||||
var response = BatchSubscribeResponse.CreateBuilder();
|
||||
var EntityId = request.EntityIdList[0];
|
||||
response.AddSubscribeFailed(SubscribeResult.CreateBuilder().SetEntityId(request.EntityIdList[0]).SetResult(0));
|
||||
Task.Run(() =>
|
||||
{
|
||||
|
||||
foreach (var req in request.EntityIdList)
|
||||
{
|
||||
switch (req.GetHighIdType())
|
||||
{
|
||||
case EntityIdHelper.HighIdType.AccountId:
|
||||
var account = AccountManager.GetAccountByPersistentID(req.Low);
|
||||
if (account != null)
|
||||
{
|
||||
Logger.Trace("Subscribe() {0} {1}", ((controller as HandlerController).Client), account);
|
||||
account.AddSubscriber(((controller as HandlerController).Client), request.ObjectId);
|
||||
response.AddSubscribeFailed(SubscribeResult.CreateBuilder().SetEntityId(req).SetResult(0));
|
||||
}
|
||||
|
||||
break;
|
||||
case EntityIdHelper.HighIdType.GameAccountId:
|
||||
var gameaccount = GameAccountManager.GetAccountByPersistentID(req.Low);
|
||||
if (gameaccount != null)
|
||||
{
|
||||
Logger.Trace("Subscribe() {0} {1}", ((controller as HandlerController).Client), gameaccount);
|
||||
gameaccount.AddSubscriber(((controller as HandlerController).Client), request.ObjectId);
|
||||
response.AddSubscribeFailed(SubscribeResult.CreateBuilder().SetEntityId(req).SetResult(0));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Logger.Warn("Recieved an unhandled Presence.Subscribe request with type {0} (0x{1})", req.GetHighIdType(), req.High.ToString("X16"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
//*/
|
||||
});
|
||||
|
||||
done(response.Build());
|
||||
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void BatchUnsubscribe(IRpcController controller, BatchUnsubscribeRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Query(IRpcController controller, QueryRequest request, Action<QueryResponse> done)
|
||||
{
|
||||
var builder = bgs.protocol.presence.v1.QueryResponse.CreateBuilder();
|
||||
|
||||
switch (request.EntityId.GetHighIdType())
|
||||
{
|
||||
case EntityIdHelper.HighIdType.AccountId:
|
||||
var account = AccountManager.GetAccountByPersistentID(request.EntityId.Low);
|
||||
foreach (var key in request.KeyList)
|
||||
{
|
||||
Logger.Trace("Query() {0} {1} - {2}, {3}, {4}", ((controller as HandlerController).Client), account, (FieldKeyHelper.Program)key.Program, (FieldKeyHelper.OriginatingClass)key.Group, key.Field);
|
||||
var field = account.QueryField(key);
|
||||
if (field != null) builder.AddField(field);
|
||||
}
|
||||
break;
|
||||
|
||||
case EntityIdHelper.HighIdType.GameAccountId:
|
||||
var gameaccount = GameAccountManager.GetAccountByPersistentID(request.EntityId.Low);
|
||||
foreach (var key in request.KeyList)
|
||||
{
|
||||
Logger.Trace("Query() {0} {1} - {2}, {3}, {4}", ((controller as HandlerController).Client), gameaccount, (FieldKeyHelper.Program)key.Program, (FieldKeyHelper.OriginatingClass)key.Group, key.Field);
|
||||
var field = gameaccount.QueryField(key);
|
||||
if (field != null) builder.AddField(field);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Logger.Warn("Recieved an unhandled Presence.Query request with type {0} (0x{1})", request.EntityId.GetHighIdType(), request.EntityId.High.ToString("X16"));
|
||||
break;
|
||||
}
|
||||
|
||||
done(builder.Build());
|
||||
}
|
||||
|
||||
public override void Subscribe(IRpcController controller, bgs.protocol.presence.v1.SubscribeRequest request, Action<NoData> done)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
switch (request.EntityId.GetHighIdType())
|
||||
{
|
||||
case EntityIdHelper.HighIdType.AccountId:
|
||||
var account = AccountManager.GetAccountByPersistentID(request.EntityId.Low);
|
||||
if (account != null)
|
||||
{
|
||||
Logger.Trace("Subscribe() {0} {1}", ((controller as HandlerController).Client), account);
|
||||
account.AddSubscriber(((controller as HandlerController).Client), request.ObjectId);
|
||||
}
|
||||
break;
|
||||
case EntityIdHelper.HighIdType.GameAccountId:
|
||||
var gameaccount = GameAccountManager.GetAccountByPersistentID(request.EntityId.Low);
|
||||
if (gameaccount != null)
|
||||
{
|
||||
Logger.Trace("Subscribe() {0} {1}", ((controller as HandlerController).Client), gameaccount);
|
||||
gameaccount.AddSubscriber(((controller as HandlerController).Client), request.ObjectId);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Logger.Warn("Recieved an unhandled Presence.Subscribe request with type {0} (0x{1})", request.EntityId.GetHighIdType(), request.EntityId.High.ToString("X16"));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
var builder = bgs.protocol.NoData.CreateBuilder();
|
||||
done(builder.Build());
|
||||
|
||||
}
|
||||
|
||||
public override void Unsubscribe(IRpcController controller, bgs.protocol.presence.v1.UnsubscribeRequest request, Action<NoData> done)
|
||||
{
|
||||
switch (request.EntityId.GetHighIdType())
|
||||
{
|
||||
case EntityIdHelper.HighIdType.AccountId:
|
||||
var account = AccountManager.GetAccountByPersistentID(request.EntityId.Low);
|
||||
// The client will probably make sure it doesn't unsubscribe to a null ID, but just to make sure..
|
||||
if (account != null)
|
||||
{
|
||||
account.RemoveSubscriber(((controller as HandlerController).Client));
|
||||
Logger.Trace("Unsubscribe() {0} {1}", ((controller as HandlerController).Client), account);
|
||||
}
|
||||
break;
|
||||
case EntityIdHelper.HighIdType.GameAccountId:
|
||||
var gameaccount = GameAccountManager.GetAccountByPersistentID(request.EntityId.Low);
|
||||
if (gameaccount != null)
|
||||
{
|
||||
gameaccount.RemoveSubscriber(((controller as HandlerController).Client));
|
||||
Logger.Trace("Unsubscribe() {0} {1}", ((controller as HandlerController).Client), gameaccount);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Logger.Warn("Recieved an unhandled Presence.Unsubscribe request with type {0} (0x{1})", request.EntityId.GetHighIdType(), request.EntityId.High.ToString("X16"));
|
||||
break;
|
||||
}
|
||||
|
||||
var builder = bgs.protocol.NoData.CreateBuilder();
|
||||
done(builder.Build());
|
||||
}
|
||||
|
||||
public override void Update(IRpcController controller, UpdateRequest request, Action<NoData> done)
|
||||
{
|
||||
//4,1
|
||||
//4,2
|
||||
switch(request.EntityId.GetHighIdType())
|
||||
{
|
||||
case EntityIdHelper.HighIdType.AccountId:
|
||||
if (request.EntityId.Low <= 0) break;
|
||||
var account = AccountManager.GetAccountByPersistentID(request.EntityId.Low);
|
||||
if (account == null) break;
|
||||
var a_trace = string.Format("Update() {0} {1} - {2} Operations", ((controller as HandlerController).Client), account, request.FieldOperationCount);
|
||||
foreach (var fieldOp in request.FieldOperationList)
|
||||
{
|
||||
a_trace += string.Format("\t{0}, {1}, {2}", (FieldKeyHelper.Program)fieldOp.Field.Key.Program, (FieldKeyHelper.OriginatingClass)fieldOp.Field.Key.Group, fieldOp.Field.Key.Field);
|
||||
}
|
||||
account.Update(request.FieldOperationList);
|
||||
Logger.Trace(a_trace);
|
||||
break;
|
||||
case EntityIdHelper.HighIdType.GameAccountId:
|
||||
if (request.EntityId.Low <= 0) break;
|
||||
var gameaccount = GameAccountManager.GetAccountByPersistentID(request.EntityId.Low);
|
||||
if (gameaccount == null) break;
|
||||
var ga_trace = string.Format("Update() {0} {1} - {2} Operations", ((controller as HandlerController).Client), gameaccount, request.FieldOperationCount);
|
||||
foreach (var fieldOp in request.FieldOperationList)
|
||||
{
|
||||
ga_trace += string.Format("\t{0}, {1}, {2}", (FieldKeyHelper.Program)fieldOp.Field.Key.Program, (FieldKeyHelper.OriginatingClass)fieldOp.Field.Key.Group, fieldOp.Field.Key.Field);
|
||||
}
|
||||
gameaccount.Update(request.FieldOperationList);
|
||||
Logger.Trace(ga_trace);
|
||||
break;
|
||||
default:
|
||||
Logger.Warn("Recieved an unhandled Presence.Update request with type {0} (0x{1})", request.EntityId.GetHighIdType(), request.EntityId.High.ToString("X16"));
|
||||
break;
|
||||
}
|
||||
|
||||
var builder = bgs.protocol.NoData.CreateBuilder();
|
||||
done(builder.Build());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.resources.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Extensions;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Helpers;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x3d, serviceName: "bnet.protocol.resources.Resources")]
|
||||
public class ResourceService : bgs.protocol.resources.v1.ResourcesService, IServerService
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
private static byte[] PFTY_HASH = new byte[] { (byte)0xCF, (byte)0x61, (byte)0xE0, (byte)0x81, (byte)0x09, (byte)0x19, (byte)0xC6, (byte)0xA6, (byte)0xF9, (byte)0xC1, (byte)0xCB, (byte)0x24, (byte)0xB3, (byte)0xC6, (byte)0x9D, (byte)0x03, (byte)0xB0, (byte)0x37, (byte)0x08, (byte)0xEC, (byte)0x16, (byte)0xD9, (byte)0x44, (byte)0x51, (byte)0xC5, (byte)0x1F, (byte)0x90, (byte)0x38, (byte)0xE9, (byte)0x09, (byte)0xA7, (byte)0x5A };
|
||||
private static byte[] NEW_PFTY_HASH = new byte[] { 0x06, 0xCD, 0x1B, 0x9A, 0x6E, 0xC5, 0x80, 0xE4, 0xCF, 0xF7, 0xB0, 0x42, 0xA0, 0x53, 0x19, 0x07, 0x59, 0xC3, 0xA1, 0x45, 0x4B, 0xC7, 0x9D, 0xBB, 0x6D, 0x3E, 0xFF, 0x2C, 0xB4, 0x16, 0x8B, 0x61 };
|
||||
public override void GetContentHandle(IRpcController controller, ContentHandleRequest request, Action<ContentHandle> done)
|
||||
{
|
||||
|
||||
Logger.Trace("GetContentHandle(): ProgramId: 0x{0:X8} StreamId: 0x{1:X8}", request.Program, request.Stream);
|
||||
if (request.Program == (uint)FieldKeyHelper.Program.BNet)
|
||||
{
|
||||
var builder = ContentHandle.CreateBuilder()
|
||||
.SetRegion(21843)
|
||||
.SetUsage(0x70667479) //pfty - ProfanityFilter
|
||||
.SetHash(ByteString.CopyFrom(NEW_PFTY_HASH));
|
||||
|
||||
done(builder.Build());
|
||||
}
|
||||
else if (request.Program == (uint)FieldKeyHelper.Program.D3)
|
||||
{
|
||||
var builder = ContentHandle.CreateBuilder()
|
||||
.SetRegion(0x5553)
|
||||
.SetUsage(0x643373)
|
||||
.SetProtoUrl("https://prod.depot.battle.net/${hash}.${usage}");
|
||||
;
|
||||
|
||||
switch (request.Stream)
|
||||
{
|
||||
case 0x61637473: //acts - Available Acts
|
||||
builder.SetHash(ByteString.CopyFrom("bd9e8fc323fe1dbc1ef2e0e95e46355953040488621933d0685feba5e1163a25".ToByteArray()));
|
||||
break;
|
||||
case 0x71756573: //ques - Available Quests
|
||||
builder.SetHash(ByteString.CopyFrom("9303df8f917e2db14ec20724c04ea5d2af4e4cb6c72606b67a262178b7e18104".ToByteArray()));
|
||||
break;
|
||||
case 0x72707273: //rprs - RichPresence
|
||||
builder.SetHash(ByteString.CopyFrom("8F9D8409EA441140E5676823BA867EC56CB2D3D4DF6FC187BA46CBD2855EF799".ToByteArray()));
|
||||
break;
|
||||
case 0x61706674: //apft - ProfanityFilter
|
||||
builder.SetHash(ByteString.CopyFrom("de1862793fdbabb6eb1edec6ad1c95dd99e2fd3fc6ca730ab95091d694318a24".ToByteArray()));
|
||||
break;
|
||||
default:
|
||||
Logger.Warn("Unknown StreamId: 0x{0:X8}", request.Stream);
|
||||
builder.SetHash(ByteString.Empty);
|
||||
(controller as HandlerController).Status = 4;
|
||||
break;
|
||||
}
|
||||
done(builder.Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.session.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Helpers.Math;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x13, serviceName: "bnet.protocol.session.SessionService")]
|
||||
public class SessionService : bgs.protocol.session.v1.SessionService, IServerService
|
||||
{
|
||||
public override void CreateSession(IRpcController controller, CreateSessionRequest request, Action<CreateSessionResponse> done)
|
||||
{
|
||||
string Start = "A7B5C8B0593FFEC10000000";
|
||||
string End = "BCABD";
|
||||
|
||||
string session = Start + RandomHelper.Next(0, 9).ToString() + RandomHelper.Next(0, 9).ToString() + RandomHelper.Next(0, 9).ToString() + RandomHelper.Next(0, 9).ToString() + End;
|
||||
CreateSessionResponse.Builder builder = CreateSessionResponse.CreateBuilder();
|
||||
builder.SetSessionId(session);
|
||||
done(builder.Build());
|
||||
|
||||
SessionCreatedNotification.Builder n = SessionCreatedNotification.CreateBuilder();
|
||||
n.SetIdentity(request.Identity)
|
||||
.SetReason(0)
|
||||
.SetSessionId(session);
|
||||
(controller as HandlerController).Client.MakeRPC((lid) => SessionListener.CreateStub((controller as HandlerController).Client).OnSessionCreated(controller, n.Build(), callback => { }));
|
||||
}
|
||||
|
||||
private void DisconnectClient(HandlerController controller)
|
||||
{
|
||||
if (controller.Client.Account != null && controller.Client.Account.GameAccount != null) controller.Client.Account.GameAccount.LoggedInClient = null;
|
||||
LoginServer.Battle.PlayerManager.PlayerDisconnected(controller.Client);
|
||||
}
|
||||
|
||||
public override void DestroySession(IRpcController controller, DestroySessionRequest request, Action<NoData> done)
|
||||
{
|
||||
Console.WriteLine("Клиент - {0} , отключен", (controller as HandlerController).Client.SocketConnection.RemoteAddress);
|
||||
this.DisconnectClient(controller as HandlerController);
|
||||
if ((controller as HandlerController).Client.Account != null)
|
||||
(controller as HandlerController).Client.Account.GameAccount.Logined = false;
|
||||
((controller as HandlerController).Client).Connect.CloseAsync();
|
||||
(controller as HandlerController).Client.SocketConnection.CloseAsync();
|
||||
|
||||
done(NoData.CreateBuilder().Build());
|
||||
}
|
||||
|
||||
|
||||
public override void GetSessionState(IRpcController controller, GetSessionStateRequest request, Action<GetSessionStateResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetSessionStateByBenefactor(IRpcController controller, GetSessionStateByBenefactorRequest request, Action<GetSessionStateByBenefactorResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetSignedSessionState(IRpcController controller, GetSignedSessionStateRequest request, Action<GetSignedSessionStateResponse> done)
|
||||
{
|
||||
done(GetSignedSessionStateResponse.CreateBuilder().SetToken("eyJ0eXAiOiJKV1QiLCJlbnYiOiJwcm9kLmV1IiwiYWxnIjoiUlMyNTYiLCJraWQiOiJmMDE5NzgzMi0zMWMwLTQzN2MtOTc2NC1iMzliOTM5MDJlNWMiLCJrdHkiOiJSU0EifQ").Build());
|
||||
}
|
||||
|
||||
public override void MarkSessionsAlive(IRpcController controller, MarkSessionsAliveRequest request, Action<MarkSessionsAliveResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void RefreshSessionKey(IRpcController controller, RefreshSessionKeyRequest request, Action<RefreshSessionKeyResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void UpdateSession(IRpcController controller, UpdateSessionRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.sns.v1;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x51, serviceName: "bnet.protocol.sns.SocialNetworkService")]
|
||||
|
||||
public class SocialNetworkService : bgs.protocol.sns.v1.SocialNetworkService, IServerService
|
||||
{
|
||||
public override void GetFacebookAccountLinkStatus(IRpcController controller, GetFacebookAccountLinkStatusRequest request, Action<GetFacebookAccountLinkStatusResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetFacebookAuthCode(IRpcController controller, GetFacebookAuthCodeRequest request, Action<GetFacebookAuthCodeResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetFacebookBnetFriends(IRpcController controller, GetFacebookBnetFriendsRequest request, Action<NoData> done)
|
||||
{
|
||||
done(NoData.CreateBuilder().Build());
|
||||
}
|
||||
|
||||
public override void GetFacebookSettings(IRpcController controller, NoData request, Action<GetFacebookSettingsResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetGoogleAccountLinkStatus(IRpcController controller, GetGoogleAccountLinkStatusRequest request, Action<GetGoogleAccountLinkStatusResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetGoogleAuthToken(IRpcController controller, GetGoogleAuthTokenRequest request, Action<GetGoogleAuthTokenResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetGoogleSettings(IRpcController controller, NoData request, Action<GetGoogleSettingsResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.user_manager.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Base;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.FriendsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Helpers;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x35, serviceName: "bnet.protocol.user_manager.UserManagerService")]
|
||||
public class UserManagerService : bgs.protocol.user_manager.v1.UserManagerService, IServerService
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
|
||||
public override void Subscribe(Google.ProtocolBuffers.IRpcController controller, SubscribeRequest request, System.Action<SubscribeResponse> done)
|
||||
{
|
||||
Logger.Trace("Subscribe() {0}", ((controller as HandlerController).Client));
|
||||
|
||||
UserManager.Instance.AddSubscriber(((controller as HandlerController).Client), request.ObjectId);
|
||||
|
||||
var builder = SubscribeResponse.CreateBuilder();
|
||||
|
||||
var blockedIds = (controller as HandlerController).Client.Account.IgnoreIds;
|
||||
|
||||
foreach (var blocked in blockedIds)
|
||||
{
|
||||
var blockedAccount = AccountManager.GetAccountByPersistentID(blocked);
|
||||
var blockedPlayer = BlockedPlayer.CreateBuilder()
|
||||
.SetAccountId(EntityId.CreateBuilder().SetHigh((ulong)EntityIdHelper.HighIdType.AccountId).SetLow(blockedAccount.PersistentID))
|
||||
.SetName(blockedAccount.BattleTag)
|
||||
.Build();
|
||||
builder.AddBlockedPlayers(blockedPlayer);
|
||||
}
|
||||
|
||||
done(builder.Build());
|
||||
}
|
||||
|
||||
public override void AddRecentPlayers(IRpcController controller, AddRecentPlayersRequest request, Action<NoData> done)
|
||||
{
|
||||
Logger.Trace("AddRecentPlayers()");
|
||||
done(NoData.DefaultInstance);
|
||||
}
|
||||
|
||||
public override void ClearRecentPlayers(IRpcController controller, ClearRecentPlayersRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void BlockPlayer(IRpcController controller, BlockPlayerRequest request, Action<NoData> done)
|
||||
{
|
||||
Logger.Trace("BlockEntity()");
|
||||
done(NoData.CreateBuilder().Build());
|
||||
|
||||
UserManager.BlockAccount(((controller as HandlerController).Client), request);
|
||||
}
|
||||
|
||||
public override void UnblockPlayer(IRpcController controller, UnblockPlayerRequest request, Action<NoData> done)
|
||||
{
|
||||
Logger.Trace("UnblockPlayer()");
|
||||
done(NoData.CreateBuilder().Build());
|
||||
|
||||
UserManager.UnblockAccount(((controller as HandlerController).Client), request);
|
||||
}
|
||||
|
||||
public override void BlockPlayerForSession(IRpcController controller, BlockPlayerRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Unsubscribe(IRpcController controller, UnsubscribeRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol;
|
||||
//Blizzless Project 2022
|
||||
using bgs.protocol.whisper.v1;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.ServicesSystem;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DiIiS_NA.BGS_Server.ServicesSystem.Services
|
||||
{
|
||||
[Service(serviceID: 0x96, serviceHash: 0xC12828F9)]
|
||||
public class WhisperService : bgs.protocol.whisper.v1.WhisperService, IServerService
|
||||
{
|
||||
public override void AdvanceClearTime(IRpcController controller, AdvanceClearTimeRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void AdvanceViewTime(IRpcController controller, AdvanceViewTimeRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetWhisperMessages(IRpcController controller, GetWhisperMessagesRequest request, Action<GetWhisperMessagesResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SendWhisper(IRpcController controller, SendWhisperRequest request, Action<SendWhisperResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetTypingIndicator(IRpcController controller, SetTypingIndicatorRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Subscribe(IRpcController controller, SubscribeRequest request, Action<SubscribeResponse> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Unsubscribe(IRpcController controller, UnsubscribeRequest request, Action<NoData> done)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
1393
src/DiIiS-NA/BGS-Server/Toons/Toon.cs
Normal file
1393
src/DiIiS-NA/BGS-Server/Toons/Toon.cs
Normal file
File diff suppressed because it is too large
Load Diff
59
src/DiIiS-NA/BGS-Server/Toons/ToonHandleHelper.cs
Normal file
59
src/DiIiS-NA/BGS-Server/Toons/ToonHandleHelper.cs
Normal file
@ -0,0 +1,59 @@
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.Helpers;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Toons
|
||||
{
|
||||
public class ToonHandleHelper
|
||||
{
|
||||
public ulong ID { get; private set; }
|
||||
|
||||
// Commented out all Program, Region, Realm stuff because **as of right now** we don't section Toon High's with that fields.
|
||||
// All we have in hand is a default program, region, realm there. So after we get all stuff implemented, we should also
|
||||
// sectionize HighId's so we'll have technical correctiness :)
|
||||
|
||||
// And remember that this data is the sectioning for the EntityId High value. For type-checking we can't just do
|
||||
// eid.High == EntityHelper.ToonId, instead we need to mask off the first 7 bytes to get _only_ the object type.
|
||||
|
||||
//public uint Program { get; private set; }
|
||||
//public uint Region { get; private set; }
|
||||
//public uint Realm { get; private set; }
|
||||
|
||||
public ToonHandleHelper(ulong id)
|
||||
{
|
||||
this.ID = id;
|
||||
//this.Program = 0x00004433;
|
||||
//this.Region = 0x62;
|
||||
//this.Realm = 0x01;
|
||||
}
|
||||
|
||||
public ToonHandleHelper(D3.OnlineService.EntityId entityID)
|
||||
{
|
||||
var stream = CodedInputStream.CreateInstance(entityID.ToByteArray());
|
||||
ulong tmp = 0;
|
||||
// I believe this actually calls ReadRawVarint64(), but just to be sure..
|
||||
stream.ReadUInt64(ref tmp);
|
||||
this.ID = tmp;
|
||||
//this.Program = stream.ReadRawVarint32();
|
||||
//this.Region = stream.ReadRawVarint32();
|
||||
//this.Realm = stream.ReadRawVarint32();
|
||||
}
|
||||
|
||||
public D3.OnlineService.EntityId ToD3EntityID()
|
||||
{
|
||||
return D3.OnlineService.EntityId.CreateBuilder().SetIdHigh((ulong)EntityIdHelper.HighIdType.ToonId).SetIdLow(this.ID).Build();
|
||||
}
|
||||
|
||||
public bgs.protocol.EntityId ToBnetEntityID()
|
||||
{
|
||||
return bgs.protocol.EntityId.CreateBuilder().SetHigh((ulong)EntityIdHelper.HighIdType.ToonId).SetLow(this.ID).Build();
|
||||
}
|
||||
}
|
||||
}
|
||||
558
src/DiIiS-NA/BGS-Server/Toons/ToonManager.cs
Normal file
558
src/DiIiS-NA/BGS-Server/Toons/ToonManager.cs
Normal file
@ -0,0 +1,558 @@
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Helpers.Math;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage.AccountDataBase.Entities;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.GameServer.GSSystem.SkillsSystem;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.LoginServer.AccountsSystem;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Concurrent;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using static DiIiS_NA.LoginServer.Toons.Toon;
|
||||
|
||||
namespace DiIiS_NA.LoginServer.Toons
|
||||
{
|
||||
public static class ToonManager
|
||||
{
|
||||
private static readonly ConcurrentDictionary<ulong, Toon> LoadedToons = new ConcurrentDictionary<ulong, Toon>();
|
||||
private static readonly Logger Logger = LogManager.CreateLogger("DataBaseSystem");
|
||||
|
||||
private static readonly DBInventory NewbiePants = new DBInventory
|
||||
{
|
||||
EquipmentSlot = 9,
|
||||
LocationX = 0,
|
||||
LocationY = 0,
|
||||
Count = 1,
|
||||
FirstGem = -1,
|
||||
SecondGem = -1,
|
||||
ThirdGem = -1,
|
||||
TransmogGBID = -1,
|
||||
GbId = -1512732138,
|
||||
Durability = 1183,
|
||||
Affixes = "",
|
||||
Attributes = "383,:1|1E-45;103,:0|0;406,:0|0;409,:1|1E-45;401,:1|1E-45;405,:1623510521|1.1345012E+20;33,:1086324736|6;36,:1086324736|6;37,:1086324736|6;38,:1094713344|12;31,:1086324736|6;381,:1183|1.658E-42;380,:1183|1.658E-42;388,57:0|0"
|
||||
};
|
||||
|
||||
private static readonly DBInventory NewbieArmor = new DBInventory
|
||||
{
|
||||
EquipmentSlot = 2,
|
||||
LocationX = 0,
|
||||
LocationY = 0,
|
||||
Count = 1,
|
||||
FirstGem = -1,
|
||||
SecondGem = -1,
|
||||
ThirdGem = -1,
|
||||
TransmogGBID = -1,
|
||||
GbId = 1612257704,
|
||||
Durability = 1115,
|
||||
Affixes = "",
|
||||
Attributes = "383,:0|0;103,:0|0;406,:0|0;409,:1|1E-45;401,:1|1E-45;405,:1287202746|97074640;33,:1077936128|3;36,:1077936128|3;37,:1077936128|3;38,:1086324736|6;31,:1077936128|3;381,:1115|1.562E-42;380,:1115|1.562E-42;388,57:0|0"
|
||||
};
|
||||
|
||||
private static readonly DBInventory NewbieKnife = new DBInventory
|
||||
{
|
||||
EquipmentSlot = 4,
|
||||
LocationX = 0,
|
||||
LocationY = 0,
|
||||
Count = 1,
|
||||
FirstGem = -1,
|
||||
SecondGem = -1,
|
||||
ThirdGem = -1,
|
||||
TransmogGBID = -1,
|
||||
GbId = -635269584,
|
||||
Durability = 800,
|
||||
Affixes = "",
|
||||
Attributes = "383,:1|1E-45;103,:0|0;406,:0|0;409,:1|1E-45;401,:1|1E-45;405,:1391170218|5.0588896E+11;194,:1067030938|1.2;196,:1067030938|1.2;198,:1067030938|1.2;538,:1067030938|1.2;540,:1067030938|1.2;546,:1067030938|1.2;201,:1067030938|1.2;446,:0|0;447,:0|0;448,:0|0;449,:0|0;539,:0|0;541,:0|0;195,:0|0;197,:0|0;231,0:1073741824|2;224,0:1077936128|3;232,0:1073741824|2;225,0:1077936128|3;226,:1077936128|3;233,:1073741824|2;236,:1075838976|2.5;235,0:1075838976|2.5;542,0:1073741824|2;547,0:1073741824|2;220,0:1073741824|2;216,0:1073741824|2;543,0:0|0;234,0:1075838976|2.5;222,0:1065353216|1;223,0:1065353216|1;227,0:1065353216|1;228,:1065353216|1;544,0:1065353216|1;548,0:1065353216|1;213,0:1065353216|1;545,0:0|0;381,:800|1.121E-42;380,:800|1.121E-42;100,30592:1|1E-45;102,30592:1|1E-45;388,57:0|0"
|
||||
};
|
||||
|
||||
private static readonly DBInventory NewbieAxe = new DBInventory
|
||||
{
|
||||
EquipmentSlot = 4,
|
||||
LocationX = 0,
|
||||
LocationY = 0,
|
||||
Count = 1,
|
||||
FirstGem = -1,
|
||||
SecondGem = -1,
|
||||
ThirdGem = -1,
|
||||
TransmogGBID = -1,
|
||||
GbId = 1661412389,
|
||||
Durability = 1000,
|
||||
Affixes = "",
|
||||
Attributes = "383,:1|1E-45;103,:0|0;406,:0|0;409,:1|1E-45;401,:1|1E-45;405,:1941814752|3.0065772E+31;194,:1067030938|1.2;196,:1067030938|1.2;198,:1067030938|1.2;538,:1067030938|1.2;540,:1067030938|1.2;546,:1067030938|1.2;201,:1067030938|1.2;446,:0|0;447,:0|0;448,:0|0;449,:0|0;539,:0|0;541,:0|0;195,:0|0;197,:0|0;231,0:1073741824|2;224,0:1077936128|3;232,0:1073741824|2;225,0:1077936128|3;226,:1077936128|3;233,:1073741824|2;236,:1075838976|2.5;235,0:1075838976|2.5;542,0:1073741824|2;547,0:1073741824|2;220,0:1073741824|2;216,0:1073741824|2;543,0:0|0;234,0:1075838976|2.5;222,0:1065353216|1;223,0:1065353216|1;227,0:1065353216|1;228,:1065353216|1;544,0:1065353216|1;548,0:1065353216|1;213,0:1065353216|1;545,0:0|0;381,:1000|1.401E-42;380,:1000|1.401E-42;100,30592:1|1E-45;102,30592:1|1E-45;388,57:0|0"
|
||||
};
|
||||
|
||||
private static readonly DBInventory NewbieBow = new DBInventory
|
||||
{
|
||||
EquipmentSlot = 4,
|
||||
LocationX = 0,
|
||||
LocationY = 0,
|
||||
Count = 1,
|
||||
FirstGem = -1,
|
||||
SecondGem = -1,
|
||||
ThirdGem = -1,
|
||||
TransmogGBID = -1,
|
||||
GbId = unchecked((int)-2091504072),
|
||||
Durability = 920,
|
||||
Affixes = "",
|
||||
Attributes = "383,:1|1E-45;103,:0|0;406,:0|0;409,:1|1E-45;401,:1|1E-45;405,:1256226286|7356151;194,:1068708659|1.4;196,:1068708659|1.4;198,:1068708659|1.4;538,:1068708659|1.4;540,:1068708659|1.4;546,:1068708659|1.4;201,:1068708659|1.4;446,:0|0;447,:0|0;448,:0|0;449,:0|0;539,:0|0;541,:0|0;195,:0|0;197,:0|0;231,0:1065353216|1;224,0:1090519040|8;232,0:1065353216|1;225,0:1090519040|8;226,:1090519040|8;233,:1065353216|1;236,:1083179008|4.5;235,0:1083179008|4.5;542,0:1065353216|1;547,0:1065353216|1;220,0:1065353216|1;216,0:1065353216|1;543,0:0|0;234,0:1083179008|4.5;222,0:1088421888|7;223,0:1088421888|7;227,0:1088421888|7;228,:1088421888|7;544,0:1088421888|7;548,0:1088421888|7;213,0:1088421888|7;545,0:0|0;381,:920|1.289E-42;380,:920|1.289E-42;100,30599:1|1E-45;102,30599:1|1E-45;388,57:0|0"
|
||||
};
|
||||
|
||||
private static readonly DBInventory NewbieFlail = new DBInventory
|
||||
{
|
||||
EquipmentSlot = 4,
|
||||
LocationX = 0,
|
||||
LocationY = 0,
|
||||
Count = 1,
|
||||
FirstGem = -1,
|
||||
SecondGem = -1,
|
||||
ThirdGem = -1,
|
||||
TransmogGBID = -1,
|
||||
GbId = -912456881,
|
||||
Durability = 1000,
|
||||
Affixes = "",
|
||||
Attributes = "383,:1|1E-45;103,:0|0;406,:0|0;409,:1|1E-45;401,:1|1E-45;405,:1569321797|1.2425456E+18;194,:1067030938|1.2;196,:1067030938|1.2;198,:1067030938|1.2;538,:1067030938|1.2;540,:1067030938|1.2;546,:1067030938|1.2;201,:1067030938|1.2;446,:0|0;447,:0|0;448,:0|0;449,:0|0;539,:0|0;541,:0|0;195,:0|0;197,:0|0;231,0:1073741824|2;224,0:1077936128|3;232,0:1073741824|2;225,0:1077936128|3;226,:1077936128|3;233,:1073741824|2;236,:1075838976|2.5;235,0:1075838976|2.5;542,0:1073741824|2;547,0:1073741824|2;220,0:1073741824|2;216,0:1073741824|2;543,0:0|0;234,0:1075838976|2.5;222,0:1065353216|1;223,0:1065353216|1;227,0:1065353216|1;228,:1065353216|1;544,0:1065353216|1;548,0:1065353216|1;213,0:1065353216|1;545,0:0|0;381,:1000|1.401E-42;380,:1000|1.401E-42;100,30592:1|1E-45;102,30592:1|1E-45;388,57:0|0"
|
||||
};
|
||||
|
||||
private static readonly DBInventory NewbieWand = new DBInventory
|
||||
{
|
||||
EquipmentSlot = 4,
|
||||
LocationX = 0,
|
||||
LocationY = 0,
|
||||
Count = 1,
|
||||
FirstGem = -1,
|
||||
SecondGem = -1,
|
||||
ThirdGem = -1,
|
||||
TransmogGBID = -1,
|
||||
GbId = 88665049,
|
||||
Durability = 800,
|
||||
Affixes = "",
|
||||
Attributes = "383,:1|1E-45;103,:0|0;406,:0|0;409,:1|1E-45;401,:1|1E-45;405,:415523826|5.076573E-24;194,:1067030938|1.2;196,:1067030938|1.2;198,:1067030938|1.2;538,:1067030938|1.2;540,:1067030938|1.2;546,:1067030938|1.2;201,:1067030938|1.2;446,:0|0;447,:0|0;448,:0|0;449,:0|0;539,:0|0;541,:0|0;195,:0|0;197,:0|0;231,0:1073741824|2;224,0:1077936128|3;232,0:1073741824|2;225,0:1077936128|3;226,:1077936128|3;233,:1073741824|2;236,:1075838976|2.5;235,0:1075838976|2.5;542,0:1073741824|2;547,0:1073741824|2;220,0:1073741824|2;216,0:1073741824|2;543,0:0|0;234,0:1075838976|2.5;222,0:1065353216|1;223,0:1065353216|1;227,0:1065353216|1;228,:1065353216|1;544,0:1065353216|1;548,0:1065353216|1;213,0:1065353216|1;545,0:0|0;381,:800|1.121E-42;380,:800|1.121E-42;100,30601:1|1E-45;102,30601:1|1E-45;388,57:0|0"
|
||||
};
|
||||
|
||||
private static readonly DBInventory NewbieFist = new DBInventory
|
||||
{
|
||||
EquipmentSlot = 4,
|
||||
LocationX = 0,
|
||||
LocationY = 0,
|
||||
Count = 1,
|
||||
FirstGem = -1,
|
||||
SecondGem = -1,
|
||||
ThirdGem = -1,
|
||||
TransmogGBID = -1,
|
||||
GbId = 1236604967,
|
||||
Durability = 800,
|
||||
Affixes = "",
|
||||
Attributes = "383,:1|1E-45;103,:0|0;406,:0|0;409,:1|1E-45;401,:1|1E-45;405,:1941814752|3.0065772E+31;194,:1067030938|1.2;196,:1067030938|1.2;198,:1067030938|1.2;538,:1067030938|1.2;540,:1067030938|1.2;546,:1067030938|1.2;201,:1067030938|1.2;446,:0|0;447,:0|0;448,:0|0;449,:0|0;539,:0|0;541,:0|0;195,:0|0;197,:0|0;231,0:1073741824|2;224,0:1077936128|3;232,0:1073741824|2;225,0:1077936128|3;226,:1077936128|3;233,:1073741824|2;236,:1075838976|2.5;235,0:1075838976|2.5;542,0:1073741824|2;547,0:1073741824|2;220,0:1073741824|2;216,0:1073741824|2;543,0:0|0;234,0:1075838976|2.5;222,0:1065353216|1;223,0:1065353216|1;227,0:1065353216|1;228,:1065353216|1;544,0:1065353216|1;548,0:1065353216|1;213,0:1065353216|1;545,0:0|0;381,:1000|1.401E-42;380,:1000|1.401E-42;100,30592:1|1E-45;102,30592:1|1E-45;388,57:0|0"
|
||||
};
|
||||
|
||||
private static readonly DBInventory NewbieNecr = new DBInventory
|
||||
{
|
||||
EquipmentSlot = 4,
|
||||
LocationX = 0,
|
||||
LocationY = 0,
|
||||
Count = 1,
|
||||
FirstGem = -1,
|
||||
SecondGem = -1,
|
||||
ThirdGem = -1,
|
||||
TransmogGBID = -1,
|
||||
GbId = unchecked((int)111732407),
|
||||
Durability = 1000,
|
||||
Affixes = "",
|
||||
Attributes = "383,:1|1E-45;103,:0|0;406,:0|0;409,:1|1E-45;401,:1|1E-45;405,:1007483702|0.008604815;194,:1067030938|1.2;196,:1067030938|1.2;198,:1067030938|1.2;538,:1067030938|1.2;540,:1067030938|1.2;546,:1067030938|1.2;201,:1067030938|1.2;446,:0|0;447,:0|0;448,:0|0;449,:0|0;539,:0|0;541,:0|0;195,:0|0;197,:0|0;231,0:1073741824|2;224,0:1077936128|3;232,0:1073741824|2;225,0:1077936128|3;226,:1077936128|3;233,:1073741824|2;236,:1075838976|2.5;235,0:1075838976|2.5;542,0:1073741824|2;547,0:1073741824|2;220,0:1073741824|2;216,0:1073741824|2;543,0:0|0;234,0:1075838976|2.5;222,0:1065353216|1;223,0:1065353216|1;227,0:1065353216|1;228,:1065353216|1;544,0:1065353216|1;548,0:1065353216|1;213,0:1065353216|1;545,0:0|0;381,:1000|1.401E-42;380,:1000|1.401E-42;100,30592:1|1E-45;102,30592:1|1E-45;388,57:0|0"
|
||||
};
|
||||
|
||||
private static readonly DBInventory NewbieShield = new DBInventory
|
||||
{
|
||||
EquipmentSlot = 3,
|
||||
LocationX = 0,
|
||||
LocationY = 0,
|
||||
Count = 1,
|
||||
FirstGem = -1,
|
||||
SecondGem = -1,
|
||||
ThirdGem = -1,
|
||||
TransmogGBID = -1,
|
||||
GbId = unchecked((int)1815806856 ),
|
||||
Durability = 1138,
|
||||
Affixes = "",
|
||||
Attributes = "383,:1|1E-45;103,:0|0;406,:0|0;409,:1|1E-45;401,:1|1E-45;405,:1477195667|6.166813E+14;33,:1090519040|8;36,:1090519040|8;37,:1090519040|8;38,:1098907648|16;31,:1090519040|8;381,:1138|1.595E-42;380,:1138|1.595E-42;264,:1041865114|0.15;272,:1088421888|7;273,:1084227584|5;275,:1084227584|5;276,:1084227584|5;388,57:0|0"
|
||||
};
|
||||
|
||||
public static void PreLoadToons()
|
||||
{
|
||||
Logger.Info("Loading Diablo III - Toons...");
|
||||
List<DBToon> all_toons = DBSessions.SessionQuery<DBToon>();
|
||||
foreach (var toon in all_toons)
|
||||
{
|
||||
LoadedToons.TryAdd(toon.Id, new Toon(toon, null));
|
||||
}
|
||||
}
|
||||
|
||||
public static Toon GetToonByDBToon(DBToon dbToon, GameDBSession session = null)
|
||||
{
|
||||
if (dbToon == null) return null;
|
||||
if (LoadedToons.ContainsKey(dbToon.Id))
|
||||
return LoadedToons[dbToon.Id];
|
||||
else
|
||||
{
|
||||
var toon = new Toon(dbToon, session);
|
||||
LoadedToons.TryAdd(dbToon.Id, toon);
|
||||
return toon;
|
||||
}
|
||||
}
|
||||
|
||||
public static Account GetOwnerAccountByToonLowId(ulong id)
|
||||
{
|
||||
return GetToonByLowID(id).GameAccount.Owner;
|
||||
}
|
||||
|
||||
public static GameAccount GetOwnerGameAccountByToonLowId(ulong id)
|
||||
{
|
||||
return GetToonByLowID(id).GameAccount;
|
||||
}
|
||||
|
||||
public static Toon GetToonByLowID(ulong id, GameDBSession session = null)
|
||||
{
|
||||
if (LoadedToons.ContainsKey(id))
|
||||
return LoadedToons[id];
|
||||
else
|
||||
{
|
||||
var dbToon = DBSessions.SessionGet<DBToon>(id);
|
||||
return GetToonByDBToon(dbToon, session);
|
||||
}
|
||||
}
|
||||
|
||||
public static Toon GetDeletedToon(GameAccount account)
|
||||
{
|
||||
var query = DBSessions.SessionQueryWhere<DBToon>(dbt => dbt.DBGameAccount.Id == account.PersistentID && dbt.Deleted);
|
||||
return query.Any() ? GetToonByLowID(query.Last().Id) : null;
|
||||
}
|
||||
|
||||
public static List<Toon> GetToonsForGameAccount(GameAccount account)
|
||||
{
|
||||
var toons = DBSessions.SessionQueryWhere<DBToon>(t => t.DBGameAccount.Id == account.PersistentID).Select(dbt => GetToonByLowID(dbt.Id));
|
||||
return toons.ToList();
|
||||
}
|
||||
|
||||
|
||||
public static int TotalToons
|
||||
{
|
||||
get { return DBSessions.SessionQuery<DBToon>().Count; }
|
||||
}
|
||||
|
||||
|
||||
public static Toon CreateNewToon(string name, int classId, ToonFlags flags, byte level, bool IsHardcore, GameAccount gameAccount, int Season)
|
||||
{
|
||||
var dbGameAccount = DBSessions.SessionQuerySingle<DBGameAccount>(dba => dba.Id == gameAccount.PersistentID);
|
||||
var toonFlags = flags;
|
||||
//if (IsHardcore) toonFlags = toonFlags | ToonFlags.Hardcore;
|
||||
|
||||
var newDBToon = new DBToon
|
||||
{
|
||||
Class = @Toon.GetClassByID(classId),
|
||||
Name = name,
|
||||
/*HashCode = GetUnusedHashCodeForToonName(name),*/
|
||||
Flags = toonFlags,
|
||||
StoneOfPortal = false,
|
||||
isHardcore = IsHardcore,
|
||||
isSeasoned = Season == 0 ? false : true,
|
||||
CreatedSeason = Season,
|
||||
Level = level,
|
||||
ParagonBonuses = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
Experience = 280,
|
||||
CurrentQuestId = 87700,
|
||||
CurrentQuestStepId = -1,
|
||||
CurrentDifficulty = 0,
|
||||
Lore = new byte[0],
|
||||
Stats = "0;0;0;0;0;0",
|
||||
DBGameAccount = dbGameAccount,
|
||||
Cosmetic1 = -1,
|
||||
Cosmetic2 = -1,
|
||||
Cosmetic3 = -1,
|
||||
Cosmetic4 = -1,
|
||||
};
|
||||
|
||||
DBSessions.SessionSave(newDBToon);
|
||||
|
||||
Toon createdToon = GetToonByLowID(newDBToon.Id);
|
||||
|
||||
CreateSkillSet(newDBToon);
|
||||
|
||||
CreateStartEquipment(createdToon, IsHardcore);
|
||||
|
||||
CreateHirelingProfile(createdToon, 1);
|
||||
CreateHirelingProfile(createdToon, 2);
|
||||
CreateHirelingProfile(createdToon, 3);
|
||||
|
||||
LoadedToons.TryAdd(newDBToon.Id, createdToon);
|
||||
return createdToon;
|
||||
}
|
||||
|
||||
private static void CreateSkillSet(DBToon toon)
|
||||
{
|
||||
int[] ActiveSkillsList = Skills.GetAllActiveSkillsByClass(toon.Class).Take(1).ToArray();
|
||||
var ActiveSkills = new DBActiveSkills
|
||||
{
|
||||
DBToon = toon,
|
||||
Skill0 = ActiveSkillsList[0],
|
||||
Skill1 = -1,
|
||||
Skill2 = -1,
|
||||
Skill3 = -1,
|
||||
Skill4 = -1,
|
||||
Skill5 = -1,
|
||||
Rune0 = -1,
|
||||
Rune1 = -1,
|
||||
Rune2 = -1,
|
||||
Rune3 = -1,
|
||||
Rune4 = -1,
|
||||
Rune5 = -1,
|
||||
Passive0 = -1,
|
||||
Passive1 = -1,
|
||||
Passive2 = -1,
|
||||
Passive3 = -1,
|
||||
PotionGBID = -1
|
||||
};
|
||||
DBSessions.SessionSave(ActiveSkills);
|
||||
}
|
||||
|
||||
public static void CreateStartEquipment(Toon toon, bool isHardcore)
|
||||
{
|
||||
DBInventory pants = NewbiePants;
|
||||
pants.DBToon = toon.DBToon;
|
||||
pants.DBGameAccount = toon.GameAccount.DBGameAccount;
|
||||
pants.isHardcore = isHardcore;
|
||||
DBSessions.SessionSave(pants);
|
||||
|
||||
DBInventory armor = NewbieArmor;
|
||||
armor.DBToon = toon.DBToon;
|
||||
armor.DBGameAccount = toon.GameAccount.DBGameAccount;
|
||||
armor.isHardcore = isHardcore;
|
||||
DBSessions.SessionSave(armor);
|
||||
|
||||
DBInventory weapon = new DBInventory();
|
||||
switch (toon.DBToon.Class)
|
||||
{
|
||||
case ToonClass.Barbarian:
|
||||
weapon = NewbieAxe;
|
||||
break;
|
||||
case ToonClass.Crusader:
|
||||
weapon = NewbieFlail;
|
||||
break;
|
||||
case ToonClass.DemonHunter:
|
||||
weapon = NewbieBow;
|
||||
break;
|
||||
case ToonClass.Monk:
|
||||
weapon = NewbieFist;
|
||||
break;
|
||||
case ToonClass.WitchDoctor:
|
||||
weapon = NewbieKnife;
|
||||
break;
|
||||
case ToonClass.Wizard:
|
||||
weapon = NewbieWand;
|
||||
break;
|
||||
case ToonClass.Necromancer:
|
||||
weapon = NewbieNecr;
|
||||
break;
|
||||
default:
|
||||
weapon = NewbieKnife;
|
||||
break;
|
||||
}
|
||||
weapon.DBToon = toon.DBToon;
|
||||
weapon.DBGameAccount = toon.GameAccount.DBGameAccount;
|
||||
weapon.isHardcore = isHardcore;
|
||||
DBSessions.SessionSave(weapon);
|
||||
if (toon.DBToon.Class == ToonClass.Crusader) //add shield
|
||||
{
|
||||
DBInventory shield = new DBInventory();
|
||||
weapon = NewbieShield;
|
||||
weapon.DBToon = toon.DBToon;
|
||||
weapon.DBGameAccount = toon.GameAccount.DBGameAccount;
|
||||
weapon.isHardcore = isHardcore;
|
||||
DBSessions.SessionSave(weapon);
|
||||
}
|
||||
}
|
||||
|
||||
public static void CreateHirelingProfile(Toon toon, int type)
|
||||
{
|
||||
var hireling = new DBHireling();
|
||||
hireling.Class = type;
|
||||
hireling.DBToon = toon.DBToon;
|
||||
hireling.Skill1SNOId = -1;
|
||||
hireling.Skill2SNOId = -1;
|
||||
hireling.Skill3SNOId = -1;
|
||||
hireling.Skill4SNOId = -1;
|
||||
DBSessions.SessionSave(hireling);
|
||||
}
|
||||
|
||||
public static void DeleteToon(Toon toon)
|
||||
{
|
||||
if (toon == null) return;
|
||||
|
||||
if (toon.GameAccount.DBGameAccount.LastPlayedHero != null && toon.GameAccount.DBGameAccount.LastPlayedHero.Id == toon.PersistentID)
|
||||
toon.GameAccount.DBGameAccount.LastPlayedHero = null;
|
||||
|
||||
toon.Deleted = true;
|
||||
Logger.Debug("Deleting toon {0}", toon.PersistentID);
|
||||
}
|
||||
|
||||
public static DBToon CreateFakeDBToon(string name, DBGameAccount gaccount)
|
||||
{
|
||||
int class_seed = FastRandom.Instance.Next(100);
|
||||
int gender_seed = FastRandom.Instance.Next(100);
|
||||
ToonClass class_name = ToonClass.Barbarian;
|
||||
if (class_seed > 20)
|
||||
class_name = ToonClass.Monk;
|
||||
if (class_seed > 40)
|
||||
class_name = ToonClass.DemonHunter;
|
||||
if (class_seed > 60)
|
||||
class_name = ToonClass.WitchDoctor;
|
||||
if (class_seed > 80)
|
||||
class_name = ToonClass.Wizard;
|
||||
DBToon fakeToon = new DBToon
|
||||
{
|
||||
Name = name,
|
||||
Class = class_name,
|
||||
Flags = gender_seed > 50 ? ToonFlags.Male : ToonFlags.Female,
|
||||
Level = 60,
|
||||
Experience = 0,
|
||||
PvERating = 0,
|
||||
isHardcore = false,
|
||||
CurrentAct = 0,
|
||||
CurrentQuestId = -1,
|
||||
CurrentQuestStepId = -1,
|
||||
CurrentDifficulty = 0,
|
||||
DBGameAccount = gaccount,
|
||||
TimePlayed = 0,
|
||||
Stats = "",
|
||||
Lore = new byte[0],
|
||||
Deleted = false,
|
||||
Archieved = false,
|
||||
Cosmetic1 = -1,
|
||||
Cosmetic2 = -1,
|
||||
Cosmetic3 = -1,
|
||||
Cosmetic4 = -1
|
||||
};
|
||||
/*switch (class_name)
|
||||
{
|
||||
case ToonClass.Barbarian:
|
||||
fakeToon.DBActiveSkills = new DBActiveSkills
|
||||
{
|
||||
Skill0 = 78548,
|
||||
Rune0 = 4,
|
||||
Skill1 = 96296,
|
||||
Rune1 = 1,
|
||||
Skill2 = 93409,
|
||||
Rune2 = 4,
|
||||
Skill3 = 93885,
|
||||
Rune3 = 2,
|
||||
Skill4 = 79076,
|
||||
Rune4 = 1,
|
||||
Skill5 = 98878,
|
||||
Rune5 = 2,
|
||||
Passive0 = 205300,
|
||||
Passive1 = 205217,
|
||||
Passive2 = 205707,
|
||||
PotionGBID = -1
|
||||
};
|
||||
break;
|
||||
case ToonClass.Monk:
|
||||
fakeToon.DBActiveSkills = new DBActiveSkills
|
||||
{
|
||||
Skill0 = 95940,
|
||||
Rune0 = 0,
|
||||
Skill1 = 97328,
|
||||
Rune1 = 1,
|
||||
Skill2 = 96215,
|
||||
Rune2 = 1,
|
||||
Skill3 = 96203,
|
||||
Rune3 = 3,
|
||||
Skill4 = 192405,
|
||||
Rune4 = 2,
|
||||
Skill5 = 96694,
|
||||
Rune5 = 0,
|
||||
Passive0 = 209622,
|
||||
Passive1 = 209029,
|
||||
Passive2 = 209104,
|
||||
PotionGBID = -1
|
||||
};
|
||||
break;
|
||||
case ToonClass.DemonHunter:
|
||||
fakeToon.DBActiveSkills = new DBActiveSkills
|
||||
{
|
||||
Skill0 = 77552,
|
||||
Rune0 = 2,
|
||||
Skill1 = 131325,
|
||||
Rune1 = 4,
|
||||
Skill2 = 130830,
|
||||
Rune2 = 4,
|
||||
Skill3 = 111215,
|
||||
Rune3 = 3,
|
||||
Skill4 = 77546,
|
||||
Rune4 = 0,
|
||||
Skill5 = 129214,
|
||||
Rune5 = 0,
|
||||
Passive0 = 211225,
|
||||
Passive1 = 155721,
|
||||
Passive2 = 218385,
|
||||
PotionGBID = -1
|
||||
};
|
||||
break;
|
||||
case ToonClass.WitchDoctor:
|
||||
fakeToon.DBActiveSkills = new DBActiveSkills
|
||||
{
|
||||
Skill0 = 106465,
|
||||
Rune0 = 0,
|
||||
Skill1 = 108506,
|
||||
Rune1 = 1,
|
||||
Skill2 = 69182,
|
||||
Rune2 = 2,
|
||||
Skill3 = 70455,
|
||||
Rune3 = 1,
|
||||
Skill4 = 67567,
|
||||
Rune4 = 1,
|
||||
Skill5 = 67616,
|
||||
Rune5 = 0,
|
||||
Passive0 = 217968,
|
||||
Passive1 = 208568,
|
||||
Passive2 = 208569,
|
||||
PotionGBID = -1
|
||||
};
|
||||
break;
|
||||
case ToonClass.Wizard:
|
||||
fakeToon.DBActiveSkills = new DBActiveSkills
|
||||
{
|
||||
Skill0 = 30668,
|
||||
Rune0 = 4,
|
||||
Skill1 = 91549,
|
||||
Rune1 = 1,
|
||||
Skill2 = 168344,
|
||||
Rune2 = 1,
|
||||
Skill3 = 86991,
|
||||
Rune3 = 0,
|
||||
Skill4 = 30680,
|
||||
Rune4 = 4,
|
||||
Skill5 = 30796,
|
||||
Rune5 = 4,
|
||||
Passive0 = 208468,
|
||||
Passive1 = 208473,
|
||||
Passive2 = 208477,
|
||||
PotionGBID = -1
|
||||
};
|
||||
break;
|
||||
}*/
|
||||
return fakeToon;
|
||||
}
|
||||
}
|
||||
}
|
||||
79
src/DiIiS-NA/BGS-Server/Watchdog.cs
Normal file
79
src/DiIiS-NA/BGS-Server/Watchdog.cs
Normal file
@ -0,0 +1,79 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Schedulers;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
|
||||
namespace DiIiS_NA.LoginServer
|
||||
{
|
||||
public class Watchdog
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger();
|
||||
|
||||
private uint Seconds = 0;
|
||||
|
||||
private List<ScheduledTask> ScheduledTasks = new List<ScheduledTask>();
|
||||
private List<ScheduledTask> TasksToRemove = new List<ScheduledTask>();
|
||||
|
||||
private List<ScheduledEvent> ScheduledEvents = new List<ScheduledEvent>();
|
||||
|
||||
public void Run()
|
||||
{
|
||||
ScheduledEvents.Add(new SpawnDensityRegen());
|
||||
|
||||
while (true)
|
||||
{
|
||||
System.Threading.Thread.Sleep(1000);
|
||||
this.Seconds++;
|
||||
try
|
||||
{
|
||||
lock (this.ScheduledTasks)
|
||||
{
|
||||
foreach (var task in this.TasksToRemove)
|
||||
{
|
||||
if (this.ScheduledTasks.Contains(task))
|
||||
this.ScheduledTasks.Remove(task);
|
||||
}
|
||||
this.TasksToRemove.Clear();
|
||||
|
||||
foreach (var task in this.ScheduledTasks.Where(t => this.Seconds % t.Delay == 0))
|
||||
{
|
||||
try
|
||||
{
|
||||
task.Task.Invoke();
|
||||
}
|
||||
catch
|
||||
{
|
||||
//this.TasksToRemove.Add(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var s_event in this.ScheduledEvents)
|
||||
{
|
||||
if (s_event.TimedOut)
|
||||
s_event.ExecuteEvent();
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
public void AddTask(uint Delay, Action Task)
|
||||
{
|
||||
this.ScheduledTasks.Add(new ScheduledTask { Delay = Delay, Task = Task });
|
||||
}
|
||||
|
||||
public class ScheduledTask
|
||||
{
|
||||
public Action Task;
|
||||
public uint Delay;
|
||||
}
|
||||
}
|
||||
}
|
||||
129
src/DiIiS-NA/Blizzless.csproj
Normal file
129
src/DiIiS-NA/Blizzless.csproj
Normal file
@ -0,0 +1,129 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<RootNamespace>DiIiS_NA</RootNamespace>
|
||||
<Company>DiIiS Team</Company>
|
||||
<Copyright>iEvE</Copyright>
|
||||
<AssemblyVersion>3.0.0.0</AssemblyVersion>
|
||||
<FileVersion>3.0.0.0</FileVersion>
|
||||
<StartupObject>DiIiS_NA.Program</StartupObject>
|
||||
<DebugType>full</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
|
||||
<!-- Native AOT settings -->
|
||||
<RootAllApplicationAssemblies>true</RootAllApplicationAssemblies>
|
||||
<IlcGenerateCompleteTypeMetadata>true</IlcGenerateCompleteTypeMetadata>
|
||||
<IlcGenerateStackTraceData>true</IlcGenerateStackTraceData>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<TrimmerRootAssembly Include="mscorlib" />
|
||||
<TrimmerRootAssembly Include="System.Runtime" />
|
||||
<TrimmerRootAssembly Include="System.Diagnostics.FileVersionInfo" />
|
||||
<TrimmerRootAssembly Include="System.Threading.Thread" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DiIiSNet\BZNET.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="CrystalMpq">
|
||||
<HintPath>libs\CrystalMpq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Gibbed.IO">
|
||||
<HintPath>libs\Gibbed.IO.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Google.ProtocolBuffers">
|
||||
<HintPath>libs\Google.ProtocolBuffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ICSharpCode.SharpZipLib">
|
||||
<HintPath>libs\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Nini">
|
||||
<HintPath>libs\Nini.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PowerCollections">
|
||||
<HintPath>libs\PowerCollections.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="WatsonTcp">
|
||||
<HintPath>libs\WatsonTcp.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Castle.Core" Version="4.4.1" />
|
||||
<PackageReference Include="DotNetty.Buffers" Version="0.6.0" />
|
||||
<PackageReference Include="DotNetty.Codecs" Version="0.6.0" />
|
||||
<PackageReference Include="DotNetty.Codecs.Http" Version="0.6.0" />
|
||||
<PackageReference Include="DotNetty.Common" Version="0.6.0" />
|
||||
<PackageReference Include="DotNetty.Handlers" Version="0.6.0" />
|
||||
<PackageReference Include="DotNetty.Transport" Version="0.6.0" />
|
||||
<PackageReference Include="FluentNHibernate" Version="3.1.0" />
|
||||
<PackageReference Include="ILCompile" Version="1.0.5" />
|
||||
<PackageReference Include="log4net" Version="2.0.12" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.1" />
|
||||
<PackageReference Include="Npgsql" Version="4.0.4" />
|
||||
<PackageReference Include="Skater.exe" Version="7.5.0" />
|
||||
<PackageReference Include="SoapCore" Version="0.9.9.1" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="2c11c0ec90a821ecc3dac6b81db355b2a2ff9f15e1d4f9512f3a96380c980887.achu">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="bnetserver.p12">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="config.ini">
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="database.Account.config">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="database.Worlds.config">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="DataBase\mpqdata.db">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="DataBase\MPQ\Core.mpq">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="DataBase\MPQ\Core1.mpq">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="DataBase\MPQ\Core2.mpq">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="DataBase\MPQ\Core3.mpq">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="DataBase\MPQ\Core4.mpq">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="DiIiS_Battle.net.p12">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
31
src/DiIiS-NA/Core/Config/Config.cs
Normal file
31
src/DiIiS-NA/Core/Config/Config.cs
Normal file
@ -0,0 +1,31 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using Nini.Config;
|
||||
|
||||
namespace DiIiS_NA.Core.Config
|
||||
{
|
||||
public class Config
|
||||
{
|
||||
private readonly IConfig _section;
|
||||
|
||||
public Config(string sectionName)
|
||||
{
|
||||
this._section = ConfigurationManager.Section(sectionName) ?? ConfigurationManager.AddSection(sectionName);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
ConfigurationManager.Save();
|
||||
}
|
||||
|
||||
protected bool GetBoolean(string key, bool defaultValue) { return this._section.GetBoolean(key, defaultValue); }
|
||||
protected double GetDouble(string key, double defaultValue) { return this._section.GetDouble(key, defaultValue); }
|
||||
protected float GetFloat(string key, float defaultValue) { return this._section.GetFloat(key, defaultValue); }
|
||||
protected int GetInt(string key, int defaultValue) { return this._section.GetInt(key, defaultValue); }
|
||||
protected int GetInt(string key, int defaultValue, bool fromAlias) { return this._section.GetInt(key, defaultValue, fromAlias); }
|
||||
protected long GetLong(string key, long defaultValue) { return this._section.GetLong(key, defaultValue); }
|
||||
protected string GetString(string key, string defaultValue) { return this._section.Get(key, defaultValue); }
|
||||
protected string[] GetEntryKeys() { return this._section.GetKeys(); }
|
||||
protected void Set(string key, object value) { this._section.Set(key, value); }
|
||||
}
|
||||
}
|
||||
68
src/DiIiS-NA/Core/Config/ConfigManager.cs
Normal file
68
src/DiIiS-NA/Core/Config/ConfigManager.cs
Normal file
@ -0,0 +1,68 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Helpers.IO;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using Nini.Config;
|
||||
|
||||
namespace DiIiS_NA.Core.Config
|
||||
{
|
||||
public sealed class ConfigurationManager
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger("ConfM");
|
||||
private static readonly IniConfigSource Parser; // the ini parser.
|
||||
private static readonly string ConfigFile;
|
||||
private static bool _fileExists = false; // does the ini file exists?
|
||||
|
||||
static ConfigurationManager()
|
||||
{
|
||||
try
|
||||
{
|
||||
ConfigFile = string.Format("{0}/{1}", FileHelpers.AssemblyRoot, "config.ini"); // the config file's location.
|
||||
Parser = new IniConfigSource(ConfigFile); // see if the file exists by trying to parse it.
|
||||
_fileExists = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Parser = new IniConfigSource(); // initiate a new .ini source.
|
||||
_fileExists = false;
|
||||
Logger.Warn("Error loading settings config.ini, will be using default settings.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// adds aliases so we can use On and Off directives in ini files.
|
||||
Parser.Alias.AddAlias("On", true);
|
||||
Parser.Alias.AddAlias("Off", false);
|
||||
|
||||
// logger level aliases.
|
||||
Parser.Alias.AddAlias("MinimumLevel", Logger.Level.Trace);
|
||||
Parser.Alias.AddAlias("MaximumLevel", Logger.Level.Trace);
|
||||
}
|
||||
|
||||
Parser.ExpandKeyValues();
|
||||
}
|
||||
|
||||
static internal IConfig Section(string section) // Returns the asked config section.
|
||||
{
|
||||
return Parser.Configs[section];
|
||||
}
|
||||
|
||||
static internal IConfig AddSection(string section) // Adds a config section.
|
||||
{
|
||||
return Parser.AddConfig(section);
|
||||
}
|
||||
|
||||
static internal void Save() // Saves the settings.
|
||||
{
|
||||
if (_fileExists) Parser.Save();
|
||||
else
|
||||
{
|
||||
Parser.Save(ConfigFile);
|
||||
_fileExists = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
src/DiIiS-NA/Core/Extensions/ArrayExtensions.cs
Normal file
117
src/DiIiS-NA/Core/Extensions/ArrayExtensions.cs
Normal file
@ -0,0 +1,117 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
|
||||
namespace DiIiS_NA.Core.Extensions
|
||||
{
|
||||
public static class ArrayExtensions
|
||||
{
|
||||
public static IEnumerable<T> EnumerateFrom<T>(this T[] array, int start)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException("array");
|
||||
|
||||
return Enumerate<T>(array, start, array.Length);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Enumerate<T>(this T[] array, int start, int count)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException("array");
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
yield return array[start + i];
|
||||
}
|
||||
|
||||
public static byte[] Append(this byte[] a, byte[] b)
|
||||
{
|
||||
var result = new byte[a.Length + b.Length];
|
||||
|
||||
a.CopyTo(result, 0);
|
||||
b.CopyTo(result, a.Length);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool CompareTo(this byte[] byteArray, byte[] second)
|
||||
{
|
||||
if (byteArray.Length != second.Length)
|
||||
return false;
|
||||
|
||||
return !byteArray.Where((t, i) => second[i] != t).Any();
|
||||
}
|
||||
|
||||
public static string Dump(this byte[] array)
|
||||
{
|
||||
return EnumerableExtensions.Dump(array);
|
||||
}
|
||||
|
||||
public static string ToHexString(this byte[] byteArray)
|
||||
{
|
||||
return byteArray.Aggregate("", (current, b) => current + b.ToString("X2"));
|
||||
}
|
||||
|
||||
public static string ToFormatedHexString(this byte[] byteArray)
|
||||
{
|
||||
return byteArray.Aggregate("", (current, b) => current + " " + b.ToString("X2"));
|
||||
}
|
||||
|
||||
public static byte[] ToByteArray(this string str)
|
||||
{
|
||||
str = str.Replace(" ", String.Empty);
|
||||
|
||||
var res = new byte[str.Length / 2];
|
||||
for (int i = 0; i < res.Length; ++i)
|
||||
{
|
||||
string temp = String.Concat(str[i * 2], str[i * 2 + 1]);
|
||||
res[i] = Convert.ToByte(temp, 16);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static void ForEach(this Array array, Action<Array, int[]> action)
|
||||
{
|
||||
if (array.LongLength == 0) return;
|
||||
ArrayTraverse walker = new ArrayTraverse(array);
|
||||
do action(array, walker.Position);
|
||||
while (walker.Step());
|
||||
}
|
||||
}
|
||||
|
||||
internal class ArrayTraverse
|
||||
{
|
||||
public int[] Position;
|
||||
private int[] maxLengths;
|
||||
|
||||
public ArrayTraverse(Array array)
|
||||
{
|
||||
maxLengths = new int[array.Rank];
|
||||
for (int i = 0; i < array.Rank; ++i)
|
||||
{
|
||||
maxLengths[i] = array.GetLength(i) - 1;
|
||||
}
|
||||
Position = new int[array.Rank];
|
||||
}
|
||||
|
||||
public bool Step()
|
||||
{
|
||||
for (int i = 0; i < Position.Length; ++i)
|
||||
{
|
||||
if (Position[i] < maxLengths[i])
|
||||
{
|
||||
Position[i]++;
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
Position[j] = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/DiIiS-NA/Core/Extensions/BigIntegerExtensions.cs
Normal file
49
src/DiIiS-NA/Core/Extensions/BigIntegerExtensions.cs
Normal file
@ -0,0 +1,49 @@
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Numerics;
|
||||
|
||||
namespace DiIiS_NA.Core.Extensions
|
||||
{
|
||||
public static class BigIntegerExtensions
|
||||
{
|
||||
public static BigInteger ToBigInteger(this byte[] src)
|
||||
{
|
||||
var dst = new byte[src.Length + 1];
|
||||
Array.Copy(src, dst, src.Length);
|
||||
return new BigInteger(dst);
|
||||
}
|
||||
|
||||
public static byte[] ToArray(this BigInteger b)
|
||||
{
|
||||
var result = b.ToByteArray();
|
||||
if (result[result.Length - 1] == 0 && (result.Length % 0x10) != 0)
|
||||
Array.Resize(ref result, result.Length - 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] ToArray(this BigInteger b, int size)
|
||||
{
|
||||
byte[] result = b.ToArray();
|
||||
if (result.Length > size)
|
||||
throw new ArgumentOutOfRangeException("size", size, "must be large enough to convert the BigInteger");
|
||||
|
||||
// If the size is already correct, return the result.
|
||||
if (result.Length == size)
|
||||
return result;
|
||||
|
||||
// Resize the array.
|
||||
int n = size - result.Length;
|
||||
Array.Resize(ref result, size);
|
||||
|
||||
// Fill the extra bytes with 0x00.
|
||||
while (n > 0)
|
||||
{
|
||||
result[size - n] = 0x00;
|
||||
n--;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/DiIiS-NA/Core/Extensions/CodedOutputStream.cs
Normal file
14
src/DiIiS-NA/Core/Extensions/CodedOutputStream.cs
Normal file
@ -0,0 +1,14 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
|
||||
namespace DiIiS_NA.Core.Extensions
|
||||
{
|
||||
public static class CodedOutputStreamExtensions
|
||||
{
|
||||
public static void WriteInt16NoTag(this Google.ProtocolBuffers.CodedOutputStream s, short value)
|
||||
{
|
||||
s.WriteRawBytes(BitConverter.GetBytes(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/DiIiS-NA/Core/Extensions/DateTimeExtensions.cs
Normal file
25
src/DiIiS-NA/Core/Extensions/DateTimeExtensions.cs
Normal file
@ -0,0 +1,25 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
|
||||
namespace DiIiS_NA.Core.Extensions
|
||||
{
|
||||
public static class DateTimeExtensions
|
||||
{
|
||||
public static uint ToUnixTime(this DateTime time)
|
||||
{
|
||||
return (uint)((time.ToUniversalTime().Ticks - 621355968000000000L) / 10000000L);
|
||||
}
|
||||
|
||||
public static ulong ToExtendedEpoch(this DateTime time)
|
||||
{
|
||||
return (ulong)((time.ToUniversalTime().Ticks - 621355968000000000L) / 10L);
|
||||
}
|
||||
|
||||
public static ulong ToBlizzardEpoch(this DateTime time)
|
||||
{
|
||||
TimeSpan diff = time.ToUniversalTime() - DateTime.UnixEpoch;
|
||||
return (ulong)((diff.TotalSeconds - 946695547L) * 1000000000L);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
src/DiIiS-NA/Core/Extensions/EnumerableExtensions.cs
Normal file
65
src/DiIiS-NA/Core/Extensions/EnumerableExtensions.cs
Normal file
@ -0,0 +1,65 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
|
||||
namespace DiIiS_NA.Core.Extensions
|
||||
{
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
public static string HexDump(this IEnumerable<byte> collection)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (byte value in collection)
|
||||
{
|
||||
sb.Append(value.ToString("X2"));
|
||||
sb.Append(' ');
|
||||
}
|
||||
if (sb.Length > 0)
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string ToEncodedString(this IEnumerable<byte> collection, Encoding encoding)
|
||||
{
|
||||
return encoding.GetString(collection.ToArray());
|
||||
}
|
||||
|
||||
public static string Dump(this IEnumerable<byte> collection)
|
||||
{
|
||||
var output = new StringBuilder();
|
||||
var hex = new StringBuilder();
|
||||
var text = new StringBuilder();
|
||||
int i = 0;
|
||||
foreach (byte value in collection)
|
||||
{
|
||||
if (i > 0 && ((i % 16) == 0))
|
||||
{
|
||||
output.Append(hex);
|
||||
output.Append(' ');
|
||||
output.Append(text);
|
||||
output.Append(Environment.NewLine);
|
||||
hex.Clear(); text.Clear();
|
||||
}
|
||||
hex.Append(value.ToString("X2"));
|
||||
hex.Append(' ');
|
||||
text.Append(string.Format("{0}", (char.IsWhiteSpace((char)value) && (char)value != ' ') ? '.' : (char)value)); // prettify text
|
||||
++i;
|
||||
}
|
||||
var hexstring = hex.ToString();
|
||||
if (text.Length < 16)
|
||||
{
|
||||
hexstring = hexstring.PadRight(48); // pad the hex representation in-case it's smaller than a regular 16 value line.
|
||||
}
|
||||
output.Append(hexstring);
|
||||
output.Append(' ');
|
||||
output.Append(text);
|
||||
return output.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
212
src/DiIiS-NA/Core/Extensions/IMessageExtensions.cs
Normal file
212
src/DiIiS-NA/Core/Extensions/IMessageExtensions.cs
Normal file
@ -0,0 +1,212 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers;
|
||||
//Blizzless Project 2022
|
||||
using Google.ProtocolBuffers.Descriptors;
|
||||
|
||||
namespace DiIiS_NA.Core.Extensions
|
||||
{
|
||||
static class IMessageExtensions
|
||||
{
|
||||
|
||||
static void AppendLevel(StringBuilder result, int level)
|
||||
{
|
||||
for (int i = 0; i < level; i++) result.Append(' ');
|
||||
}
|
||||
|
||||
static void AppendLine(StringBuilder result, int level, string str) { for (int i = 0; i < level; i++) result.Append(' '); result.AppendLine(str); }
|
||||
static char ToHexChar(byte b)
|
||||
{
|
||||
return (b >= 0x20 && b < 0x80) ? (char)b : '.';
|
||||
}
|
||||
|
||||
static void AppendHexdump(StringBuilder b, int level, byte[] buffer)
|
||||
{
|
||||
b.AppendLine();
|
||||
AppendLine(b, level++, "[0x" + buffer.Length.ToString("X8") + "] {");
|
||||
|
||||
int length = Math.Min(buffer.Length, 0xFFFF);
|
||||
for (int i = 0; i < length; i += 16)
|
||||
{
|
||||
|
||||
AppendLevel(b, level);
|
||||
|
||||
b.AppendFormat("{0:X4} ", i);
|
||||
for (int n = 0; n < 8; n++)
|
||||
{
|
||||
int o = i + n;
|
||||
if (o < length)
|
||||
b.AppendFormat("{0:X2} ", buffer[o]);
|
||||
else
|
||||
b.Append(" ");
|
||||
}
|
||||
b.Append(" ");
|
||||
for (int n = 0; n < 8; n++)
|
||||
{
|
||||
int o = i + n + 8;
|
||||
if (o < length)
|
||||
b.AppendFormat("{0:X2} ", buffer[o]);
|
||||
else
|
||||
b.Append(" ");
|
||||
}
|
||||
b.Append(" ");
|
||||
|
||||
for (int n = 0; n < 8; n++)
|
||||
{
|
||||
int o = i + n;
|
||||
if (o < length)
|
||||
b.AppendFormat("{0}", ToHexChar(buffer[o]));
|
||||
else
|
||||
b.Append(" ");
|
||||
}
|
||||
b.Append(" ");
|
||||
for (int n = 0; n < 8; n++)
|
||||
{
|
||||
int o = i + n + 8;
|
||||
if (o < length)
|
||||
b.AppendFormat("{0}", ToHexChar(buffer[o]));
|
||||
else
|
||||
b.Append(" ");
|
||||
}
|
||||
b.AppendLine();
|
||||
}
|
||||
AppendLine(b, --level, "}");
|
||||
}
|
||||
|
||||
static void AppendFieldValue(StringBuilder result, int level, FieldType type, object value)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case FieldType.Bool:
|
||||
result.AppendLine((bool)value ? "true" : "false");
|
||||
break;
|
||||
case FieldType.Bytes:
|
||||
result.AppendLine(EscapeBytes((ByteString)value));
|
||||
AppendHexdump(result, level, ((ByteString)value).ToByteArray());
|
||||
break;
|
||||
case FieldType.Double:
|
||||
result.AppendLine(((double)value).ToString("G"));
|
||||
break;
|
||||
case FieldType.Float:
|
||||
result.AppendLine(((float)value).ToString("G"));
|
||||
break;
|
||||
case FieldType.Int32:
|
||||
case FieldType.SFixed32:
|
||||
case FieldType.SInt32:
|
||||
result.AppendLine(value.ToString());
|
||||
break;
|
||||
case FieldType.Int64:
|
||||
case FieldType.SFixed64:
|
||||
case FieldType.SInt64:
|
||||
result.AppendLine("0x" + ((long)value).ToString("X16") + "l (" + ((long)value) + ")");
|
||||
break;
|
||||
case FieldType.Fixed32:
|
||||
case FieldType.UInt32:
|
||||
result.AppendLine("0x" + ((uint)value).ToString("X8") + "u (" + ((uint)value) + ")");
|
||||
break;
|
||||
case FieldType.Fixed64:
|
||||
case FieldType.UInt64:
|
||||
result.AppendLine("0x" + ((ulong)value).ToString("X16") + "ul (" + ((ulong)value) + ")");
|
||||
break;
|
||||
case FieldType.Message:
|
||||
result.AppendLine();
|
||||
AppendMessage(result, level, (IMessage)value);
|
||||
break;
|
||||
case FieldType.Enum:
|
||||
{
|
||||
EnumValueDescriptor e = (EnumValueDescriptor)value;
|
||||
|
||||
result.AppendLine(e.Name);
|
||||
}
|
||||
break;
|
||||
case FieldType.String:
|
||||
result.AppendLine(value.ToString());
|
||||
break;
|
||||
case FieldType.Group:
|
||||
default:
|
||||
throw new Exception("Unhandled FieldType");
|
||||
}
|
||||
}
|
||||
static void AppendField(StringBuilder result, int level, FieldDescriptor fieldDesc, object value)
|
||||
{
|
||||
if (value == null)
|
||||
return;
|
||||
if (fieldDesc.IsRepeated)
|
||||
{
|
||||
var e = ((System.Collections.IEnumerable)value).GetEnumerator();
|
||||
while (e.MoveNext())
|
||||
{
|
||||
AppendLevel(result, level);
|
||||
result.Append(fieldDesc.Name);
|
||||
result.Append(": ");
|
||||
AppendFieldValue(result, level, fieldDesc.FieldType, e.Current);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendLevel(result, level);
|
||||
result.Append(fieldDesc.Name);
|
||||
result.Append(": ");
|
||||
AppendFieldValue(result, level, fieldDesc.FieldType, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void AppendMessage(StringBuilder result, int level, IMessage msg)
|
||||
{
|
||||
AppendLine(result, level++, "{");
|
||||
var fields = msg.AllFields;
|
||||
foreach (var pair in fields)
|
||||
AppendField(result, level, pair.Key, pair.Value);
|
||||
AppendLine(result, --level, "}");
|
||||
}
|
||||
|
||||
public static string AsText(this IMessage msg)
|
||||
{
|
||||
var msgDesc = msg.DescriptorForType;
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.AppendLine(msgDesc.FullName);
|
||||
AppendMessage(result, 0, msg);
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
static String EscapeBytes(ByteString input)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder(input.Length);
|
||||
foreach (byte b in input)
|
||||
{
|
||||
switch (b)
|
||||
{
|
||||
// C# does not use \a or \v
|
||||
case 0x07: builder.Append("\\a"); break;
|
||||
case (byte)'\b': builder.Append("\\b"); break;
|
||||
case (byte)'\f': builder.Append("\\f"); break;
|
||||
case (byte)'\n': builder.Append("\\n"); break;
|
||||
case (byte)'\r': builder.Append("\\r"); break;
|
||||
case (byte)'\t': builder.Append("\\t"); break;
|
||||
case 0x0b: builder.Append("\\v"); break;
|
||||
case (byte)'\\': builder.Append("\\\\"); break;
|
||||
case (byte)'\'': builder.Append("\\\'"); break;
|
||||
case (byte)'"': builder.Append("\\\""); break;
|
||||
default:
|
||||
if (b >= 0x20 && b < 128)
|
||||
{
|
||||
builder.Append((char)b);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append('\\');
|
||||
builder.Append((char)('0' + ((b >> 6) & 3)));
|
||||
builder.Append((char)('0' + ((b >> 3) & 7)));
|
||||
builder.Append((char)('0' + (b & 7)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/DiIiS-NA/Core/Extensions/ListExtensions.cs
Normal file
29
src/DiIiS-NA/Core/Extensions/ListExtensions.cs
Normal file
@ -0,0 +1,29 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DiIiS_NA.Core.Extensions
|
||||
{
|
||||
public static class ListExtensions
|
||||
{
|
||||
public static bool ContainsAtLeastOne<T>(this List<T> list1, List<T> list2)
|
||||
{
|
||||
foreach (T m in list2)
|
||||
{
|
||||
if (list1.Contains(m))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ContainsAtLeastOne<T>(this List<T> list, T[] array)
|
||||
{
|
||||
foreach (T m in array)
|
||||
{
|
||||
if (list.Contains(m))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/DiIiS-NA/Core/Extensions/ObjectExtensions.cs
Normal file
24
src/DiIiS-NA/Core/Extensions/ObjectExtensions.cs
Normal file
@ -0,0 +1,24 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
//Blizzless Project 2022
|
||||
using System.IO;
|
||||
|
||||
namespace DiIiS_NA.Core.Extensions
|
||||
{
|
||||
public static class DeepCopy
|
||||
{
|
||||
public static T DeepClone<T>(T obj)
|
||||
{
|
||||
//Blizzless Project 2022
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
var formatter = new BinaryFormatter();
|
||||
formatter.Serialize(ms, obj);
|
||||
ms.Position = 0;
|
||||
|
||||
return (T)formatter.Deserialize(ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
63
src/DiIiS-NA/Core/Extensions/StringExtensions.cs
Normal file
63
src/DiIiS-NA/Core/Extensions/StringExtensions.cs
Normal file
@ -0,0 +1,63 @@
|
||||
//Blizzless Project 2022
|
||||
namespace DiIiS_NA.Core.Extensions
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static string ZipCompress(this string value)
|
||||
{
|
||||
byte[] byteArray = new byte[value.Length];
|
||||
int indexBA = 0;
|
||||
foreach (char item in value.ToCharArray())
|
||||
{
|
||||
byteArray[indexBA++] = (byte)item;
|
||||
}
|
||||
|
||||
System.IO.MemoryStream ms = new System.IO.MemoryStream();
|
||||
System.IO.Compression.GZipStream sw = new System.IO.Compression.GZipStream(ms,
|
||||
System.IO.Compression.CompressionMode.Compress);
|
||||
|
||||
sw.Write(byteArray, 0, byteArray.Length);
|
||||
sw.Close();
|
||||
|
||||
byteArray = ms.ToArray();
|
||||
System.Text.StringBuilder sB = new System.Text.StringBuilder(byteArray.Length);
|
||||
foreach (byte item in byteArray)
|
||||
{
|
||||
sB.Append((char)item);
|
||||
}
|
||||
ms.Close();
|
||||
sw.Dispose();
|
||||
ms.Dispose();
|
||||
return sB.ToString();
|
||||
}
|
||||
|
||||
public static string UnZipCompress(this string value)
|
||||
{
|
||||
byte[] byteArray = new byte[value.Length];
|
||||
int indexBA = 0;
|
||||
foreach (char item in value.ToCharArray())
|
||||
{
|
||||
byteArray[indexBA++] = (byte)item;
|
||||
}
|
||||
|
||||
System.IO.MemoryStream ms = new System.IO.MemoryStream(byteArray);
|
||||
System.IO.Compression.GZipStream sr = new System.IO.Compression.GZipStream(ms,
|
||||
System.IO.Compression.CompressionMode.Decompress);
|
||||
|
||||
byteArray = new byte[byteArray.Length];
|
||||
|
||||
int rByte = sr.Read(byteArray, 0, byteArray.Length);
|
||||
|
||||
System.Text.StringBuilder sB = new System.Text.StringBuilder(rByte);
|
||||
for (int i = 0; i < rByte; i++)
|
||||
{
|
||||
sB.Append((char)byteArray[i]);
|
||||
}
|
||||
sr.Close();
|
||||
ms.Close();
|
||||
sr.Dispose();
|
||||
ms.Dispose();
|
||||
return sB.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
82
src/DiIiS-NA/Core/Extensions/Utilities.cs
Normal file
82
src/DiIiS-NA/Core/Extensions/Utilities.cs
Normal file
@ -0,0 +1,82 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DiIiS_NA.Core.Extensions
|
||||
{
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Globalization;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
|
||||
public static class Utilities
|
||||
{
|
||||
public static string BinToHex(byte[] bin)
|
||||
{
|
||||
string str = "";
|
||||
for (int i = 0; i < bin.Length; i++)
|
||||
{
|
||||
str = str + bin[i].ToString("X2");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public static byte[] HexToBin(string hexString)
|
||||
{
|
||||
hexString = hexString.ToLower().Replace(" ", "");
|
||||
int num = hexString.Length / 2;
|
||||
byte[] buffer = new byte[num];
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
buffer[i] = (byte)int.Parse(hexString.Substring(i * 2, 2), NumberStyles.HexNumber);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static void MemSet(ref byte[] buffer, byte byteToSet, int size)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
buffer[i] = byteToSet;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SafeMemCpy(ref byte[] dest, int startPos, byte[] src, int len)
|
||||
{
|
||||
for (int i = startPos; i < (len + startPos); i++)
|
||||
{
|
||||
dest[i] = src[i - startPos];
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] StrToByteArray(string str)
|
||||
{
|
||||
ASCIIEncoding encoding = new ASCIIEncoding();
|
||||
return encoding.GetBytes(str);
|
||||
}
|
||||
|
||||
public static string ToHexDump(byte[] byteArray)
|
||||
{
|
||||
string str = string.Empty;
|
||||
for (int i = 0; i < byteArray.Length; i++)
|
||||
{
|
||||
if ((i > 0) && ((i % 0x10) == 0))
|
||||
{
|
||||
str = str + Environment.NewLine;
|
||||
}
|
||||
str = str + byteArray[i].ToString("X2") + " ";
|
||||
}
|
||||
return (str + Environment.NewLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/DiIiS-NA/Core/Helpers/Hash/StringHashHelper.cs
Normal file
35
src/DiIiS-NA/Core/Helpers/Hash/StringHashHelper.cs
Normal file
@ -0,0 +1,35 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
|
||||
namespace DiIiS_NA.Core.Helpers.Hash
|
||||
{
|
||||
public class StringHashHelper
|
||||
{
|
||||
public static uint HashIdentity(string input)
|
||||
{
|
||||
var bytes = Encoding.ASCII.GetBytes(input);
|
||||
return bytes.Aggregate(0x811C9DC5, (current, t) => 0x1000193 * (t ^ current));
|
||||
}
|
||||
|
||||
public static int HashItemName(string input)
|
||||
{
|
||||
int hash = 0;
|
||||
input = input.ToLower();
|
||||
for (int i = 0; i < input.Length; ++i)
|
||||
hash = (hash << 5) + hash + input[i];
|
||||
return hash;
|
||||
}
|
||||
|
||||
public static int HashNormal(string input)
|
||||
{
|
||||
int hash = 0;
|
||||
for (int i = 0; i < input.Length; ++i)
|
||||
hash = (hash << 5) + hash + input[i];
|
||||
return hash;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
45
src/DiIiS-NA/Core/Helpers/IO/FileHelpers.cs
Normal file
45
src/DiIiS-NA/Core/Helpers/IO/FileHelpers.cs
Normal file
@ -0,0 +1,45 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.IO;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Reflection;
|
||||
|
||||
namespace DiIiS_NA.Core.Helpers.IO
|
||||
{
|
||||
public static class FileHelpers
|
||||
{
|
||||
public static string AssemblyRoot
|
||||
{
|
||||
get { return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); }
|
||||
}
|
||||
|
||||
public static List<string> GetFilesByExtensionRecursive(string directory, string fileExtension)
|
||||
{
|
||||
var files = new List<string>();
|
||||
var stack = new Stack<string>();
|
||||
|
||||
stack.Push(directory);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var topDir = stack.Pop();
|
||||
var dirInfo = new DirectoryInfo(topDir);
|
||||
|
||||
files.AddRange((from fileInfo in dirInfo.GetFiles()
|
||||
where string.Compare(fileInfo.Extension, fileExtension, System.StringComparison.OrdinalIgnoreCase) == 0
|
||||
select topDir + "/" + fileInfo.Name).ToList());
|
||||
|
||||
foreach (var dir in Directory.GetDirectories(topDir))
|
||||
{
|
||||
stack.Push(dir);
|
||||
}
|
||||
}
|
||||
|
||||
return files.Select(file => file.Replace("\\", "/")).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
216
src/DiIiS-NA/Core/Helpers/Math/FastRandom.cs
Normal file
216
src/DiIiS-NA/Core/Helpers/Math/FastRandom.cs
Normal file
@ -0,0 +1,216 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
|
||||
namespace DiIiS_NA.Core.Helpers.Math
|
||||
{
|
||||
public class FastRandom
|
||||
{
|
||||
#region Static Fields
|
||||
|
||||
private static readonly FastRandom __seedRng = new FastRandom((int)Environment.TickCount);
|
||||
|
||||
public static readonly FastRandom Instance = new FastRandom();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Instance Fields
|
||||
|
||||
const double REAL_UNIT_INT = 1.0 / ((double)int.MaxValue + 1.0);
|
||||
const double REAL_UNIT_UINT = 1.0 / ((double)uint.MaxValue + 1.0);
|
||||
const uint Y = 842502087, Z = 3579807591, W = 273326509;
|
||||
|
||||
uint _x, _y, _z, _w;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public FastRandom()
|
||||
{
|
||||
Reinitialise(__seedRng.NextInt());
|
||||
}
|
||||
|
||||
public FastRandom(int seed)
|
||||
{
|
||||
Reinitialise(seed);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods [Reinitialisation]
|
||||
|
||||
public void Reinitialise(int seed)
|
||||
{
|
||||
_x = (uint)seed;
|
||||
_y = Y;
|
||||
_z = Z;
|
||||
_w = W;
|
||||
|
||||
_bitBuffer = 0;
|
||||
_bitMask = 1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods [System.Random functionally equivalent methods]
|
||||
|
||||
public int Next()
|
||||
{
|
||||
uint t = _x ^ (_x << 11);
|
||||
_x = _y; _y = _z; _z = _w;
|
||||
_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
uint rtn = _w & 0x7FFFFFFF;
|
||||
if (rtn == 0x7FFFFFFF)
|
||||
{
|
||||
return Next();
|
||||
}
|
||||
return (int)rtn;
|
||||
}
|
||||
|
||||
public int Next(int upperBound)
|
||||
{
|
||||
if (upperBound < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=0");
|
||||
}
|
||||
|
||||
uint t = _x ^ (_x << 11);
|
||||
_x = _y; _y = _z; _z = _w;
|
||||
|
||||
return (int)((REAL_UNIT_INT * (int)(0x7FFFFFFF & (_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8))))) * upperBound);
|
||||
}
|
||||
|
||||
public int Next(int lowerBound, int upperBound)
|
||||
{
|
||||
if (lowerBound > upperBound)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=lowerBound");
|
||||
}
|
||||
|
||||
uint t = _x ^ (_x << 11);
|
||||
_x = _y; _y = _z; _z = _w;
|
||||
|
||||
int range = upperBound - lowerBound;
|
||||
if (range < 0)
|
||||
{
|
||||
return lowerBound + (int)((REAL_UNIT_UINT * (double)(_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8)))) * (double)((long)upperBound - (long)lowerBound));
|
||||
}
|
||||
|
||||
return lowerBound + (int)((REAL_UNIT_INT * (double)(int)(0x7FFFFFFF & (_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8))))) * (double)range);
|
||||
}
|
||||
|
||||
public double NextDouble()
|
||||
{
|
||||
uint t = _x ^ (_x << 11);
|
||||
_x = _y; _y = _z; _z = _w;
|
||||
|
||||
return REAL_UNIT_INT * (int)(0x7FFFFFFF & (_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8))));
|
||||
}
|
||||
|
||||
public double NextDouble(double lowerBound, double upperBound)
|
||||
{
|
||||
return NextDouble() * (upperBound - lowerBound) + lowerBound;
|
||||
}
|
||||
|
||||
public void NextBytes(byte[] buffer)
|
||||
{
|
||||
uint x = this._x, y = this._y, z = this._z, w = this._w;
|
||||
int i = 0;
|
||||
uint t;
|
||||
for (int bound = buffer.Length - 3; i < bound;)
|
||||
{
|
||||
t = x ^ (x << 11);
|
||||
x = y; y = z; z = w;
|
||||
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
buffer[i++] = (byte)w;
|
||||
buffer[i++] = (byte)(w >> 8);
|
||||
buffer[i++] = (byte)(w >> 16);
|
||||
buffer[i++] = (byte)(w >> 24);
|
||||
}
|
||||
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
t = x ^ (x << 11);
|
||||
x = y; y = z; z = w;
|
||||
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
buffer[i++] = (byte)w;
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i++] = (byte)(w >> 8);
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i++] = (byte)(w >> 16);
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i] = (byte)(w >> 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this._x = x; this._y = y; this._z = z; this._w = w;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods [Methods not present on System.Random]
|
||||
|
||||
public uint NextUInt()
|
||||
{
|
||||
uint t = _x ^ (_x << 11);
|
||||
_x = _y; _y = _z; _z = _w;
|
||||
return _w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8));
|
||||
}
|
||||
|
||||
public int NextInt()
|
||||
{
|
||||
uint t = _x ^ (_x << 11);
|
||||
_x = _y; _y = _z; _z = _w;
|
||||
return (int)(0x7FFFFFFF & (_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8))));
|
||||
}
|
||||
|
||||
uint _bitBuffer;
|
||||
uint _bitMask;
|
||||
|
||||
public bool NextBool()
|
||||
{
|
||||
if (0 == _bitMask)
|
||||
{
|
||||
uint t = _x ^ (_x << 11);
|
||||
_x = _y; _y = _z; _z = _w;
|
||||
_bitBuffer = _w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
_bitMask = 0x80000000;
|
||||
return (_bitBuffer & _bitMask) == 0;
|
||||
}
|
||||
|
||||
return (_bitBuffer & (_bitMask >>= 1)) == 0;
|
||||
}
|
||||
|
||||
uint _byteBuffer;
|
||||
byte _byteBufferState;
|
||||
|
||||
public byte NextByte()
|
||||
{
|
||||
if (0 == _byteBufferState)
|
||||
{
|
||||
uint t = _x ^ (_x << 11);
|
||||
_x = _y; _y = _z; _z = _w;
|
||||
_byteBuffer = _w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8));
|
||||
_byteBufferState = 0x4;
|
||||
return (byte)_byteBuffer;
|
||||
}
|
||||
_byteBufferState >>= 1;
|
||||
return (byte)(_byteBuffer >>= 1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
116
src/DiIiS-NA/Core/Helpers/Math/RandomHelper.cs
Normal file
116
src/DiIiS-NA/Core/Helpers/Math/RandomHelper.cs
Normal file
@ -0,0 +1,116 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
|
||||
namespace DiIiS_NA.Core.Helpers.Math
|
||||
{
|
||||
public class RandomHelper
|
||||
{
|
||||
private readonly static Random _random;
|
||||
|
||||
static RandomHelper()
|
||||
{
|
||||
_random = new Random();
|
||||
}
|
||||
|
||||
public static int Next()
|
||||
{
|
||||
return _random.Next();
|
||||
}
|
||||
|
||||
public static int Next(Int32 maxValue)
|
||||
{
|
||||
return _random.Next(maxValue);
|
||||
}
|
||||
|
||||
public static int Next(Int32 minValue, Int32 maxValue)
|
||||
{
|
||||
return _random.Next(minValue, maxValue);
|
||||
}
|
||||
|
||||
public static void NextBytes(byte[] buffer)
|
||||
{
|
||||
_random.NextBytes(buffer);
|
||||
}
|
||||
|
||||
public static double NextDouble()
|
||||
{
|
||||
return _random.NextDouble();
|
||||
}
|
||||
|
||||
/*IEnumerable<TValue>*/
|
||||
public static TValue RandomValue<TKey, TValue>(IDictionary<TKey, TValue> dictionary)
|
||||
{
|
||||
List<TValue> values = Enumerable.ToList(dictionary.Values);
|
||||
int size = dictionary.Count;
|
||||
/*while (true)
|
||||
{
|
||||
yield return values[_random.Next(size)];
|
||||
}*/
|
||||
return values[_random.Next(size)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Picks a random item from a list
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="list"></param>
|
||||
/// <param name="probability">A function that assigns each item a probability. If the probabilities dont sum up to 1, they are normalized</param>
|
||||
/// <returns></returns>
|
||||
public static T RandomItem<T>(IEnumerable<T> list, Func<T, float> probability)
|
||||
{
|
||||
int cumulative = (int)list.Select(x => probability(x)).Sum();
|
||||
|
||||
int randomRoll = RandomHelper.Next(cumulative);
|
||||
float cumulativePercentages = 0;
|
||||
|
||||
foreach (T element in list)
|
||||
{
|
||||
cumulativePercentages += probability(element);
|
||||
if (cumulativePercentages > randomRoll)
|
||||
return element;
|
||||
}
|
||||
|
||||
return list.First();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ItemRandomHelper
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.CreateLogger("RH");
|
||||
uint a;
|
||||
uint b;
|
||||
public ItemRandomHelper(int seed)
|
||||
{
|
||||
a = (uint)seed;
|
||||
b = 666;
|
||||
}
|
||||
|
||||
public void ReinitSeed()
|
||||
{
|
||||
b = 666;
|
||||
}
|
||||
|
||||
public uint Next()
|
||||
{
|
||||
ulong temp = 1791398085UL * (ulong)a + (ulong)b;
|
||||
a = (uint)temp;
|
||||
b = (uint)(temp >> 32);
|
||||
|
||||
//Logger.Debug("Next(): a {0}, b {1}", a, b);
|
||||
return (uint)a;
|
||||
}
|
||||
|
||||
public float Next(float min, float max)
|
||||
{
|
||||
return min + (Next() % (uint)(max - min + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
69
src/DiIiS-NA/Core/Logging/ConsoleTarget.cs
Normal file
69
src/DiIiS-NA/Core/Logging/ConsoleTarget.cs
Normal file
@ -0,0 +1,69 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
|
||||
namespace DiIiS_NA.Core.Logging
|
||||
{
|
||||
public class ConsoleTarget : LogTarget
|
||||
{
|
||||
/// <param name="minLevel">Minimum level of messages to emit</param>
|
||||
/// <param name="maxLevel">Maximum level of messages to emit</param>
|
||||
/// <param name="includeTimeStamps">Include timestamps in log?</param>
|
||||
public ConsoleTarget(Logger.Level minLevel, Logger.Level maxLevel, bool includeTimeStamps)
|
||||
{
|
||||
MinimumLevel = minLevel;
|
||||
MaximumLevel = maxLevel;
|
||||
this.IncludeTimeStamps = includeTimeStamps;
|
||||
}
|
||||
|
||||
/// <param name="level">Log level.</param>
|
||||
/// <param name="logger">Source of the log message.</param>
|
||||
/// <param name="message">Log message.</param>
|
||||
public override void LogMessage(Logger.Level level, string logger, string message)
|
||||
{
|
||||
var timeStamp = this.IncludeTimeStamps ? "[" + DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.fff") + "] " : "";
|
||||
SetConsoleForegroundColor(level);
|
||||
Console.WriteLine(string.Format("{0}[{1}] [{2}]: {3}", timeStamp, level.ToString().PadLeft(5), logger, message));
|
||||
}
|
||||
|
||||
/// <param name="level">Log level.</param>
|
||||
/// <param name="logger">Source of the log message.</param>
|
||||
/// <param name="message">Log message.</param>
|
||||
/// <param name="exception">Exception to be included with log message.</param>
|
||||
public override void LogException(Logger.Level level, string logger, string message, Exception exception)
|
||||
{
|
||||
var timeStamp = this.IncludeTimeStamps ? "[" + DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.fff") + "] " : "";
|
||||
SetConsoleForegroundColor(level);
|
||||
Console.WriteLine(string.Format("{0}[{1}] [{2}]: {3} - [Exception] {4}", timeStamp, level.ToString().PadLeft(5), logger, message, exception));
|
||||
}
|
||||
|
||||
/// <param name="level"></param>
|
||||
private static void SetConsoleForegroundColor(Logger.Level level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case Logger.Level.Trace:
|
||||
case Logger.Level.PacketDump:
|
||||
Console.ForegroundColor = ConsoleColor.DarkGray;
|
||||
break;
|
||||
case Logger.Level.Debug:
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
break;
|
||||
case Logger.Level.Info:
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
break;
|
||||
case Logger.Level.Warn:
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
break;
|
||||
case Logger.Level.Error:
|
||||
Console.ForegroundColor = ConsoleColor.Magenta;
|
||||
break;
|
||||
case Logger.Level.Fatal:
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
147
src/DiIiS-NA/Core/Logging/FileTarget.cs
Normal file
147
src/DiIiS-NA/Core/Logging/FileTarget.cs
Normal file
@ -0,0 +1,147 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Concurrent;
|
||||
//Blizzless Project 2022
|
||||
using System.IO;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading;
|
||||
|
||||
namespace DiIiS_NA.Core.Logging
|
||||
{
|
||||
public class FileTarget : LogTarget, IDisposable
|
||||
{
|
||||
private readonly string _fileName;
|
||||
private readonly string _filePath;
|
||||
private readonly string _fileTimestamp;
|
||||
private FileStream _fileStream;
|
||||
private StreamWriter _logStream;
|
||||
private int _fileIndex;
|
||||
private ConcurrentQueue<Action> TaskQueue;
|
||||
private Thread LoggerThread;
|
||||
|
||||
/// <param name="fileName">Filename of the logfile.</param>
|
||||
/// <param name="minLevel">Minimum level of messages to emit</param>
|
||||
/// <param name="maxLevel">Maximum level of messages to emit</param>
|
||||
/// <param name="includeTimeStamps">Include timestamps in log?</param>
|
||||
/// <param name="reset">Reset log file on application startup?</param>
|
||||
public FileTarget(string fileName, Logger.Level minLevel, Logger.Level maxLevel, bool includeTimeStamps, bool reset = false)
|
||||
{
|
||||
this.MinimumLevel = minLevel;
|
||||
this.MaximumLevel = maxLevel;
|
||||
this.IncludeTimeStamps = includeTimeStamps;
|
||||
this._fileName = fileName;
|
||||
this._fileTimestamp = DateTime.Now.ToString("yyyyMMdd_HHmm");
|
||||
this._filePath = string.Format("{0}/{1}/{2}", LogConfig.Instance.LoggingRoot, this._fileTimestamp, _fileName);
|
||||
this._fileIndex = 0;
|
||||
|
||||
if (!Directory.Exists(LogConfig.Instance.LoggingRoot)) // create logging directory if it does not exist yet.
|
||||
Directory.CreateDirectory(LogConfig.Instance.LoggingRoot);
|
||||
|
||||
if (!Directory.Exists(string.Format("{0}/{1}", LogConfig.Instance.LoggingRoot, this._fileTimestamp))) // create logging directory if it does not exist yet.
|
||||
Directory.CreateDirectory(string.Format("{0}/{1}", LogConfig.Instance.LoggingRoot, this._fileTimestamp));
|
||||
|
||||
this._fileStream = new FileStream(_filePath, reset ? FileMode.Create : FileMode.Append, FileAccess.Write, FileShare.Read); // init the file stream.
|
||||
this._logStream = new StreamWriter(this._fileStream) { AutoFlush = true }; // init the stream writer.
|
||||
this.TaskQueue = new ConcurrentQueue<Action>();
|
||||
this.LoggerThread = new Thread(this.CheckQueue) { Name = "Logger", IsBackground = true };
|
||||
this.LoggerThread.Start();
|
||||
}
|
||||
|
||||
public void CheckQueue()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Action action = null;
|
||||
if (this.TaskQueue.TryDequeue(out action))
|
||||
action.Invoke();
|
||||
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="level">Log level.</param>
|
||||
/// <param name="logger">Source of the log message.</param>
|
||||
/// <param name="message">Log message.</param>
|
||||
public override void LogMessage(Logger.Level level, string logger, string message)
|
||||
{
|
||||
this.TaskQueue.Enqueue(() =>
|
||||
{
|
||||
var timeStamp = this.IncludeTimeStamps ? "[" + DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.fff") + "] " : "";
|
||||
if (!this._disposed) // make sure we're not disposed.
|
||||
{
|
||||
/*
|
||||
if (this._fileStream.Length >= 20971520) //20 MB limit
|
||||
{
|
||||
//System.IO.File.Move(this._filePath, string.Format("{0}.{1}", this._filePath, this._fileIndex));
|
||||
//this._fileIndex++;
|
||||
this._fileStream = new FileStream(_filePath, FileMode.Create, FileAccess.Write, FileShare.Read); // init the file stream.
|
||||
this._logStream = new StreamWriter(this._fileStream) { AutoFlush = true }; // init the stream writer.
|
||||
}
|
||||
//*/
|
||||
if (level > Logger.Level.ChatMessage)
|
||||
this._logStream.WriteLine(string.Format("{0}[{1}] [{2}]: {3}", timeStamp, level.ToString().PadLeft(5), logger, message));
|
||||
else
|
||||
this._logStream.WriteLine(string.Format("{0}{1}", timeStamp, message));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <param name="level">Log level.</param>
|
||||
/// <param name="logger">Source of the log message.</param>
|
||||
/// <param name="message">Log message.</param>
|
||||
/// <param name="exception">Exception to be included with log message.</param>
|
||||
public override void LogException(Logger.Level level, string logger, string message, Exception exception)
|
||||
{
|
||||
this.TaskQueue.Enqueue(() =>
|
||||
{
|
||||
var timeStamp = this.IncludeTimeStamps ? "[" + DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.fff") + "] " : "";
|
||||
if (!this._disposed) // make sure we're not disposed.
|
||||
{
|
||||
if (this._fileStream.Length >= 20971520) //20 MB limit
|
||||
{
|
||||
System.IO.File.Move(this._filePath, string.Format("{0}.{1}", this._filePath, this._fileIndex));
|
||||
this._fileIndex++;
|
||||
this._fileStream = new FileStream(_filePath, FileMode.Create, FileAccess.Write, FileShare.Read); // init the file stream.
|
||||
this._logStream = new StreamWriter(this._fileStream) { AutoFlush = true }; // init the stream writer.
|
||||
}
|
||||
this._logStream.WriteLine(string.Format("{0}[{1}] [{2}]: {3} - [Exception] {4}", timeStamp, level.ToString().PadLeft(5), logger, message, exception));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#region de-ctor
|
||||
|
||||
// IDisposable pattern: http://msdn.microsoft.com/en-us/library/fs2xkftw%28VS.80%29.aspx
|
||||
|
||||
private bool _disposed = false;
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this); // Take object out the finalization queue to prevent finalization code for it from executing a second time.
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (this._disposed) return; // if already disposed, just return
|
||||
|
||||
if (disposing) // only dispose managed resources if we're called from directly or in-directly from user code.
|
||||
{
|
||||
this._logStream.Close();
|
||||
this._logStream.Dispose();
|
||||
this._fileStream.Close();
|
||||
this._fileStream.Dispose();
|
||||
}
|
||||
|
||||
this._logStream = null;
|
||||
this._fileStream = null;
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
~FileTarget() { Dispose(false); } // finalizer called by the runtime. we should only dispose unmanaged objects and should NOT reference managed ones.
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
75
src/DiIiS-NA/Core/Logging/LogConfig.cs
Normal file
75
src/DiIiS-NA/Core/Logging/LogConfig.cs
Normal file
@ -0,0 +1,75 @@
|
||||
//Blizzless Project 2022
|
||||
namespace DiIiS_NA.Core.Logging
|
||||
{
|
||||
public sealed class LogConfig : Config.Config
|
||||
{
|
||||
public string LoggingRoot
|
||||
{
|
||||
get { return this.GetString("Root", @"logs"); }
|
||||
set { this.Set("Root", value); }
|
||||
}
|
||||
|
||||
public LogTargetConfig[] Targets = new[]
|
||||
{
|
||||
new LogTargetConfig("ConsoleLog"),
|
||||
//new LogTargetConfig("ServerLog"),
|
||||
//new LogTargetConfig("ChatLog"),
|
||||
//new LogTargetConfig("RenameAccountLog"),
|
||||
//new LogTargetConfig("PacketLog")
|
||||
};
|
||||
|
||||
private LogConfig() :
|
||||
base("Logging")
|
||||
{ }
|
||||
|
||||
public static LogConfig Instance { get { return _instance; } }
|
||||
private static readonly LogConfig _instance = new LogConfig();
|
||||
}
|
||||
public class LogTargetConfig : Config.Config
|
||||
{
|
||||
public bool Enabled
|
||||
{
|
||||
get { return this.GetBoolean("Enabled", true); }
|
||||
set { this.Set("Enabled", value); }
|
||||
}
|
||||
|
||||
public string Target
|
||||
{
|
||||
get { return this.GetString("Target", "Console"); }
|
||||
set { this.GetString("Target", value); }
|
||||
}
|
||||
|
||||
public bool IncludeTimeStamps
|
||||
{
|
||||
get { return this.GetBoolean("IncludeTimeStamps", false); }
|
||||
set { this.Set("IncludeTimeStamps", value); }
|
||||
}
|
||||
|
||||
public string FileName
|
||||
{
|
||||
get { return this.GetString("FileName", ""); }
|
||||
set { this.GetString("FileName", value); }
|
||||
}
|
||||
|
||||
public Logger.Level MinimumLevel
|
||||
{
|
||||
get { return (Logger.Level)(this.GetInt("MinimumLevel", (int)Logger.Level.Info, true)); }
|
||||
set { this.Set("MinimumLevel", (int)value); }
|
||||
}
|
||||
|
||||
public Logger.Level MaximumLevel
|
||||
{
|
||||
get { return (Logger.Level)(this.GetInt("MaximumLevel", (int)Logger.Level.Fatal, true)); }
|
||||
set { this.Set("MaximumLevel", (int)value); }
|
||||
}
|
||||
|
||||
public bool ResetOnStartup
|
||||
{
|
||||
get { return this.GetBoolean("ResetOnStartup", false); }
|
||||
set { this.Set("ResetOnStartup", value); }
|
||||
}
|
||||
|
||||
public LogTargetConfig(string loggerName)
|
||||
: base(loggerName) { }
|
||||
}
|
||||
}
|
||||
68
src/DiIiS-NA/Core/Logging/LogManager.cs
Normal file
68
src/DiIiS-NA/Core/Logging/LogManager.cs
Normal file
@ -0,0 +1,68 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace DiIiS_NA.Core.Logging
|
||||
{
|
||||
public static class LogManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Is logging enabled?
|
||||
/// </summary>
|
||||
public static bool Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Available & configured log targets.
|
||||
/// </summary>
|
||||
internal readonly static List<LogTarget> Targets = new List<LogTarget>();
|
||||
|
||||
/// <summary>
|
||||
/// Available loggers.
|
||||
/// </summary>
|
||||
internal static readonly Dictionary<string, Logger> Loggers = new Dictionary<string, Logger>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates and returns a logger named with declaring type.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Logger"/> instance.</returns>
|
||||
public static Logger CreateLogger()
|
||||
{
|
||||
var frame = new StackFrame(1, false); // read stack frame.
|
||||
var name = frame.GetMethod().DeclaringType.Name; // get declaring type's name.
|
||||
|
||||
if (name == null) // see if we got a name.
|
||||
throw new Exception("Error getting full name for declaring type.");
|
||||
|
||||
if (!Loggers.ContainsKey(name)) // see if we already have instance for the given name.
|
||||
Loggers.Add(name, new Logger(name)); // add it to dictionary of loggers.
|
||||
|
||||
return Loggers[name]; // return the newly created logger.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and returns a logger with given name.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns>A <see cref="Logger"/> instance.</returns>
|
||||
public static Logger CreateLogger(string name)
|
||||
{
|
||||
if (!Loggers.ContainsKey(name)) // see if we already have instance for the given name.
|
||||
Loggers.Add(name, new Logger(name)); // add it to dictionary of loggers.
|
||||
|
||||
return Loggers[name]; // return the newly created logger.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attachs a new log target.
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
public static void AttachLogTarget(LogTarget target)
|
||||
{
|
||||
Targets.Add(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/DiIiS-NA/Core/Logging/LogRouter.cs
Normal file
53
src/DiIiS-NA/Core/Logging/LogRouter.cs
Normal file
@ -0,0 +1,53 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
|
||||
namespace DiIiS_NA.Core.Logging
|
||||
{
|
||||
internal static class LogRouter
|
||||
{
|
||||
/// <param name="level">Log level.</param>
|
||||
/// <param name="logger">Source of the log message.</param>
|
||||
/// <param name="message">Log message.</param>
|
||||
public static void RouteMessage(Logger.Level level, string logger, string message)
|
||||
{
|
||||
if (!LogManager.Enabled) // if we logging is not enabled,
|
||||
{ }// return; // just skip.
|
||||
|
||||
if (LogManager.Targets.Count == 0) // if we don't have any active log-targets,
|
||||
{
|
||||
var t = new FileTarget(@"log.txt", Logger.Level.PacketDump,
|
||||
Logger.Level.PacketDump, false, true);
|
||||
LogManager.Targets.Add(t);
|
||||
}// return; // just skip
|
||||
|
||||
|
||||
foreach (var target in LogManager.Targets.Where(target => level >= target.MinimumLevel && level <= target.MaximumLevel))
|
||||
{
|
||||
target.LogMessage(level, logger, message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <param name="level">Log level.</param>
|
||||
/// <param name="logger">Source of the log message.</param>
|
||||
/// <param name="message">Log message.</param>
|
||||
/// <param name="exception">Exception to be included with log message.</param>
|
||||
public static void RouteException(Logger.Level level, string logger, string message, Exception exception)
|
||||
{
|
||||
if (!LogManager.Enabled) // if we logging is not enabled,
|
||||
return; // just skip.
|
||||
|
||||
if (LogManager.Targets.Count == 0) // if we don't have any active log-targets,
|
||||
return; // just skip
|
||||
|
||||
// loop through all available logs targets and route the messages that meet the filters.
|
||||
foreach (var target in LogManager.Targets.Where(target => level >= target.MinimumLevel && level <= target.MaximumLevel))
|
||||
{
|
||||
target.LogException(level, logger, message, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/DiIiS-NA/Core/Logging/LogTarget.cs
Normal file
23
src/DiIiS-NA/Core/Logging/LogTarget.cs
Normal file
@ -0,0 +1,23 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
|
||||
namespace DiIiS_NA.Core.Logging
|
||||
{
|
||||
public class LogTarget
|
||||
{
|
||||
public Logger.Level MinimumLevel { get; protected set; }
|
||||
public Logger.Level MaximumLevel { get; protected set; }
|
||||
public bool IncludeTimeStamps { get; protected set; }
|
||||
|
||||
public virtual void LogMessage(Logger.Level level, string logger, string message)
|
||||
{
|
||||
throw new NotSupportedException("Vanilla log-targets are not supported! Instead use a log-target implementation.");
|
||||
}
|
||||
|
||||
public virtual void LogException(Logger.Level level, string logger, string message, Exception exception)
|
||||
{
|
||||
throw new NotSupportedException("Vanilla log-targets are not supported! Instead use a log-target implementation.");
|
||||
}
|
||||
}
|
||||
}
|
||||
268
src/DiIiS-NA/Core/Logging/Logger.cs
Normal file
268
src/DiIiS-NA/Core/Logging/Logger.cs
Normal file
@ -0,0 +1,268 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Globalization;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Extensions;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.GameServer.MessageSystem;
|
||||
|
||||
namespace DiIiS_NA.Core.Logging
|
||||
{
|
||||
public class Logger
|
||||
{
|
||||
public string Name { get; protected set; }
|
||||
|
||||
/// <param name="name">Name of the logger.</param>
|
||||
public Logger(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public enum Level
|
||||
{
|
||||
RenameAccountLog,
|
||||
ChatMessage,
|
||||
BotCommand,
|
||||
Trace,
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
Fatal,
|
||||
PacketDump,
|
||||
}
|
||||
|
||||
#region message loggers
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
public void ChatMessage(string message) { Log(Level.ChatMessage, message, null); }
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void ChatMessage(string message, params object[] args) { Log(Level.ChatMessage, message, args); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
public void BotCommand(string message) { Log(Level.BotCommand, message, null); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void BotCommand(string message, params object[] args) { Log(Level.BotCommand, message, args); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
public void RenameAccount(string message) { Log(Level.RenameAccountLog, message, null); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void RenameAccount(string message, params object[] args) { Log(Level.RenameAccountLog, message, args); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
public void Trace(string message) { Log(Level.Trace, message, null); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void Trace(string message, params object[] args) { Log(Level.Trace, message, args); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
public void Debug(string message) { Log(Level.Debug, message, null); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void Debug(string message, params object[] args) { Log(Level.Debug, message, args); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
public void Info(string message) { Log(Level.Info, message, null); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void Info(string message, params object[] args) { Log(Level.Info, message, args); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
public void Warn(string message) { Log(Level.Warn, message, null); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void Warn(string message, params object[] args) { Log(Level.Warn, message, args); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
public void Error(string message) { Log(Level.Error, message, null); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void Error(string message, params object[] args) { Log(Level.Error, message, args); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
public void Fatal(string message) { Log(Level.Fatal, message, null); }
|
||||
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void Fatal(string message, params object[] args) { Log(Level.Fatal, message, args); }
|
||||
|
||||
#endregion
|
||||
|
||||
#region message loggers with additional exception info included
|
||||
|
||||
/// <summary>
|
||||
/// Logs a trace message with an exception included.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to include in log line.</param>
|
||||
/// <param name="message">The log message.</param>
|
||||
public void TraceException(Exception exception, string message) { LogException(Level.Trace, message, null, exception); }
|
||||
|
||||
/// <summary>
|
||||
/// Logs a trace message with an exception included.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to include in log line.</param>
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void TraceException(Exception exception, string message, params object[] args) { LogException(Level.Trace, message, args, exception); }
|
||||
|
||||
/// <summary>
|
||||
/// Logs a debug message with an exception included.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to include in log line.</param>
|
||||
/// <param name="message">The log message.</param>
|
||||
public void DebugException(Exception exception, string message) { LogException(Level.Debug, message, null, exception); }
|
||||
|
||||
/// <summary>
|
||||
/// Logs a debug message with an exception included.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to include in log line.</param>
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void DebugException(Exception exception, string message, params object[] args) { LogException(Level.Debug, message, args, exception); }
|
||||
|
||||
/// <summary>
|
||||
/// Logs an info message with an exception included.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to include in log line.</param>
|
||||
/// <param name="message">The log message.</param>
|
||||
public void InfoException(Exception exception, string message) { LogException(Level.Info, message, null, exception); }
|
||||
|
||||
/// <summary>
|
||||
/// Logs an info message with an exception included.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to include in log line.</param>
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void InfoException(Exception exception, string message, params object[] args) { LogException(Level.Info, message, args, exception); }
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning message with an exception included.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to include in log line.</param>
|
||||
/// <param name="message">The log message.</param>
|
||||
public void WarnException(Exception exception, string message) { LogException(Level.Warn, message, null, exception); }
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning message with an exception included.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to include in log line.</param>
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void WarnException(Exception exception, string message, params object[] args) { LogException(Level.Warn, message, args, exception); }
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error message with an exception included.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to include in log line.</param>
|
||||
/// <param name="message">The log message.</param>
|
||||
public void ErrorException(Exception exception, string message) { LogException(Level.Error, message, null, exception); }
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error message with an exception included.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to include in log line.</param>
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void ErrorException(Exception exception, string message, params object[] args) { LogException(Level.Error, message, args, exception); }
|
||||
|
||||
/// <summary>
|
||||
/// Logs a fatal error message with an exception included.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to include in log line.</param>
|
||||
/// <param name="message">The log message.</param>
|
||||
public void FatalException(Exception exception, string message) { LogException(Level.Fatal, message, null, exception); }
|
||||
|
||||
/// <summary>
|
||||
/// Logs a fatal error message with an exception included.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to include in log line.</param>
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="args">Additional arguments.</param>
|
||||
public void FatalException(Exception exception, string message, params object[] args) { LogException(Level.Fatal, message, args, exception); }
|
||||
|
||||
#endregion
|
||||
|
||||
#region packet loggers
|
||||
|
||||
/// <summary>
|
||||
/// Logs an incoming moonet (protocol-buffer) packet.
|
||||
/// </summary>
|
||||
/// <param name="message">Incoming protocol-buffer packet.</param>
|
||||
/// <param name="header"><see cref="bgs.protocol.Header"/> header</param>
|
||||
public void LogIncomingPacket(Google.ProtocolBuffers.IMessage message, bgs.protocol.Header header)
|
||||
{
|
||||
Log(Level.PacketDump, ShortHeader(header) + "[I(M)] " + message.AsText(), null);
|
||||
}
|
||||
//*
|
||||
/// <summary>
|
||||
/// Logs an incoming game-server packet.
|
||||
/// </summary>
|
||||
/// <param name="message">Gameserver packet to log.</param>
|
||||
public void LogIncomingPacket(GameMessage message)
|
||||
{
|
||||
Log(Level.PacketDump, "[I(G)] " + message.AsText(), null);
|
||||
}
|
||||
//*/
|
||||
/// <summary>
|
||||
/// Logs an incoming moonet (protocol-buffer) packet.
|
||||
/// </summary>
|
||||
/// <param name="message">Outgoing protocol-buffer packet.</param>
|
||||
/// <param name="header"><see cref="bgs.protocol.Header"/> header</param>
|
||||
public void LogOutgoingPacket(Google.ProtocolBuffers.IMessage message, bgs.protocol.Header header)
|
||||
{
|
||||
Log(Level.PacketDump, ShortHeader(header) + "[O(M)] " + message.AsText(), null);
|
||||
}
|
||||
//*
|
||||
/// <summary>
|
||||
/// Logs an outgoing game-server packet.
|
||||
/// </summary>
|
||||
/// <param name="message">Gameserver packet to log.</param>
|
||||
public void LogOutgoingPacket(GameMessage message)
|
||||
{
|
||||
Log(Level.PacketDump, "[O(G)] " + message.AsText(), null);
|
||||
}
|
||||
//*/
|
||||
#endregion
|
||||
|
||||
|
||||
#region utility functions
|
||||
|
||||
private void Log(Level level, string message, object[] args) // sends logs to log-router.
|
||||
{
|
||||
LogRouter.RouteMessage(level, this.Name, args == null ? message : string.Format(CultureInfo.InvariantCulture, message, args));
|
||||
}
|
||||
|
||||
private void LogException(Level level, string message, object[] args, Exception exception) // sends logs to log-router.
|
||||
{
|
||||
LogRouter.RouteException(level, this.Name, args == null ? message : string.Format(CultureInfo.InvariantCulture, message, args), exception);
|
||||
}
|
||||
|
||||
private StringBuilder ShortHeader(bgs.protocol.Header header)
|
||||
{
|
||||
var result = new StringBuilder("service_id: " + header.ServiceId);
|
||||
result.Append(header.HasMethodId ? " method_id: " + header.MethodId.ToString() : "");
|
||||
result.Append(header.HasToken ? " token: " + header.Token.ToString() : "");
|
||||
result.Append(header.HasObjectId ? " object_id: " + header.ObjectId.ToString() : "");
|
||||
result.Append(header.HasSize ? " size: " + header.Size.ToString() : "");
|
||||
result.Append(header.HasStatus ? " status: " + header.Status.ToString() : "");
|
||||
result.AppendLine();
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
57
src/DiIiS-NA/Core/MPQ/Asset.cs
Normal file
57
src/DiIiS-NA/Core/MPQ/Asset.cs
Normal file
@ -0,0 +1,57 @@
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.GameServer.Core.Types.SNO;
|
||||
//Blizzless Project 2022
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
|
||||
namespace DiIiS_NA.Core.MPQ
|
||||
{
|
||||
public abstract class Asset
|
||||
{
|
||||
public SNOGroup Group { get; private set; }
|
||||
public Int32 SNOId { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public string FileName { get; protected set; }
|
||||
public Type Parser { get; set; }
|
||||
|
||||
protected FileFormat _data = null;
|
||||
protected static readonly Logger Logger = LogManager.CreateLogger("A");
|
||||
|
||||
public FileFormat Data
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_data == null && SourceAvailable && Parser != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
RunParser();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.FatalException(e, "Bad MPQ detected, failed parsing asset: {0}", this.FileName);
|
||||
}
|
||||
}
|
||||
return _data;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract bool SourceAvailable { get; }
|
||||
|
||||
|
||||
public Asset(SNOGroup group, Int32 snoId, string name)
|
||||
{
|
||||
this.FileName = group + "\\" + name + FileExtensions.Extensions[(int)group];
|
||||
this.Group = group;
|
||||
this.SNOId = snoId;
|
||||
this.Name = name;
|
||||
}
|
||||
|
||||
public abstract void RunParser();
|
||||
}
|
||||
}
|
||||
33
src/DiIiS-NA/Core/MPQ/DBAsset.cs
Normal file
33
src/DiIiS-NA/Core/MPQ/DBAsset.cs
Normal file
@ -0,0 +1,33 @@
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Storage;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.GameServer.Core.Types.SNO;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Globalization;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading;
|
||||
|
||||
namespace DiIiS_NA.Core.MPQ
|
||||
{
|
||||
public class DBAsset : Asset
|
||||
{
|
||||
|
||||
protected override bool SourceAvailable
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public DBAsset(SNOGroup group, Int32 snoId, string name)
|
||||
: base(group, snoId, name)
|
||||
{
|
||||
}
|
||||
|
||||
public override void RunParser()
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; // Use invariant culture so that we don't hit pitfalls in non en/US systems with different number formats.
|
||||
_data = (FileFormat)PersistenceManager.Load(Parser, SNOId.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
341
src/DiIiS-NA/Core/MPQ/Data.cs
Normal file
341
src/DiIiS-NA/Core/MPQ/Data.cs
Normal file
@ -0,0 +1,341 @@
|
||||
//Blizzless Project 2022
|
||||
using CrystalMpq;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.Core.Logging;
|
||||
//Blizzless Project 2022
|
||||
using DiIiS_NA.GameServer.Core.Types.SNO;
|
||||
//Blizzless Project 2022
|
||||
using Gibbed.IO;
|
||||
//Blizzless Project 2022
|
||||
using Microsoft.Data.Sqlite;
|
||||
//Blizzless Project 2022
|
||||
using System;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Concurrent;
|
||||
//Blizzless Project 2022
|
||||
using System.Collections.Generic;
|
||||
//Blizzless Project 2022
|
||||
using System.IO;
|
||||
//Blizzless Project 2022
|
||||
using System.Linq;
|
||||
//Blizzless Project 2022
|
||||
using System.Reflection;
|
||||
//Blizzless Project 2022
|
||||
using System.Text;
|
||||
//Blizzless Project 2022
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DiIiS_NA.Core.MPQ
|
||||
{
|
||||
public class Data : MPQPatchChain
|
||||
{
|
||||
public Dictionary<SNOGroup, ConcurrentDictionary<int, Asset>> Assets = new Dictionary<SNOGroup, ConcurrentDictionary<int, Asset>>();
|
||||
public readonly Dictionary<SNOGroup, Type> Parsers = new Dictionary<SNOGroup, Type>();
|
||||
private readonly List<Task> _tasks = new List<Task>();
|
||||
private static readonly SNOGroup[] PatchExceptions = new[] { SNOGroup.TimedEvent, SNOGroup.Script, SNOGroup.AiBehavior, SNOGroup.AiState, SNOGroup.Conductor, SNOGroup.FlagSet, SNOGroup.Code };
|
||||
#region Словари
|
||||
public static Dictionary<string, int> DictSNOAccolade = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOAct = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOActor = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOAdventure = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOAmbientSound = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOAnim = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOAnimation2D = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOAnimSet = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOBossEncounter = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOCondition = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOConversation = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOEffectGroup = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOEncounter = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOGameBalance = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOMarkerSet = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOMonster = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOMusic = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOObserver = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOLore = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOLevelArea = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOPower = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOPhysMesh = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNORopes = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOQuest = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOQuestRange = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNORecipe = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOScene = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOSkillKit = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOTutorial = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOWeathers = new Dictionary<string, int>();
|
||||
public static Dictionary<string, int> DictSNOWorlds = new Dictionary<string, int>();
|
||||
#endregion
|
||||
|
||||
|
||||
private static readonly Logger Logger = LogManager.CreateLogger("DataBaseWorker");
|
||||
|
||||
public Data()
|
||||
//: base(0, new List<string> { "CoreData.mpq", "ClientData.mpq" }, "/base/d3-update-base-(?<version>.*?).mpq")
|
||||
: base(0, new List<string> { "Core.mpq", "Core1.mpq", "Core2.mpq", "Core3.mpq", "Core4.mpq" }, "/base/d3-update-base-(?<version>.*?).mpq")
|
||||
{ }
|
||||
|
||||
public void Init()
|
||||
{
|
||||
Logger.Info("Loading Diablo III Assets..");
|
||||
DictSNOAccolade = Dicts.LoadAccolade();
|
||||
DictSNOAct = Dicts.LoadActs();
|
||||
DictSNOActor = Dicts.LoadActors();
|
||||
DictSNOAdventure = Dicts.LoadAdventure();
|
||||
DictSNOAmbientSound = Dicts.LoadAmbientSound();
|
||||
DictSNOAnim = Dicts.LoadAnim();
|
||||
DictSNOAnimation2D = Dicts.LoadAnimation2D();
|
||||
DictSNOAnimSet = Dicts.LoadAnimSet();
|
||||
DictSNOBossEncounter = Dicts.LoadBossEncounter();
|
||||
DictSNOCondition = Dicts.LoadCondition();
|
||||
DictSNOConversation = Dicts.LoadConversation();
|
||||
DictSNOEffectGroup = Dicts.LoadEffectGroup();
|
||||
DictSNOEncounter = Dicts.LoadEncounter();
|
||||
DictSNOGameBalance = Dicts.LoadGameBalance();
|
||||
DictSNOMarkerSet = Dicts.LoadMarkerSet();
|
||||
DictSNOMonster = Dicts.LoadMonster();
|
||||
DictSNOMusic = Dicts.LoadMusic();
|
||||
DictSNOObserver = Dicts.LoadObserver();
|
||||
DictSNOLore = Dicts.LoadLore();
|
||||
DictSNOLevelArea = Dicts.LoadLevelArea();
|
||||
DictSNOPower = Dicts.LoadPower();
|
||||
DictSNOPhysMesh = Dicts.LoadPhysMesh();
|
||||
DictSNOQuest = Dicts.LoadQuest();
|
||||
DictSNOQuestRange = Dicts.LoadQuestRange();
|
||||
DictSNORecipe = Dicts.LoadRecipe();
|
||||
DictSNORopes = Dicts.LoadRopes();
|
||||
DictSNOScene = Dicts.LoadScene();
|
||||
DictSNOSkillKit = Dicts.LoadSkillKit();
|
||||
DictSNOTutorial = Dicts.LoadTutorial();
|
||||
DictSNOWeathers = Dicts.LoadWeathers();
|
||||
DictSNOWorlds = Dicts.LoadWorlds();
|
||||
|
||||
this.InitCatalog();
|
||||
this.LoadCatalogs();
|
||||
}
|
||||
|
||||
private void InitCatalog()
|
||||
{
|
||||
foreach (SNOGroup group in Enum.GetValues(typeof(SNOGroup)))
|
||||
{
|
||||
this.Assets.Add(group, new ConcurrentDictionary<int, Asset>());
|
||||
}
|
||||
|
||||
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
|
||||
{
|
||||
if (!type.IsSubclassOf(typeof(FileFormat))) continue;
|
||||
var attributes = (FileFormatAttribute[])type.GetCustomAttributes(typeof(FileFormatAttribute), false);
|
||||
if (attributes.Length == 0) continue;
|
||||
|
||||
Parsers.Add(attributes[0].Group, type);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadCatalogs()
|
||||
{
|
||||
/*
|
||||
string[] MyFiles = Directory.GetFiles(@"E:\\Unpacked\\2.7.1\\Rope\\", @"*", SearchOption.AllDirectories);
|
||||
string writePath = @"E:\Unpacked\Rope.txt";
|
||||
int i = 0;
|
||||
//Blizzless Project 2022
|
||||
using (StreamWriter sw = new StreamWriter(writePath, false, System.Text.Encoding.Default))
|
||||
{
|
||||
|
||||
foreach (var file in MyFiles)
|
||||
{
|
||||
var splited = file.Split('\\');
|
||||
string name = splited[8].Split('.')[0];
|
||||
|
||||
if (name != "Axe Bad Data")
|
||||
{
|
||||
var asset = new MPQAsset(SNOGroup.Rope, i, name);
|
||||
asset.MpqFile = this.GetFile(asset.FileName, PatchExceptions.Contains(asset.Group));
|
||||
if (asset.MpqFile != null)
|
||||
this.ProcessAsset(asset);
|
||||
i++;
|
||||
|
||||
try
|
||||
{
|
||||
sw.WriteLine(@"('{0}', {1});", name, (asset.Data as FileFormats.Rope).Header.SNOId);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine("Ошибка ассета {0}", name);
|
||||
sw.WriteLine(@"('{0}', {1});", name, (asset.Data as FileFormats.Rope).Header.SNOId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//*/
|
||||
this.LoadSNODict(DictSNOAccolade, SNOGroup.Accolade);
|
||||
this.LoadSNODict(DictSNOAct, SNOGroup.Act);
|
||||
this.LoadSNODict(DictSNOActor, SNOGroup.Actor);
|
||||
this.LoadSNODict(DictSNOAdventure, SNOGroup.Adventure);
|
||||
this.LoadSNODict(DictSNOAmbientSound, SNOGroup.AmbientSound);
|
||||
this.LoadSNODict(DictSNOAnim, SNOGroup.Anim);
|
||||
this.LoadSNODict(DictSNOAnimation2D, SNOGroup.Animation2D);
|
||||
this.LoadSNODict(DictSNOAnimSet, SNOGroup.AnimSet);
|
||||
this.LoadSNODict(DictSNOBossEncounter, SNOGroup.BossEncounter);
|
||||
this.LoadSNODict(DictSNOConversation, SNOGroup.Conversation);
|
||||
this.LoadSNODict(DictSNOEffectGroup, SNOGroup.EffectGroup);
|
||||
this.LoadSNODict(DictSNOEncounter, SNOGroup.Encounter);
|
||||
this.LoadSNODict(DictSNOGameBalance, SNOGroup.GameBalance);
|
||||
this.LoadSNODict(DictSNOLevelArea, SNOGroup.LevelArea);
|
||||
this.LoadSNODict(DictSNOLore, SNOGroup.Lore);
|
||||
this.LoadSNODict(DictSNOMarkerSet, SNOGroup.MarkerSet);
|
||||
this.LoadSNODict(DictSNOMonster, SNOGroup.Monster);
|
||||
this.LoadSNODict(DictSNOMusic, SNOGroup.Music);
|
||||
this.LoadSNODict(DictSNOObserver, SNOGroup.Observer);
|
||||
this.LoadSNODict(DictSNOPhysMesh, SNOGroup.PhysMesh);
|
||||
this.LoadSNODict(DictSNOPower, SNOGroup.Power);
|
||||
this.LoadSNODict(DictSNOQuest, SNOGroup.Quest);
|
||||
this.LoadSNODict(DictSNOQuestRange, SNOGroup.QuestRange);
|
||||
this.LoadSNODict(DictSNORecipe, SNOGroup.Recipe);
|
||||
this.LoadSNODict(DictSNORopes, SNOGroup.Rope);
|
||||
this.LoadSNODict(DictSNOScene, SNOGroup.Scene);
|
||||
this.LoadSNODict(DictSNOSkillKit, SNOGroup.SkillKit);
|
||||
this.LoadSNODict(DictSNOTutorial, SNOGroup.Tutorial);
|
||||
this.LoadSNODict(DictSNOWeathers, SNOGroup.Weather);
|
||||
this.LoadSNODict(DictSNOWorlds, SNOGroup.Worlds);
|
||||
|
||||
this.LoadDBCatalog();
|
||||
}
|
||||
|
||||
private void LoadSNODict(Dictionary<string, int> DictSNO, SNOGroup group)
|
||||
{
|
||||
foreach (var point in DictSNO)
|
||||
{
|
||||
var asset = new MPQAsset(group, point.Value, point.Key);
|
||||
asset.MpqFile = this.GetFile(asset.FileName, PatchExceptions.Contains(asset.Group));
|
||||
if (asset.MpqFile != null)
|
||||
this.ProcessAsset(asset);
|
||||
}
|
||||
//Logger.Info("Loaded assets - {0}, Category - {1}", Assets[group].Count, group);
|
||||
|
||||
}
|
||||
|
||||
private void LoadCatalog(string fileName, bool useBaseMPQ = false, List<SNOGroup> groupsToLoad = null)
|
||||
{
|
||||
var catalogFile = this.GetFile(fileName, useBaseMPQ);
|
||||
this._tasks.Clear();
|
||||
|
||||
if (catalogFile == null)
|
||||
{
|
||||
Logger.Error("Couldn't load catalog file: {0}.", fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
var stream = catalogFile.Open();
|
||||
var assetsCount = stream.ReadValueS32();
|
||||
|
||||
var timerStart = DateTime.Now;
|
||||
|
||||
// read all assets from the catalog first and process them (ie. find the parser if any available).
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
stream.Position += 8;
|
||||
var group = (SNOGroup)stream.ReadValueS32();
|
||||
var snoId = stream.ReadValueS32();
|
||||
var name = stream.ReadString(128, true);
|
||||
if (group == SNOGroup.SkillKit)
|
||||
;
|
||||
if (groupsToLoad != null && !groupsToLoad.Contains(group)) // if we're handled groups to load, just ignore the ones not in the list.
|
||||
continue;
|
||||
|
||||
var asset = new MPQAsset(group, snoId, name);
|
||||
asset.MpqFile = this.GetFile(asset.FileName, PatchExceptions.Contains(asset.Group)); // get the file. note: if file is in any of the groups in PatchExceptions it'll from load the original version - the reason is that assets in those groups got patched to 0 bytes. /raist.
|
||||
|
||||
if (asset.MpqFile != null)
|
||||
this.ProcessAsset(asset); // process the asset.
|
||||
}
|
||||
|
||||
stream.Close();
|
||||
|
||||
// Run the parsers for assets (that have a parser).
|
||||
|
||||
if (this._tasks.Count > 0) // if we're running in tasked mode, run the parser tasks.
|
||||
{
|
||||
foreach (var task in this._tasks)
|
||||
{
|
||||
task.Start();
|
||||
}
|
||||
|
||||
Task.WaitAll(this._tasks.ToArray()); // Wait all tasks to finish.
|
||||
}
|
||||
|
||||
GC.Collect(); // force a garbage collection.
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
var elapsedTime = DateTime.Now - timerStart;
|
||||
|
||||
//if (Storage.Config.Instance.LazyLoading)
|
||||
Logger.Trace("Found a total of {0} assets from {1} catalog and postponed loading because lazy loading is activated.", assetsCount, fileName);
|
||||
//else
|
||||
// Logger.Trace("Found a total of {0} assets from {1} catalog and parsed {2} of them in {3:c}.", assetsCount, fileName, this._tasks.Count, elapsedTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the table of contents from the database. the database toc contains the sno ids of all objects
|
||||
/// that should / can no longer be loaded from mpq because it is zeroed out or because we need to edit
|
||||
/// some of the fields
|
||||
/// </summary>
|
||||
private void LoadDBCatalog()
|
||||
{
|
||||
int assetCount = 0;
|
||||
var timerStart = DateTime.Now;
|
||||
|
||||
//Blizzless Project 2022
|
||||
using (var cmd = new SqliteCommand("SELECT * FROM TOC", Storage.DBManager.MPQMirror))
|
||||
{
|
||||
var itemReader = cmd.ExecuteReader();
|
||||
|
||||
if (itemReader.HasRows)
|
||||
{
|
||||
while (itemReader.Read())
|
||||
{
|
||||
ProcessAsset(new DBAsset(
|
||||
(SNOGroup)Enum.Parse(typeof(SNOGroup), itemReader["SNOGroup"].ToString()),
|
||||
Convert.ToInt32(itemReader["SNOId"]),
|
||||
itemReader["Name"].ToString()));
|
||||
assetCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the asset to the dictionary and tries to parse it if a parser
|
||||
/// is found and lazy loading is deactivated
|
||||
/// </summary>
|
||||
/// <param name="asset">New asset to be processed</param>
|
||||
private void ProcessAsset(Asset asset)
|
||||
{
|
||||
this.Assets[asset.Group].TryAdd(asset.SNOId, asset);
|
||||
if (!this.Parsers.ContainsKey(asset.Group)) return;
|
||||
|
||||
asset.Parser = this.Parsers[asset.Group];
|
||||
this._tasks.Add(new Task(() => asset.RunParser()));
|
||||
}
|
||||
|
||||
private MpqFile GetFile(string fileName, bool startSearchingFromBaseMPQ = false)
|
||||
{
|
||||
MpqFile file = null;
|
||||
|
||||
if (!startSearchingFromBaseMPQ)
|
||||
file = this.FileSystem.FindFile(fileName);
|
||||
else
|
||||
{
|
||||
foreach (MpqArchive archive in this.FileSystem.Archives.Reverse()) //search mpqs starting from base
|
||||
{
|
||||
file = archive.FindFile(fileName);
|
||||
if (file != null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
user.block.title